summaryrefslogtreecommitdiffstats
path: root/doc/src/snippets/code/tools_patternist_qapplicationargumentparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/snippets/code/tools_patternist_qapplicationargumentparser.cpp')
0 files changed, 0 insertions, 0 deletions
- (PyClass_Check((x)) \ - ? PyString_AS_STRING(((PyClassObject*)(x))->cl_name) \ - : (char *)(((PyTypeObject*)(x))->tp_name)) +#define PyExceptionClass_Name(x) \ + ((char *)(((PyTypeObject*)(x))->tp_name)) -#define PyExceptionInstance_Class(x) \ - ((PyInstance_Check((x)) \ - ? (PyObject*)((PyInstanceObject*)(x))->in_class \ - : (PyObject*)((x)->ob_type))) +#define PyExceptionInstance_Class(x) ((PyObject*)((x)->ob_type)) /* Predefined exceptions */ diff --git a/Include/pythonrun.h b/Include/pythonrun.h index 1ecb3d7..52623ba 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -7,9 +7,8 @@ extern "C" { #endif -#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSIMPORT | \ - CO_FUTURE_WITH_STATEMENT) -#define PyCF_MASK_OBSOLETE (CO_NESTED) +#define PyCF_MASK 0 +#define PyCF_MASK_OBSOLETE 0 #define PyCF_SOURCE_IS_UTF8 0x0100 #define PyCF_DONT_IMPLY_DEDENT 0x0200 #define PyCF_ONLY_AST 0x0400 diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py index f499013..169520d 100644 --- a/Lib/copy_reg.py +++ b/Lib/copy_reg.py @@ -4,17 +4,12 @@ This is only useful to add pickle support for extension types defined in C, not for instances of user-defined classes. """ -from types import ClassType as _ClassType - __all__ = ["pickle", "constructor", "add_extension", "remove_extension", "clear_extension_cache"] dispatch_table = {} def pickle(ob_type, pickle_function, constructor_ob=None): - if type(ob_type) is _ClassType: - raise TypeError("copy_reg is not intended for use with classes") - if not callable(pickle_function): raise TypeError("reduction functions must be callable") dispatch_table[ob_type] = pickle_function diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 9bdbb16..dc603be 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -16,7 +16,7 @@ import re import string import sys -from errors import DistutilsPlatformError +from .errors import DistutilsPlatformError # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index 01463bc..ddaacb9 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -27,7 +27,8 @@ Written by Marc-Andre Lemburg (mal@lemburg.com). """#" -import codecs, types, aliases +import codecs, types +from . import aliases _cache = {} _unknown = '--unknown--' diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py index c192963..742267f 100644 --- a/Lib/test/test_opcodes.py +++ b/Lib/test/test_opcodes.py @@ -25,9 +25,9 @@ if n != 90: print '2.2 raise class exceptions' -class AClass: pass +class AClass(Exception): pass class BClass(AClass): pass -class CClass: pass +class CClass(Exception): pass class DClass(AClass): def __init__(self, ignore): pass @@ -58,8 +58,8 @@ except AClass, v: if v != b: raise TestFailed, "v!=b AClass" # not enough arguments -try: raise BClass, a -except TypeError: pass +##try: raise BClass, a +##except TypeError: pass try: raise DClass, a except DClass, v: diff --git a/Misc/NEWS b/Misc/NEWS index 1c142fb..111112b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -4,6245 +4,58 @@ Python News (editors: check NEWS.help for information about editing NEWS using ReST.) -What's New in Python 2.5 alpha 1? -================================= +What's New in Python 3000? +========================== -*Release date: XX-XXX-2006* +*Release date: XX-XXX-200X* -Core and builtins ------------------ - -- Patch #1434038: property() now uses the getter's docstring if there is - no "doc" argument given. This makes it possible to legitimately use - property() as a decorator to produce a read-only property. - -- PEP 357, patch 1436368: add an __index__ method to int/long and a matching - nb_index slot to the PyNumberMethods struct. The slot is consulted instead - of requiring an int or long in slicing and a few other contexts, enabling - other objects (e.g. Numeric Python's integers) to be used as slice indices. - -- Fixed various bugs reported by Coverity's Prevent tool. - -- PEP 352, patch #1104669: Make exceptions new-style objects. Introduced the - new exception base class, BaseException, which has a new message attribute. - KeyboardInterrupt and SystemExit to directly inherit from BaseException now. - Raising a string exception now raises a DeprecationWarning. - -- Patch #1438387, PEP 328: relative and absolute imports. Imports can now be - explicitly relative, using 'from .module import name' to mean 'from the same - package as this module is in. Imports without dots still default to the - old relative-then-absolute, unless 'from __future__ import - absolute_import' is used. - -- Properly check if 'warnings' raises an exception (usually when a filter set - to "error" is triggered) when raising a warning for raising string - exceptions. - -- CO_GENERATOR_ALLOWED is no longer defined, this behavior is the default. - The name was removed from Include/code.h. - -- PEP 308: conditional expressions were added (x if cond else y). - -- Patch 1433928: - - The copy module now "copies" function objects (as atomic objects). - - dict.__getitem__ now looks for a __missing__ hook before raising - KeyError. - -- PEP 343: with statement implemented. Needs ``from __future__ import - with_statement``. Use of 'with' as a variable will generate a warning. - Use of 'as' as a variable will also generate a warning (unless it's - part of an import statement). - The following objects have __context__ methods: - - The built-in file type. - - The thread.LockType type. - - The following types defined by the threading module: - Lock, RLock, Condition, Semaphore, BoundedSemaphore. - - The decimal.Context class. - -- Fix the encodings package codec search function to only search - inside its own package. Fixes problem reported in patch #1433198. - - Note: Codec packages should implement and register their own - codec search function. PEP 100 has the details. - -- PEP 353: Using ssize_t as the index type. - -- Patch #1400181, fix unicode string formatting to not use the locale. - This is how string objects work. u'%f' could use , instead of . - for the decimal point. Now both strings and unicode always use periods. - -- Bug #1244610, #1392915, fix build problem on OpenBSD 3.7 and 3.8. - configure would break checking curses.h. - -- Bug #959576: The pwd module is now builtin. This allows Python to be - built on UNIX platforms without $HOME set. - -- Bug #1072182, fix some potential problems if characters are signed. - -- Bug #889500, fix line number on SyntaxWarning for global declarations. - -- Bug #1378022, UTF-8 files with a leading BOM crashed the interpreter. - -- Support for converting hex strings to floats no longer works. - This was not portable. float('0x3') now raises a ValueError. - -- Patch #1382163: Expose Subversion revision number to Python. New C API - function Py_GetBuildNumber(). New attribute sys.subversion. Build number - is now displayed in interactive prompt banner. - -- Implementation of PEP 341 - Unification of try/except and try/finally. - "except" clauses can now be written together with a "finally" clause in - one try statement instead of two nested ones. Patch #1355913. - -- Bug #1379994: Builtin unicode_escape and raw_unicode_escape codec - now encodes backslash correctly. - -- Patch #1350409: Work around signal handling bug in Visual Studio 2005. - -- Bug #1281408: Py_BuildValue now works correct even with unsigned longs - and long longs. - -- SF Bug #1350188, "setdlopenflags" leads to crash upon "import" - It was possible dlerror() returns a NULL pointer, use a default error - message in this case. - -- Replaced most Unicode charmap codecs with new ones using the - new Unicode translate string feature in the builtin charmap - codec; the codecs were created from the mapping tables available - at ftp.unicode.org and contain a few updates (e.g. the Mac OS - encodings now include a mapping for the Apple logo) - -- Added a few more codecs for Mac OS encodings - -- Speed up some Unicode operations. - -- A new AST parser implementation was completed. The abstract - syntax tree is available for read-only (non-compile) access - to Python code; an _ast module was added. - -- SF bug #1167751: fix incorrect code being for generator expressions. - The following code now raises a SyntaxError: foo(a = i for i in range(10)) - -- SF Bug #976608: fix SystemError when mtime of an imported file is -1. - -- SF Bug #887946: fix segfault when redirecting stdin from a directory. - Provide a warning when a directory is passed on the command line. - -- Fix segfault with invalid coding. - -- SF bug #772896: unknown encoding results in MemoryError. - -- All iterators now have a Boolean value of true. Formerly, some iterators - supported a __len__() method which evaluated to False when the iterator - was empty. - -- On 64-bit platforms, when __len__() returns a value that cannot be - represented as a C int, raise OverflowError. - -- test__locale is skipped on OS X < 10.4 (only partial locale support is - present). - -- SF bug #893549: parsing keyword arguments was broken with a few format - codes. - -- Changes donated by Elemental Security to make it work on AIX 5.3 - with IBM's 64-bit compiler (SF patch #1284289). This also closes SF - bug #105470: test_pwd fails on 64bit system (Opteron). - -- Changes donated by Elemental Security to make it work on HP-UX 11 on - Itanium2 with HP's 64-bit compiler (SF patch #1225212). - -- Disallow keyword arguments for type constructors that don't use them - (fixes bug #1119418). - -- Forward UnicodeDecodeError into SyntaxError for source encoding errors. - -- SF bug #900092: When tracing (e.g. for hotshot), restore 'return' events for - exceptions that cause a function to exit. - -- The implementation of set() and frozenset() was revised to use its - own internal data structure. Memory consumption is reduced by 1/3 - and there are modest speed-ups as well. The API is unchanged. - -- SF bug #1238681: freed pointer is used in longobject.c:long_pow(). - -- SF bug #1229429: PyObject_CallMethod failed to decrement some - reference counts in some error exit cases. - -- SF bug #1185883: Python's small-object memory allocator took over - a block managed by the platform C library whenever a realloc specified - a small new size. However, there's no portable way to know then how - much of the address space following the pointer is valid, so no - portable way to copy data from the C-managed block into Python's - small-object space without risking a memory fault. Python's small-object - realloc now leaves such blocks under the control of the platform C - realloc. - -- SF bug #1232517: An overflow error was not detected properly when - attempting to convert a large float to an int in os.utime(). - -- SF bug #1224347: hex longs now print with lowercase letters just - like their int counterparts. - -- SF bug #1163563: the original fix for bug #1010677 ("thread Module - Breaks PyGILState_Ensure()") broke badly in the case of multiple - interpreter states; back out that fix and do a better job (see - http://mail.python.org/pipermail/python-dev/2005-June/054258.html - for a longer write-up of the problem). - -- SF patch #1180995: marshal now uses a binary format by default when - serializing floats. - -- SF patch #1181301: on platforms that appear to use IEEE 754 floats, - the routines that promise to produce IEEE 754 binary representations - of floats now simply copy bytes around. - -- bug #967182: disallow opening files with 'wU' or 'aU' as specified by PEP - 278. - -- patch #1109424: int, long, float, complex, and unicode now check for the - proper magic slot for type conversions when subclassed. Previously the - magic slot was ignored during conversion. Semantics now match the way - subclasses of str always behaved. int/long/float, conversion of an instance - to the base class has been moved to the proper nb_* magic slot and out of - PyNumber_*(). - Thanks Walter Dörwald. - -- Descriptors defined in C with a PyGetSetDef structure, where the setter is - NULL, now raise an AttributeError when attempting to set or delete the - attribute. Previously a TypeError was raised, but this was inconsistent - with the equivalent pure-Python implementation. - -- It is now safe to call PyGILState_Release() before - PyEval_InitThreads() (note that if there is reason to believe there - are multiple threads around you still must call PyEval_InitThreads() - before using the Python API; this fix is for extension modules that - have no way of knowing if Python is multi-threaded yet). - -- Typing Ctrl-C whilst raw_input() was waiting in a build with threads - disabled caused a crash. - -- Bug #1165306: instancemethod_new allowed the creation of a method - with im_class == im_self == NULL, which caused a crash when called. - -- Move exception finalisation later in the shutdown process - this - fixes the crash seen in bug #1165761 - -- Added two new builtins, any() and all(). - -- Defining a class with empty parentheses is now allowed - (e.g., ``class C(): pass`` is no longer a syntax error). - Patch #1176012 added support to the 'parser' module and 'compiler' package - (thanks to logistix for that added support). - -- Patch #1115086: Support PY_LONGLONG in structmember. - -- Bug #1155938: new style classes did not check that __init__() was - returning None. - -- Patch #802188: Report characters after line continuation character - ('\') with a specific error message. - -- Bug #723201: Raise a TypeError for passing bad objects to 'L' format. - -- Bug #1124295: the __name__ attribute of file objects was - inadvertently made inaccessible in restricted mode. - -- Bug #1074011: closing sys.std{out,err} now causes a flush() and - an ferror() call. - -- min() and max() now support key= arguments with the same meaning as in - list.sort(). - -- The peephole optimizer now performs simple constant folding in expressions: - (2+3) --> (5). - -- set and frozenset objects can now be marshalled. SF #1098985. - -- Bug #1077106: Poor argument checking could cause memory corruption - in calls to os.read(). - -- The parser did not complain about future statements in illegal - positions. It once again reports a syntax error if a future - statement occurs after anything other than a doc string. - -- Change the %s format specifier for str objects so that it returns a - unicode instance if the argument is not an instance of basestring and - calling __str__ on the argument returns a unicode instance. - -- Patch #1413181: changed ``PyThreadState_Delete()`` to forget about the - current thread state when the auto-GIL-state machinery knows about - it (since the thread state is being deleted, continuing to remember it - can't help, but can hurt if another thread happens to get created with - the same thread id). - -Extension Modules ------------------ - -- Bug #1448490: Fixed a bug that ISO-2022 codecs could not handle - SS2 (single-shift 2) escape sequences correctly. - -- The unicodedata module was updated to the 4.1 version of the Unicode - database. The 3.2 version is still available as unicodedata.db_3_2_0 - for applications that require this specific version (such as IDNA). - -- The timing module is no longer built by default. It was deprecated - in PEP 4 in Python 2.0 or earlier. - -- Patch 1433928: Added a new type, defaultdict, to the collections module. - This uses the new __missing__ hook behavior added to dict (see above). - -- Bug #854823: socketmodule now builds on Sun platforms even when - INET_ADDRSTRLEN is not defined. - -- Patch #1393157: os.startfile() now has an optional argument to specify - a "command verb" to invoke on the file. - -- Bug #876637, prevent stack corruption when socket descriptor - is larger than FD_SETSIZE. - -- Patch #1407135, bug #1424041: harmonize mmap behavior of anonymous memory. - mmap.mmap(-1, size) now returns anonymous memory in both Unix and Windows. - mmap.mmap(0, size) should not be used on Windows for anonymous memory. - -- Patch #1422385: The nis module now supports access to domains other - than the system default domain. - -- Use Win32 API to implement os.stat/fstat. As a result, subsecond timestamps - are reported, the limit on path name lengths is removed, and stat reports - WindowsError now (instead of OSError). - -- Add bsddb.db.DBEnv.set_tx_timestamp allowing time based database recovery. - -- Bug #1413192, fix seg fault in bsddb if a transaction was deleted - before the env. - -- Patch #1103116: Basic AF_NETLINK support. - -- Bug #1402308, (possible) segfault when using mmap.mmap(-1, ...) - -- Bug #1400822, _curses over{lay,write} doesn't work when passing 6 ints. - Also fix ungetmouse() which did not accept arguments properly. - The code now conforms to the documented signature. - -- Bug #1400115, Fix segfault when calling curses.panel.userptr() - without prior setting of the userptr. - -- Fix 64-bit problems in bsddb. - -- Patch #1365916: fix some unsafe 64-bit mmap methods. - -- Bug #1290333: Added a workaround for cjkcodecs' _codecs_cn build - problem on AIX. - -- Bug #869197: os.setgroups rejects long integer arguments - -- Bug #1346533, select.poll() doesn't raise an error if timeout > sys.maxint - -- Bug #1344508, Fix UNIX mmap leaking file descriptors - -- Patch #1338314, Bug #1336623: fix tarfile so it can extract - REGTYPE directories from tarfiles written by old programs. - -- Patch #1407992, fixes broken bsddb module db associate when using - BerkeleyDB 3.3, 4.0 or 4.1. - -- Get bsddb module to build with BerkeleyDB version 4.4 - -- Get bsddb module to build with BerkeleyDB version 3.2 - -- Patch #1309009, Fix segfault in pyexpat when the XML document is in latin_1, - but Python incorrectly assumes it is in UTF-8 format - -- Fix parse errors in the readline module when compiling without threads. - -- Patch #1288833: Removed thread lock from socket.getaddrinfo on - FreeBSD 5.3 and later versions which got thread-safe getaddrinfo(3). - -- Patches #1298449 and #1298499: Add some missing checks for error - returns in cStringIO.c. - -- Patch #1297028: fix segfault if call type on MultibyteCodec, - MultibyteStreamReader, or MultibyteStreamWriter - -- Fix memory leak in posix.access(). - -- Patch #1213831: Fix typo in unicodedata._getcode. - -- Bug #1007046: os.startfile() did not accept unicode strings encoded in - the file system encoding. - -- Patch #756021: Special-case socket.inet_aton('255.255.255.255') for - platforms that don't have inet_aton(). - -- Bug #1215928: Fix bz2.BZ2File.seek() for 64-bit file offsets. - -- Bug #1191043: Fix bz2.BZ2File.(x)readlines for files containing one - line without newlines. - -- Bug #728515: mmap.resize() now resizes the file on Unix as it did - on Windows. - -- Patch #1180695: Add nanosecond stat resolution, and st_gen, - st_birthtime for FreeBSD. - -- Patch #1231069: The fcntl.ioctl function now uses the 'I' code for - the request code argument, which results in more C-like behaviour - for large or negative values. - -- Bug #1234979: For the argument of thread.Lock.acquire, the Windows - implementation treated all integer values except 1 as false. - -- Bug #1194181: bz2.BZ2File didn't handle mode 'U' correctly. - -- Patch #1212117: os.stat().st_flags is now accessible as a attribute - if available on the platform. - -- Patch #1103951: Expose O_SHLOCK and O_EXLOCK in the posix module if - available on the platform. - -- Bug #1166660: The readline module could segfault if hook functions - were set in a different thread than that which called readline. - -- collections.deque objects now support a remove() method. - -- operator.itemgetter() and operator.attrgetter() now support retrieving - multiple fields. This provides direct support for sorting on multiple - keys (primary, secondary, etc). - -- os.access now supports Unicode path names on non-Win32 systems. - -- Patches #925152, #1118602: Avoid reading after the end of the buffer - in pyexpat.GetInputContext. - -- Patches #749830, #1144555: allow UNIX mmap size to default to current - file size. - -- Added functional.partial(). See PEP309. - -- Patch #1093585: raise a ValueError for negative history items in readline. - {remove_history,replace_history} - -- The spwd module has been added, allowing access to the shadow password - database. - -- stat_float_times is now True. - -- array.array objects are now picklable. - -- the cPickle module no longer accepts the deprecated None option in the - args tuple returned by __reduce__(). - -- itertools.islice() now accepts None for the start and step arguments. - This allows islice() to work more readily with slices: - islice(s.start, s.stop, s.step) - -- datetime.datetime() now has a strptime class method which can be used to - create datetime object using a string and format. - -Library -------- - -- A regrtest option -w was added to re-run failed tests in verbose mode. - -- Patch #1446372: quit and exit can now be called from the interactive - interpreter to exit. - -- The function get_count() has been added to the gc module, and gc.collect() - grew an optional 'generation' argument. - -- A library msilib to generate Windows Installer files, and a distutils - command bdist_msi have been added. - -- PEP 343: new module contextlib.py defines decorator @contextmanager - and helpful context managers nested() and closing(). - -- The compiler package now supports future imports after the module docstring. - -- Bug #1413790: zipfile now sanitizes absolute archive names that are - not allowed by the specs. - -- Patch #1215184: FileInput now can be given an opening hook which can - be used to control how files are opened. - -- Patch #1212287: fileinput.input() now has a mode parameter for - specifying the file mode input files should be opened with. - -- Patch #1215184: fileinput now has a fileno() function for getting the - current file number. - -- Patch #1349274: gettext.install() now optionally installs additional - translation functions other than _() in the builtin namespace. - -- Patch #1337756: fileinput now accepts Unicode filenames. - -- Patch #1373643: The chunk module can now read chunks larger than - two gigabytes. - -- Patch #1417555: SimpleHTTPServer now returns Last-Modified headers. - -- Bug #1430298: It is now possible to send a mail with an empty - return address using smtplib. - -- Bug #1432260: The names of lambda functions are now properly displayed - in pydoc. - -- Patch #1412872: zipfile now sets the creator system to 3 (Unix) - unless the system is Win32. - -- Patch #1349118: urllib now supports user:pass@ style proxy - specifications, raises IOErrors when proxies for unsupported protocols - are defined, and uses the https proxy on https redirections. - -- Bug #902075: urllib2 now supports 'host:port' style proxy specifications. - -- Bug #1407902: Add support for sftp:// URIs to urlparse. - -- Bug #1371247: Update Windows locale identifiers in locale.py. - -- Bug #1394565: SimpleHTTPServer now doesn't choke on query parameters - any more. - -- Bug #1403410: The warnings module now doesn't get confused - when it can't find out the module name it generates a warning for. - -- Patch #1177307: Added a new codec utf_8_sig for UTF-8 with a BOM signature. - -- Patch #1157027: cookielib mishandles RFC 2109 cookies in Netscape mode - -- Patch #1117398: cookielib.LWPCookieJar and .MozillaCookieJar now raise - LoadError as documented, instead of IOError. For compatibility, - LoadError subclasses IOError. - -- Added the hashlib module. It provides secure hash functions for MD5 and - SHA1, 224, 256, 384, and 512. Note that recent developments make the - historic MD5 and SHA1 unsuitable for cryptographic-strength applications. - In - Ronald L. Rivest offered this advice for Python: - - "The consensus of researchers in this area (at least as - expressed at the NIST Hash Function Workshop 10/31/05), - is that SHA-256 is a good choice for the time being, but - that research should continue, and other alternatives may - arise from this research. The larger SHA's also seem OK." - -- Added a subset of Fredrik Lundh's ElementTree package. Available - modules are xml.etree.ElementTree, xml.etree.ElementPath, and - xml.etree.ElementInclude, from ElementTree 1.2.6. - -- Patch #1162825: Support non-ASCII characters in IDLE window titles. - -- Bug #1365984: urllib now opens "data:" URLs again. - -- Patch #1314396: prevent deadlock for threading.Thread.join() when an exception - is raised within the method itself on a previous call (e.g., passing in an - illegal argument) - -- Bug #1340337: change time.strptime() to always return ValueError when there - is an error in the format string. - -- Patch #754022: Greatly enhanced webbrowser.py (by Oleg Broytmann). - -- Bug #729103: pydoc.py: Fix docother() method to accept additional - "parent" argument. - -- Patch #1300515: xdrlib.py: Fix pack_fstring() to really use null bytes - for padding. - -- Bug #1296004: httplib.py: Limit maximal amount of data read from the - socket to avoid a MemoryError on Windows. - -- Patch #1166948: locale.py: Prefer LC_ALL, LC_CTYPE and LANG over LANGUAGE - to get the correct encoding. - -- Patch #1166938: locale.py: Parse LANGUAGE as a colon separated list of - languages. - -- Patch #1268314: Cache lines in StreamReader.readlines for performance. - -- Bug #1290505: Fix clearing the regex cache for time.strptime(). - -- Bug #1167128: Fix size of a symlink in a tarfile to be 0. - -- Patch #810023: Fix off-by-one bug in urllib.urlretrieve reporthook - functionality. - -- Bug #1163178: Make IDNA return an empty string when the input is empty. - -- Patch #848017: Make Cookie more RFC-compliant. Use CRLF as default output - separator and do not output trailing semicola. - -- Patch #1062060: urllib.urlretrieve() now raises a new exception, named - ContentTooShortException, when the actually downloaded size does not - match the Content-Length header. - -- Bug #1121494: distutils.dir_utils.mkpath now accepts Unicode strings. - -- Bug #1178484: Return complete lines from codec stream readers - even if there is an exception in later lines, resulting in - correct line numbers for decoding errors in source code. - -- Bug #1192315: Disallow negative arguments to clear() in pdb. - -- Patch #827386: Support absolute source paths in msvccompiler.py. - -- Patch #1105730: Apply the new implementation of commonprefix in posixpath - to ntpath, macpath, os2emxpath and riscospath. - -- Fix a problem in Tkinter introduced by SF patch #869468: delete bogus - __hasattr__ and __delattr__ methods on class Tk that were breaking - Tkdnd. - -- Bug #1015140: disambiguated the term "article id" in nntplib docs and - docstrings to either "article number" or "message id". - -- Bug #1238170: threading.Thread.__init__ no longer has "kwargs={}" as a - parameter, but uses the usual "kwargs=None". - -- textwrap now processes text chunks at O(n) speed instead of O(n**2). - Patch #1209527 (Contributed by Connelly). - -- urllib2 has now an attribute 'httpresponses' mapping from HTTP status code - to W3C name (404 -> 'Not Found'). RFE #1216944. - -- Bug #1177468: Don't cache the /dev/urandom file descriptor for os.urandom, - as this can cause problems with apps closing all file descriptors. - -- Bug #839151: Fix an attempt to access sys.argv in the warnings module - it can be missing in embedded interpreters - -- Bug #1155638: Fix a bug which affected HTTP 0.9 responses in httplib. - -- Bug #1100201: Cross-site scripting was possible on BaseHTTPServer via - error messages. - -- Bug #1108948: Cookie.py produced invalid JavaScript code. - -- The tokenize module now detects and reports indentation errors. - Bug #1224621. - -- The tokenize module has a new untokenize() function to support a full - roundtrip from lexed tokens back to Python sourcecode. In addition, - the generate_tokens() function now accepts a callable argument that - terminates by raising StopIteration. - -- Bug #1196315: fix weakref.WeakValueDictionary constructor. - -- Bug #1213894: os.path.realpath didn't resolve symlinks that were the first - component of the path. - -- Patch #1120353: The xmlrpclib module provides better, more transparent, - support for datetime.{datetime,date,time} objects. With use_datetime set - to True, applications shouldn't have to fiddle with the DateTime wrapper - class at all. - -- distutils.commands.upload was added to support uploading distribution - files to PyPI. - -- distutils.commands.register now encodes the data as UTF-8 before posting - them to PyPI. - -- decimal operator and comparison methods now return NotImplemented - instead of raising a TypeError when interacting with other types. This - allows other classes to implement __radd__ style methods and have them - work as expected. - -- Bug #1163325: Decimal infinities failed to hash. Attempting to - hash a NaN raised an InvalidOperation instead of a TypeError. - -- Patch #918101: Add tarfile open mode r|* for auto-detection of the - stream compression; add, for symmetry reasons, r:* as a synonym of r. - -- Patch #1043890: Add extractall method to tarfile. - -- Patch #1075887: Don't require MSVC in distutils if there is nothing - to build. - -- Patch #1103407: Properly deal with tarfile iterators when untarring - symbolic links on Windows. - -- Patch #645894: Use getrusage for computing the time consumption in - profile.py if available. - -- Patch #1046831: Use get_python_version where appropriate in sysconfig.py. - -- Patch #1117454: Remove code to special-case cookies without values - in LWPCookieJar. - -- Patch #1117339: Add cookielib special name tests. - -- Patch #1112812: Make bsddb/__init__.py more friendly for modulefinder. - -- Patch #1110248: SYNC_FLUSH the zlib buffer for GZipFile.flush. - -- Patch #1107973: Allow to iterate over the lines of a tarfile.ExFileObject. - -- Patch #1104111: Alter setup.py --help and --help-commands. - -- Patch #1121234: Properly cleanup _exit and tkerror commands. - -- Patch #1049151: xdrlib now unpacks booleans as True or False. - -- Fixed bug in a NameError bug in cookielib. Patch #1116583. - -- Applied a security fix to SimpleXMLRPCserver (PSF-2005-001). This - disables recursive traversal through instance attributes, which can - be exploited in various ways. - -- Bug #1222790: in SimpleXMLRPCServer, set the reuse-address and close-on-exec - flags on the HTTP listening socket. - -- Bug #792570: SimpleXMLRPCServer had problems if the request grew too large. - Fixed by reading the HTTP body in chunks instead of one big socket.read(). - -- Patches #893642, #1039083: add allow_none, encoding arguments to constructors of - SimpleXMLRPCServer and CGIXMLRPCRequestHandler. - -- Bug #1110478: Revert os.environ.update to do putenv again. - -- Bug #1103844: fix distutils.install.dump_dirs() with negated options. - -- os.{SEEK_SET, SEEK_CUR, SEEK_END} have been added for convenience. - -- Enhancements to the csv module: - - + Dialects are now validated by the underlying C code, better - reflecting its capabilities, and improving its compliance with - PEP 305. - + Dialect parameter parsing has been re-implemented to improve error - reporting. - + quotechar=None and quoting=QUOTE_NONE now work the way PEP 305 - dictates. - + the parser now removes the escapechar prefix from escaped characters. - + when quoting=QUOTE_NONNUMERIC, the writer now tests for numeric - types, rather than any object than can be represented as a numeric. - + when quoting=QUOTE_NONNUMERIC, the reader now casts unquoted fields - to floats. - + reader now allows \r characters to be quoted (previously it only allowed - \n to be quoted). - + writer doublequote handling improved. - + Dialect classes passed to the module are no longer instantiated by - the module before being parsed (the former validation scheme required - this, but the mechanism was unreliable). - + The dialect registry now contains instances of the internal - C-coded dialect type, rather than references to python objects. - + the internal c-coded dialect type is now immutable. - + register_dialect now accepts the same keyword dialect specifications - as the reader and writer, allowing the user to register dialects - without first creating a dialect class. - + a configurable limit to the size of parsed fields has been added - - previously, an unmatched quote character could result in the entire - file being read into the field buffer before an error was reported. - + A new module method csv.field_size_limit() has been added that sets - the parser field size limit (returning the former limit). The initial - limit is 128kB. - + A line_num attribute has been added to the reader object, which tracks - the number of lines read from the source iterator. This is not - the same as the number of records returned, as records can span - multiple lines. - + reader and writer objects were not being registered with the cyclic-GC. - This has been fixed. - -- _DummyThread objects in the threading module now delete self.__block that is - inherited from _Thread since it uses up a lock allocated by 'thread'. The - lock primitives tend to be limited in number and thus should not be wasted on - a _DummyThread object. Fixes bug #1089632. - -- The imghdr module now detects Exif files. - -- StringIO.truncate() now correctly adjusts the size attribute. - (Bug #951915). - -- locale.py now uses an updated locale alias table (built using - Tools/i18n/makelocalealias.py, a tool to parse the X11 locale - alias file); the encoding lookup was enhanced to use Python's - encoding alias table. - -- moved deprecated modules to Lib/lib-old: whrandom, tzparse, statcache. - -- the pickle module no longer accepts the deprecated None option in the - args tuple returned by __reduce__(). - -- optparse now optionally imports gettext. This allows its use in setup.py. - -- the pickle module no longer uses the deprecated bin parameter. - -- the shelve module no longer uses the deprecated binary parameter. - -- the pstats module no longer uses the deprecated ignore() method. - -- the filecmp module no longer uses the deprecated use_statcache argument. - -- unittest.TestCase.run() and unittest.TestSuite.run() can now be successfully - extended or overridden by subclasses. Formerly, the subclassed method would - be ignored by the rest of the module. (Bug #1078905). - -- heapq.nsmallest() and heapq.nlargest() now support key= arguments with - the same meaning as in list.sort(). - -- Bug #1076985: ``codecs.StreamReader.readline()`` now calls ``read()`` only - once when a size argument is given. This prevents a buffer overflow in the - tokenizer with very long source lines. - -- Bug #1083110: ``zlib.decompress.flush()`` would segfault if called - immediately after creating the object, without any intervening - ``.decompress()`` calls. - -- The reconvert.quote function can now emit triple-quoted strings. The - reconvert module now has some simple documentation. - -- ``UserString.MutableString`` now supports negative indices in - ``__setitem__`` and ``__delitem__`` - -- Bug #1149508: ``textwrap`` now handles hyphenated numbers (eg. "2004-03-05") - correctly. - -- Partial fixes for SF bugs #1163244 and #1175396: If a chunk read by - ``codecs.StreamReader.readline()`` has a trailing "\r", read one more - character even if the user has passed a size parameter to get a proper - line ending. Remove the special handling of a "\r\n" that has been split - between two lines. - -- Bug #1251300: On UCS-4 builds the "unicode-internal" codec will now complain - about illegal code points. The codec now supports PEP 293 style error - handlers. - -- Bug #1235646: ``codecs.StreamRecoder.next()`` now reencodes the data it reads - from the input stream, so that the output is a byte string in the correct - encoding instead of a unicode string. - -- Bug #1202493: Fixing SRE parser to handle '{}' as perl does, rather than - considering it exactly like a '*'. - -- Bug #1245379: Add "unicode-1-1-utf-7" as an alias for "utf-7" to - ``encodings.aliases``. - -- ` uu.encode()`` and ``uu.decode()`` now support unicode filenames. - -- Patch #1413711: Certain patterns of differences were making difflib - touch the recursion limit. - -Build ------ - -- Patch #1432345: Make python compile on DragonFly. - -- Build support for Win64-AMD64 was added. - -- Patch #1428494: Prefer linking against ncursesw over ncurses library. - -- Patch #881820: look for openpty and forkpty also in libbsd. - -- The sources of zlib are now part of the Python distribution (zlib 1.2.3). - The zlib module is now builtin on Windows. - -- Use -xcode=pic32 for CCSHARED on Solaris with SunPro. - -- Bug #1189330: configure did not correctly determine the necessary - value of LINKCC if python was built with GCC 4.0. - -- Upgrade Windows build to zlib 1.2.3 which eliminates a potential security - vulnerability in zlib 1.2.1 and 1.2.2. - -- EXTRA_CFLAGS has been introduced as an environment variable to hold compiler - flags that change binary compatibility. Changes were also made to - distutils.sysconfig to also use the environment variable when used during - compilation of the interpreter and of C extensions through distutils. - -- SF patch 1171735: Darwin 8's headers are anal about POSIX compliance, - and linking has changed (prebinding is now deprecated, and libcc_dynamic - no longer exists). This configure patch makes things right. - -- Bug #1158607: Build with --disable-unicode again. - -- spwdmodule.c is built only if either HAVE_GETSPNAM or HAVE_HAVE_GETSPENT is - defined. Discovered as a result of not being able to build on OS X. - -- setup.py now uses the directories specified in LDFLAGS using the -L option - and in CPPFLAGS using the -I option for adding library and include - directories, respectively, for compiling extension modules against. This has - led to the core being compiled using the values in CPPFLAGS. It also removes - the need for the special-casing of both DarwinPorts and Fink for darwin since - the proper directories can be specified in LDFLAGS (``-L/sw/lib`` for Fink, - ``-L/opt/local/lib`` for DarwinPorts) and CPPFLAGS (``-I/sw/include`` for - Fink, ``-I/opt/local/include`` for DarwinPorts). - -- Test in configure.in that checks for tzset no longer dependent on tm->tm_zone - to exist in the struct (not required by either ISO C nor the UNIX 2 spec). - Tests for sanity in tzname when HAVE_TZNAME defined were also defined. - Closes bug #1096244. Thanks Gregory Bond. - -C API ------ - -- Added a C API for set and frozenset objects. - -- Removed PyRange_New(). - -- Patch #1313939: PyUnicode_DecodeCharmap() accepts a unicode string as the - mapping argument now. This string is used as a mapping table. Byte values - greater than the length of the string and 0xFFFE are treated as undefined - mappings. - - -Tests ------ - -- In test_os, st_?time is now truncated before comparing it with ST_?TIME. - -- Patch #1276356: New resource "urlfetch" is implemented. This enables - even impatient people to run tests that require remote files. - - -Documentation -------------- - -- Bug #1402224: Add warning to dl docs about crashes. - -- Bug #1396471: Document that Windows' ftell() can return invalid - values for text files with UNIX-style line endings. - -- Bug #1274828: Document os.path.splitunc(). - -- Bug #1190204: Clarify which directories are searched by site.py. - -- Bug #1193849: Clarify os.path.expanduser() documentation. - -- Bug #1243192: re.UNICODE and re.LOCALE affect \d, \D, \s and \S. - -- Bug #755617: Document the effects of os.chown() on Windows. - -- Patch #1180012: The documentation for modulefinder is now in the library reference. - -- Patch #1213031: Document that os.chown() accepts argument values of -1. - -- Bug #1190563: Document os.waitpid() return value with WNOHANG flag. - -- Bug #1175022: Correct the example code for property(). - -- Document the IterableUserDict class in the UserDict module. - Closes bug #1166582. - -- Remove all latent references for "Macintosh" that referred to semantics for - Mac OS 9 and change to reflect the state for OS X. - Closes patch #1095802. Thanks Jack Jansen. - -Mac ---- - - -New platforms -------------- - -- FreeBSD 7 support is added. - - -Tools/Demos ------------ - -- Created Misc/Vim/vim_syntax.py to auto-generate a python.vim file in that - directory for syntax highlighting in Vim. Vim directory was added and placed - vimrc to it (was previous up a level). - -- Added two new files to Tools/scripts: pysource.py, which recursively - finds Python source files, and findnocoding.py, which finds Python - source files that need an encoding declaration. - Patch #784089, credits to Oleg Broytmann. - -- Bug #1072853: pindent.py used an uninitialized variable. - -- Patch #1177597: Correct Complex.__init__. - -- Fixed a display glitch in Pynche, which could cause the right arrow to - wiggle over by a pixel. - -What's New in Python 2.4 final? -=============================== - -*Release date: 30-NOV-2004* - -Core and builtins ------------------ - -- Bug 875692: Improve signal handling, especially when using threads, by - forcing an early re-execution of PyEval_EvalFrame() "periodic" code when - things_to_do is not cleared by Py_MakePendingCalls(). - - -What's New in Python 2.4 (release candidate 1) -============================================== - -*Release date: 18-NOV-2004* - -Core and builtins ------------------ - -- Bug 1061968: Fixes in 2.4a3 to address thread bug 1010677 reintroduced - the years-old thread shutdown race bug 225673. Numeric history lesson - aside, all bugs in all three reports are fixed now. - - -Library -------- - -- Bug 1052242: If exceptions are raised by an atexit handler function an - attempt is made to execute the remaining handlers. The last exception - raised is re-raised. - -- ``doctest``'s new support for adding ``pdb.set_trace()`` calls to - doctests was broken in a dramatic but shallow way. Fixed. - -- Bug 1065388: ``calendar``'s ``day_name``, ``day_abbr``, ``month_name``, - and ``month_abbr`` attributes emulate sequences of locale-correct - spellings of month and day names. Because the locale can change at - any time, the correct spelling is recomputed whenever one of these is - indexed. In the worst case, the index may be a slice object, so these - recomputed every day or month name each time they were indexed. This is - much slower than necessary in the usual case, when the index is just an - integer. In that case, only the single spelling needed is recomputed - now; and, when the index is a slice object, only the spellings needed - by the slice are recomputed now. - -- Patch 1061679: Added ``__all__`` to pickletools.py. - -Build ------ - -- Bug 1034277 / Patch 1035255: Remove compilation of core against CoreServices - and CoreFoundation on OS X. Involved removing PyMac_GetAppletScriptFile() - which has no known users. Thanks Bob Ippolito. - -C API ------ - -- The PyRange_New() function is deprecated. - - -What's New in Python 2.4 beta 2? -================================ - -*Release date: 03-NOV-2004* - -License -------- - -The Python Software Foundation changed the license under which Python -is released, to remove Python version numbers. There were no other -changes to the license. So, for example, wherever the license for -Python 2.3 said "Python 2.3", the new license says "Python". The -intent is to make it possible to refer to the PSF license in a more -durable way. For example, some people say they're confused by that -the Open Source Initiative's entry for the Python Software Foundation -License:: - - http://www.opensource.org/licenses/PythonSoftFoundation.php - -says "Python 2.1.1" all over it, wondering whether it applies only -to Python 2.1.1. - -The official name of the new license is the Python Software Foundation -License Version 2. - -Core and builtins ------------------ - -- Bug #1055820 Cyclic garbage collection was not protecting against that - calling a live weakref to a piece of cyclic trash could resurrect an - insane mutation of the trash if any Python code ran during gc (via - running a dead object's __del__ method, running another callback on a - weakref to a dead object, or via any Python code run in any other thread - that managed to obtain the GIL while a __del__ or callback was running - in the thread doing gc). The most likely symptom was "impossible" - ``AttributeError`` exceptions, appearing seemingly at random, on weakly - referenced objects. The cure was to clear all weakrefs to unreachable - objects before allowing any callbacks to run. - -- Bug #1054139 _PyString_Resize() now invalidates its cached hash value. - -Extension Modules ------------------ - -- Bug #1048870: the compiler now generates distinct code objects for - functions with identical bodies. This was producing confusing - traceback messages which pointed to the function where the code - object was first defined rather than the function being executed. - -Library -------- - -- Patch #1056967 changes the semantics of Template.safe_substitute() so that - no ValueError is raised on an 'invalid' match group. Now the delimiter is - returned. - -- Bug #1052503 pdb.runcall() was not passing along keyword arguments. - -- Bug #902037: XML.sax.saxutils.prepare_input_source() now combines relative - paths with a base path before checking os.path.isfile(). - -- The whichdb module can now be run from the command line. - -- Bug #1045381: time.strptime() can now infer the date using %U or %W (week of - the year) when the day of the week and year are also specified. - -- Bug #1048816: fix bug in Ctrl-K at start of line in curses.textpad.Textbox - -- Bug #1017553: fix bug in tarfile.filemode() - -- Patch #737473: fix bug that old source code is shown in tracebacks even if - the source code is updated and reloaded. - -Build ------ - -- Patch #1044395: --enable-shared is allowed in FreeBSD also. - -What's New in Python 2.4 beta 1? -================================ - -*Release date: 15-OCT-2004* - -Core and builtins ------------------ - -- Patch #975056: Restartable signals were not correctly disabled on - BSD systems. Consistently use PyOS_setsig() instead of signal(). - -- The internal portable implementation of thread-local storage (TLS), used - by the ``PyGILState_Ensure()``/``PyGILState_Release()`` API, was not - thread-correct. This could lead to a variety of problems, up to and - including segfaults. See bug 1041645 for an example. - -- Added a command line option, -m module, which searches sys.path for the - module and then runs it. (Contributed by Nick Coghlan.) - -- The bytecode optimizer now folds tuples of constants into a single - constant. - -- SF bug #513866: Float/long comparison anomaly. Prior to 2.4b1, when - an integer was compared to a float, the integer was coerced to a float. - That could yield spurious overflow errors (if the integer was very - large), and to anomalies such as - ``long(1e200)+1 == 1e200 == long(1e200)-1``. Coercion to float is no - longer performed, and cases like ``long(1e200)-1 < 1e200``, - ``long(1e200)+1 > 1e200`` and ``(1 << 20000) > 1e200`` are computed - correctly now. - -Extension modules ------------------ - -- ``collections.deque`` objects didn't play quite right with garbage - collection, which could lead to a segfault in a release build, or - an assert failure in a debug build. Also, added overflow checks, - better detection of mutation during iteration, and shielded deque - comparisons from unusual subclass overrides of the __iter__() method. - -Library -------- - -- Patch 1046644: distutils build_ext grew two new options - --swig for - specifying the swig executable to use, and --swig-opts to specify - options to pass to swig. --swig-opts="-c++" is the new way to spell - --swig-cpp. - -- Patch 983206: distutils now obeys environment variable LDSHARED, if - it is set. - -- Added Peter Astrand's subprocess.py module. See PEP 324 for details. - -- time.strptime() now properly escapes timezones and all other locale-specific - strings for regex-specific symbols. Was breaking under Japanese Windows when - the timezone was specified as "Tokyo (standard time)". - Closes bug #1039270. - -- Updates for the email package: - - + email.Utils.formatdate() grew a 'usegmt' argument for HTTP support. - + All deprecated APIs that in email 2.x issued warnings have been removed: - _encoder argument to the MIMEText constructor, Message.add_payload(), - Utils.dump_address_pair(), Utils.decode(), Utils.encode() - + New deprecations: Generator.__call__(), Message.get_type(), - Message.get_main_type(), Message.get_subtype(), the 'strict' argument to - the Parser constructor. These will be removed in email 3.1. - + Support for Python earlier than 2.3 has been removed (see PEP 291). - + All defect classes have been renamed to end in 'Defect'. - + Some FeedParser fixes; also a MultipartInvariantViolationDefect will be - added to messages that claim to be multipart but really aren't. - + Updates to documentation. - -- re's findall() and finditer() functions now take an optional flags argument - just like the compile(), search(), and match() functions. Also, documented - the previously existing start and stop parameters for the findall() and - finditer() methods of regular expression objects. - -- rfc822 Messages now support iterating over the headers. - -- The (undocumented) tarfile.Tarfile.membernames has been removed; - applications should use the getmember function. - -- httplib now offers symbolic constants for the HTTP status codes. - -- SF bug #1028306: Trying to compare a ``datetime.date`` to a - ``datetime.datetime`` mistakenly compared only the year, month and day. - Now it acts like a mixed-type comparison: ``False`` for ``==``, - ``True`` for ``!=``, and raises ``TypeError`` for other comparison - operators. Because datetime is a subclass of date, comparing only the - base class (date) members can still be done, if that's desired, by - forcing using of the approprate date method; e.g., - ``a_date.__eq__(a_datetime)`` is true if and only if the year, month - and day members of ``a_date`` and ``a_datetime`` are equal. - -- bdist_rpm now supports command line options --force-arch, - {pre,post}-install, {pre,post}-uninstall, and - {prep,build,install,clean,verify}-script. - -- SF patch #998993: The UTF-8 and the UTF-16 stateful decoders now support - decoding incomplete input (when the input stream is temporarily exhausted). - ``codecs.StreamReader`` now implements buffering, which enables proper - readline support for the UTF-16 decoders. ``codecs.StreamReader.read()`` - has a new argument ``chars`` which specifies the number of characters to - return. ``codecs.StreamReader.readline()`` and - ``codecs.StreamReader.readlines()`` have a new argument ``keepends``. - Trailing "\n"s will be stripped from the lines if ``keepends`` is false. - -- The documentation for doctest is greatly expanded, and now covers all - the new public features (of which there are many). - -- ``doctest.master`` was put back in, and ``doctest.testmod()`` once again - updates it. This isn't good, because every ``testmod()`` call - contributes to bloating the "hidden" state of ``doctest.master``, but - some old code apparently relies on it. For now, all we can do is - encourage people to stitch doctests together via doctest's unittest - integration features instead. - -- httplib now handles ipv6 address/port pairs. - -- SF bug #1017864: ConfigParser now correctly handles default keys, - processing them with ``ConfigParser.optionxform`` when supplied, - consistent with the handling of config file entries and runtime-set - options. - -- SF bug #997050: Document, test, & check for non-string values in - ConfigParser. Moved the new string-only restriction added in - rev. 1.65 to the SafeConfigParser class, leaving existing - ConfigParser & RawConfigParser behavior alone, and documented the - conditions under which non-string values work. - -Build ------ - -- Building on darwin now includes /opt/local/include and /opt/local/lib for - building extension modules. This is so as to include software installed as - a DarwinPorts port - -- pyport.h now defines a Py_IS_NAN macro. It works as-is when the - platform C computes true for ``x != x`` if and only if X is a NaN. - Other platforms can override the default definition with a platform- - specific spelling in that platform's pyconfig.h. You can also override - pyport.h's default Py_IS_INFINITY definition now. - -C API ------ - -- SF patch 1044089: New function ``PyEval_ThreadsInitialized()`` returns - non-zero if PyEval_InitThreads() has been called. - -- The undocumented and unused extern int ``_PyThread_Started`` was removed. - -- The C API calls ``PyInterpreterState_New()`` and ``PyThreadState_New()`` - are two of the very few advertised as being safe to call without holding - the GIL. However, this wasn't true in a debug build, as bug 1041645 - demonstrated. In a debug build, Python redirects the ``PyMem`` family - of calls to Python's small-object allocator, to get the benefit of - its extra debugging capabilities. But Python's small-object allocator - isn't threadsafe, relying on the GIL to avoid the expense of doing its - own locking. ``PyInterpreterState_New()`` and ``PyThreadState_New()`` - call the platform ``malloc()`` directly now, regardless of build type. - -- PyLong_AsUnsignedLong[Mask] now support int objects as well. - -- SF patch #998993: ``PyUnicode_DecodeUTF8Stateful`` and - ``PyUnicode_DecodeUTF16Stateful`` have been added, which implement stateful - decoding. - -Tests ------ - -- test__locale ported to unittest - -Mac ---- - -- ``plistlib`` now supports non-dict root objects. There is also a new - interface for reading and writing plist files: ``readPlist(pathOrFile)`` - and ``writePlist(rootObject, pathOrFile)`` - -Tools/Demos ------------ - -- The text file comparison scripts ``ndiff.py`` and ``diff.py`` now - read the input files in universal-newline mode. This spares them - from consuming a great deal of time to deduce the useless result that, - e.g., a file with Windows line ends and a file with Linux line ends - have no lines in common. - - -What's New in Python 2.4 alpha 3? -================================= - -*Release date: 02-SEP-2004* - -Core and builtins ------------------ - -- SF patch #1007189: ``from ... import ...`` statements now allow the name - list to be surrounded by parentheses. - -- Some speedups for long arithmetic, thanks to Trevor Perrin. Gradeschool - multiplication was sped a little by optimizing the C code. Gradeschool - squaring was sped by about a factor of 2, by exploiting that about half - the digit products are duplicates in a square. Because exponentiation - uses squaring often, this also speeds long power. For example, the time - to compute 17**1000000 dropped from about 14 seconds to 9 on my box due - to this much. The cutoff for Karatsuba multiplication was raised, - since gradeschool multiplication got quicker, and the cutoff was - aggressively small regardless. The exponentiation algorithm was switched - from right-to-left to left-to-right, which is more efficient for small - bases. In addition, if the exponent is large, the algorithm now does - 5 bits (instead of 1 bit) at a time. That cut the time to compute - 17**1000000 on my box in half again, down to about 4.5 seconds. - -- OverflowWarning is no longer generated. PEP 237 scheduled this to - occur in Python 2.3, but since OverflowWarning was disabled by default, - nobody realized it was still being generated. On the chance that user - code is still using them, the Python builtin OverflowWarning, and - corresponding C API PyExc_OverflowWarning, will exist until Python 2.5. - -- Py_InitializeEx has been added. - -- Fix the order of application of decorators. The proper order is bottom-up; - the first decorator listed is the last one called. - -- SF patch #1005778. Fix a seg fault if the list size changed while - calling list.index(). This could happen if a rich comparison function - modified the list. - -- The ``func_name`` (a.k.a. ``__name__``) attribute of user-defined - functions is now writable. - -- code_new (a.k.a new.code()) now checks its arguments sufficiently - carefully that passing them on to PyCode_New() won't trigger calls - to Py_FatalError() or PyErr_BadInternalCall(). It is still the case - that the returned code object might be entirely insane. - -- Subclasses of string can no longer be interned. The semantics of - interning were not clear here -- a subclass could be mutable, for - example -- and had bugs. Explicitly interning a subclass of string - via intern() will raise a TypeError. Internal operations that attempt - to intern a string subclass will have no effect. - -- Bug 1003935: xrange() could report bogus OverflowErrors. Documented - what xrange() intends, and repaired tests accordingly. - -Extension modules ------------------ - -- difflib now supports HTML side-by-side diff. - -- os.urandom has been added for systems that support sources of random - data. - -- Patch 1012740: truncate() on a writeable cStringIO now resets the - position to the end of the stream. This is consistent with the original - StringIO module and avoids inadvertently resurrecting data that was - supposed to have been truncated away. - -- Added socket.socketpair(). - -- Added CurrentByteIndex, CurrentColumnNumber, CurrentLineNumber - members to xml.parsers.expat.XMLParser object. - -- The mpz, rotor, and xreadlines modules, all deprecated in earlier - versions of Python, have now been removed. - -Library -------- - -- Patch #934356: if a module defines __all__, believe that rather than using - heuristics for filtering out imported names. - -- Patch #941486: added os.path.lexists(), which returns True for broken - symlinks, unlike os.path.exists(). - -- the random module now uses os.urandom() for seeding if it is available. - Added a new generator based on os.urandom(). - -- difflib and diff.py can now generate HTML. - -- bdist_rpm now includes version and release in the BuildRoot, and - replaces - by ``_`` in version and release. - -- distutils build/build_scripts now has an -e option to specify the - path to the Python interpreter for installed scripts. - -- PEP 292 classes Template and SafeTemplate are added to the string module. - -- tarfile now generates GNU tar files by default. - -- HTTPResponse has now a getheaders method. - -- Patch #1006219: let inspect.getsource handle '@' decorators. Thanks Simon - Percivall. - -- logging.handlers.SMTPHandler.date_time has been removed; - the class now uses email.Utils.formatdate to generate the time stamp. - -- A new function tkFont.nametofont was added to return an existing - font. The Font class constructor now has an additional exists argument - which, if True, requests to return/configure an existing font, rather - than creating a new one. - -- Updated the decimal package's min() and max() methods to match the - latest revision of the General Decimal Arithmetic Specification. - Quiet NaNs are ignored and equal values are sorted based on sign - and exponent. - -- The decimal package's Context.copy() method now returns deep copies. - -- Deprecated sys.exitfunc in favor of the atexit module. The sys.exitfunc - attribute will be kept around for backwards compatibility and atexit - will just become the one preferred way to do it. - -- patch #675551: Add get_history_item and replace_history_item functions - to the readline module. - -- bug #989672: pdb.doc and the help messages for the help_d and help_u methods - of the pdb.Pdb class gives have been corrected. d(own) goes to a newer - frame, u(p) to an older frame, not the other way around. - -- bug #990669: os.path.realpath() will resolve symlinks before normalizing the - path, as normalizing the path may alter the meaning of the path if it - contains symlinks. - -- bug #851123: shutil.copyfile will raise an exception when trying to copy a - file onto a link to itself. Thanks Gregory Ball. - -- bug #570300: Fix inspect to resolve file locations using os.path.realpath() - so as to properly list all functions in a module when the module itself is - reached through a symlink. Thanks Johannes Gijsbers. - -- doctest refactoring continued. See the docs for details. As part of - this effort, some old and little- (never?) used features are now - deprecated: the Tester class, the module is_private() function, and the - isprivate argument to testmod(). The Tester class supplied a feeble - "by hand" way to combine multiple doctests, if you knew exactly what - you were doing. The newer doctest features for unittest integration - already did a better job of that, are stronger now than ever, and the - new DocTestRunner class is a saner foundation if you want to do it by - hand. The "private name" filtering gimmick was a mistake from the - start, and testmod() changed long ago to ignore it by default. If - you want to filter out tests, the new DocTestFinder class can be used - to return a list of all doctests, and you can filter that list by - any computable criteria before passing it to a DocTestRunner instance. - -- Bug #891637, patch #1005466: fix inspect.getargs() crash on def foo((bar)). - -Tools/Demos ------------ - -- IDLE's shortcut keys for windows are now case insensitive so that - Control-V works the same as Control-v. - -- pygettext.py: Generate POT-Creation-Date header in ISO format. - -Build ------ - -- Backward incompatibility: longintrepr.h now triggers a compile-time - error if SHIFT (the number of bits in a Python long "digit") isn't - divisible by 5. This new requirement allows simple code for the new - 5-bits-at-a-time long_pow() implementation. If necessary, the - restriction could be removed (by complicating long_pow(), or by - falling back to the 1-bit-at-a-time algorithm), but there are no - plans to do so. - -- bug #991962: When building with --disable-toolbox-glue on Darwin no - attempt to build Mac-specific modules occurs. - -- The --with-tsc flag to configure to enable VM profiling with the - processor's timestamp counter now works on PPC platforms. - -- patch #1006629: Define _XOPEN_SOURCE to 500 on Solaris 8/9 to match - GCC's definition and avoid redefinition warnings. - -- Detect pthreads support (provided by gnu pth pthread emulation) on - GNU/k*BSD systems. - -- bug #1005737, #1007249: Fixed several build problems and warnings - found on old/legacy C compilers of HP-UX, IRIX and Tru64. - -C API ------ - -.. - -Documentation -------------- - -- patch #1005936, bug #1009373: fix index entries which contain - an underscore when viewed with Acrobat. - -- bug #990669: os.path.normpath may alter the meaning of a path if - it contains symbolic links. This has been documented in a comment - since 1992, but is now in the library reference as well. - -New platforms -------------- - -- FreeBSD 6 is now supported. - -Tests ------ - -.. - -Windows -------- - -- Boosted the stack reservation for python.exe and pythonw.exe from - the default 1MB to 2MB. Stack frames under VC 7.1 for 2.4 are enough - bigger than under VC 6.0 for 2.3.4 that deeply recursive progams - within the default sys.getrecursionlimit() default value of 1000 were - able to suffer undetected C stack overflows. The standard test program - test_compiler was one such program. If a Python process on Windows - "just vanishes" without a trace, and without an error message of any - kind, but with an exit code of 128, undetected stack overflow may be - the problem. - -Mac ---- - -.. - - -What's New in Python 2.4 alpha 2? -================================= - -*Release date: 05-AUG-2004* - -Core and builtins ------------------ - -- Patch #980695: Implements efficient string concatenation for statements - of the form s=s+t and s+=t. This will vary across implementations. - Accordingly, the str.join() method is strongly preferred for performance - sensitive code. - -- PEP-0318, Function Decorators have been added to the language. These are - implemented using the Java-style @decorator syntax, like so:: - - @staticmethod - def foo(bar): - - (The PEP needs to be updated to reflect the current state) - -- When importing a module M raises an exception, Python no longer leaves M - in sys.modules. Before 2.4a2 it did, and a subsequent import of M would - succeed, picking up a module object from sys.modules reflecting as much - of the initialization of M as completed before the exception was raised. - Subsequent imports got no indication that M was in a partially- - initialized state, and the importers could get into arbitrarily bad - trouble as a result (the M they got was in an unintended state, - arbitrarily far removed from M's author's intent). Now subsequent - imports of M will continue raising exceptions (but if, for example, the - source code for M is edited between import attempts, then perhaps later - attempts will succeed, or raise a different exception). - - This can break existing code, but in such cases the code was probably - working before by accident. In the Python source, the only case of - breakage discovered was in a test accidentally relying on a damaged - module remaining in sys.modules. Cases are also known where tests - deliberately provoking import errors remove damaged modules from - sys.modules themselves, and such tests will break now if they do an - unconditional del sys.modules[M]. - -- u'%s' % obj will now try obj.__unicode__() first and fallback to - obj.__str__() if no __unicode__ method can be found. - -- Patch #550732: Add PyArg_VaParseTupleAndKeywords(). Analogous to - PyArg_VaParse(). Both are now documented. Thanks Greg Chapman. - -- Allow string and unicode return types from .encode()/.decode() - methods on string and unicode objects. Added unicode.decode() - which was missing for no apparent reason. - -- An attempt to fix the mess that is Python's behaviour with - signal handlers and threads, complicated by readline's behaviour. - It's quite possible that there are still bugs here. - -- Added C macros Py_CLEAR and Py_VISIT to ease the implementation of - types that support garbage collection. - -- Compiler now treats None as a constant. - -- The type of values returned by __int__, __float__, __long__, - __oct__, and __hex__ are now checked. Returning an invalid type - will cause a TypeError to be raised. This matches the behavior of - Jython. - -- Implemented bind_textdomain_codeset() in locale module. - -- Added a workaround for proper string operations in BSDs. str.split - and str.is* methods can now work correctly with UTF-8 locales. - -- Bug #989185: unicode.iswide() and unicode.width() is dropped and - the East Asian Width support is moved to unicodedata extension - module. - -- Patch #941229: The source code encoding in interactive mode - now refers sys.stdin.encoding not just ISO-8859-1 anymore. This - allows for non-latin-1 users to write unicode strings directly. - -Extension modules ------------------ - -- cpickle now supports the same keyword arguments as pickle. - -Library -------- - -- Added new codecs and aliases for ISO_8859-11, ISO_8859-16 and - TIS-620 - -- Thanks to Edward Loper, doctest has been massively refactored, and - many new features were added. Full docs will appear later. For now - the doctest module comments and new test cases give good coverage. - The refactoring provides many hook points for customizing behavior - (such as how to report errors, and how to compare expected to actual - output). New features include a marker for expected - output containing blank lines, options to produce unified or context - diffs when actual output doesn't match expectations, an option to - normalize whitespace before comparing, and an option to use an - ellipsis to signify "don't care" regions of output. - -- Tkinter now supports the wish -sync and -use options. - -- The following methods in time support passing of None: ctime(), gmtime(), - and localtime(). If None is provided, the current time is used (the - same as when the argument is omitted). - [SF bug 658254, patch 663482] - -- nntplib does now allow to ignore a .netrc file. - -- urllib2 now recognizes Basic authentication even if other authentication - schemes are offered. - -- Bug #1001053. wave.open() now accepts unicode filenames. - -- gzip.GzipFile has a new fileno() method, to retrieve the handle of the - underlying file object (provided it has a fileno() method). This is - needed if you want to use os.fsync() on a GzipFile. - -- imaplib has two new methods: deleteacl and myrights. - -- nntplib has two new methods: description and descriptions. They - use a more RFC-compliant way of getting a newsgroup description. - -- Bug #993394. Fix a possible red herring of KeyError in 'threading' being - raised during interpreter shutdown from a registered function with atexit - when dummy_threading is being used. - -- Bug #857297/Patch #916874. Fix an error when extracting a hard link - from a tarfile. - -- Patch #846659. Fix an error in tarfile.py when using - GNU longname/longlink creation. - -- The obsolete FCNTL.py has been deleted. The builtin fcntl module - has been available (on platforms that support fcntl) since Python - 1.5a3, and all FCNTL.py did is export fcntl's names, after generating - a deprecation warning telling you to use fcntl directly. - -- Several new unicode codecs are added: big5hkscs, euc_jis_2004, - iso2022_jp_2004, shift_jis_2004. - -- Bug #788520. Queue.{get, get_nowait, put, put_nowait} have new - implementations, exploiting Conditions (which didn't exist at the time - Queue was introduced). A minor semantic change is that the Full and - Empty exceptions raised by non-blocking calls now occur only if the - queue truly was full or empty at the instant the queue was checked (of - course the Queue may no longer be full or empty by the time a calling - thread sees those exceptions, though). Before, the exceptions could - also be raised if it was "merely inconvenient" for the implementation - to determine the true state of the Queue (because the Queue was locked - by some other method in progress). - -- Bugs #979794 and #980117: difflib.get_grouped_opcodes() now handles the - case of comparing two empty lists. This affected both context_diff() and - unified_diff(), - -- Bug #980938: smtplib now prints debug output to sys.stderr. - -- Bug #930024: posixpath.realpath() now handles infinite loops in symlinks by - returning the last point in the path that was not part of any loop. Thanks - AM Kuchling. - -- Bug #980327: ntpath not handles compressing erroneous slashes between the - drive letter and the rest of the path. Also clearly handles UNC addresses now - as well. Thanks Paul Moore. - -- bug #679953: zipfile.py should now work for files over 2 GB. The packed data - for file sizes (compressed and uncompressed) was being stored as signed - instead of unsigned. - -- decimal.py now only uses signals in the IBM spec. The other conditions are - no longer part of the public API. - -- codecs module now has two new generic APIs: encode() and decode() - which don't restrict the return types (unlike the unicode and - string methods of the same name). - -- Non-blocking SSL sockets work again; they were broken in Python 2.3. - SF patch 945642. - -- doctest unittest integration improvements: - - o Improved the unitest test output for doctest-based unit tests - - o Can now pass setUp and tearDown functions when creating - DocTestSuites. - -- The threading module has a new class, local, for creating objects - that provide thread-local data. - -- Bug #990307: when keep_empty_values is True, cgi.parse_qsl() - no longer returns spurious empty fields. - -- Implemented bind_textdomain_codeset() in gettext module. - -- Introduced in gettext module the l*gettext() family of functions, - which return translation strings encoded in the preferred encoding, - as informed by locale module's getpreferredencoding(). - -- optparse module (and tests) upgraded to Optik 1.5a1. Changes: - - - Add expansion of default values in help text: the string - "%default" in an option's help string is expanded to str() of - that option's default value, or "none" if no default value. - - - Bug #955889: option default values that happen to be strings are - now processed in the same way as values from the command line; this - allows generation of nicer help when using custom types. Can - be disabled with parser.set_process_default_values(False). - - - Bug #960515: don't crash when generating help for callback - options that specify 'type', but not 'dest' or 'metavar'. - - - Feature #815264: change the default help format for short options - that take an argument from e.g. "-oARG" to "-o ARG"; add - set_short_opt_delimiter() and set_long_opt_delimiter() methods to - HelpFormatter to allow (slight) customization of the formatting. - - - Patch #736940: internationalize Optik: all built-in user- - targeted literal strings are passed through gettext.gettext(). (If - you want translations (.po files), they're not included with Python - -- you'll find them in the Optik source distribution from - http://optik.sourceforge.net/ .) - - - Bug #878453: respect $COLUMNS environment variable for - wrapping help output. - - - Feature #988122: expand "%prog" in the 'description' passed - to OptionParser, just like in the 'usage' and 'version' strings. - (This is *not* done in the 'description' passed to OptionGroup.) - -C API ------ - -- PyImport_ExecCodeModule() and PyImport_ExecCodeModuleEx(): if an - error occurs while loading the module, these now delete the module's - entry from sys.modules. All ways of loading modules eventually call - one of these, so this is an error-case change in semantics for all - ways of loading modules. In rare cases, a module loader may wish - to keep a module object in sys.modules despite that the module's - code cannot be executed. In such cases, the module loader must - arrange to reinsert the name and module object in sys.modules. - PyImport_ReloadModule() has been changed to reinsert the original - module object into sys.modules if the module reload fails, so that - its visible semantics have not changed. - -- A large pile of datetime field-extraction macros is now documented, - thanks to Anthony Tuininga (patch #986010). - -Documentation -------------- - -- Improved the tutorial on creating types in C. - - - point out the importance of reassigning data members before - assigning their values - - - correct my misconception about return values from visitprocs. Sigh. - - - mention the labor saving Py_VISIT and Py_CLEAR macros. - -- Major rewrite of the math module docs, to address common confusions. - -Tests ------ - -- The test data files for the decimal test suite are now installed on - platforms that use the Makefile. - -- SF patch 995225: The test file testtar.tar accidentally contained - CVS keywords (like $Id$), which could cause spurious failures in - test_tarfile.py depending on how the test file was checked out. - - -What's New in Python 2.4 alpha 1? -================================= - -*Release date: 08-JUL-2004* - -Core and builtins ------------------ - -- weakref.ref is now the type object also known as - weakref.ReferenceType; it can be subclassed like any other new-style - class. There's less per-entry overhead in WeakValueDictionary - objects now (one object instead of three). - -- Bug #951851: Python crashed when reading import table of certain - Windows DLLs. - -- Bug #215126. The locals argument to eval(), execfile(), and exec now - accept any mapping type. - -- marshal now shares interned strings. This change introduces - a new .pyc magic. - -- Bug #966623. classes created with type() in an exec(, {}) don't - have a __module__, but code in typeobject assumed it would always - be there. - -- Python no longer relies on the LC_NUMERIC locale setting to be - the "C" locale; as a result, it no longer tries to prevent changing - the LC_NUMERIC category. - -- Bug #952807: Unpickling pickled instances of subclasses of - datetime.date, datetime.datetime and datetime.time could yield insane - objects. Thanks to Jiwon Seo for a fix. - -- Bug #845802: Python crashes when __init__.py is a directory. - -- Unicode objects received two new methods: iswide() and width(). - These query East Asian width information, as specified in Unicode - TR11. - -- Improved the tuple hashing algorithm to give fewer collisions in - common cases. Fixes bug #942952. - -- Implemented generator expressions (PEP 289). Coded by Jiwon Seo. - -- Enabled the profiling of C extension functions (and builtins) - check - new documentation and modified profile and bdb modules for more details - -- Set file.name to the object passed to open (instead of a new string) - -- Moved tracebackobject into traceback.h and renamed to PyTracebackObject - -- Optimized the byte coding for multiple assignments like "a,b=b,a" and - "a,b,c=1,2,3". Improves their speed by 25% to 30%. - -- Limit the nested depth of a tuple for the second argument to isinstance() - and issubclass() to the recursion limit of the interpreter. - Fixes bug #858016 . - -- Optimized dict iterators, creating separate types for each - and having them reveal their length. Also optimized the - methods: keys(), values(), and items(). - -- Implemented a newcode opcode, LIST_APPEND, that simplifies - the generated bytecode for list comprehensions and further - improves their performance (about 35%). - -- Implemented rich comparisons for floats, which seems to make - comparisons involving NaNs somewhat less surprising when the - underlying C compiler actually implements C99 semantics. - -- Optimized list.extend() to save memory and no longer create - intermediate sequences. Also, extend() now pre-allocates the - needed memory whenever the length of the iterable is known in - advance -- this halves the time to extend the list. - -- Optimized list resize operations to make fewer calls to the system - realloc(). Significantly speeds up list appends, list pops, - list comprehensions, and the list constructor (when the input iterable - length is not known). - -- Changed the internal list over-allocation scheme. For larger lists, - overallocation ranged between 3% and 25%. Now, it is a constant 12%. - For smaller lists (n<8), overallocation was upto eight elements. Now, - the overallocation is no more than three elements -- this improves space - utilization for applications that have large numbers of small lists. - -- Most list bodies now get re-used rather than freed. Speeds up list - instantiation and deletion by saving calls to malloc() and free(). - -- The dict.update() method now accepts all the same argument forms - as the dict() constructor. This now includes item lists and/or - keyword arguments. - -- Support for arbitrary objects supporting the read-only buffer - interface as the co_code field of code objects (something that was - only possible to create from C code) has been removed. - -- Made omitted callback and None equivalent for weakref.ref() and - weakref.proxy(); the None case wasn't handled correctly in all - cases. - -- Fixed problem where PyWeakref_NewRef() and PyWeakref_NewProxy() - assumed that initial existing entries in an object's weakref list - would not be removed while allocating a new weakref object. Since - GC could be invoked at that time, however, that assumption was - invalid. In a truly obscure case of GC being triggered during - creation for a new weakref object for an referent which already - has a weakref without a callback which is only referenced from - cyclic trash, a memory error can occur. This consistently created a - segfault in a debug build, but provided less predictable behavior in - a release build. - -- input() builtin function now respects compiler flags such as - __future__ statements. SF patch 876178. - -- Removed PendingDeprecationWarning from apply(). apply() remains - deprecated, but the nuisance warning will not be issued. - -- At Python shutdown time (Py_Finalize()), 2.3 called cyclic garbage - collection twice, both before and after tearing down modules. The - call after tearing down modules has been disabled, because too much - of Python has been torn down then for __del__ methods and weakref - callbacks to execute sanely. The most common symptom was a sequence - of uninformative messages on stderr when Python shut down, produced - by threads trying to raise exceptions, but unable to report the nature - of their problems because too much of the sys module had already been - destroyed. - -- Removed FutureWarnings related to hex/oct literals and conversions - and left shifts. (Thanks to Kalle Svensson for SF patch 849227.) - This addresses most of the remaining semantic changes promised by - PEP 237, except for repr() of a long, which still shows the trailing - 'L'. The PEP appears to promise warnings for operations that - changed semantics compared to Python 2.3, but this is not - implemented; we've suffered through enough warnings related to - hex/oct literals and I think it's best to be silent now. - -- For str and unicode objects, the ljust(), center(), and rjust() - methods now accept an optional argument specifying a fill - character other than a space. - -- When method objects have an attribute that can be satisfied either - by the function object or by the method object, the function - object's attribute usually wins. Christian Tismer pointed out that - that this is really a mistake, because this only happens for special - methods (like __reduce__) where the method object's version is - really more appropriate than the function's attribute. So from now - on, all method attributes will have precedence over function - attributes with the same name. - -- Critical bugfix, for SF bug 839548: if a weakref with a callback, - its callback, and its weakly referenced object, all became part of - cyclic garbage during a single run of garbage collection, the order - in which they were torn down was unpredictable. It was possible for - the callback to see partially-torn-down objects, leading to immediate - segfaults, or, if the callback resurrected garbage objects, to - resurrect insane objects that caused segfaults (or other surprises) - later. In one sense this wasn't surprising, because Python's cyclic gc - had no knowledge of Python's weakref objects. It does now. When - weakrefs with callbacks become part of cyclic garbage now, those - weakrefs are cleared first. The callbacks don't trigger then, - preventing the problems. If you need callbacks to trigger, then just - as when cyclic gc is not involved, you need to write your code so - that weakref objects outlive the objects they weakly reference. - -- Critical bugfix, for SF bug 840829: if cyclic garbage collection - happened to occur during a weakref callback for a new-style class - instance, subtle memory corruption was the result (in a release build; - in a debug build, a segfault occurred reliably very soon after). - This has been repaired. - -- Compiler flags set in PYTHONSTARTUP are now active in __main__. - -- Added two builtin types, set() and frozenset(). - -- Added a reversed() builtin function that returns a reverse iterator - over a sequence. - -- Added a sorted() builtin function that returns a new sorted list - from any iterable. - -- CObjects are now mutable (on the C level) through PyCObject_SetVoidPtr. - -- list.sort() now supports three keyword arguments: cmp, key, and reverse. - The key argument can be a function of one argument that extracts a - comparison key from the original record: mylist.sort(key=str.lower). - The reverse argument is a boolean value and if True will change the - sort order as if the comparison arguments were reversed. In addition, - the documentation has been amended to provide a guarantee that all sorts - starting with Py2.3 are guaranteed to be stable (the relative order of - records with equal keys is unchanged). - -- Added test whether wchar_t is signed or not. A signed wchar_t is not - usable as internal unicode type base for Py_UNICODE since the - unicode implementation assumes an unsigned type. - -- Fixed a bug in the cache of length-one Unicode strings that could - lead to a seg fault. The specific problem occurred when an earlier, - non-fatal error left an uninitialized Unicode object in the - freelist. - -- The % formatting operator now supports '%F' which is equivalent to - '%f'. This has always been documented but never implemented. - -- complex(obj) could leak a little memory if obj wasn't a string or - number. - -- zip() with no arguments now returns an empty list instead of raising - a TypeError exception. - -- obj.__contains__() now returns True/False instead of 1/0. SF patch - 820195. - -- Python no longer tries to be smart about recursive comparisons. - When comparing containers with cyclic references to themselves it - will now just hit the recursion limit. See SF patch 825639. - -- str and unicode builtin types now have an rsplit() method that is - same as split() except that it scans the string from the end - working towards the beginning. See SF feature request 801847. - -- Fixed a bug in object.__reduce_ex__ when using protocol 2. Failure - to clear the error when attempts to get the __getstate__ attribute - fail caused intermittent errors and odd behavior. - -- buffer objects based on other objects no longer cache a pointer to - the data and the data length. Instead, the appropriate tp_as_buffer - method is called as necessary. - -- fixed: if a file is opened with an explicit buffer size >= 1, repeated - close() calls would attempt to free() the buffer already free()ed on - the first call. - - -Extension modules ------------------ - -- Added socket.getservbyport(), and make the second argument in - getservbyname() and getservbyport() optional. - -- time module code that deals with input POSIX timestamps will now raise - ValueError if more than a second is lost in precision when the - timestamp is cast to the platform C time_t type. There's no chance - that the platform will do anything sensible with the result in such - cases. This includes ctime(), localtime() and gmtime(). Assorted - fromtimestamp() and utcfromtimestamp() methods in the datetime module - were also protected. Closes bugs #919012 and 975996. - -- fcntl.ioctl now warns if the mutate flag is not specified. - -- nt now properly allows to refer to UNC roots, e.g. in nt.stat(). - -- the weakref module now supports additional objects: array.array, - sre.pattern_objects, file objects, and sockets. - -- operator.isMappingType() and operator.isSequenceType() now give - fewer false positives. - -- socket.sslerror is now a subclass of socket.error . Also added - socket.error to the socket module's C API. - -- Bug #920575: A problem where the _locale module segfaults on - nl_langinfo(ERA) caused by GNU libc's illegal NULL return is fixed. - -- array objects now support the copy module. Also, their resizing - scheme has been updated to match that used for list objects. This improves - the performance (speed and memory usage) of append() operations. - Also, array.array() and array.extend() now accept any iterable argument - for repeated appends without needing to create another temporary array. - -- cStringIO.writelines() now accepts any iterable argument and writes - the lines one at a time rather than joining them and writing once. - Made a parallel change to StringIO.writelines(). Saves memory and - makes suitable for use with generator expressions. - -- time.strftime() now checks that the values in its time tuple argument - are within the proper boundaries to prevent possible crashes from the - platform's C library implementation of strftime(). Can possibly - break code that uses values outside the range that didn't cause - problems previously (such as sitting day of year to 0). Fixes bug - #897625. - -- The socket module now supports Bluetooth sockets, if the - system has - -- Added a collections module containing a new datatype, deque(), - offering high-performance, thread-safe, memory friendly appends - and pops on either side of the deque. - -- Several modules now take advantage of collections.deque() for - improved performance: Queue, mutex, shlex, threading, and pydoc. - -- The operator module has two new functions, attrgetter() and - itemgetter() which are useful for creating fast data extractor - functions for map(), list.sort(), itertools.groupby(), and - other functions that expect a function argument. - -- socket.SHUT_{RD,WR,RDWR} was added. - -- os.getsid was added. - -- The pwd module incorrectly advertised its struct type as - struct_pwent; this has been renamed to struct_passwd. (The old name - is still supported for backwards compatibility.) - -- The xml.parsers.expat module now provides Expat 1.95.7. - -- socket.IPPROTO_IPV6 was added. - -- readline.clear_history was added. - -- select.select() now accepts sequences for its first three arguments. - -- cStringIO now supports the f.closed attribute. - -- The signal module now exposes SIGRTMIN and SIGRTMAX (if available). - -- curses module now supports use_default_colors(). [patch #739124] - -- Bug #811028: ncurses.h breakage on FreeBSD/MacOS X - -- Bug #814613: INET_ADDRSTRLEN fix needed for all compilers on SGI - -- Implemented non-recursive SRE matching scheme (#757624). - -- Implemented (?(id/name)yes|no) support in SRE (#572936). - -- random.seed() with no arguments or None uses time.time() as a default - seed. Modified to match Py2.2 behavior and use fractional seconds so - that successive runs are more likely to produce different sequences. - -- random.Random has a new method, getrandbits(k), which returns an int - with k random bits. This method is now an optional part of the API - for user defined generators. Any generator that defines genrandbits() - can now use randrange() for ranges with a length >= 2**53. Formerly, - randrange would return only even numbers for ranges that large (see - SF bug #812202). Generators that do not define genrandbits() now - issue a warning when randrange() is called with a range that large. - -- itertools has a new function, groupby() for aggregating iterables - into groups sharing the same key (as determined by a key function). - It offers some of functionality of SQL's groupby keyword and of - the Unix uniq filter. - -- itertools now has a new tee() function which produces two independent - iterators from a single iterable. - -- itertools.izip() with no arguments now returns an empty iterator instead - of raising a TypeError exception. - -- Fixed #853061: allow BZ2Compressor.compress() to receive an empty string - as parameter. - -Library -------- - -- Added a new module: cProfile, a C profiler with the same interface as the - profile module. cProfile avoids some of the drawbacks of the hotshot - profiler and provides a bit more information than the other two profilers. - Based on "lsprof" (patch #1212837). - -- Bug #1266283: The new function "lexists" is now in os.path.__all__. - -- Bug #981530: Fix UnboundLocalError in shutil.rmtree(). This affects - the documented behavior: the function passed to the onerror() - handler can now also be os.listdir. - -- Bug #754449: threading.Thread objects no longer mask exceptions raised during - interpreter shutdown with another exception from attempting to handle the - original exception. - -- Added decimal.py per PEP 327. - -- Bug #981299: rsync is now a recognized protocol in urlparse that uses a - "netloc" portion of a URL. - -- Bug #919012: shutil.move() will not try to move a directory into itself. - Thanks Johannes Gijsbers. - -- Bug #934282: pydoc.stripid() is now case-insensitive. Thanks Robin Becker. - -- Bug #823209: cmath.log() now takes an optional base argument so that its - API matches math.log(). - -- Bug #957381: distutils bdist_rpm no longer fails on recent RPM versions - that generate a -debuginfo.rpm - -- os.path.devnull has been added for all supported platforms. - -- Fixed #877165: distutils now picks the right C++ compiler command - on cygwin and mingw32. - -- urllib.urlopen().readline() now handles HTTP/0.9 correctly. - -- refactored site.py into functions. Also wrote regression tests for the - module. - -- The distutils install command now supports the --home option and - installation scheme for all platforms. - -- asyncore.loop now has a repeat count parameter that defaults to - looping forever. - -- The distutils sdist command now ignores all .svn directories, in - addition to CVS and RCS directories. .svn directories hold - administrative files for the Subversion source control system. - -- Added a new module: cookielib. Automatic cookie handling for HTTP - clients. Also, support for cookielib has been added to urllib2, so - urllib2.urlopen() can transparently handle cookies. - -- stringprep.py now uses built-in set() instead of sets.Set(). - -- Bug #876278: Unbounded recursion in modulefinder - -- Bug #780300: Swap public and system ID in LexicalHandler.startDTD. - Applications relying on the wrong order need to be corrected. - -- Bug #926075: Fixed a bug that returns a wrong pattern object - for a string or unicode object in sre.compile() when a different - type pattern with the same value exists. - -- Added countcallers arg to trace.Trace class (--trackcalls command line arg - when run from the command prompt). - -- Fixed a caching bug in platform.platform() where the argument of 'terse' was - not taken into consideration when caching value. - -- Added two new command-line arguments for profile (output file and - default sort). - -- Added global runctx function to profile module - -- Add hlist missing entryconfigure and entrycget methods. - -- The ptcp154 codec was added for Kazakh character set support. - -- Support non-anonymous ftp URLs in urllib2. - -- The encodings package will now apply codec name aliases - first before starting to try the import of the codec module. - This simplifies overriding built-in codecs with external - packages, e.g. the included CJK codecs with the JapaneseCodecs - package, by adjusting the aliases dictionary in encodings.aliases - accordingly. - -- base64 now supports RFC 3548 Base16, Base32, and Base64 encoding and - decoding standards. - -- urllib2 now supports processors. A processor is a handler that - implements an xxx_request or xxx_response method. These methods are - called for all requests. - -- distutils compilers now compile source files in the same order as - they are passed to the compiler. - -- pprint.pprint() and pprint.pformat() now have additional parameters - indent, width and depth. - -- Patch #750542: pprint now will pretty print subclasses of list, tuple - and dict too, as long as they don't overwrite __repr__(). - -- Bug #848614: distutils' msvccompiler fails to find the MSVC6 - compiler because of incomplete registry entries. - -- httplib.HTTP.putrequest now offers to omit the implicit Accept-Encoding. - -- Patch #841977: modulefinder didn't find extension modules in packages - -- imaplib.IMAP4.thread was added. - -- Plugged a minor hole in tempfile.mktemp() due to the use of - os.path.exists(), switched to using os.lstat() directly if possible. - -- bisect.py and heapq.py now have underlying C implementations - for better performance. - -- heapq.py has two new functions, nsmallest() and nlargest(). - -- traceback.format_exc has been added (similar to print_exc but it returns - a string). - -- xmlrpclib.MultiCall has been added. - -- poplib.POP3_SSL has been added. - -- tmpfile.mkstemp now returns an absolute path even if dir is relative. - -- urlparse is RFC 2396 compliant. - -- The fieldnames argument to the csv module's DictReader constructor is now - optional. If omitted, the first row of the file will be used as the - list of fieldnames. - -- encodings.bz2_codec was added for access to bz2 compression - using "a long string".encode('bz2') - -- Various improvements to unittest.py, realigned with PyUnit CVS. - -- dircache now passes exceptions to the caller, instead of returning - empty lists. - -- The bsddb module and dbhash module now support the iterator and - mapping protocols which make them more substitutable for dictionaries - and shelves. - -- The csv module's DictReader and DictWriter classes now accept keyword - arguments. This was an omission in the initial implementation. - -- The email package handles some RFC 2231 parameters with missing - CHARSET fields better. It also includes a patch to parameter - parsing when semicolons appear inside quotes. - -- sets.py now runs under Py2.2. In addition, the argument restrictions - for most set methods (but not the operators) have been relaxed to - allow any iterable. - -- _strptime.py now has a behind-the-scenes caching mechanism for the most - recent TimeRE instance used along with the last five unique directive - patterns. The overall module was also made more thread-safe. - -- random.cunifvariate() and random.stdgamma() were deprecated in Py2.3 - and removed in Py2.4. - -- Bug #823328: urllib2.py's HTTP Digest Auth support works again. - -- Patch #873597: CJK codecs are imported into rank of default codecs. - -Tools/Demos ------------ - -- A hotshotmain script was added to the Tools/scripts directory that - makes it easy to run a script under control of the hotshot profiler. - -- The db2pickle and pickle2db scripts can now dump/load gdbm files. - -- The file order on the command line of the pickle2db script was reversed. - It is now [ picklefile ] dbfile. This provides better symmetry with - db2pickle. The file arguments to both scripts are now source followed by - destination in situations where both files are given. - -- The pydoc script will display a link to the module documentation for - modules determined to be part of the core distribution. The documentation - base directory defaults to http://www.python.org/doc/current/lib/ but can - be changed by setting the PYTHONDOCS environment variable. - -- texcheck.py now detects double word errors. - -- md5sum.py mistakenly opened input files in text mode by default, a - silent and dangerous change from previous releases. It once again - opens input files in binary mode by default. The -t and -b flags - remain for compatibility with the 2.3 release, but -b is the default - now. - -- py-electric-colon now works when pending-delete/delete-selection mode is - in effect - -- py-help-at-point is no longer bound to the F1 key - it's still bound to - C-c C-h - -- Pynche was fixed to not crash when there is no ~/.pynche file and no - -d option was given. - -Build ------ - -- Bug #978645: Modules/getpath.c now builds properly in --disable-framework - build under OS X. - -- Profiling using gprof is now available if Python is configured with - --enable-profiling. - -- Profiling the VM using the Pentium TSC is now possible if Python - is configured --with-tsc. - -- In order to find libraries, setup.py now also looks in /lib64, for use - on AMD64. - -- Bug #934635: Fixed a bug where the configure script couldn't detect - getaddrinfo() properly if the KAME stack had SCTP support. - -- Support for missing ANSI C header files (limits.h, stddef.h, etc) was - removed. - -- Systems requiring the D4, D6 or D7 variants of pthreads are no longer - supported (see PEP 11). - -- Universal newline support can no longer be disabled (see PEP 11). - -- Support for DGUX, SunOS 4, IRIX 4 and Minix was removed (see PEP 11). - -- Support for systems requiring --with-dl-dld or --with-sgi-dl was removed - (see PEP 11). - -- Tests for sizeof(char) were removed since ANSI C mandates that - sizeof(char) must be 1. - -C API ------ - -- Thanks to Anthony Tuininga, the datetime module now supplies a C API - containing type-check macros and constructors. See new docs in the - Python/C API Reference Manual for details. - -- Private function _PyTime_DoubleToTimet added, to convert a Python - timestamp (C double) to platform time_t with some out-of-bounds - checking. Declared in new header file timefuncs.h. It would be - good to expose some other internal timemodule.c functions there. - -- New public functions PyEval_EvaluateFrame and PyGen_New to expose - generator objects. - -- New public functions Py_IncRef() and Py_DecRef(), exposing the - functionality of the Py_XINCREF() and Py_XDECREF macros. Useful for - runtime dynamic embedding of Python. See patch #938302, by Bob - Ippolito. - -- Added a new macro, PySequence_Fast_ITEMS, which retrieves a fast sequence's - underlying array of PyObject pointers. Useful for high speed looping. - -- Created a new method flag, METH_COEXIST, which causes a method to be loaded - even if already defined by a slot wrapper. This allows a __contains__ - method, for example, to co-exist with a defined sq_contains slot. This - is helpful because the PyCFunction can take advantage of optimized calls - whenever METH_O or METH_NOARGS flags are defined. - -- Added a new function, PyDict_Contains(d, k) which is like - PySequence_Contains() but is specific to dictionaries and executes - about 10% faster. - -- Added three new macros: Py_RETURN_NONE, Py_RETURN_TRUE, and Py_RETURN_FALSE. - Each return the singleton they mention after Py_INCREF()ing them. - -- Added a new function, PyTuple_Pack(n, ...) for constructing tuples from a - variable length argument list of Python objects without having to invoke - the more complex machinery of Py_BuildValue(). PyTuple_Pack(3, a, b, c) - is equivalent to Py_BuildValue("(OOO)", a, b, c). - -Windows -------- - -- The _winreg module could segfault when reading very large registry - values, due to unchecked alloca() calls (SF bug 851056). The fix is - uses either PyMem_Malloc(n) or PyString_FromStringAndSize(NULL, n), - as appropriate, followed by a size check. - -- file.truncate() could misbehave if the file was open for update - (modes r+, rb+, w+, wb+), and the most recent file operation before - the truncate() call was an input operation. SF bug 801631. - - -What's New in Python 2.3 final? -=============================== - -*Release date: 29-Jul-2003* - -IDLE ----- - -- Bug 778400: IDLE hangs when selecting "Edit with IDLE" from explorer. - This was unique to Windows, and was fixed by adding an -n switch to - the command the Windows installer creates to execute "Edit with IDLE" - context-menu actions. - -- IDLE displays a new message upon startup: some "personal firewall" - kinds of programs (for example, ZoneAlarm) open a dialog of their - own when any program opens a socket. IDLE does use sockets, talking - on the computer's internal loopback interface. This connection is not - visible on any external interface and no data is sent to or received - from the Internet. So, if you get such a dialog when opening IDLE, - asking whether to let pythonw.exe talk to address 127.0.0.1, say yes, - and rest assured no communication external to your machine is taking - place. If you don't allow it, IDLE won't be able to start. - - -What's New in Python 2.3 release candidate 2? -============================================= - -*Release date: 24-Jul-2003* - -Core and builtins ------------------ - -- It is now possible to import from zipfiles containing additional - data bytes before the zip compatible archive. Zipfiles containing a - comment at the end are still unsupported. - -Extension modules ------------------ - -- A longstanding bug in the parser module's initialization could cause - fatal internal refcount confusion when the module got initialized more - than once. This has been fixed. - -- Fixed memory leak in pyexpat; using the parser's ParseFile() method - with open files that aren't instances of the standard file type - caused an instance of the bound .read() method to be leaked on every - call. - -- Fixed some leaks in the locale module. - -Library -------- - -- Lib/encodings/rot_13.py when used as a script, now more properly - uses the first Python interpreter on your path. - -- Removed caching of TimeRE (and thus LocaleTime) in _strptime.py to - fix a locale related bug in the test suite. Although another patch - was needed to actually fix the problem, the cache code was not - restored. - -IDLE ----- - -- Calltips patches. - -Build ------ - -- For MacOSX, added -mno-fused-madd to BASECFLAGS to fix test_coercion - on Panther (OSX 10.3). - -C API ------ - -Windows -------- - -- The tempfile module could do insane imports on Windows if PYTHONCASEOK - was set, making temp file creation impossible. Repaired. - -- Add a patch to workaround pthread_sigmask() bugs in Cygwin. - -Mac ---- - -- Various fixes to pimp. - -- Scripts runs with pythonw no longer had full window manager access. - -- Don't force boot-disk-only install, for reasons unknown it causes - more problems than it solves. - - -What's New in Python 2.3 release candidate 1? -============================================= - -*Release date: 18-Jul-2003* - -Core and builtins ------------------ - -- The new function sys.getcheckinterval() returns the last value set - by sys.setcheckinterval(). - -- Several bugs in the symbol table phase of the compiler have been - fixed. Errors could be lost and compilation could fail without - reporting an error. SF patch 763201. - -- The interpreter is now more robust about importing the warnings - module. In an executable generated by freeze or similar programs, - earlier versions of 2.3 would fail if the warnings module could - not be found on the file system. Fixes SF bug 771097. - -- A warning about assignments to module attributes that shadow - builtins, present in earlier releases of 2.3, has been removed. - -- It is not possible to create subclasses of builtin types like str - and tuple that define an itemsize. Earlier releases of Python 2.3 - allowed this by mistake, leading to crashes and other problems. - -- The thread_id is now initialized to 0 in a non-thread build. SF bug - 770247. - -- SF bug 762891: "del p[key]" on proxy object no longer raises SystemError. - -Extension modules ------------------ - -- weakref.proxy() can now handle "del obj[i]" for proxy objects - defining __delitem__. Formerly, it generated a SystemError. - -- SSL no longer crashes the interpreter when the remote side disconnects. - -- On Unix the mmap module can again be used to map device files. - -- time.strptime now exclusively uses the Python implementation - contained within the _strptime module. - -- The print slot of weakref proxy objects was removed, because it was - not consistent with the object's repr slot. - -- The mmap module only checks file size for regular files, not - character or block devices. SF patch 708374. - -- The cPickle Pickler garbage collection support was fixed to traverse - the find_class attribute, if present. - -- There are several fixes for the bsddb3 wrapper module. - - bsddb3 no longer crashes if an environment is closed before a cursor - (SF bug 763298). - - The DB and DBEnv set_get_returns_none function was extended to take - a level instead of a boolean flag. The new level 2 means that in - addition, cursor.set()/.get() methods return None instead of raising - an exception. - - A typo was fixed in DBCursor.join_item(), preventing a crash. - -Library -------- - -- distutils now supports MSVC 7.1 - -- doctest now examines all docstrings by default. Previously, it would - skip over functions with private names (as indicated by the underscore - naming convention). The old default created too much of a risk that - user tests were being skipped inadvertently. Note, this change could - break code in the unlikely case that someone had intentionally put - failing tests in the docstrings of private functions. The breakage - is easily fixable by specifying the old behavior when calling testmod() - or Tester(). - -- There were several fixes to the way dumbdbms are closed. It's vital - that a dumbdbm database be closed properly, else the on-disk data - and directory files can be left in mutually inconsistent states. - dumbdbm.py's _Database.__del__() method attempted to close the - database properly, but a shutdown race in _Database._commit() could - prevent this from working, so that a program trusting __del__() to - get the on-disk files in synch could be badly surprised. The race - has been repaired. A sync() method was also added so that shelve - can guarantee data is written to disk. - - The close() method can now be called more than once without complaint. - -- The classes in threading.py are now new-style classes. That they - weren't before was an oversight. - -- The urllib2 digest authentication handlers now define the correct - auth_header. The earlier versions would fail at runtime. - -- SF bug 763023: fix uncaught ZeroDivisionError in difflib ratio methods - when there are no lines. - -- SF bug 763637: fix exception in Tkinter with after_cancel - which could occur with Tk 8.4 - -- SF bug 770601: CGIHTTPServer.py now passes the entire environment - to child processes. - -- SF bug 765238: add filter to fnmatch's __all__. - -- SF bug 748201: make time.strptime() error messages more helpful. - -- SF patch 764470: Do not dump the args attribute of a Fault object in - xmlrpclib. - -- SF patch 549151: urllib and urllib2 now redirect POSTs on 301 - responses. - -- SF patch 766650: The whichdb module was fixed to recognize dbm files - generated by gdbm on OS/2 EMX. - -- SF bugs 763047 and 763052: fixes bug of timezone value being left as - -1 when ``time.tzname[0] == time.tzname[1] and not time.daylight`` - is true when it should only when time.daylight is true. - -- SF bug 764548: re now allows subclasses of str and unicode to be - used as patterns. - -- SF bug 763637: In Tkinter, change after_cancel() to handle tuples - of varying sizes. Tk 8.4 returns a different number of values - than Tk 8.3. - -- SF bug 763023: difflib.ratio() did not catch zero division. - -- The Queue module now has an __all__ attribute. - -Tools/Demos ------------ - -- See Lib/idlelib/NEWS.txt for IDLE news. - -- SF bug 753592: webchecker/wsgui now handles user supplied directories. - -- The trace.py script has been removed. It is now in the standard library. - -Build ------ - -- Python now compiles with -fno-strict-aliasing if possible (SF bug 766696). - -- The socket module compiles on IRIX 6.5.10. - -- An irix64 system is treated the same way as an irix6 system (SF - patch 764560). - -- Several definitions were missing on FreeBSD 5.x unless the - __BSD_VISIBLE symbol was defined. configure now defines it as - needed. - -C API ------ - -- Unicode objects now support mbcs as a built-in encoding, so the C - API can use it without deferring to the encodings package. - -Windows -------- - -- The Windows implementation of PyThread_start_new_thread() never - checked error returns from Windows functions correctly. As a result, - it could claim to start a new thread even when the Microsoft - _beginthread() function failed (due to "too many threads" -- this is - on the order of thousands when it happens). In these cases, the - Python exception :: - - thread.error: can't start new thread - - is raised now. - -- SF bug 766669: Prevent a GPF on interpreter exit when sockets are in - use. The interpreter now calls WSACleanup() from Py_Finalize() - instead of from DLL teardown. - -Mac ---- - -- Bundlebuilder now inherits default values in the right way. It was - previously possible for app bundles to get a type of "BNDL" instead - of "APPL." Other improvements include, a --build-id option to - specify the CFBundleIdentifier and using the --python option to set - the executable in the bundle. - -- Fixed two bugs in MacOSX framework handling. - -- pythonw did not allow user interaction in 2.3rc1, this has been fixed. - -- Python is now compiled with -mno-fused-madd, making all tests pass - on Panther. - -What's New in Python 2.3 beta 2? -================================ - -*Release date: 29-Jun-2003* - -Core and builtins ------------------ - -- A program can now set the environment variable PYTHONINSPECT to some - string value in Python, and cause the interpreter to enter the - interactive prompt at program exit, as if Python had been invoked - with the -i option. - -- list.index() now accepts optional start and stop arguments. Similar - changes were made to UserList.index(). SF feature request 754014. - -- SF patch 751998 fixes an unwanted side effect of the previous fix - for SF bug 742860 (the next item). - -- SF bug 742860: "WeakKeyDictionary __delitem__ uses iterkeys". This - wasn't threadsafe, was very inefficient (expected time O(len(dict)) - instead of O(1)), and could raise a spurious RuntimeError if another - thread mutated the dict during __delitem__, or if a comparison function - mutated it. It also neglected to raise KeyError when the key wasn't - present; didn't raise TypeError when the key wasn't of a weakly - referencable type; and broke various more-or-less obscure dict - invariants by using a sequence of equality comparisons over the whole - set of dict keys instead of computing the key's hash code to narrow - the search to those keys with the same hash code. All of these are - considered to be bugs. A new implementation of __delitem__ repairs all - that, but note that fixing these bugs may change visible behavior in - code relying (whether intentionally or accidentally) on old behavior. - -- SF bug 734869: Fixed a compiler bug that caused a fatal error when - compiling a list comprehension that contained another list comprehension - embedded in a lambda expression. - -- SF bug 705231: builtin pow() no longer lets the platform C pow() - raise -1.0 to integer powers, because (at least) glibc gets it wrong - in some cases. The result should be -1.0 if the power is odd and 1.0 - if the power is even, and any float with a sufficiently large exponent - is (mathematically) an exact even integer. - -- SF bug 759227: A new-style class that implements __nonzero__() must - return a bool or int (but not an int subclass) from that method. This - matches the restriction on classic classes. - -- The encoding attribute has been added for file objects, and set to - the terminal encoding on Unix and Windows. - -- The softspace attribute of file objects became read-only by oversight. - It's writable again. - -- Reverted a 2.3 beta 1 change to iterators for subclasses of list and - tuple. By default, the iterators now access data elements directly - instead of going through __getitem__. If __getitem__ access is - preferred, then __iter__ can be overridden. - -- SF bug 735247: The staticmethod and super types participate in - garbage collection. Before this change, it was possible for leaks to - occur in functions with non-global free variables that used these types. - -Extension modules ------------------ - -- the socket module has a new exception, socket.timeout, to allow - timeouts to be handled separately from other socket errors. - -- SF bug 751276: cPickle has fixed to propagate exceptions raised in - user code. In earlier versions, cPickle caught and ignored any - exception when it performed operations that it expected to raise - specific exceptions like AttributeError. - -- cPickle Pickler and Unpickler objects now participate in garbage - collection. - -- mimetools.choose_boundary() could return duplicate strings at times, - especially likely on Windows. The strings returned are now guaranteed - unique within a single program run. - -- thread.interrupt_main() raises KeyboardInterrupt in the main thread. - dummy_thread has also been modified to try to simulate the behavior. - -- array.array.insert() now treats negative indices as being relative - to the end of the array, just like list.insert() does. (SF bug #739313) - -- The datetime module classes datetime, time, and timedelta are now - properly subclassable. - -- _tkinter.{get|set}busywaitinterval was added. - -- itertools.islice() now accepts stop=None as documented. - Fixes SF bug #730685. - -- the bsddb185 module is built in one restricted instance - - /usr/include/db.h exists and defines HASHVERSION to be 2. This is true - for many BSD-derived systems. - - -Library -------- - -- Some happy doctest extensions from Jim Fulton have been added to - doctest.py. These are already being used in Zope3. The two - primary ones: - - doctest.debug(module, name) extracts the doctests from the named object - in the given module, puts them in a temp file, and starts pdb running - on that file. This is great when a doctest fails. - - doctest.DocTestSuite(module=None) returns a synthesized unittest - TestSuite instance, to be run by the unittest framework, which - runs all the doctests in the module. This allows writing tests in - doctest style (which can be clearer and shorter than writing tests - in unittest style), without losing unittest's powerful testing - framework features (which doctest lacks). - -- For compatibility with doctests created before 2.3, if an expected - output block consists solely of "1" and the actual output block - consists solely of "True", it's accepted as a match; similarly - for "0" and "False". This is quite un-doctest-like, but is practical. - The behavior can be disabled by passing the new doctest module - constant DONT_ACCEPT_TRUE_FOR_1 to the new optionflags optional - argument. - -- ZipFile.testzip() now only traps BadZipfile exceptions. Previously, - a bare except caught to much and reported all errors as a problem - in the archive. - -- The logging module now has a new function, makeLogRecord() making - LogHandler easier to interact with DatagramHandler and SocketHandler. - -- The cgitb module has been extended to support plain text display (SF patch - 569574). - -- A brand new version of IDLE (from the IDLEfork project at - SourceForge) is now included as Lib/idlelib. The old Tools/idle is - no more. - -- Added a new module: trace (documentation missing). This module used - to be distributed in Tools/scripts. It uses sys.settrace() to trace - code execution -- either function calls or individual lines. It can - generate tracing output during execution or a post-mortem report of - code coverage. - -- The threading module has new functions settrace() and setprofile() - that cooperate with the functions of the same name in the sys - module. A function registered with the threading module will - be used for all threads it creates. The new trace module uses this - to provide tracing for code running in threads. - -- copy.py: applied SF patch 707900, fixing bug 702858, by Steven - Taschuk. Copying a new-style class that had a reference to itself - didn't work. (The same thing worked fine for old-style classes.) - Builtin functions are now treated as atomic, fixing bug #746304. - -- difflib.py has two new functions: context_diff() and unified_diff(). - -- More fixes to urllib (SF 549151): (a) When redirecting, always use - GET. This is common practice and more-or-less sanctioned by the - HTTP standard. (b) Add a handler for 307 redirection, which becomes - an error for POST, but a regular redirect for GET and HEAD - -- Added optional 'onerror' argument to os.walk(), to control error - handling. - -- inspect.is{method|data}descriptor was added, to allow pydoc display - __doc__ of data descriptors. - -- Fixed socket speed loss caused by use of the _socketobject wrapper class - in socket.py. - -- timeit.py now checks the current directory for imports. - -- urllib2.py now knows how to order proxy classes, so the user doesn't - have to insert it in front of other classes, nor do dirty tricks like - inserting a "dummy" HTTPHandler after a ProxyHandler when building an - opener with proxy support. - -- Iterators have been added for dbm keys. - -- random.Random objects can now be pickled. - -Tools/Demos ------------ - -- pydoc now offers help on keywords and topics. - -- Tools/idle is gone; long live Lib/idlelib. - -- diff.py prints file diffs in context, unified, or ndiff formats, - providing a command line interface to difflib.py. - -- texcheck.py is a new script for making a rough validation of Python LaTeX - files. - -Build ------ - -- Setting DESTDIR during 'make install' now allows specifying a - different root directory. - -C API ------ - -- PyType_Ready(): If a type declares that it participates in gc - (Py_TPFLAGS_HAVE_GC), and its base class does not, and its base class's - tp_free slot is the default _PyObject_Del, and type does not define - a tp_free slot itself, _PyObject_GC_Del is assigned to type->tp_free. - Previously _PyObject_Del was inherited, which could at best lead to a - segfault. In addition, if even after this magic the type's tp_free - slot is _PyObject_Del or NULL, and the type is a base type - (Py_TPFLAGS_BASETYPE), TypeError is raised: since the type is a base - type, its dealloc function must call type->tp_free, and since the type - is gc'able, tp_free must not be NULL or _PyObject_Del. - -- PyThreadState_SetAsyncExc(): A new API (deliberately accessible only - from C) to interrupt a thread by sending it an exception. It is - intentional that you have to write your own C extension to call it - from Python. - - -New platforms -------------- - -None this time. - -Tests ------ - -- test_imp rewritten so that it doesn't raise RuntimeError if run as a - side effect of being imported ("import test.autotest"). - -Windows -------- - -- The Windows installer ships with Tcl/Tk 8.4.3 (upgraded from 8.4.1). - -- The installer always suggested that Python be installed on the C: - drive, due to a hardcoded "C:" generated by the Wise installation - wizard. People with machines where C: is not the system drive - usually want Python installed on whichever drive is their system drive - instead. We removed the hardcoded "C:", and two testers on machines - where C: is not the system drive report that the installer now - suggests their system drive. Note that you can always select the - directory you want in the "Select Destination Directory" dialog -- - that's what it's for. - -Mac ---- - -- There's a new module called "autoGIL", which offers a mechanism to - automatically release the Global Interpreter Lock when an event loop - goes to sleep, allowing other threads to run. It's currently only - supported on OSX, in the Mach-O version. -- The OSA modules now allow direct access to properties of the - toplevel application class (in AppleScript terminology). -- The Package Manager can now update itself. - -SourceForge Bugs and Patches Applied ------------------------------------- - -430160, 471893, 501716, 542562, 549151, 569574, 595837, 596434, -598163, 604210, 604716, 610332, 612627, 614770, 620190, 621891, -622042, 639139, 640236, 644345, 649742, 649742, 658233, 660022, -661318, 661676, 662807, 662923, 666219, 672855, 678325, 682347, -683486, 684981, 685773, 686254, 692776, 692959, 693094, 696777, -697989, 700827, 703666, 708495, 708604, 708901, 710733, 711902, -713722, 715782, 718286, 719359, 719367, 723136, 723831, 723962, -724588, 724767, 724767, 725942, 726150, 726446, 726869, 727051, -727719, 727719, 727805, 728277, 728563, 728656, 729096, 729103, -729293, 729297, 729300, 729317, 729395, 729622, 729817, 730170, -730296, 730594, 730685, 730826, 730963, 731209, 731403, 731504, -731514, 731626, 731635, 731643, 731644, 731644, 731689, 732124, -732143, 732234, 732284, 732284, 732479, 732761, 732783, 732951, -733667, 733781, 734118, 734231, 734869, 735051, 735293, 735527, -735613, 735694, 736962, 736962, 737970, 738066, 739313, 740055, -740234, 740301, 741806, 742126, 742741, 742860, 742860, 742911, -744041, 744104, 744238, 744687, 744877, 745055, 745478, 745525, -745620, 746012, 746304, 746366, 746801, 746953, 747348, 747667, -747954, 748846, 748849, 748973, 748975, 749191, 749210, 749759, -749831, 749911, 750008, 750092, 750542, 750595, 751038, 751107, -751276, 751451, 751916, 751941, 751956, 751998, 752671, 753451, -753602, 753617, 753845, 753925, 754014, 754340, 754447, 755031, -755087, 755147, 755245, 755683, 755987, 756032, 756996, 757058, -757229, 757818, 757821, 757822, 758112, 758910, 759227, 759889, -760257, 760703, 760792, 761104, 761337, 761519, 761830, 762455 - - -What's New in Python 2.3 beta 1? -================================ - -*Release date: 25-Apr-2003* - -Core and builtins ------------------ - -- New format codes B, H, I, k and K have been implemented for - PyArg_ParseTuple and PyBuild_Value. - -- New builtin function sum(seq, start=0) returns the sum of all the - items in iterable object seq, plus start (items are normally numbers, - and cannot be strings). - -- bool() called without arguments now returns False rather than - raising an exception. This is consistent with calling the - constructors for the other builtin types -- called without argument - they all return the false value of that type. (SF patch #724135) - -- In support of PEP 269 (making the pgen parser generator accessible - from Python), some changes to the pgen code structure were made; a - few files that used to be linked only with pgen are now linked with - Python itself. - -- The repr() of a weakref object now shows the __name__ attribute of - the referenced object, if it has one. - -- super() no longer ignores data descriptors, except __class__. See - the thread started at - http://mail.python.org/pipermail/python-dev/2003-April/034338.html - -- list.insert(i, x) now interprets negative i as it would be - interpreted by slicing, so negative values count from the end of the - list. This was the only place where such an interpretation was not - placed on a list index. - -- range() now works even if the arguments are longs with magnitude - larger than sys.maxint, as long as the total length of the sequence - fits. E.g., range(2**100, 2**101, 2**100) is the following list: - [1267650600228229401496703205376L]. (SF patch #707427.) - -- Some horridly obscure problems were fixed involving interaction - between garbage collection and old-style classes with "ambitious" - getattr hooks. If an old-style instance didn't have a __del__ method, - but did have a __getattr__ hook, and the instance became reachable - only from an unreachable cycle, and the hook resurrected or deleted - unreachable objects when asked to resolve "__del__", anything up to - a segfault could happen. That's been repaired. - -- dict.pop now takes an optional argument specifying a default - value to return if the key is not in the dict. If a default is not - given and the key is not found, a KeyError will still be raised. - Parallel changes were made to UserDict.UserDict and UserDict.DictMixin. - [SF patch #693753] (contributed by Michael Stone.) - -- sys.getfilesystemencoding() was added to expose - Py_FileSystemDefaultEncoding. - -- New function sys.exc_clear() clears the current exception. This is - rarely needed, but can sometimes be useful to release objects - referenced by the traceback held in sys.exc_info()[2]. (SF patch - #693195.) - -- On 64-bit systems, a dictionary could contain duplicate long/int keys - if the key value was larger than 2**32. See SF bug #689659. - -- Fixed SF bug #663074. The codec system was using global static - variables to store internal data. As a result, any attempts to use the - unicode system with multiple active interpreters, or successive - interpreter executions, would fail. - -- "%c" % u"a" now returns a unicode string instead of raising a - TypeError. u"%c" % 0xffffffff now raises a OverflowError instead - of a ValueError to be consistent with "%c" % 256. See SF patch #710127. - -Extension modules ------------------ - -- The socket module now provides the functions inet_pton and inet_ntop - for converting between string and packed representation of IP - addresses. There is also a new module variable, has_ipv6, which is - True iff the current Python has IPv6 support. See SF patch #658327. - -- Tkinter wrappers around Tcl variables now pass objects directly - to Tcl, instead of first converting them to strings. - -- The .*? pattern in the re module is now special-cased to avoid the - recursion limit. (SF patch #720991 -- many thanks to Gary Herron - and Greg Chapman.) - -- New function sys.call_tracing() allows pdb to debug code - recursively. - -- New function gc.get_referents(obj) returns a list of objects - directly referenced by obj. In effect, it exposes what the object's - tp_traverse slot does, and can be helpful when debugging memory - leaks. - -- The iconv module has been removed from this release. - -- The platform-independent routines for packing floats in IEEE formats - (struct.pack's f, d codes; pickle and cPickle's protocol 1 - pickling of floats) ignored that rounding can cause a carry to - propagate. The worst consequence was that, in rare cases, f - could produce strings that, when unpacked again, were a factor of 2 - away from the original float. This has been fixed. See SF bug - #705836. - -- New function time.tzset() provides access to the C library tzset() - function, if supported. (SF patch #675422.) - -- Using createfilehandler, deletefilehandler, createtimerhandler functions - on Tkinter.tkinter (_tkinter module) no longer crashes the interpreter. - See SF bug #692416. - -- Modified the fcntl.ioctl() function to allow modification of a passed - mutable buffer (for details see the reference documentation). - -- Made user requested changes to the itertools module. - Subsumed the times() function into repeat(). - Added chain() and cycle(). - -- The rotor module is now deprecated; the encryption algorithm it uses - is not believed to be secure, and including crypto code with Python - has implications for exporting and importing it in various countries. - -- The socket module now always uses the _socketobject wrapper class, even on - platforms which have dup(2). The makefile() method is built directly - on top of the socket without duplicating the file descriptor, allowing - timeouts to work properly. - -Library -------- - -- New generator function os.walk() is an easy-to-use alternative to - os.path.walk(). See os module docs for details. os.path.walk() - isn't deprecated at this time, but may become deprecated in a - future release. - -- Added new module "platform" which provides a wide range of tools - for querying platform dependent features. - -- netrc now allows ASCII punctuation characters in passwords. - -- shelve now supports the optional writeback argument, and exposes - pickle protocol versions. - -- Several methods of nntplib.NNTP have grown an optional file argument - which specifies a file where to divert the command's output - (already supported by the body() method). (SF patch #720468) - -- The self-documenting XML server library DocXMLRPCServer was added. - -- Support for internationalized domain names has been added through - the 'idna' and 'punycode' encodings, the 'stringprep' module, the - 'mkstringprep' tool, and enhancements to the socket and httplib - modules. - -- htmlentitydefs has two new dictionaries: name2codepoint maps - HTML entity names to Unicode codepoints (as integers). - codepoint2name is the reverse mapping. See SF patch #722017. - -- pdb has a new command, "debug", which lets you step through - arbitrary code from the debugger's (pdb) prompt. - -- unittest.failUnlessEqual and its equivalent unittest.assertEqual now - return 'not a == b' rather than 'a != b'. This gives the desired - result for classes that define __eq__ without defining __ne__. - -- sgmllib now supports SGML marked sections, in particular the - MS Office extensions. - -- The urllib module now offers support for the iterator protocol. - SF patch 698520 contributed by Brett Cannon. - -- New module timeit provides a simple framework for timing the - execution speed of expressions and statements. - -- sets.Set objects now support mixed-type __eq__ and __ne__, instead - of raising TypeError. If x is a Set object and y is a non-Set object, - x == y is False, and x != y is True. This is akin to the change made - for mixed-type comparisons of datetime objects in 2.3a2; more info - about the rationale is in the NEWS entry for that. See also SF bug - report . - -- On Unix platforms, if os.listdir() is called with a Unicode argument, - it now returns Unicode strings. (This behavior was added earlier - to the Windows NT/2k/XP version of os.listdir().) - -- Distutils: both 'py_modules' and 'packages' keywords can now be specified - in core.setup(). Previously you could supply one or the other, but - not both of them. (SF patch #695090 from Bernhard Herzog) - -- New csv package makes it easy to read/write CSV files. - -- Module shlex has been extended to allow posix-like shell parsings, - including a split() function for easy spliting of quoted strings and - commands. An iterator interface was also implemented. - -Tools/Demos ------------ - -- New script combinerefs.py helps analyze new PYTHONDUMPREFS output. - See the module docstring for details. - -Build ------ - -- Fix problem building on OSF1 because the compiler only accepted - preprocessor directives that start in column 1. (SF bug #691793.) - -C API ------ - -- Added PyGC_Collect(), equivalent to calling gc.collect(). - -- PyThreadState_GetDict() was changed not to raise an exception or - issue a fatal error when no current thread state is available. This - makes it possible to print dictionaries when no thread is active. - -- LONG_LONG was renamed to PY_LONG_LONG. Extensions that use this and - need compatibility with previous versions can use this: - - #ifndef PY_LONG_LONG - #define PY_LONG_LONG LONG_LONG - #endif - -- Added PyObject_SelfIter() to fill the tp_iter slot for the - typical case where the method returns its self argument. - -- The extended type structure used for heap types (new-style - classes defined by Python code using a class statement) is now - exported from object.h as PyHeapTypeObject. (SF patch #696193.) - -New platforms -------------- - -None this time. - -Tests ------ - -- test_timeout now requires -u network to be passed to regrtest to run. - See SF bug #692988. - -Windows -------- - -- os.fsync() now exists on Windows, and calls the Microsoft _commit() - function. - -- New function winsound.MessageBeep() wraps the Win32 API - MessageBeep(). - -Mac ---- - -- os.listdir() now returns Unicode strings on MacOS X when called with - a Unicode argument. See the general news item under "Library". - -- A new method MacOS.WMAvailable() returns true if it is safe to access - the window manager, false otherwise. - -- EasyDialogs dialogs are now movable-modal, and if the application is - currently in the background they will ask to be moved to the foreground - before displaying. - -- OSA Scripting support has improved a lot, and gensuitemodule.py can now - be used by mere mortals. The documentation is now also more or less - complete. - -- The IDE (in a framework build) now includes introductory documentation - in Apple Help Viewer format. - - -What's New in Python 2.3 alpha 2? -================================= - -*Release date: 19-Feb-2003* - -Core and builtins ------------------ - -- Negative positions returned from PEP 293 error callbacks are now - treated as being relative to the end of the input string. Positions - that are out of bounds raise an IndexError. - -- sys.path[0] (the directory from which the script is loaded) is now - turned into an absolute pathname, unless it is the empty string. - (SF patch #664376.) - -- Finally fixed the bug in compile() and exec where a string ending - with an indented code block but no newline would raise SyntaxError. - This would have been a four-line change in parsetok.c... Except - codeop.py depends on this behavior, so a compilation flag had to be - invented that causes the tokenizer to revert to the old behavior; - this required extra changes to 2 .h files, 2 .c files, and 2 .py - files. (Fixes SF bug #501622.) - -- If a new-style class defines neither __new__ nor __init__, its - constructor would ignore all arguments. This is changed now: the - constructor refuses arguments in this case. This might break code - that worked under Python 2.2. The simplest fix is to add a no-op - __init__: ``def __init__(self, *args, **kw): pass``. - -- Through a bytecode optimizer bug (and I bet you didn't even know - Python *had* a bytecode optimizer :-), "unsigned" hex/oct constants - with a leading minus sign would come out with the wrong sign. - ("Unsigned" hex/oct constants are those with a face value in the - range sys.maxint+1 through sys.maxint*2+1, inclusive; these have - always been interpreted as negative numbers through sign folding.) - E.g. 0xffffffff is -1, and -(0xffffffff) is 1, but -0xffffffff would - come out as -4294967295. This was the case in Python 2.2 through - 2.2.2 and 2.3a1, and in Python 2.4 it will once again have that - value, but according to PEP 237 it really needs to be 1 now. This - will be backported to Python 2.2.3 a well. (SF #660455) - -- int(s, base) sometimes sign-folds hex and oct constants; it only - does this when base is 0 and s.strip() starts with a '0'. When the - sign is actually folded, as in int("0xffffffff", 0) on a 32-bit - machine, which returns -1, a FutureWarning is now issued; in Python - 2.4, this will return 4294967295L, as do int("+0xffffffff", 0) and - int("0xffffffff", 16) right now. (PEP 347) - -- super(X, x): x may now be a proxy for an X instance, i.e. - issubclass(x.__class__, X) but not issubclass(type(x), X). - -- isinstance(x, X): if X is a new-style class, this is now equivalent - to issubclass(type(x), X) or issubclass(x.__class__, X). Previously - only type(x) was tested. (For classic classes this was already the - case.) - -- compile(), eval() and the exec statement now fully support source code - passed as unicode strings. - -- int subclasses can be initialized with longs if the value fits in an int. - See SF bug #683467. - -- long(string, base) takes time linear in len(string) when base is a power - of 2 now. It used to take time quadratic in len(string). - -- filter returns now Unicode results for Unicode arguments. - -- raw_input can now return Unicode objects. - -- List objects' sort() method now accepts None as the comparison function. - Passing None is semantically identical to calling sort() with no - arguments. - -- Fixed crash when printing a subclass of str and __str__ returned self. - See SF bug #667147. - -- Fixed an invalid RuntimeWarning and an undetected error when trying - to convert a long integer into a float which couldn't fit. - See SF bug #676155. - -- Function objects now have a __module__ attribute that is bound to - the name of the module in which the function was defined. This - applies for C functions and methods as well as functions and methods - defined in Python. This attribute is used by pickle.whichmodule(), - which changes the behavior of whichmodule slightly. In Python 2.2 - whichmodule() returns "__main__" for functions that are not defined - at the top-level of a module (examples: methods, nested functions). - Now whichmodule() will return the proper module name. - -Extension modules ------------------ - -- operator.isNumberType() now checks that the object has a nb_int or - nb_float slot, rather than simply checking whether it has a non-NULL - tp_as_number pointer. - -- The imp module now has ways to acquire and release the "import - lock": imp.acquire_lock() and imp.release_lock(). Note: this is a - reentrant lock, so releasing the lock only truly releases it when - this is the last release_lock() call. You can check with - imp.lock_held(). (SF bug #580952 and patch #683257.) - -- Change to cPickle to match pickle.py (see below and PEP 307). - -- Fix some bugs in the parser module. SF bug #678518. - -- Thanks to Scott David Daniels, a subtle bug in how the zlib - extension implemented flush() was fixed. Scott also rewrote the - zlib test suite using the unittest module. (SF bug #640230 and - patch #678531.) - -- Added an itertools module containing high speed, memory efficient - looping constructs inspired by tools from Haskell and SML. - -- The SSL module now handles sockets with a timeout set correctly (SF - patch #675750, fixing SF bug #675552). - -- os/posixmodule has grown the sysexits.h constants (EX_OK and friends). - -- Fixed broken threadstate swap in readline that could cause fatal - errors when a readline hook was being invoked while a background - thread was active. (SF bugs #660476 and #513033.) - -- fcntl now exposes the strops.h I_* constants. - -- Fix a crash on Solaris that occurred when calling close() on - an mmap'ed file which was already closed. (SF patch #665913) - -- Fixed several serious bugs in the zipimport implementation. - -- datetime changes: - - The date class is now properly subclassable. (SF bug #720908) - - The datetime and datetimetz classes have been collapsed into a single - datetime class, and likewise the time and timetz classes into a single - time class. Previously, a datetimetz object with tzinfo=None acted - exactly like a datetime object, and similarly for timetz. This wasn't - enough of a difference to justify distinct classes, and life is simpler - now. - - today() and now() now round system timestamps to the closest - microsecond . This repairs an - irritation most likely seen on Windows systems. - - In dt.astimezone(tz), if tz.utcoffset(dt) returns a duration, - ValueError is raised if tz.dst(dt) returns None (2.3a1 treated it - as 0 instead, but a tzinfo subclass wishing to participate in - time zone conversion has to take a stand on whether it supports - DST; if you don't care about DST, then code dst() to return 0 minutes, - meaning that DST is never in effect). - - The tzinfo methods utcoffset() and dst() must return a timedelta object - (or None) now. In 2.3a1 they could also return an int or long, but that - was an unhelpfully redundant leftover from an earlier version wherein - they couldn't return a timedelta. TOOWTDI. - - The example tzinfo class for local time had a bug. It was replaced - by a later example coded by Guido. - - datetime.astimezone(tz) no longer raises an exception when the - input datetime has no UTC equivalent in tz. For typical "hybrid" time - zones (a single tzinfo subclass modeling both standard and daylight - time), this case can arise one hour per year, at the hour daylight time - ends. See new docs for details. In short, the new behavior mimics - the local wall clock's behavior of repeating an hour in local time. - - dt.astimezone() can no longer be used to convert between naive and aware - datetime objects. If you merely want to attach, or remove, a tzinfo - object, without any conversion of date and time members, use - dt.replace(tzinfo=whatever) instead, where "whatever" is None or a - tzinfo subclass instance. - - A new method tzinfo.fromutc(dt) can be overridden in tzinfo subclasses - to give complete control over how a UTC time is to be converted to - a local time. The default astimezone() implementation calls fromutc() - as its last step, so a tzinfo subclass can affect that too by overriding - fromutc(). It's expected that the default fromutc() implementation will - be suitable as-is for "almost all" time zone subclasses, but the - creativity of political time zone fiddling appears unbounded -- fromutc() - allows the highly motivated to emulate any scheme expressible in Python. - - datetime.now(): The optional tzinfo argument was undocumented (that's - repaired), and its name was changed to tz ("tzinfo" is overloaded enough - already). With a tz argument, now(tz) used to return the local date - and time, and attach tz to it, without any conversion of date and time - members. This was less than useful. Now now(tz) returns the current - date and time as local time in tz's time zone, akin to :: - - tz.fromutc(datetime.utcnow().replace(tzinfo=utc)) - - where "utc" is an instance of a tzinfo subclass modeling UTC. Without - a tz argument, now() continues to return the current local date and time, - as a naive datetime object. - - datetime.fromtimestamp(): Like datetime.now() above, this had less than - useful behavior when the optional tinzo argument was specified. See - also SF bug report . - - date and datetime comparison: In order to prevent comparison from - falling back to the default compare-object-addresses strategy, these - raised TypeError whenever they didn't understand the other object type. - They still do, except when the other object has a "timetuple" attribute, - in which case they return NotImplemented now. This gives other - datetime objects (e.g., mxDateTime) a chance to intercept the - comparison. - - date, time, datetime and timedelta comparison: When the exception - for mixed-type comparisons in the last paragraph doesn't apply, if - the comparison is == then False is returned, and if the comparison is - != then True is returned. Because dict lookup and the "in" operator - only invoke __eq__, this allows, for example, :: - - if some_datetime in some_sequence: - - and :: - - some_dict[some_timedelta] = whatever - - to work as expected, without raising TypeError just because the - sequence is heterogeneous, or the dict has mixed-type keys. [This - seems like a good idea to implement for all mixed-type comparisons - that don't want to allow falling back to address comparison.] - - The constructors building a datetime from a timestamp could raise - ValueError if the platform C localtime()/gmtime() inserted "leap - seconds". Leap seconds are ignored now. On such platforms, it's - possible to have timestamps that differ by a second, yet where - datetimes constructed from them are equal. - - The pickle format of date, time and datetime objects has changed - completely. The undocumented pickler and unpickler functions no - longer exist. The undocumented __setstate__() and __getstate__() - methods no longer exist either. - -Library -------- - -- The logging module was updated slightly; the WARN level was renamed - to WARNING, and the matching function/method warn() to warning(). - -- The pickle and cPickle modules were updated with a new pickling - protocol (documented by pickletools.py, see below) and several - extensions to the pickle customization API (__reduce__, __setstate__ - etc.). The copy module now uses more of the pickle customization - API to copy objects that don't implement __copy__ or __deepcopy__. - See PEP 307 for details. - -- The distutils "register" command now uses http://www.python.org/pypi - as the default repository. (See PEP 301.) - -- the platform dependent path related variables sep, altsep, extsep, - pathsep, curdir, pardir and defpath are now defined in the platform - dependent path modules (e.g. ntpath.py) rather than os.py, so these - variables are now available via os.path. They continue to be - available from the os module. - (see ). - -- array.array was added to the types repr.py knows about (see - ). - -- The new pickletools.py contains lots of documentation about pickle - internals, and supplies some helpers for working with pickles, such as - a symbolic pickle disassembler. - -- Xmlrpclib.py now supports the builtin boolean type. - -- py_compile has a new 'doraise' flag and a new PyCompileError - exception. - -- SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler - class. - -- The sets module now raises TypeError in __cmp__, to clarify that - sets are not intended to be three-way-compared; the comparison - operators are overloaded as subset/superset tests. - -- Bastion.py and rexec.py are disabled. These modules are not safe in - Python 2.2. or 2.3. - -- realpath is now exported when doing ``from poxixpath import *``. - It is also exported for ntpath, macpath, and os2emxpath. - See SF bug #659228. - -- New module tarfile from Lars Gustäbel provides a comprehensive interface - to tar archive files with transparent gzip and bzip2 compression. - See SF patch #651082. - -- urlparse can now parse imap:// URLs. See SF feature request #618024. - -- Tkinter.Canvas.scan_dragto() provides an optional parameter to support - the gain value which is passed to Tk. SF bug# 602259. - -- Fix logging.handlers.SysLogHandler protocol when using UNIX domain sockets. - See SF patch #642974. - -- The dospath module was deleted. Use the ntpath module when manipulating - DOS paths from other platforms. - -Tools/Demos ------------ - -- Two new scripts (db2pickle.py and pickle2db.py) were added to the - Tools/scripts directory to facilitate conversion from the old bsddb module - to the new one. While the user-visible API of the new module is - compatible with the old one, it's likely that the version of the - underlying database library has changed. To convert from the old library, - run the db2pickle.py script using the old version of Python to convert it - to a pickle file. After upgrading Python, run the pickle2db.py script - using the new version of Python to reconstitute your database. For - example: - - % python2.2 db2pickle.py -h some.db > some.pickle - % python2.3 pickle2db.py -h some.db.new < some.pickle - - Run the scripts without any args to get a usage message. - - -Build ------ - -- The audio driver tests (test_ossaudiodev.py and - test_linuxaudiodev.py) are no longer run by default. This is - because they don't always work, depending on your hardware and - software. To run these tests, you must use an invocation like :: - - ./python Lib/test/regrtest.py -u audio test_ossaudiodev - -- On systems which build using the configure script, compiler flags which - used to be lumped together using the OPT flag have been split into two - groups, OPT and BASECFLAGS. OPT is meant to carry just optimization- and - debug-related flags like "-g" and "-O3". BASECFLAGS is meant to carry - compiler flags that are required to get a clean compile. On some - platforms (many Linux flavors in particular) BASECFLAGS will be empty by - default. On others, such as Mac OS X and SCO, it will contain required - flags. This change allows people building Python to override OPT without - fear of clobbering compiler flags which are required to get a clean build. - -- On Darwin/Mac OS X platforms, /sw/lib and /sw/include are added to the - relevant search lists in setup.py. This allows users building Python to - take advantage of the many packages available from the fink project - . - -- A new Makefile target, scriptsinstall, installs a number of useful scripts - from the Tools/scripts directory. - -C API ------ - -- PyEval_GetFrame() is now declared to return a ``PyFrameObject *`` - instead of a plain ``PyObject *``. (SF patch #686601.) - -- PyNumber_Check() now checks that the object has a nb_int or nb_float - slot, rather than simply checking whether it has a non-NULL - tp_as_number pointer. - -- A C type that inherits from a base type that defines tp_as_buffer - will now inherit the tp_as_buffer pointer if it doesn't define one. - (SF #681367) - -- The PyArg_Parse functions now issue a DeprecationWarning if a float - argument is provided when an integer is specified (this affects the 'b', - 'B', 'h', 'H', 'i', and 'l' codes). Future versions of Python will - raise a TypeError. - -Tests ------ - -- Several tests weren't being run from regrtest.py (test_timeout.py, - test_tarfile.py, test_netrc.py, test_multifile.py, - test_importhooks.py and test_imp.py). Now they are. (Note to - developers: please read Lib/test/README when creating a new test, to - make sure to do it right! All tests need to use either unittest or - pydoc.) - -- Added test_posix.py, a test suite for the posix module. - -- Added test_hexoct.py, a test suite for hex/oct constant folding. - -Windows -------- - -- The timeout code for socket connect() didn't work right; this has - now been fixed. test_timeout.py should pass (at least most of the - time). - -- distutils' msvccompiler class now passes the preprocessor options to - the resource compiler. See SF patch #669198. - -- The bsddb module now ships with Sleepycat's 4.1.25.NC, the latest - release without strong cryptography. - -- sys.path[0], if it contains a directory name, is now always an - absolute pathname. (SF patch #664376.) - -- The new logging package is now installed by the Windows installer. It - wasn't in 2.3a1 due to oversight. - -Mac ---- - -- There are new dialogs EasyDialogs.AskFileForOpen, AskFileForSave - and AskFolder. The old macfs.StandardGetFile and friends are deprecated. - -- Most of the standard library now uses pathnames or FSRefs in preference - of FSSpecs, and use the underlying Carbon.File and Carbon.Folder modules - in stead of macfs. macfs will probably be deprecated in the future. - -- Type Carbon.File.FSCatalogInfo and supporting methods have been implemented. - This also makes macfs.FSSpec.SetDates() work again. - -- There is a new module pimp, the package install manager for Python, and - accompanying applet PackageManager. These allow you to easily download - and install pretested extension packages either in source or binary - form. Only in MacPython-OSX. - -- Applets are now built with bundlebuilder in MacPython-OSX, which should make - them more robust and also provides a path towards BuildApplication. The - downside of this change is that applets can no longer be run from the - Terminal window, this will hopefully be fixed in the 2.3b1. - - -What's New in Python 2.3 alpha 1? -================================= - -*Release date: 31-Dec-2002* - -Type/class unification and new-style classes --------------------------------------------- - -- One can now assign to __bases__ and __name__ of new-style classes. - -- dict() now accepts keyword arguments so that dict(one=1, two=2) - is the equivalent of {"one": 1, "two": 2}. Accordingly, - the existing (but undocumented) 'items' keyword argument has - been eliminated. This means that dict(items=someMapping) now has - a different meaning than before. - -- int() now returns a long object if the argument is outside the - integer range, so int("4" * 1000), int(1e200) and int(1L<<1000) will - all return long objects instead of raising an OverflowError. - -- Assignment to __class__ is disallowed if either the old or the new - class is a statically allocated type object (such as defined by an - extension module). This prevents anomalies like 2.__class__ = bool. - -- New-style object creation and deallocation have been sped up - significantly; they are now faster than classic instance creation - and deallocation. - -- The __slots__ variable can now mention "private" names, and the - right thing will happen (e.g. __slots__ = ["__foo"]). - -- The built-ins slice() and buffer() are now callable types. The - types classobj (formerly class), code, function, instance, and - instancemethod (formerly instance-method), which have no built-in - names but are accessible through the types module, are now also - callable. The type dict-proxy is renamed to dictproxy. - -- Cycles going through the __class__ link of a new-style instance are - now detected by the garbage collector. - -- Classes using __slots__ are now properly garbage collected. - [SF bug 519621] - -- Tightened the __slots__ rules: a slot name must be a valid Python - identifier. - -- The constructor for the module type now requires a name argument and - takes an optional docstring argument. Previously, this constructor - ignored its arguments. As a consequence, deriving a class from a - module (not from the module type) is now illegal; previously this - created an unnamed module, just like invoking the module type did. - [SF bug 563060] - -- A new type object, 'basestring', is added. This is a common base type - for 'str' and 'unicode', and can be used instead of - types.StringTypes, e.g. to test whether something is "a string": - isinstance(x, basestring) is True for Unicode and 8-bit strings. This - is an abstract base class and cannot be instantiated directly. - -- Changed new-style class instantiation so that when C's __new__ - method returns something that's not a C instance, its __init__ is - not called. [SF bug #537450] - -- Fixed super() to work correctly with class methods. [SF bug #535444] - -- If you try to pickle an instance of a class that has __slots__ but - doesn't define or override __getstate__, a TypeError is now raised. - This is done by adding a bozo __getstate__ to the class that always - raises TypeError. (Before, this would appear to be pickled, but the - state of the slots would be lost.) - -Core and builtins ------------------ - -- Import from zipfiles is now supported. The name of a zipfile placed - on sys.path causes the import statement to look for importable Python - modules (with .py, pyc and .pyo extensions) and packages inside the - zipfile. The zipfile import follows the specification (though not - the sample implementation) of PEP 273. The semantics of __path__ are - compatible with those that have been implemented in Jython since - Jython 2.1. - -- PEP 302 has been accepted. Although it was initially developed to - support zipimport, it offers a new, general import hook mechanism. - Several new variables have been added to the sys module: - sys.meta_path, sys.path_hooks, and sys.path_importer_cache; these - make extending the import statement much more convenient than - overriding the __import__ built-in function. For a description of - these, see PEP 302. - -- A frame object's f_lineno attribute can now be written to from a - trace function to change which line will execute next. A command to - exploit this from pdb has been added. [SF patch #643835] - -- The _codecs support module for codecs.py was turned into a builtin - module to assure that at least the builtin codecs are available - to the Python parser for source code decoding according to PEP 263. - -- issubclass now supports a tuple as the second argument, just like - isinstance does. ``issubclass(X, (A, B))`` is equivalent to - ``issubclass(X, A) or issubclass(X, B)``. - -- Thanks to Armin Rigo, the last known way to provoke a system crash - by cleverly arranging for a comparison function to mutate a list - during a list.sort() operation has been fixed. The effect of - attempting to mutate a list, or even to inspect its contents or - length, while a sort is in progress, is not defined by the language. - The C implementation of Python 2.3 attempts to detect mutations, - and raise ValueError if one occurs, but there's no guarantee that - all mutations will be caught, or that any will be caught across - releases or implementations. - -- Unicode file name processing for Windows (PEP 277) is implemented. - All platforms now have an os.path.supports_unicode_filenames attribute, - which is set to True on Windows NT/2000/XP, and False elsewhere. - -- Codec error handling callbacks (PEP 293) are implemented. - Error handling in unicode.encode or str.decode can now be customized. - -- A subtle change to the semantics of the built-in function intern(): - interned strings are no longer immortal. You must keep a reference - to the return value intern() around to get the benefit. - -- Use of 'None' as a variable, argument or attribute name now - issues a SyntaxWarning. In the future, None may become a keyword. - -- SET_LINENO is gone. co_lnotab is now consulted to determine when to - call the trace function. C code that accessed f_lineno should call - PyCode_Addr2Line instead (f_lineno is still there, but only kept up - to date when there is a trace function set). - -- There's a new warning category, FutureWarning. This is used to warn - about a number of situations where the value or sign of an integer - result will change in Python 2.4 as a result of PEP 237 (integer - unification). The warnings implement stage B0 mentioned in that - PEP. The warnings are about the following situations: - - - Octal and hex literals without 'L' prefix in the inclusive range - [0x80000000..0xffffffff]; these are currently negative ints, but - in Python 2.4 they will be positive longs with the same bit - pattern. - - - Left shifts on integer values that cause the outcome to lose - bits or have a different sign than the left operand. To be - precise: x< -*-" in the first - or second line of a Python source file indicates the encoding. - -- list.sort() has a new implementation. While cross-platform results - may vary, and in data-dependent ways, this is much faster on many - kinds of partially ordered lists than the previous implementation, - and reported to be just as fast on randomly ordered lists on - several major platforms. This sort is also stable (if A==B and A - precedes B in the list at the start, A precedes B after the sort too), - although the language definition does not guarantee stability. A - potential drawback is that list.sort() may require temp space of - len(list)*2 bytes (``*4`` on a 64-bit machine). It's therefore possible - for list.sort() to raise MemoryError now, even if a comparison function - does not. See for full details. - -- All standard iterators now ensure that, once StopIteration has been - raised, all future calls to next() on the same iterator will also - raise StopIteration. There used to be various counterexamples to - this behavior, which could caused confusion or subtle program - breakage, without any benefits. (Note that this is still an - iterator's responsibility; the iterator framework does not enforce - this.) - -- Ctrl+C handling on Windows has been made more consistent with - other platforms. KeyboardInterrupt can now reliably be caught, - and Ctrl+C at an interactive prompt no longer terminates the - process under NT/2k/XP (it never did under Win9x). Ctrl+C will - interrupt time.sleep() in the main thread, and any child processes - created via the popen family (on win2k; we can't make win9x work - reliably) are also interrupted (as generally happens on for Linux/Unix.) - [SF bugs 231273, 439992 and 581232] - -- sys.getwindowsversion() has been added on Windows. This - returns a tuple with information about the version of Windows - currently running. - -- Slices and repetitions of buffer objects now consistently return - a string. Formerly, strings would be returned most of the time, - but a buffer object would be returned when the repetition count - was one or when the slice range was all inclusive. - -- Unicode objects in sys.path are no longer ignored but treated - as directory names. - -- Fixed string.startswith and string.endswith builtin methods - so they accept negative indices. [SF bug 493951] - -- Fixed a bug with a continue inside a try block and a yield in the - finally clause. [SF bug 567538] - -- Most builtin sequences now support "extended slices", i.e. slices - with a third "stride" parameter. For example, "hello world"[::-1] - gives "dlrow olleh". - -- A new warning PendingDeprecationWarning was added to provide - direction on features which are in the process of being deprecated. - The warning will not be printed by default. To see the pending - deprecations, use -Walways::PendingDeprecationWarning:: - as a command line option or warnings.filterwarnings() in code. - -- Deprecated features of xrange objects have been removed as - promised. The start, stop, and step attributes and the tolist() - method no longer exist. xrange repetition and slicing have been - removed. - -- New builtin function enumerate(x), from PEP 279. Example: - enumerate("abc") is an iterator returning (0,"a"), (1,"b"), (2,"c"). - The argument can be an arbitrary iterable object. - -- The assert statement no longer tests __debug__ at runtime. This means - that assert statements cannot be disabled by assigning a false value - to __debug__. - -- A method zfill() was added to str and unicode, that fills a numeric - string to the left with zeros. For example, - "+123".zfill(6) -> "+00123". - -- Complex numbers supported divmod() and the // and % operators, but - these make no sense. Since this was documented, they're being - deprecated now. - -- String and unicode methods lstrip(), rstrip() and strip() now take - an optional argument that specifies the characters to strip. For - example, "Foo!!!?!?!?".rstrip("?!") -> "Foo". - -- There's a new dictionary constructor (a class method of the dict - class), dict.fromkeys(iterable, value=None). It constructs a - dictionary with keys taken from the iterable and all values set to a - single value. It can be used for building sets and for removing - duplicates from sequences. - -- Added a new dict method pop(key). This removes and returns the - value corresponding to key. [SF patch #539949] - -- A new built-in type, bool, has been added, as well as built-in - names for its two values, True and False. Comparisons and sundry - other operations that return a truth value have been changed to - return a bool instead. Read PEP 285 for an explanation of why this - is backward compatible. - -- Fixed two bugs reported as SF #535905: under certain conditions, - deallocating a deeply nested structure could cause a segfault in the - garbage collector, due to interaction with the "trashcan" code; - access to the current frame during destruction of a local variable - could access a pointer to freed memory. - -- The optional object allocator ("pymalloc") has been enabled by - default. The recommended practice for memory allocation and - deallocation has been streamlined. A header file is included, - Misc/pymemcompat.h, which can be bundled with 3rd party extensions - and lets them use the same API with Python versions from 1.5.2 - onwards. - -- PyErr_Display will provide file and line information for all exceptions - that have an attribute print_file_and_line, not just SyntaxErrors. - -- The UTF-8 codec will now encode and decode Unicode surrogates - correctly and without raising exceptions for unpaired ones. - -- Universal newlines (PEP 278) is implemented. Briefly, using 'U' - instead of 'r' when opening a text file for reading changes the line - ending convention so that any of '\r', '\r\n', and '\n' is - recognized (even mixed in one file); all three are converted to - '\n', the standard Python line end character. - -- file.xreadlines() now raises a ValueError if the file is closed: - Previously, an xreadlines object was returned which would raise - a ValueError when the xreadlines.next() method was called. - -- sys.exit() inadvertently allowed more than one argument. - An exception will now be raised if more than one argument is used. - -- Changed evaluation order of dictionary literals to conform to the - general left to right evaluation order rule. Now {f1(): f2()} will - evaluate f1 first. - -- Fixed bug #521782: when a file was in non-blocking mode, file.read() - could silently lose data or wrongly throw an unknown error. - -- The sq_repeat, sq_inplace_repeat, sq_concat and sq_inplace_concat - slots are now always tried after trying the corresponding nb_* slots. - This fixes a number of minor bugs (see bug #624807). - -- Fix problem with dynamic loading on 64-bit AIX (see bug #639945). - -Extension modules ------------------ - -- Added three operators to the operator module: - operator.pow(a,b) which is equivalent to: a**b. - operator.is_(a,b) which is equivalent to: a is b. - operator.is_not(a,b) which is equivalent to: a is not b. - -- posix.openpty now works on all systems that have /dev/ptmx. - -- A module zipimport exists to support importing code from zip - archives. - -- The new datetime module supplies classes for manipulating dates and - times. The basic design came from the Zope "fishbowl process", and - favors practical commercial applications over calendar esoterica. See - - http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage - -- _tkinter now returns Tcl objects, instead of strings. Objects which - have Python equivalents are converted to Python objects, other objects - are wrapped. This can be configured through the wantobjects method, - or Tkinter.wantobjects. - -- The PyBSDDB wrapper around the Sleepycat Berkeley DB library has - been added as the package bsddb. The traditional bsddb module is - still available in source code, but not built automatically anymore, - and is now named bsddb185. This supports Berkeley DB versions from - 3.0 to 4.1. For help converting your databases from the old module (which - probably used an obsolete version of Berkeley DB) to the new module, see - the db2pickle.py and pickle2db.py scripts described in the Tools/Demos - section above. - -- unicodedata was updated to Unicode 3.2. It supports normalization - and names for Hangul syllables and CJK unified ideographs. - -- resource.getrlimit() now returns longs instead of ints. - -- readline now dynamically adjusts its input/output stream if - sys.stdin/stdout changes. - -- The _tkinter module (and hence Tkinter) has dropped support for - Tcl/Tk 8.0 and 8.1. Only Tcl/Tk versions 8.2, 8.3 and 8.4 are - supported. - -- cPickle.BadPickleGet is now a class. - -- The time stamps in os.stat_result are floating point numbers - after stat_float_times has been called. - -- If the size passed to mmap.mmap() is larger than the length of the - file on non-Windows platforms, a ValueError is raised. [SF bug 585792] - -- The xreadlines module is slated for obsolescence. - -- The strptime function in the time module is now always available (a - Python implementation is used when the C library doesn't define it). - -- The 'new' module is no longer an extension, but a Python module that - only exists for backwards compatibility. Its contents are no longer - functions but callable type objects. - -- The bsddb.*open functions can now take 'None' as a filename. - This will create a temporary in-memory bsddb that won't be - written to disk. - -- posix.getloadavg, posix.lchown, posix.killpg, posix.mknod, and - posix.getpgid have been added where available. - -- The locale module now exposes the C library's gettext interface. It - also has a new function getpreferredencoding. - -- A security hole ("double free") was found in zlib-1.1.3, a popular - third party compression library used by some Python modules. The - hole was quickly plugged in zlib-1.1.4, and the Windows build of - Python now ships with zlib-1.1.4. - -- pwd, grp, and resource return enhanced tuples now, with symbolic - field names. - -- array.array is now a type object. A new format character - 'u' indicates Py_UNICODE arrays. For those, .tounicode and - .fromunicode methods are available. Arrays now support __iadd__ - and __imul__. - -- dl now builds on every system that has dlfcn.h. Failure in case - of sizeof(int)!=sizeof(long)!=sizeof(void*) is delayed until dl.open - is called. - -- The sys module acquired a new attribute, api_version, which evaluates - to the value of the PYTHON_API_VERSION macro with which the - interpreter was compiled. - -- Fixed bug #470582: sre module would return a tuple (None, 'a', 'ab') - when applying the regular expression '^((a)c)?(ab)$' on 'ab'. It now - returns (None, None, 'ab'), as expected. Also fixed handling of - lastindex/lastgroup match attributes in similar cases. For example, - when running the expression r'(a)(b)?b' over 'ab', lastindex must be - 1, not 2. - -- Fixed bug #581080: sre scanner was not checking the buffer limit - before increasing the current pointer. This was creating an infinite - loop in the search function, once the pointer exceeded the buffer - limit. - -- The os.fdopen function now enforces a file mode starting with the - letter 'r', 'w' or 'a', otherwise a ValueError is raised. This fixes - bug #623464. - -- The linuxaudiodev module is now deprecated; it is being replaced by - ossaudiodev. The interface has been extended to cover a lot more of - OSS (see www.opensound.com), including most DSP ioctls and the - OSS mixer API. Documentation forthcoming in 2.3a2. - -Library -------- - -- imaplib.py now supports SSL (Tino Lange and Piers Lauder). - -- Freeze's modulefinder.py has been moved to the standard library; - slightly improved so it will issue less false missing submodule - reports (see sf path #643711 for details). Documentation will follow - with Python 2.3a2. - -- os.path exposes getctime. - -- unittest.py now has two additional methods called assertAlmostEqual() - and failIfAlmostEqual(). They implement an approximate comparison - by rounding the difference between the two arguments and comparing - the result to zero. Approximate comparison is essential for - unit tests of floating point results. - -- calendar.py now depends on the new datetime module rather than - the time module. As a result, the range of allowable dates - has been increased. - -- pdb has a new 'j(ump)' command to select the next line to be - executed. - -- The distutils created windows installers now can run a - postinstallation script. - -- doctest.testmod can now be called without argument, which means to - test the current module. - -- When canceling a server that implemented threading with a keyboard - interrupt, the server would shut down but not terminate (waiting on - client threads). A new member variable, daemon_threads, was added to - the ThreadingMixIn class in SocketServer.py to make it explicit that - this behavior needs to be controlled. - -- A new module, optparse, provides a fancy alternative to getopt for - command line parsing. It is a slightly modified version of Greg - Ward's Optik package. - -- UserDict.py now defines a DictMixin class which defines all dictionary - methods for classes that already have a minimum mapping interface. - This greatly simplifies writing classes that need to be substitutable - for dictionaries (such as the shelve module). - -- shelve.py now subclasses from UserDict.DictMixin. Now shelve supports - all dictionary methods. This eases the transition to persistent - storage for scripts originally written with dictionaries in mind. - -- shelve.open and the various classes in shelve.py now accept an optional - binary flag, which defaults to False. If True, the values stored in the - shelf are binary pickles. - -- A new package, logging, implements the logging API defined by PEP - 282. The code is written by Vinay Sajip. - -- StreamReader, StreamReaderWriter and StreamRecoder in the codecs - modules are iterators now. - -- gzip.py now handles files exceeding 2GB. Files over 4GB also work - now (provided the OS supports it, and Python is configured with large - file support), but in that case the underlying gzip file format can - record only the least-significant 32 bits of the file size, so that - some tools working with gzipped files may report an incorrect file - size. - -- xml.sax.saxutils.unescape has been added, to replace entity references - with their entity value. - -- Queue.Queue.{put,get} now support an optional timeout argument. - -- Various features of Tk 8.4 are exposed in Tkinter.py. The multiple - option of tkFileDialog is exposed as function askopenfile{,name}s. - -- Various configure methods of Tkinter have been stream-lined, so that - tag_configure, image_configure, window_configure now return a - dictionary when invoked with no argument. - -- Importing the readline module now no longer has the side effect of - calling setlocale(LC_CTYPE, ""). The initial "C" locale, or - whatever locale is explicitly set by the user, is preserved. If you - want repr() of 8-bit strings in your preferred encoding to preserve - all printable characters of that encoding, you have to add the - following code to your $PYTHONSTARTUP file or to your application's - main(): - - import locale - locale.setlocale(locale.LC_CTYPE, "") - -- shutil.move was added. shutil.copytree now reports errors as an - exception at the end, instead of printing error messages. - -- Encoding name normalization was generalized to not only - replace hyphens with underscores, but also all other non-alphanumeric - characters (with the exception of the dot which is used for Python - package names during lookup). The aliases.py mapping was updated - to the new standard. - -- mimetypes has two new functions: guess_all_extensions() which - returns a list of all known extensions for a mime type, and - add_type() which adds one mapping between a mime type and - an extension to the database. - -- New module: sets, defines the class Set that implements a mutable - set type using the keys of a dict to represent the set. There's - also a class ImmutableSet which is useful when you need sets of sets - or when you need to use sets as dict keys, and a class BaseSet which - is the base class of the two. - -- Added random.sample(population,k) for random sampling without replacement. - Returns a k length list of unique elements chosen from the population. - -- random.randrange(-sys.maxint-1, sys.maxint) no longer raises - OverflowError. That is, it now accepts any combination of 'start' - and 'stop' arguments so long as each is in the range of Python's - bounded integers. - -- Thanks to Raymond Hettinger, random.random() now uses a new core - generator. The Mersenne Twister algorithm is implemented in C, - threadsafe, faster than the previous generator, has an astronomically - large period (2**19937-1), creates random floats to full 53-bit - precision, and may be the most widely tested random number generator - in existence. - - The random.jumpahead(n) method has different semantics for the new - generator. Instead of jumping n steps ahead, it uses n and the - existing state to create a new state. This means that jumpahead() - continues to support multi-threaded code needing generators of - non-overlapping sequences. However, it will break code which relies - on jumpahead moving a specific number of steps forward. - - The attributes random.whseed and random.__whseed have no meaning for - the new generator. Code using these attributes should switch to a - new class, random.WichmannHill which is provided for backward - compatibility and to make an alternate generator available. - -- New "algorithms" module: heapq, implements a heap queue. Thanks to - Kevin O'Connor for the code and François Pinard for an entertaining - write-up explaining the theory and practical uses of heaps. - -- New encoding for the Palm OS character set: palmos. - -- binascii.crc32() and the zipfile module had problems on some 64-bit - platforms. These have been fixed. On a platform with 8-byte C longs, - crc32() now returns a signed-extended 4-byte result, so that its value - as a Python int is equal to the value computed a 32-bit platform. - -- xml.dom.minidom.toxml and toprettyxml now take an optional encoding - argument. - -- Some fixes in the copy module: when an object is copied through its - __reduce__ method, there was no check for a __setstate__ method on - the result [SF patch 565085]; deepcopy should treat instances of - custom metaclasses the same way it treats instances of type 'type' - [SF patch 560794]. - -- Sockets now support timeout mode. After s.settimeout(T), where T is - a float expressing seconds, subsequent operations raise an exception - if they cannot be completed within T seconds. To disable timeout - mode, use s.settimeout(None). There's also a module function, - socket.setdefaulttimeout(T), which sets the default for all sockets - created henceforth. - -- getopt.gnu_getopt was added. This supports GNU-style option - processing, where options can be mixed with non-option arguments. - -- Stop using strings for exceptions. String objects used for - exceptions are now classes deriving from Exception. The objects - changed were: Tkinter.TclError, bdb.BdbQuit, macpath.norm_error, - tabnanny.NannyNag, and xdrlib.Error. - -- Constants BOM_UTF8, BOM_UTF16, BOM_UTF16_LE, BOM_UTF16_BE, - BOM_UTF32, BOM_UTF32_LE and BOM_UTF32_BE that represent the Byte - Order Mark in UTF-8, UTF-16 and UTF-32 encodings for little and - big endian systems were added to the codecs module. The old names - BOM32_* and BOM64_* were off by a factor of 2. - -- Added conversion functions math.degrees() and math.radians(). - -- math.log() now takes an optional argument: math.log(x[, base]). - -- ftplib.retrlines() now tests for callback is None rather than testing - for False. Was causing an error when given a callback object which - was callable but also returned len() as zero. The change may - create new breakage if the caller relied on the undocumented behavior - and called with callback set to [] or some other False value not - identical to None. - -- random.gauss() uses a piece of hidden state used by nothing else, - and the .seed() and .whseed() methods failed to reset it. In other - words, setting the seed didn't completely determine the sequence of - results produced by random.gauss(). It does now. Programs repeatedly - mixing calls to a seed method with calls to gauss() may see different - results now. - -- The pickle.Pickler class grew a clear_memo() method to mimic that - provided by cPickle.Pickler. - -- difflib's SequenceMatcher class now does a dynamic analysis of - which elements are so frequent as to constitute noise. For - comparing files as sequences of lines, this generally works better - than the IS_LINE_JUNK function, and function ndiff's linejunk - argument defaults to None now as a result. A happy benefit is - that SequenceMatcher may run much faster now when applied - to large files with many duplicate lines (for example, C program - text with lots of repeated "}" and "return NULL;" lines). - -- New Text.dump() method in Tkinter module. - -- New distutils commands for building packagers were added to - support pkgtool on Solaris and swinstall on HP-UX. - -- distutils now has a new abstract binary packager base class - command/bdist_packager, which simplifies writing packagers. - This will hopefully provide the missing bits to encourage - people to submit more packagers, e.g. for Debian, FreeBSD - and other systems. - -- The UTF-16, -LE and -BE stream readers now raise a - NotImplementedError for all calls to .readline(). Previously, they - used to just produce garbage or fail with an encoding error -- - UTF-16 is a 2-byte encoding and the C lib's line reading APIs don't - work well with these. - -- compileall now supports quiet operation. - -- The BaseHTTPServer now implements optional HTTP/1.1 persistent - connections. - -- socket module: the SSL support was broken out of the main - _socket module C helper and placed into a new _ssl helper - which now gets imported by socket.py if available and working. - -- encodings package: added aliases for all supported IANA character - sets - -- ftplib: to safeguard the user's privacy, anonymous login will use - "anonymous@" as default password, rather than the real user and host - name. - -- webbrowser: tightened up the command passed to os.system() so that - arbitrary shell code can't be executed because a bogus URL was - passed in. - -- gettext.translation has an optional fallback argument, and - gettext.find an optional all argument. Translations will now fallback - on a per-message basis. The module supports plural forms, by means - of gettext.[d]ngettext and Translation.[u]ngettext. - -- distutils bdist commands now offer a --skip-build option. - -- warnings.warn now accepts a Warning instance as first argument. - -- The xml.sax.expatreader.ExpatParser class will no longer create - circular references by using itself as the locator that gets passed - to the content handler implementation. [SF bug #535474] - -- The email.Parser.Parser class now properly parses strings regardless - of their line endings, which can be any of \r, \n, or \r\n (CR, LF, - or CRLF). Also, the Header class's constructor default arguments - has changed slightly so that an explicit maxlinelen value is always - honored, and so unicode conversion error handling can be specified. - -- distutils' build_ext command now links C++ extensions with the C++ - compiler available in the Makefile or CXX environment variable, if - running under \*nix. - -- New module bz2: provides a comprehensive interface for the bz2 compression - library. It implements a complete file interface, one-shot (de)compression - functions, and types for sequential (de)compression. - -- New pdb command 'pp' which is like 'p' except that it pretty-prints - the value of its expression argument. - -- Now bdist_rpm distutils command understands a verify_script option in - the config file, including the contents of the referred filename in - the "%verifyscript" section of the rpm spec file. - -- Fixed bug #495695: webbrowser module would run graphic browsers in a - unix environment even if DISPLAY was not set. Also, support for - skipstone browser was included. - -- Fixed bug #636769: rexec would run unallowed code if subclasses of - strings were used as parameters for certain functions. - -Tools/Demos ------------ - -- pygettext.py now supports globbing on Windows, and accepts module - names in addition to accepting file names. - -- The SGI demos (Demo/sgi) have been removed. Nobody thought they - were interesting any more. (The SGI library modules and extensions - are still there; it is believed that at least some of these are - still used and useful.) - -- IDLE supports the new encoding declarations (PEP 263); it can also - deal with legacy 8-bit files if they use the locale's encoding. It - allows non-ASCII strings in the interactive shell and executes them - in the locale's encoding. - -- freeze.py now produces binaries which can import shared modules, - unlike before when this failed due to missing symbol exports in - the generated binary. - -Build ------ - -- On Unix, IDLE is now installed automatically. - -- The fpectl module is not built by default; it's dangerous or useless - except in the hands of experts. - -- The public Python C API will generally be declared using PyAPI_FUNC - and PyAPI_DATA macros, while Python extension module init functions - will be declared with PyMODINIT_FUNC. DL_EXPORT/DL_IMPORT macros - are deprecated. - -- A bug was fixed that could cause COUNT_ALLOCS builds to segfault, or - get into infinite loops, when a new-style class got garbage-collected. - Unfortunately, to avoid this, the way COUNT_ALLOCS works requires - that new-style classes be immortal in COUNT_ALLOCS builds. Note that - COUNT_ALLOCS is not enabled by default, in either release or debug - builds, and that new-style classes are immortal only in COUNT_ALLOCS - builds. - -- Compiling out the cyclic garbage collector is no longer an option. - The old symbol WITH_CYCLE_GC is now ignored, and Python.h arranges - that it's always defined (for the benefit of any extension modules - that may be conditionalizing on it). A bonus is that any extension - type participating in cyclic gc can choose to participate in the - Py_TRASHCAN mechanism now too; in the absence of cyclic gc, this used - to require editing the core to teach the trashcan mechanism about the - new type. - -- According to Annex F of the current C standard, - - The Standard C macro HUGE_VAL and its float and long double analogs, - HUGE_VALF and HUGE_VALL, expand to expressions whose values are - positive infinities. - - Python only uses the double HUGE_VAL, and only to #define its own symbol - Py_HUGE_VAL. Some platforms have incorrect definitions for HUGE_VAL. - pyport.h used to try to worm around that, but the workarounds triggered - other bugs on other platforms, so we gave up. If your platform defines - HUGE_VAL incorrectly, you'll need to #define Py_HUGE_VAL to something - that works on your platform. The only instance of this I'm sure about - is on an unknown subset of Cray systems, described here: - - http://www.cray.com/swpubs/manuals/SN-2194_2.0/html-SN-2194_2.0/x3138.htm - - Presumably 2.3a1 breaks such systems. If anyone uses such a system, help! - -- The configure option --without-doc-strings can be used to remove the - doc strings from the builtin functions and modules; this reduces the - size of the executable. - -- The universal newlines option (PEP 278) is on by default. On Unix - it can be disabled by passing --without-universal-newlines to the - configure script. On other platforms, remove - WITH_UNIVERSAL_NEWLINES from pyconfig.h. - -- On Unix, a shared libpython2.3.so can be created with --enable-shared. - -- All uses of the CACHE_HASH, INTERN_STRINGS, and DONT_SHARE_SHORT_STRINGS - preprocessor symbols were eliminated. The internal decisions they - controlled stopped being experimental long ago. - -- The tools used to build the documentation now work under Cygwin as - well as Unix. - -- The bsddb and dbm module builds have been changed to try and avoid version - skew problems and disable linkage with Berkeley DB 1.85 unless the - installer knows what s/he's doing. See the section on building these - modules in the README file for details. - -C API ------ - -- PyNumber_Check() now returns true for string and unicode objects. - This is a result of these types having a partially defined - tp_as_number slot. (This is not a feature, but an indication that - PyNumber_Check() is not very useful to determine numeric behavior. - It may be deprecated.) - -- The string object's layout has changed: the pointer member - ob_sinterned has been replaced by an int member ob_sstate. On some - platforms (e.g. most 64-bit systems) this may change the offset of - the ob_sval member, so as a precaution the API_VERSION has been - incremented. The apparently unused feature of "indirect interned - strings", supported by the ob_sinterned member, is gone. Interned - strings are now usually mortal; there is a new API, - PyString_InternImmortal() that creates immortal interned strings. - (The ob_sstate member can only take three values; however, while - making it a char saves a few bytes per string object on average, in - it also slowed things down a bit because ob_sval was no longer - aligned.) - -- The Py_InitModule*() functions now accept NULL for the 'methods' - argument. Modules without global functions are becoming more common - now that factories can be types rather than functions. - -- New C API PyUnicode_FromOrdinal() which exposes unichr() at C - level. - -- New functions PyErr_SetExcFromWindowsErr() and - PyErr_SetExcFromWindowsErrWithFilename(). Similar to - PyErr_SetFromWindowsErrWithFilename() and - PyErr_SetFromWindowsErr(), but they allow to specify - the exception type to raise. Available on Windows. - -- Py_FatalError() is now declared as taking a const char* argument. It - was previously declared without const. This should not affect working - code. - -- Added new macro PySequence_ITEM(o, i) that directly calls - sq_item without rechecking that o is a sequence and without - adjusting for negative indices. - -- PyRange_New() now raises ValueError if the fourth argument is not 1. - This is part of the removal of deprecated features of the xrange - object. - -- PyNumber_Coerce() and PyNumber_CoerceEx() now also invoke the type's - coercion if both arguments have the same type but this type has the - CHECKTYPES flag set. This is to better support proxies. - -- The type of tp_free has been changed from "``void (*)(PyObject *)``" to - "``void (*)(void *)``". - -- PyObject_Del, PyObject_GC_Del are now functions instead of macros. - -- A type can now inherit its metatype from its base type. Previously, - when PyType_Ready() was called, if ob_type was found to be NULL, it - was always set to &PyType_Type; now it is set to base->ob_type, - where base is tp_base, defaulting to &PyObject_Type. - -- PyType_Ready() accidentally did not inherit tp_is_gc; now it does. - -- The PyCore_* family of APIs have been removed. - -- The "u#" parser marker will now pass through Unicode objects as-is - without going through the buffer API. - -- The enumerators of cmp_op have been renamed to use the prefix ``PyCmp_``. - -- An old #define of ANY as void has been removed from pyport.h. This - hasn't been used since Python's pre-ANSI days, and the #define has - been marked as obsolete since then. SF bug 495548 says it created - conflicts with other packages, so keeping it around wasn't harmless. - -- Because Python's magic number scheme broke on January 1st, we decided - to stop Python development. Thanks for all the fish! - -- Some of us don't like fish, so we changed Python's magic number - scheme to a new one. See Python/import.c for details. - -New platforms -------------- - -- OpenVMS is now supported. - -- AtheOS is now supported. - -- the EMX runtime environment on OS/2 is now supported. - -- GNU/Hurd is now supported. - -Tests ------ - -- The regrtest.py script's -u option now provides a way to say "allow - all resources except this one." For example, to allow everything - except bsddb, give the option '-uall,-bsddb'. - -Windows -------- - -- The Windows distribution now ships with version 4.0.14 of the - Sleepycat Berkeley database library. This should be a huge - improvement over the previous Berkeley DB 1.85, which had many - bugs. - XXX What are the licensing issues here? - XXX If a user has a database created with a previous version of - XXX Python, what must they do to convert it? - XXX I'm still not sure how to link this thing (see PCbuild/readme.txt). - XXX The version # is likely to change before 2.3a1. - -- The Windows distribution now ships with a Secure Sockets Library (SLL) - module (_ssl.pyd) - -- The Windows distribution now ships with Tcl/Tk version 8.4.1 (it - previously shipped with Tcl/Tk 8.3.2). - -- When Python is built under a Microsoft compiler, sys.version now - includes the compiler version number (_MSC_VER). For example, under - MSVC 6, sys.version contains the substring "MSC v.1200 ". 1200 is - the value of _MSC_VER under MSVC 6. - -- Sometimes the uninstall executable (UNWISE.EXE) vanishes. One cause - of that has been fixed in the installer (disabled Wise's "delete in- - use files" uninstall option). - -- Fixed a bug in urllib's proxy handling in Windows. [SF bug #503031] - -- The installer now installs Start menu shortcuts under (the local - equivalent of) "All Users" when doing an Admin install. - -- file.truncate([newsize]) now works on Windows for all newsize values. - It used to fail if newsize didn't fit in 32 bits, reflecting a - limitation of MS _chsize (which is no longer used). - -- os.waitpid() is now implemented for Windows, and can be used to block - until a specified process exits. This is similar to, but not exactly - the same as, os.waitpid() on POSIX systems. If you're waiting for - a specific process whose pid was obtained from one of the spawn() - functions, the same Python os.waitpid() code works across platforms. - See the docs for details. The docs were changed to clarify that - spawn functions return, and waitpid requires, a process handle on - Windows (not the same thing as a Windows process id). - -- New tempfile.TemporaryFile implementation for Windows: this doesn't - need a TemporaryFileWrapper wrapper anymore, and should be immune - to a nasty problem: before 2.3, if you got a temp file on Windows, it - got wrapped in an object whose close() method first closed the - underlying file, then deleted the file. This usually worked fine. - However, the spawn family of functions on Windows create (at a low C - level) the same set of open files in the spawned process Q as were - open in the spawning process P. If a temp file f was among them, then - doing f.close() in P first closed P's C-level file handle on f, but Q's - C-level file handle on f remained open, so the attempt in P to delete f - blew up with a "Permission denied" error (Windows doesn't allow - deleting open files). This was surprising, subtle, and difficult to - work around. - -- The os module now exports all the symbolic constants usable with the - low-level os.open() on Windows: the new constants in 2.3 are - O_NOINHERIT, O_SHORT_LIVED, O_TEMPORARY, O_RANDOM and O_SEQUENTIAL. - The others were also available in 2.2: O_APPEND, O_BINARY, O_CREAT, - O_EXCL, O_RDONLY, O_RDWR, O_TEXT, O_TRUNC and O_WRONLY. Contrary - to Microsoft docs, O_SHORT_LIVED does not seem to imply O_TEMPORARY - (so specify both if you want both; note that neither is useful unless - specified with O_CREAT too). - -Mac ----- - -- Mac/Relnotes is gone, the release notes are now here. - -- Python (the OSX-only, unix-based version, not the OS9-compatible CFM - version) now fully supports unicode strings as arguments to various file - system calls, eg. open(), file(), os.stat() and os.listdir(). - -- The current naming convention for Python on the Macintosh is that MacPython - refers to the unix-based OSX-only version, and MacPython-OS9 refers to the - CFM-based version that runs on both OS9 and OSX. - -- All MacPython-OS9 functionality is now available in an OSX unix build, - including the Carbon modules, the IDE, OSA support, etc. A lot of this - will only work correctly in a framework build, though, because you cannot - talk to the window manager unless your application is run from a .app - bundle. There is a command line tool "pythonw" that runs your script - with an interpreter living in such a .app bundle, this interpreter should - be used to run any Python script using the window manager (including - Tkinter or wxPython scripts). - -- Most of Mac/Lib has moved to Lib/plat-mac, which is again used both in - MacPython-OSX and MacPython-OS9. The only modules remaining in Mac/Lib - are specifically for MacPython-OS9 (CFM support, preference resources, etc). - -- A new utility PythonLauncher will start a Python interpreter when a .py or - .pyw script is double-clicked in the Finder. By default .py scripts are - run with a normal Python interpreter in a Terminal window and .pyw - files are run with a window-aware pythonw interpreter without a Terminal - window, but all this can be customized. - -- MacPython-OS9 is now Carbon-only, so it runs on Mac OS 9 or Mac OS X and - possibly on Mac OS 8.6 with the right CarbonLib installed, but not on earlier - releases. - -- Many tools such as BuildApplet.py and gensuitemodule.py now support a command - line interface too. - -- All the Carbon classes are now PEP253 compliant, meaning that you can - subclass them from Python. Most of the attributes have gone, you should - now use the accessor function call API, which is also what Apple's - documentation uses. Some attributes such as grafport.visRgn are still - available for convenience. - -- New Carbon modules File (implementing the APIs in Files.h and Aliases.h) - and Folder (APIs from Folders.h). The old macfs builtin module is - gone, and replaced by a Python wrapper around the new modules. - -- Pathname handling should now be fully consistent: MacPython-OSX always uses - unix pathnames and MacPython-OS9 always uses colon-separated Mac pathnames - (also when running on Mac OS X). - -- New Carbon modules Help and AH give access to the Carbon Help Manager. - There are hooks in the IDE to allow accessing the Python documentation - (and Apple's Carbon and Cocoa documentation) through the Help Viewer. - See Mac/OSX/README for converting the Python documentation to a - Help Viewer compatible form and installing it. - -- OSA support has been redesigned and the generated Python classes now - mirror the inheritance defined by the underlying OSA classes. - -- MacPython no longer maps both \r and \n to \n on input for any text file. - This feature has been replaced by universal newline support (PEP278). - -- The default encoding for Python sourcefiles in MacPython-OS9 is no longer - mac-roman (or whatever your local Mac encoding was) but "ascii", like on - other platforms. If you really need sourcefiles with Mac characters in them - you can change this in site.py. - - -What's New in Python 2.2 final? -=============================== - -*Release date: 21-Dec-2001* - -Type/class unification and new-style classes --------------------------------------------- - -- pickle.py, cPickle: allow pickling instances of new-style classes - with a custom metaclass. - -Core and builtins ------------------ - -- weakref proxy object: when comparing, unwrap both arguments if both - are proxies. - -Extension modules ------------------ - -- binascii.b2a_base64(): fix a potential buffer overrun when encoding - very short strings. - -- cPickle: the obscure "fast" mode was suspected of causing stack - overflows on the Mac. Hopefully fixed this by setting the recursion - limit much smaller. If the limit is too low (it only affects - performance), you can change it by defining PY_CPICKLE_FAST_LIMIT - when compiling cPickle.c (or in pyconfig.h). - -Library -------- - -- dumbdbm.py: fixed a dumb old bug (the file didn't get synched at - close or delete time). - -- rfc822.py: fixed a bug where the address '<>' was converted to None - instead of an empty string (also fixes the email.Utils module). - -- xmlrpclib.py: version 1.0.0; uses precision for doubles. - -- test suite: the pickle and cPickle tests were not executing any code - when run from the standard regression test. - -Tools/Demos ------------ - -Build ------ - -C API ------ - -New platforms -------------- - -Tests ------ - -Windows -------- - -- distutils package: fixed broken Windows installers (bdist_wininst). - -- tempfile.py: prevent mysterious warnings when TemporaryFileWrapper - instances are deleted at process exit time. - -- socket.py: prevent mysterious warnings when socket instances are - deleted at process exit time. - -- posixmodule.c: fix a Windows crash with stat() of a filename ending - in backslash. - -Mac ----- - -- The Carbon toolbox modules have been upgraded to Universal Headers - 3.4, and experimental CoreGraphics and CarbonEvents modules have - been added. All only for framework-enabled MacOSX. - - -What's New in Python 2.2c1? -=========================== - -*Release date: 14-Dec-2001* - -Type/class unification and new-style classes --------------------------------------------- - -- Guido's tutorial introduction to the new type/class features has - been extensively updated. See - - http://www.python.org/2.2/descrintro.html - - That remains the primary documentation in this area. - -- Fixed a leak: instance variables declared with __slots__ were never - deleted! - -- The "delete attribute" method of descriptor objects is called - __delete__, not __del__. In previous releases, it was mistakenly - called __del__, which created an unfortunate overloading condition - with finalizers. (The "get attribute" and "set attribute" methods - are still called __get__ and __set__, respectively.) - -- Some subtle issues with the super built-in were fixed: - - (a) When super itself is subclassed, its __get__ method would still - return an instance of the base class (i.e., of super). - - (b) super(C, C()).__class__ would return C rather than super. This - is confusing. To fix this, I decided to change the semantics of - super so that it only applies to code attributes, not to data - attributes. After all, overriding data attributes is not - supported anyway. - - (c) The __get__ method didn't check whether the argument was an - instance of the type used in creation of the super instance. - -- Previously, hash() of an instance of a subclass of a mutable type - (list or dictionary) would return some value, rather than raising - TypeError. This has been fixed. Also, directly calling - dict.__hash__ and list.__hash__ now raises the same TypeError - (previously, these were the same as object.__hash__). - -- New-style objects now support deleting their __dict__. This is for - all intents and purposes equivalent to assigning a brand new empty - dictionary, but saves space if the object is not used further. - -Core and builtins ------------------ - -- -Qnew now works as documented in PEP 238: when -Qnew is passed on - the command line, all occurrences of "/" use true division instead - of classic division. See the PEP for details. Note that "all" - means all instances in library and 3rd-party modules, as well as in - your own code. As the PEP says, -Qnew is intended for use only in - educational environments with control over the libraries in use. - Note that test_coercion.py in the standard Python test suite fails - under -Qnew; this is expected, and won't be repaired until true - division becomes the default (in the meantime, test_coercion is - testing the current rules). - -- complex() now only allows the first argument to be a string - argument, and raises TypeError if either the second arg is a string - or if the second arg is specified when the first is a string. - -Extension modules ------------------ - -- gc.get_referents was renamed to gc.get_referrers. - -Library -------- - -- Functions in the os.spawn() family now release the global interpreter - lock around calling the platform spawn. They should always have done - this, but did not before 2.2c1. Multithreaded programs calling - an os.spawn function with P_WAIT will no longer block all Python threads - until the spawned program completes. It's possible that some programs - relies on blocking, although more likely by accident than by design. - -- webbrowser defaults to netscape.exe on OS/2 now. - -- Tix.ResizeHandle exposes detach_widget, hide, and show. - -- The charset alias windows_1252 has been added. - -- types.StringTypes is a tuple containing the defined string types; - usually this will be (str, unicode), but if Python was compiled - without Unicode support it will be just (str,). - -- The pulldom and minidom modules were synchronized to PyXML. - -Tools/Demos ------------ - -- A new script called Tools/scripts/google.py was added, which fires - off a search on Google. - -Build ------ - -- Note that release builds of Python should arrange to define the - preprocessor symbol NDEBUG on the command line (or equivalent). - In the 2.2 pre-release series we tried to define this by magic in - Python.h instead, but it proved to cause problems for extension - authors. The Unix, Windows and Mac builds now all define NDEBUG in - release builds via cmdline (or equivalent) instead. Ports to - other platforms should do likewise. - -- It is no longer necessary to use --with-suffix when building on a - case-insensitive file system (such as Mac OS X HFS+). In the build - directory an extension is used, but not in the installed python. - -C API ------ - -- New function PyDict_MergeFromSeq2() exposes the builtin dict - constructor's logic for updating a dictionary from an iterable object - producing key-value pairs. - -- PyArg_ParseTupleAndKeywords() requires that the number of entries in - the keyword list equal the number of argument specifiers. This - wasn't checked correctly, and PyArg_ParseTupleAndKeywords could even - dump core in some bad cases. This has been repaired. As a result, - PyArg_ParseTupleAndKeywords may raise RuntimeError in bad cases that - previously went unchallenged. - -New platforms -------------- - -Tests ------ - -Windows -------- - -Mac ----- - -- In unix-Python on Mac OS X (and darwin) sys.platform is now "darwin", - without any trailing digits. - -- Changed logic for finding python home in Mac OS X framework Pythons. - Now sys.executable points to the executable again, in stead of to - the shared library. The latter is used only for locating the python - home. - - -What's New in Python 2.2b2? -=========================== - -*Release date: 16-Nov-2001* - -Type/class unification and new-style classes --------------------------------------------- - -- Multiple inheritance mixing new-style and classic classes in the - list of base classes is now allowed, so this works now: - - class Classic: pass - class Mixed(Classic, object): pass - - The MRO (method resolution order) for each base class is respected - according to its kind, but the MRO for the derived class is computed - using new-style MRO rules if any base class is a new-style class. - This needs to be documented. - -- The new builtin dictionary() constructor, and dictionary type, have - been renamed to dict. This reflects a decade of common usage. - -- dict() now accepts an iterable object producing 2-sequences. For - example, dict(d.items()) == d for any dictionary d. The argument, - and the elements of the argument, can be any iterable objects. - -- New-style classes can now have a __del__ method, which is called - when the instance is deleted (just like for classic classes). - -- Assignment to object.__dict__ is now possible, for objects that are - instances of new-style classes that have a __dict__ (unless the base - class forbids it). - -- Methods of built-in types now properly check for keyword arguments - (formerly these were silently ignored). The only built-in methods - that take keyword arguments are __call__, __init__ and __new__. - -- The socket function has been converted to a type; see below. - -Core and builtins ------------------ - -- Assignment to __debug__ raises SyntaxError at compile-time. This - was promised when 2.1c1 was released as "What's New in Python 2.1c1" - (see below) says. - -- Clarified the error messages for unsupported operands to an operator - (like 1 + ''). - -Extension modules ------------------ - -- mmap has a new keyword argument, "access", allowing a uniform way for - both Windows and Unix users to create read-only, write-through and - copy-on-write memory mappings. This was previously possible only on - Unix. A new keyword argument was required to support this in a - uniform way because the mmap() signatures had diverged across - platforms. Thanks to Jay T Miller for repairing this! - -- By default, the gc.garbage list now contains only those instances in - unreachable cycles that have __del__ methods; in 2.1 it contained all - instances in unreachable cycles. "Instances" here has been generalized - to include instances of both new-style and old-style classes. - -- The socket module defines a new method for socket objects, - sendall(). This is like send() but may make multiple calls to - send() until all data has been sent. Also, the socket function has - been converted to a subclassable type, like list and tuple (etc.) - before it; socket and SocketType are now the same thing. - -- Various bugfixes to the curses module. There is now a test suite - for the curses module (you have to run it manually). - -- binascii.b2a_base64 no longer places an arbitrary restriction of 57 - bytes on its input. - -Library -------- - -- tkFileDialog exposes a Directory class and askdirectory - convenience function. - -- Symbolic group names in regular expressions must be unique. For - example, the regexp r'(?P)(?P)' is not allowed, because a - single name can't mean both "group 1" and "group 2" simultaneously. - Python 2.2 detects this error at regexp compilation time; - previously, the error went undetected, and results were - unpredictable. Also in sre, the pattern.split(), pattern.sub(), and - pattern.subn() methods have been rewritten in C. Also, an - experimental function/method finditer() has been added, which works - like findall() but returns an iterator. - -- Tix exposes more commands through the classes DirSelectBox, - DirSelectDialog, ListNoteBook, Meter, CheckList, and the - methods tix_addbitmapdir, tix_cget, tix_configure, tix_filedialog, - tix_getbitmap, tix_getimage, tix_option_get, and tix_resetoptions. - -- Traceback objects are now scanned by cyclic garbage collection, so - cycles created by casual use of sys.exc_info() no longer cause - permanent memory leaks (provided garbage collection is enabled). - -- os.extsep -- a new variable needed by the RISCOS support. It is the - separator used by extensions, and is '.' on all platforms except - RISCOS, where it is '/'. There is no need to use this variable - unless you have a masochistic desire to port your code to RISCOS. - -- mimetypes.py has optional support for non-standard, but commonly - found types. guess_type() and guess_extension() now accept an - optional 'strict' flag, defaulting to true, which controls whether - recognize non-standard types or not. A few non-standard types we - know about have been added. Also, when run as a script, there are - new -l and -e options. - -- statcache is now deprecated. - -- email.Utils.formatdate() now produces the preferred RFC 2822 style - dates with numeric timezones (it used to produce obsolete dates - hard coded to "GMT" timezone). An optional 'localtime' flag is - added to produce dates in the local timezone, with daylight savings - time properly taken into account. - -- In pickle and cPickle, instead of masking errors in load() by - transforming them into SystemError, we let the original exception - propagate out. Also, implement support for __safe_for_unpickling__ - in pickle, as it already was supported in cPickle. - -Tools/Demos ------------ - -Build ------ - -- The dbm module is built using libdb1 if available. The bsddb module - is built with libdb3 if available. - -- Misc/Makefile.pre.in has been removed by BDFL pronouncement. - -C API ------ - -- New function PySequence_Fast_GET_SIZE() returns the size of a non- - NULL result from PySequence_Fast(), more quickly than calling - PySequence_Size(). - -- New argument unpacking function PyArg_UnpackTuple() added. - -- New functions PyObject_CallFunctionObjArgs() and - PyObject_CallMethodObjArgs() have been added to make it more - convenient and efficient to call functions and methods from C. - -- PyArg_ParseTupleAndKeywords() no longer masks errors, so it's - possible that this will propagate errors it didn't before. - -- New function PyObject_CheckReadBuffer(), which returns true if its - argument supports the single-segment readable buffer interface. - -New platforms -------------- - -- We've finally confirmed that this release builds on HP-UX 11.00, - *with* threads, and passes the test suite. - -- Thanks to a series of patches from Michael Muller, Python may build - again under OS/2 Visual Age C++. - -- Updated RISCOS port by Dietmar Schwertberger. - -Tests ------ - -- Added a test script for the curses module. It isn't run automatically; - regrtest.py must be run with '-u curses' to enable it. - -Windows -------- - -Mac ----- - -- PythonScript has been moved to unsupported and is slated to be - removed completely in the next release. - -- It should now be possible to build applets that work on both OS9 and - OSX. - -- The core is now linked with CoreServices not Carbon; as a side - result, default 8bit encoding on OSX is now ASCII. - -- Python should now build on OSX 10.1.1 - - -What's New in Python 2.2b1? -=========================== - -*Release date: 19-Oct-2001* - -Type/class unification and new-style classes --------------------------------------------- - -- New-style classes are now always dynamic (except for built-in and - extension types). There is no longer a performance penalty, and I - no longer see another reason to keep this baggage around. One relic - remains: the __dict__ of a new-style class is a read-only proxy; you - must set the class's attribute to modify it. As a consequence, the - __defined__ attribute of new-style types no longer exists, for lack - of need: there is once again only one __dict__ (although in the - future a __cache__ may be resurrected with a similar function, if I - can prove that it actually speeds things up). - -- C.__doc__ now works as expected for new-style classes (in 2.2a4 it - always returned None, even when there was a class docstring). - -- doctest now finds and runs docstrings attached to new-style classes, - class methods, static methods, and properties. - -Core and builtins ------------------ - -- A very subtle syntactical pitfall in list comprehensions was fixed. - For example: [a+b for a in 'abc', for b in 'def']. The comma in - this example is a mistake. Previously, this would silently let 'a' - iterate over the singleton tuple ('abc',), yielding ['abcd', 'abce', - 'abcf'] rather than the intended ['ad', 'ae', 'af', 'bd', 'be', - 'bf', 'cd', 'ce', 'cf']. Now, this is flagged as a syntax error. - Note that [a for a in ] is a convoluted way to say - [] anyway, so it's not like any expressiveness is lost. - -- getattr(obj, name, default) now only catches AttributeError, as - documented, rather than returning the default value for all - exceptions (which could mask bugs in a __getattr__ hook, for - example). - -- Weak reference objects are now part of the core and offer a C API. - A bug which could allow a core dump when binary operations involved - proxy reference has been fixed. weakref.ReferenceError is now a - built-in exception. - -- unicode(obj) now behaves more like str(obj), accepting arbitrary - objects, and calling a __unicode__ method if it exists. - unicode(obj, encoding) and unicode(obj, encoding, errors) still - require an 8-bit string or character buffer argument. - -- isinstance() now allows any object as the first argument and a - class, a type or something with a __bases__ tuple attribute for the - second argument. The second argument may also be a tuple of a - class, type, or something with __bases__, in which case isinstance() - will return true if the first argument is an instance of any of the - things contained in the second argument tuple. E.g. - - isinstance(x, (A, B)) - - returns true if x is an instance of A or B. - -Extension modules ------------------ - -- thread.start_new_thread() now returns the thread ID (previously None). - -- binascii has now two quopri support functions, a2b_qp and b2a_qp. - -- readline now supports setting the startup_hook and the - pre_event_hook, and adds the add_history() function. - -- os and posix supports chroot(), setgroups() and unsetenv() where - available. The stat(), fstat(), statvfs() and fstatvfs() functions - now return "pseudo-sequences" -- the various fields can now be - accessed as attributes (e.g. os.stat("/").st_mtime) but for - backwards compatibility they also behave as a fixed-length sequence. - Some platform-specific fields (e.g. st_rdev) are only accessible as - attributes. - -- time: localtime(), gmtime() and strptime() now return a - pseudo-sequence similar to the os.stat() return value, with - attributes like tm_year etc. - -- Decompression objects in the zlib module now accept an optional - second parameter to decompress() that specifies the maximum amount - of memory to use for the uncompressed data. - -- optional SSL support in the socket module now exports OpenSSL - functions RAND_add(), RAND_egd(), and RAND_status(). These calls - are useful on platforms like Solaris where OpenSSL does not - automatically seed its PRNG. Also, the keyfile and certfile - arguments to socket.ssl() are now optional. - -- posixmodule (and by extension, the os module on POSIX platforms) now - exports O_LARGEFILE, O_DIRECT, O_DIRECTORY, and O_NOFOLLOW. - -Library -------- - -- doctest now excludes functions and classes not defined by the module - being tested, thanks to Tim Hochberg. - -- HotShot, a new profiler implemented using a C-based callback, has - been added. This substantially reduces the overhead of profiling, - but it is still quite preliminary. Support modules and - documentation will be added in upcoming releases (before 2.2 final). - -- profile now produces correct output in situations where an exception - raised in Python is cleared by C code (e.g. hasattr()). This used - to cause wrong output, including spurious claims of recursive - functions and attribution of time spent to the wrong function. - - The code and documentation for the derived OldProfile and HotProfile - profiling classes was removed. The code hasn't worked for years (if - you tried to use them, they raised exceptions). OldProfile - intended to reproduce the behavior of the profiler Python used more - than 7 years ago, and isn't interesting anymore. HotProfile intended - to provide a faster profiler (but producing less information), and - that's a worthy goal we intend to meet via a different approach (but - without losing information). - -- Profile.calibrate() has a new implementation that should deliver - a much better system-specific calibration constant. The constant can - now be specified in an instance constructor, or as a Profile class or - instance variable, instead of by editing profile.py's source code. - Calibration must still be done manually (see the docs for the profile - module). - - Note that Profile.calibrate() must be overridden by subclasses. - Improving the accuracy required exploiting detailed knowledge of - profiler internals; the earlier method abstracted away the details - and measured a simplified model instead, but consequently computed - a constant too small by a factor of 2 on some modern machines. - -- quopri's encode and decode methods take an optional header parameter, - which indicates whether output is intended for the header 'Q' - encoding. - -- The SocketServer.ThreadingMixIn class now closes the request after - finish_request() returns. (Not when it errors out though.) - -- The nntplib module's NNTP.body() method has grown a 'file' argument - to allow saving the message body to a file. - -- The email package has added a class email.Parser.HeaderParser which - only parses headers and does not recurse into the message's body. - Also, the module/class MIMEAudio has been added for representing - audio data (contributed by Anthony Baxter). - -- ftplib should be able to handle files > 2GB. - -- ConfigParser.getboolean() now also interprets TRUE, FALSE, YES, NO, - ON, and OFF. - -- xml.dom.minidom NodeList objects now support the length attribute - and item() method as required by the DOM specifications. - -Tools/Demos ------------ - -- Demo/dns was removed. It no longer serves any purpose; a package - derived from it is now maintained by Anthony Baxter, see - http://PyDNS.SourceForge.net. - -- The freeze tool has been made more robust, and two new options have - been added: -X and -E. - -Build ------ - -- configure will use CXX in LINKCC if CXX is used to build main() and - the system requires to link a C++ main using the C++ compiler. - -C API ------ - -- The documentation for the tp_compare slot is updated to require that - the return value must be -1, 0, 1; an arbitrary number <0 or >0 is - not correct. This is not yet enforced but will be enforced in - Python 2.3; even later, we may use -2 to indicate errors and +2 for - "NotImplemented". Right now, -1 should be used for an error return. - -- PyLong_AsLongLong() now accepts int (as well as long) arguments. - Consequently, PyArg_ParseTuple's 'L' code also accepts int (as well - as long) arguments. - -- PyThread_start_new_thread() now returns a long int giving the thread - ID, if one can be calculated; it returns -1 for error, 0 if no - thread ID is calculated (this is an incompatible change, but only - the thread module used this API). This code has only really been - tested on Linux and Windows; other platforms please beware (and - report any bugs or strange behavior). - -- PyUnicode_FromEncodedObject() no longer accepts Unicode objects as - input. - -New platforms -------------- - -Tests +TO DO ----- -Windows -------- - -- Installer: If you install IDLE, and don't disable file-extension - registration, a new "Edit with IDLE" context (right-click) menu entry - is created for .py and .pyw files. - -- The signal module now supports SIGBREAK on Windows, thanks to Steven - Scott. Note that SIGBREAK is unique to Windows. The default SIGBREAK - action remains to call Win32 ExitProcess(). This can be changed via - signal.signal(). For example:: - - # Make Ctrl+Break raise KeyboardInterrupt, like Python's default Ctrl+C - # (SIGINT) behavior. - import signal - signal.signal(signal.SIGBREAK, signal.default_int_handler) +- Make strings all Unicode. (First have to introduce the bytes type.) - try: - while 1: - pass - except KeyboardInterrupt: - # We get here on Ctrl+C or Ctrl+Break now; if we had not changed - # SIGBREAK, only on Ctrl+C (and Ctrl+Break would terminate the - # program without the possibility for any Python-level cleanup). - print "Clean exit" +- Get rid of classic class implementation. +- Get rid of various compatibility-related flags (e.g. division flags). -What's New in Python 2.2a4? -=========================== - -*Release date: 28-Sep-2001* - -Type/class unification and new-style classes --------------------------------------------- - -- pydoc and inspect are now aware of new-style classes; - e.g. help(list) at the interactive prompt now shows proper - documentation for all operations on list objects. - -- Applications using Jim Fulton's ExtensionClass module can now safely - be used with Python 2.2. In particular, Zope 2.4.1 now works with - Python 2.2 (as well as with Python 2.1.1). The Demo/metaclass - examples also work again. It is hoped that Gtk and Boost also work - with 2.2a4 and beyond. (If you can confirm this, please write - webmaster@python.org; if there are still problems, please open a bug - report on SourceForge.) - -- property() now takes 4 keyword arguments: fget, fset, fdel and doc. - These map to read-only attributes 'fget', 'fset', 'fdel', and '__doc__' - in the constructed property object. fget, fset and fdel weren't - discoverable from Python in 2.2a3. __doc__ is new, and allows to - associate a docstring with a property. - -- Comparison overloading is now more completely implemented. For - example, a str subclass instance can properly be compared to a str - instance, and it can properly overload comparison. Ditto for most - other built-in object types. - -- The repr() of new-style classes has changed; instead of a new-style class is now rendered as , - *except* for built-in types, which are still rendered as (to avoid upsetting existing code that might parse or - otherwise rely on repr() of certain type objects). - -- The repr() of new-style objects is now always ; - previously, it was sometimes . - -- For new-style classes, what was previously called __getattr__ is now - called __getattribute__. This method, if defined, is called for - *every* attribute access. A new __getattr__ hook more similar to the - one in classic classes is defined which is called only if regular - attribute access raises AttributeError; to catch *all* attribute - access, you can use __getattribute__ (for new-style classes). If - both are defined, __getattribute__ is called first, and if it raises - AttributeError, __getattr__ is called. - -- The __class__ attribute of new-style objects can be assigned to. - The new class must have the same C-level object layout as the old - class. - -- The builtin file type can be subclassed now. In the usual pattern, - "file" is the name of the builtin type, and file() is a new builtin - constructor, with the same signature as the builtin open() function. - file() is now the preferred way to open a file. - -- Previously, __new__ would only see sequential arguments passed to - the type in a constructor call; __init__ would see both sequential - and keyword arguments. This made no sense whatsoever any more, so - now both __new__ and __init__ see all arguments. - -- Previously, hash() applied to an instance of a subclass of str or - unicode always returned 0. This has been repaired. - -- Previously, an operation on an instance of a subclass of an - immutable type (int, long, float, complex, tuple, str, unicode), - where the subtype didn't override the operation (and so the - operation was handled by the builtin type), could return that - instance instead a value of the base type. For example, if s was of - a str subclass type, s[:] returned s as-is. Now it returns a str - with the same value as s. +Core and Builtins +----------------- -- Provisional support for pickling new-style objects has been added. +- Classic classes are a thing of the past. All classes are new style. -Core ----- +- Exceptions *must* derive from BaseException. -- file.writelines() now accepts any iterable object producing strings. +- Integer division always returns a float. The -Q option is no more. -- PyUnicode_FromEncodedObject() now works very much like - PyObject_Str(obj) in that it tries to use __str__/tp_str - on the object if the object is not a string or buffer. This - makes unicode() behave like str() when applied to non-string/buffer - objects. +- 'as' and 'with' are keywords. -- PyFile_WriteObject now passes Unicode objects to the file's write - method. As a result, all file-like objects which may be the target - of a print statement must support Unicode objects, i.e. they must - at least convert them into ASCII strings. +- Absolute import is the default behavior for 'import foo' etc. -- Thread scheduling on Solaris should be improved; it is no longer - necessary to insert a small sleep at the start of a thread in order - to let other runnable threads be scheduled. +Extension Modules +----------------- Library ------- -- StringIO.StringIO instances and cStringIO.StringIO instances support - read character buffer compatible objects for their .write() methods. - These objects are converted to strings and then handled as such - by the instances. - -- The "email" package has been added. This is basically a port of the - mimelib package with API changes - and some implementations updated to use iterators and generators. - -- difflib.ndiff() and difflib.Differ.compare() are generators now. This - restores the ability of Tools/scripts/ndiff.py to start producing output - before the entire comparison is complete. - -- StringIO.StringIO instances and cStringIO.StringIO instances support - iteration just like file objects (i.e. their .readline() method is - called for each iteration until it returns an empty string). - -- The codecs module has grown four new helper APIs to access - builtin codecs: getencoder(), getdecoder(), getreader(), - getwriter(). - -- SimpleXMLRPCServer: a new module (based upon SimpleHTMLServer) - simplifies writing XML RPC servers. - -- os.path.realpath(): a new function that returns the absolute pathname - after interpretation of symbolic links. On non-Unix systems, this - is an alias for os.path.abspath(). - -- operator.indexOf() (PySequence_Index() in the C API) now works with any - iterable object. - -- smtplib now supports various authentication and security features of - the SMTP protocol through the new login() and starttls() methods. - -- hmac: a new module implementing keyed hashing for message - authentication. - -- mimetypes now recognizes more extensions and file types. At the - same time, some mappings not sanctioned by IANA were removed. - -- The "compiler" package has been brought up to date to the state of - Python 2.2 bytecode generation. It has also been promoted from a - Tool to a standard library package. (Tools/compiler still exists as - a sample driver.) - Build ----- -- Large file support (LFS) is now automatic when the platform supports - it; no more manual configuration tweaks are needed. On Linux, at - least, it's possible to have a system whose C library supports large - files but whose kernel doesn't; in this case, large file support is - still enabled but doesn't do you any good unless you upgrade your - kernel or share your Python executable with another system whose - kernel has large file support. - -- The configure script now supplies plausible defaults in a - cross-compilation environment. This doesn't mean that the supplied - values are always correct, or that cross-compilation now works - flawlessly -- but it's a first step (and it shuts up most of - autoconf's warnings about AC_TRY_RUN). - -- The Unix build is now a bit less chatty, courtesy of the parser - generator. The build is completely silent (except for errors) when - using "make -s", thanks to a -q option to setup.py. - C API ----- -- The "structmember" API now supports some new flag bits to deny read - and/or write access to attributes in restricted execution mode. - -New platforms -------------- - -- Compaq's iPAQ handheld, running the "familiar" Linux distribution - (http://familiar.handhelds.org). - Tests ----- -- The "classic" standard tests, which work by comparing stdout to - an expected-output file under Lib/test/output/, no longer stop at - the first mismatch. Instead the test is run to completion, and a - variant of ndiff-style comparison is used to report all differences. - This is much easier to understand than the previous style of reporting. - -- The unittest-based standard tests now use regrtest's test_main() - convention, instead of running as a side-effect of merely being - imported. This allows these tests to be run in more natural and - flexible ways as unittests, outside the regrtest framework. - -- regrtest.py is much better integrated with unittest and doctest now, - especially in regard to reporting errors. - -Windows -------- - -- Large file support now also works for files > 4GB, on filesystems - that support it (NTFS under Windows 2000). See "What's New in - Python 2.2a3" for more detail. - - -What's New in Python 2.2a3? -=========================== - -*Release Date: 07-Sep-2001* - -Core ----- - -- Conversion of long to float now raises OverflowError if the long is too - big to represent as a C double. - -- The 3-argument builtin pow() no longer allows a third non-None argument - if either of the first two arguments is a float, or if both are of - integer types and the second argument is negative (in which latter case - the arguments are converted to float, so this is really the same - restriction). - -- The builtin dir() now returns more information, and sometimes much - more, generally naming all attributes of an object, and all attributes - reachable from the object via its class, and from its class's base - classes, and so on from them too. Example: in 2.2a2, dir([]) returned - an empty list. In 2.2a3, - - >>> dir([]) - ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', - '__eq__', '__ge__', '__getattr__', '__getitem__', '__getslice__', - '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__le__', - '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__repr__', - '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', - 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', - 'reverse', 'sort'] - - dir(module) continues to return only the module's attributes, though. - -- Overflowing operations on plain ints now return a long int rather - than raising OverflowError. This is a partial implementation of PEP - 237. You can use -Wdefault::OverflowWarning to enable a warning for - this situation, and -Werror::OverflowWarning to revert to the old - OverflowError exception. - -- A new command line option, -Q, is added to control run-time - warnings for the use of classic division. (See PEP 238.) Possible - values are -Qold, -Qwarn, -Qwarnall, and -Qnew. The default is - -Qold, meaning the / operator has its classic meaning and no - warnings are issued. Using -Qwarn issues a run-time warning about - all uses of classic division for int and long arguments; -Qwarnall - also warns about classic division for float and complex arguments - (for use with fixdiv.py). - [Note: the remainder of this item (preserved below) became - obsolete in 2.2c1 -- -Qnew has global effect in 2.2] :: - - Using -Qnew is questionable; it turns on new division by default, but - only in the __main__ module. You can usefully combine -Qwarn or - -Qwarnall and -Qnew: this gives the __main__ module new division, and - warns about classic division everywhere else. - -- Many built-in types can now be subclassed. This applies to int, - long, float, str, unicode, and tuple. (The types complex, list and - dictionary can also be subclassed; this was introduced earlier.) - Note that restrictions apply when subclassing immutable built-in - types: you can only affect the value of the instance by overloading - __new__. You can add mutable attributes, and the subclass instances - will have a __dict__ attribute, but you cannot change the "value" - (as implemented by the base class) of an immutable subclass instance - once it is created. - -- The dictionary constructor now takes an optional argument, a - mapping-like object, and initializes the dictionary from its - (key, value) pairs. - -- A new built-in type, super, has been added. This facilitates making - "cooperative super calls" in a multiple inheritance setting. For an - explanation, see http://www.python.org/2.2/descrintro.html#cooperation - -- A new built-in type, property, has been added. This enables the - creation of "properties". These are attributes implemented by - getter and setter functions (or only one of these for read-only or - write-only attributes), without the need to override __getattr__. - See http://www.python.org/2.2/descrintro.html#property - -- The syntax of floating-point and imaginary literals has been - liberalized, to allow leading zeroes. Examples of literals now - legal that were SyntaxErrors before: - - 00.0 0e3 0100j 07.5 00000000000000000008. - -- An old tokenizer bug allowed floating point literals with an incomplete - exponent, such as 1e and 3.1e-. Such literals now raise SyntaxError. - -Library -------- - -- telnetlib includes symbolic names for the options, and support for - setting an option negotiation callback. It also supports processing - of suboptions. - -- The new C standard no longer requires that math libraries set errno to - ERANGE on overflow. For platform libraries that exploit this new - freedom, Python's overflow-checking was wholly broken. A new overflow- - checking scheme attempts to repair that, but may not be reliable on all - platforms (C doesn't seem to provide anything both useful and portable - in this area anymore). - -- Asynchronous timeout actions are available through the new class - threading.Timer. - -- math.log and math.log10 now return sensible results for even huge - long arguments. For example, math.log10(10 ** 10000) ~= 10000.0. - -- A new function, imp.lock_held(), returns 1 when the import lock is - currently held. See the docs for the imp module. - -- pickle, cPickle and marshal on 32-bit platforms can now correctly read - dumps containing ints written on platforms where Python ints are 8 bytes. - When read on a box where Python ints are 4 bytes, such values are - converted to Python longs. - -- In restricted execution mode (using the rexec module), unmarshalling - code objects is no longer allowed. This plugs a security hole. - -- unittest.TestResult instances no longer store references to tracebacks - generated by test failures. This prevents unexpected dangling references - to objects that should be garbage collected between tests. - -Tools ------ - -- Tools/scripts/fixdiv.py has been added which can be used to fix - division operators as per PEP 238. - -Build ------ - -- If you are an adventurous person using Mac OS X you may want to look at - Mac/OSX. There is a Makefile there that will build Python as a real Mac - application, which can be used for experimenting with Carbon or Cocoa. - Discussion of this on pythonmac-sig, please. - -C API ------ - -- New function PyObject_Dir(obj), like Python __builtin__.dir(obj). - -- 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. */ - } - -- The GC API has been changed. Extensions that use the old API will still - compile but will not participate in GC. To upgrade an extension - module: - - - rename Py_TPFLAGS_GC to PyTPFLAGS_HAVE_GC - - - use PyObject_GC_New or PyObject_GC_NewVar to allocate objects and - PyObject_GC_Del to deallocate them - - - rename PyObject_GC_Init to PyObject_GC_Track and PyObject_GC_Fini - to PyObject_GC_UnTrack - - - remove PyGC_HEAD_SIZE from object size calculations - - - remove calls to PyObject_AS_GC and PyObject_FROM_GC - -- Two new functions: PyString_FromFormat() and PyString_FromFormatV(). - These can be used safely to construct string objects from a - sprintf-style format string (similar to the format string supported - by PyErr_Format()). - -New platforms +Documentation ------------- -- Stephen Hansen contributed patches sufficient to get a clean compile - under Borland C (Windows), but he reports problems running it and ran - out of time to complete the port. Volunteers? Expect a MemoryError - when importing the types module; this is probably shallow, and - causing later failures too. - -Tests ------ - -Windows -------- - -- Large file support is now enabled on Win32 platforms as well as on - Win64. This means that, for example, you can use f.tell() and f.seek() - to manipulate files larger than 2 gigabytes (provided you have enough - disk space, and are using a Windows filesystem that supports large - partitions). Windows filesystem limits: FAT has a 2GB (gigabyte) - filesize limit, and large file support makes no difference there. - FAT32's limit is 4GB, and files >= 2GB are easier to use from Python now. - NTFS has no practical limit on file size, and files of any size can be - used from Python now. - -- The w9xpopen hack is now used on Windows NT and 2000 too when COMPSPEC - points to command.com (patch from Brian Quinlan). - - -What's New in Python 2.2a2? -=========================== - -*Release Date: 22-Aug-2001* - -Build ------ - -- Tim Peters developed a brand new Windows installer using Wise 8.1, - generously donated to us by Wise Solutions. - -- configure supports a new option --enable-unicode, with the values - ucs2 and ucs4 (new in 2.2a1). With --disable-unicode, the Unicode - type and supporting code is completely removed from the interpreter. - -- A new configure option --enable-framework builds a Mac OS X framework, - which "make frameworkinstall" will install. This provides a starting - point for more mac-like functionality, join pythonmac-sig@python.org - if you are interested in helping. - -- The NeXT platform is no longer supported. - -- The 'new' module is now statically linked. - -Tools ------ - -- The new Tools/scripts/cleanfuture.py can be used to automatically - edit out obsolete future statements from Python source code. See - the module docstring for details. - -Tests ------ - -- regrtest.py now knows which tests are expected to be skipped on some - platforms, allowing to give clearer test result output. regrtest - also has optional --use/-u switch to run normally disabled tests - which require network access or consume significant disk resources. - -- Several new tests in the standard test suite, with special thanks to - Nick Mathewson. - -Core ----- - -- The floor division operator // has been added as outlined in PEP - 238. The / operator still provides classic division (and will until - Python 3.0) unless "from __future__ import division" is included, in - which case the / operator will provide true division. The operator - module provides truediv() and floordiv() functions. Augmented - assignment variants are included, as are the equivalent overloadable - methods and C API methods. See the PEP for a full discussion: - - -- Future statements are now effective in simulated interactive shells - (like IDLE). This should "just work" by magic, but read Michael - Hudson's "Future statements in simulated shells" PEP 264 for full - details: . - -- The type/class unification (PEP 252-253) was integrated into the - trunk and is not so tentative any more (the exact specification of - some features is still tentative). A lot of work has done on fixing - bugs and adding robustness and features (performance still has to - come a long way). - -- Warnings about a mismatch in the Python API during extension import - now use the Python warning framework (which makes it possible to - write filters for these warnings). - -- A function's __dict__ (aka func_dict) will now always be a - dictionary. It used to be possible to delete it or set it to None, - but now both actions raise TypeErrors. It is still legal to set it - to a dictionary object. Getting func.__dict__ before any attributes - have been assigned now returns an empty dictionary instead of None. - -- A new command line option, -E, was added which disables the use of - all environment variables, or at least those that are specifically - significant to Python. Usually those have a name starting with - "PYTHON". This was used to fix a problem where the tests fail if - the user happens to have PYTHONHOME or PYTHONPATH pointing to an - older distribution. - -Library -------- - -- New class Differ and new functions ndiff() and restore() in difflib.py. - These package the algorithms used by the popular Tools/scripts/ndiff.py, - for programmatic reuse. - -- New function xml.sax.saxutils.quoteattr(): Quote an XML attribute - value using the minimal quoting required for the value; more - reliable than using xml.sax.saxutils.escape() for attribute values. - -- Readline completion support for cmd.Cmd was added. - -- Calling os.tempnam() or os.tmpnam() generate RuntimeWarnings. - -- Added function threading.BoundedSemaphore() - -- Added Ka-Ping Yee's cgitb.py module. - -- The 'new' module now exposes the CO_xxx flags. - -- The gc module offers the get_referents function. +Mac +--- New platforms ------------- -C API ------ - -- Two new APIs PyOS_snprintf() and PyOS_vsnprintf() were added - which provide a cross-platform implementations for the - relatively new snprintf()/vsnprintf() C lib APIs. In contrast to - the standard sprintf() and vsprintf() C lib APIs, these versions - apply bounds checking on the used buffer which enhances protection - against buffer overruns. - -- Unicode APIs now use name mangling to assure that mixing interpreters - and extensions using different Unicode widths is rendered next to - impossible. Trying to import an incompatible Unicode-aware extension - will result in an ImportError. Unicode extensions writers must make - sure to check the Unicode width compatibility in their extensions by - using at least one of the mangled Unicode APIs in the extension. - -- Two new flags METH_NOARGS and METH_O are available in method definition - tables to simplify implementation of methods with no arguments and a - single untyped argument. Calling such methods is more efficient than - calling corresponding METH_VARARGS methods. METH_OLDARGS is now - deprecated. - -Windows -------- - -- "import module" now compiles module.pyw if it exists and nothing else - relevant is found. - - -What's New in Python 2.2a1? -=========================== - -*Release date: 18-Jul-2001* - -Core ----- - -- TENTATIVELY, a large amount of code implementing much of what's - described in PEP 252 (Making Types Look More Like Classes) and PEP - 253 (Subtyping Built-in Types) was added. This will be released - with Python 2.2a1. Documentation will be provided separately - through http://www.python.org/2.2/. The purpose of releasing this - with Python 2.2a1 is to test backwards compatibility. It is - possible, though not likely, that a decision is made not to release - this code as part of 2.2 final, if any serious backwards - incompatibilities are found during alpha testing that cannot be - repaired. - -- Generators were added; this is a new way to create an iterator (see - below) using what looks like a simple function containing one or - more 'yield' statements. See PEP 255. Since this adds a new - keyword to the language, this feature must be enabled by including a - future statement: "from __future__ import generators" (see PEP 236). - Generators will become a standard feature in a future release - (probably 2.3). Without this future statement, 'yield' remains an - ordinary identifier, but a warning is issued each time it is used. - (These warnings currently don't conform to the warnings framework of - PEP 230; we intend to fix this in 2.2a2.) - -- The UTF-16 codec was modified to be more RFC compliant. It will now - only remove BOM characters at the start of the string and then - only if running in native mode (UTF-16-LE and -BE won't remove a - leading BMO character). - -- Strings now have a new method .decode() to complement the already - existing .encode() method. These two methods provide direct access - to the corresponding decoders and encoders of the registered codecs. - - To enhance the usability of the .encode() method, the special - casing of Unicode object return values was dropped (Unicode objects - were auto-magically converted to string using the default encoding). - - Both methods will now return whatever the codec in charge of the - requested encoding returns as object, e.g. Unicode codecs will - return Unicode objects when decoding is requested ("äöü".decode("latin-1") - will return u"äöü"). This enables codec writer to create codecs - for various simple to use conversions. - - New codecs were added to demonstrate these new features (the .encode() - and .decode() columns indicate the type of the returned objects): - - +---------+-----------+-----------+-----------------------------+ - |Name | .encode() | .decode() | Description | - +=========+===========+===========+=============================+ - |uu | string | string | UU codec (e.g. for email) | - +---------+-----------+-----------+-----------------------------+ - |base64 | string | string | base64 codec | - +---------+-----------+-----------+-----------------------------+ - |quopri | string | string | quoted-printable codec | - +---------+-----------+-----------+-----------------------------+ - |zlib | string | string | zlib compression | - +---------+-----------+-----------+-----------------------------+ - |hex | string | string | 2-byte hex codec | - +---------+-----------+-----------+-----------------------------+ - |rot-13 | string | Unicode | ROT-13 Unicode charmap codec| - +---------+-----------+-----------+-----------------------------+ - -- Some operating systems now support the concept of a default Unicode - encoding for file system operations. Notably, Windows supports 'mbcs' - as the default. The Macintosh will also adopt this concept in the medium - term, although the default encoding for that platform will be other than - 'mbcs'. - - On operating system that support non-ASCII filenames, it is common for - functions that return filenames (such as os.listdir()) to return Python - string objects pre-encoded using the default file system encoding for - the platform. As this encoding is likely to be different from Python's - default encoding, converting this name to a Unicode object before passing - it back to the Operating System would result in a Unicode error, as Python - would attempt to use its default encoding (generally ASCII) rather than - the default encoding for the file system. - - In general, this change simply removes surprises when working with - Unicode and the file system, making these operations work as you expect, - increasing the transparency of Unicode objects in this context. - See [????] for more details, including examples. - -- Float (and complex) literals in source code were evaluated to full - precision only when running from a .py file; the same code loaded from a - .pyc (or .pyo) file could suffer numeric differences starting at about the - 12th significant decimal digit. For example, on a machine with IEEE-754 - floating arithmetic, - - x = 9007199254740992.0 - print long(x) - - printed 9007199254740992 if run directly from .py, but 9007199254740000 - if from a compiled (.pyc or .pyo) file. This was due to marshal using - str(float) instead of repr(float) when building code objects. marshal - now uses repr(float) instead, which should reproduce floats to full - machine precision (assuming the platform C float<->string I/O conversion - functions are of good quality). - - This may cause floating-point results to change in some cases, and - usually for the better, but may also cause numerically unstable - algorithms to break. - -- The implementation of dicts suffers fewer collisions, which has speed - benefits. However, the order in which dict entries appear in dict.keys(), - dict.values() and dict.items() may differ from previous releases for a - given dict. Nothing is defined about this order, so no program should - rely on it. Nevertheless, it's easy to write test cases that rely on the - order by accident, typically because of printing the str() or repr() of a - dict to an "expected results" file. See Lib/test/test_support.py's new - sortdict(dict) function for a simple way to display a dict in sorted - order. - -- Many other small changes to dicts were made, resulting in faster - operation along the most common code paths. - -- Dictionary objects now support the "in" operator: "x in dict" means - the same as dict.has_key(x). - -- The update() method of dictionaries now accepts generic mapping - objects. Specifically the argument object must support the .keys() - and __getitem__() methods. This allows you to say, for example, - {}.update(UserDict()) - -- Iterators were added; this is a generalized way of providing values - to a for loop. See PEP 234. There's a new built-in function iter() - to return an iterator. There's a new protocol to get the next value - from an iterator using the next() method (in Python) or the - tp_iternext slot (in C). There's a new protocol to get iterators - using the __iter__() method (in Python) or the tp_iter slot (in C). - Iterating (i.e. a for loop) over a dictionary generates its keys. - Iterating over a file generates its lines. - -- The following functions were generalized to work nicely with iterator - arguments:: - - map(), filter(), reduce(), zip() - list(), tuple() (PySequence_Tuple() and PySequence_Fast() in C API) - max(), min() - join() method of strings - extend() method of lists - 'x in y' and 'x not in y' (PySequence_Contains() in C API) - operator.countOf() (PySequence_Count() in C API) - right-hand side of assignment statements with multiple targets, such as :: - x, y, z = some_iterable_object_returning_exactly_3_values - -- Accessing module attributes is significantly faster (for example, - random.random or os.path or yourPythonModule.yourAttribute). - -- Comparing dictionary objects via == and != is faster, and now works even - if the keys and values don't support comparisons other than ==. - -- Comparing dictionaries in ways other than == and != is slower: there were - insecurities in the dict comparison implementation that could cause Python - to crash if the element comparison routines for the dict keys and/or - values mutated the dicts. Making the code bulletproof slowed it down. - -- Collisions in dicts are resolved via a new approach, which can help - dramatically in bad cases. For example, looking up every key in a dict - d with d.keys() == [i << 16 for i in range(20000)] is approximately 500x - faster now. Thanks to Christian Tismer for pointing out the cause and - the nature of an effective cure (last December! better late than never). - -- repr() is much faster for large containers (dict, list, tuple). - - -Library -------- - -- The constants ascii_letters, ascii_lowercase. and ascii_uppercase - were added to the string module. These a locale-independent - constants, unlike letters, lowercase, and uppercase. These are now - use in appropriate locations in the standard library. - -- The flags used in dlopen calls can now be configured using - sys.setdlopenflags and queried using sys.getdlopenflags. - -- Fredrik Lundh's xmlrpclib is now a standard library module. This - provides full client-side XML-RPC support. In addition, - Demo/xmlrpc/ contains two server frameworks (one SocketServer-based, - one asyncore-based). Thanks to Eric Raymond for the documentation. - -- The xrange() object is simplified: it no longer supports slicing, - repetition, comparisons, efficient 'in' checking, the tolist() - method, or the start, stop and step attributes. See PEP 260. - -- A new function fnmatch.filter to filter lists of file names was added. - -- calendar.py uses month and day names based on the current locale. - -- strop is now *really* obsolete (this was announced before with 1.6), - and issues DeprecationWarning when used (except for the four items - that are still imported into string.py). - -- Cookie.py now sorts key+value pairs by key in output strings. - -- pprint.isrecursive(object) didn't correctly identify recursive objects. - Now it does. - -- pprint functions now much faster for large containers (tuple, list, dict). - -- New 'q' and 'Q' format codes in the struct module, corresponding to C - types "long long" and "unsigned long long" (on Windows, __int64). In - native mode, these can be used only when the platform C compiler supports - these types (when HAVE_LONG_LONG is #define'd by the Python config - process), and then they inherit the sizes and alignments of the C types. - In standard mode, 'q' and 'Q' are supported on all platforms, and are - 8-byte integral types. - -- The site module installs a new built-in function 'help' that invokes - pydoc.help. It must be invoked as 'help()'; when invoked as 'help', - it displays a message reminding the user to use 'help()' or - 'help(object)'. - -Tests ------ - -- New test_mutants.py runs dict comparisons where the key and value - comparison operators mutate the dicts randomly during comparison. This - rapidly causes Python to crash under earlier releases (not for the faint - of heart: it can also cause Win9x to freeze or reboot!). - -- New test_pprint.py verifies that pprint.isrecursive() and - pprint.isreadable() return sensible results. Also verifies that simple - cases produce correct output. - -C API ------ - -- Removed the unused last_is_sticky argument from the internal - _PyTuple_Resize(). If this affects you, you were cheating. - ----- +Tools/Demos +----------- **(For information about older versions, consult the HISTORY file.)** diff --git a/Modules/main.c b/Modules/main.c index 8e7c50b..c8298fb 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2,7 +2,6 @@ #include "Python.h" #include "osdefs.h" -#include "code.h" /* For CO_FUTURE_DIVISION */ #include "import.h" #ifdef __VMS @@ -34,7 +33,7 @@ static char **orig_argv; static int orig_argc; /* command line options */ -#define BASE_OPTS "c:dEhim:OQ:StuUvVW:xX" +#define BASE_OPTS "c:dEhim:OStuvVW:xX" #ifndef RISCOS #define PROGRAM_OPTS BASE_OPTS @@ -64,7 +63,6 @@ static char *usage_2 = "\ -m mod : run library module as a script (terminates option list)\n\ -O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\ -OO : remove doc-strings in addition to the -O optimizations\n\ --Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n\ -S : don't imply 'import site' on initialization\n\ -t : issue warnings about inconsistent tab usage (-tt: issue errors)\n\ -u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\ @@ -220,33 +218,6 @@ Py_Main(int argc, char **argv) Py_DebugFlag++; break; - case 'Q': - if (strcmp(_PyOS_optarg, "old") == 0) { - Py_DivisionWarningFlag = 0; - break; - } - if (strcmp(_PyOS_optarg, "warn") == 0) { - Py_DivisionWarningFlag = 1; - break; - } - if (strcmp(_PyOS_optarg, "warnall") == 0) { - Py_DivisionWarningFlag = 2; - break; - } - if (strcmp(_PyOS_optarg, "new") == 0) { - /* This only affects __main__ */ - cf.cf_flags |= CO_FUTURE_DIVISION; - /* And this tells the eval loop to treat - BINARY_DIVIDE as BINARY_TRUE_DIVIDE */ - _Py_QnewFlag = 1; - break; - } - fprintf(stderr, - "-Q option should be `-Qold', " - "`-Qwarn', `-Qwarnall', or `-Qnew' only\n"); - return usage(2, argv[0]); - /* NOTREACHED */ - case 'i': inspect++; saw_inspect_flag = 1; @@ -288,12 +259,10 @@ Py_Main(int argc, char **argv) skipfirstline = 1; break; - case 'U': - Py_UnicodeFlag++; - break; case 'h': help++; break; + case 'V': version++; break; diff --git a/Objects/abstract.c b/Objects/abstract.c index 399656f..052e3ca 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2106,12 +2106,7 @@ recursive_isinstance(PyObject *inst, PyObject *cls, int recursion_depth) return -1; } - if (PyClass_Check(cls) && PyInstance_Check(inst)) { - PyObject *inclass = - (PyObject*)((PyInstanceObject*)inst)->in_class; - retval = PyClass_IsSubclass(inclass, cls); - } - else if (PyType_Check(cls)) { + if (PyType_Check(cls)) { retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls); if (retval == 0) { PyObject *c = PyObject_GetAttr(inst, __class__); @@ -2177,7 +2172,7 @@ recursive_issubclass(PyObject *derived, PyObject *cls, int recursion_depth) { int retval; - if (!PyClass_Check(derived) || !PyClass_Check(cls)) { + { if (!check_class(derived, "issubclass() arg 1 must be a class")) return -1; @@ -2212,11 +2207,6 @@ recursive_issubclass(PyObject *derived, PyObject *cls, int recursion_depth) retval = abstract_issubclass(derived, cls); } - else { - /* shortcut */ - if (!(retval = (derived == cls))) - retval = PyClass_IsSubclass(derived, cls); - } return retval; } diff --git a/Parser/parser.c b/Parser/parser.c index ada6be2..213410c 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -149,6 +149,7 @@ classify(parser_state *ps, int type, char *str) strcmp(l->lb_str, s) != 0) continue; #ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD + /* Leaving this in as an example */ if (!(ps->p_flags & CO_FUTURE_WITH_STATEMENT)) { if (s[0] == 'w' && strcmp(s, "with") == 0) break; /* not a keyword yet */ @@ -177,6 +178,7 @@ classify(parser_state *ps, int type, char *str) } #ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD +/* Leaving this in as an example */ static void future_hack(parser_state *ps) { diff --git a/Parser/parsetok.c b/Parser/parsetok.c index 77a2cac..07590c2 100644 --- a/Parser/parsetok.c +++ b/Parser/parsetok.c @@ -192,7 +192,8 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, col_offset = -1; if ((err_ret->error = - PyParser_AddToken(ps, (int)type, str, tok->lineno, col_offset, + PyParser_AddToken(ps, (int)type, str, + tok->lineno, col_offset, &(err_ret->expected))) != E_OK) { if (err_ret->error != E_DONE) PyObject_FREE(str); diff --git a/Python/ceval.c b/Python/ceval.c index de2b35b..c854fcf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3025,15 +3025,7 @@ do_raise(PyObject *type, PyObject *value, PyObject *tb) Py_DECREF(tmp); } - if (PyString_CheckExact(type)) { - /* Raising builtin string is deprecated but still allowed -- - * do nothing. Raising an instance of a new-style str - * subclass is right out. */ - if (PyErr_Warn(PyExc_DeprecationWarning, - "raising a string exception is deprecated")) - goto raise_error; - } - else if (PyExceptionClass_Check(type)) + if (PyExceptionClass_Check(type)) PyErr_NormalizeException(&type, &value, &tb); else if (PyExceptionInstance_Check(type)) { @@ -3054,10 +3046,8 @@ do_raise(PyObject *type, PyObject *value, PyObject *tb) else { /* Not something you can raise. You get an exception anyway, just not what you specified :-) */ - PyErr_Format(PyExc_TypeError, - "exceptions must be classes, instances, or " - "strings (deprecated), not %s", - type->ob_type->tp_name); + PyErr_SetString(PyExc_TypeError, + "exceptions must derive from BaseException"); goto raise_error; } PyErr_Restore(type, value, tb); @@ -4148,7 +4138,7 @@ build_class(PyObject *methods, PyObject *bases, PyObject *name) if (g != NULL && PyDict_Check(g)) metaclass = PyDict_GetItemString(g, "__metaclass__"); if (metaclass == NULL) - metaclass = (PyObject *) &PyClass_Type; + metaclass = (PyObject *) &PyType_Type; Py_INCREF(metaclass); } result = PyObject_CallFunction(metaclass, "OOO", name, bases, methods); diff --git a/Python/compile.c b/Python/compile.c index baf3989..cfc6ef1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2464,11 +2464,7 @@ compiler_import(struct compiler *c, stmt_ty s) int r; PyObject *level; - if (c->c_flags && (c->c_flags->cf_flags & CO_FUTURE_ABSIMPORT)) - level = PyInt_FromLong(0); - else - level = PyInt_FromLong(-1); - + level = PyInt_FromLong(0); if (level == NULL) return 0; @@ -2511,12 +2507,7 @@ compiler_from_import(struct compiler *c, stmt_ty s) if (!names) return 0; - if (s->v.ImportFrom.level == 0 && c->c_flags && - !(c->c_flags->cf_flags & CO_FUTURE_ABSIMPORT)) - level = PyInt_FromLong(-1); - else - level = PyInt_FromLong(s->v.ImportFrom.level); - + level = PyInt_FromLong(s->v.ImportFrom.level); if (!level) { Py_DECREF(names); return 0; @@ -2746,10 +2737,7 @@ binop(struct compiler *c, operator_ty op) case Mult: return BINARY_MULTIPLY; case Div: - if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) - return BINARY_TRUE_DIVIDE; - else - return BINARY_DIVIDE; + return BINARY_TRUE_DIVIDE; case Mod: return BINARY_MODULO; case Pow: @@ -2809,10 +2797,7 @@ inplace_binop(struct compiler *c, operator_ty op) case Mult: return INPLACE_MULTIPLY; case Div: - if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) - return INPLACE_TRUE_DIVIDE; - else - return INPLACE_DIVIDE; + return INPLACE_TRUE_DIVIDE; case Mod: return INPLACE_MODULO; case Pow: diff --git a/Python/errors.c b/Python/errors.c index 7fc4c97..a64900b 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -557,7 +557,8 @@ PyErr_NewException(char *name, PyObject *base, PyObject *dict) bases = PyTuple_Pack(1, base); if (bases == NULL) goto failure; - result = PyClass_New(bases, dict, classname); + result = PyObject_CallFunction((PyObject *) (base->ob_type), + "OOO", classname, bases, dict); failure: Py_XDECREF(bases); Py_XDECREF(mydict); diff --git a/Python/future.c b/Python/future.c index 4a48ba5..1902f1d 100644 --- a/Python/future.c +++ b/Python/future.c @@ -28,11 +28,11 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename) } else if (strcmp(feature, FUTURE_GENERATORS) == 0) { continue; } else if (strcmp(feature, FUTURE_DIVISION) == 0) { - ff->ff_features |= CO_FUTURE_DIVISION; + continue; } else if (strcmp(feature, FUTURE_ABSIMPORT) == 0) { - ff->ff_features |= CO_FUTURE_ABSIMPORT; + continue; } else if (strcmp(feature, FUTURE_WITH_STATEMENT) == 0) { - ff->ff_features |= CO_FUTURE_WITH_STATEMENT; + continue; } else if (strcmp(feature, "braces") == 0) { PyErr_SetString(PyExc_SyntaxError, "not a chance"); diff --git a/Python/getargs.c b/Python/getargs.c index 8ee7d2f..fac0b6f 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -486,15 +486,16 @@ converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize) #define CONV_UNICODE "(unicode conversion error)" -/* explicitly check for float arguments when integers are expected. For now - * signal a warning. Returns true if an exception was raised. */ +/* Explicitly check for float arguments when integers are expected. + Return 1 for error, 0 if ok. */ static int float_argument_error(PyObject *arg) { - if (PyFloat_Check(arg) && - PyErr_Warn(PyExc_DeprecationWarning, - "integer argument expected, got float" )) + if (PyFloat_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); return 1; + } else return 0; } diff --git a/Python/graminit.c b/Python/graminit.c index 1853ca4..40f1770 100644 --- a/Python/graminit.c +++ b/Python/graminit.c @@ -556,9 +556,8 @@ static state states_26[9] = { static arc arcs_27_0[1] = { {19, 1}, }; -static arc arcs_27_1[3] = { +static arc arcs_27_1[2] = { {78, 2}, - {19, 2}, {0, 1}, }; static arc arcs_27_2[1] = { @@ -569,16 +568,15 @@ static arc arcs_27_3[1] = { }; static state states_27[4] = { {1, arcs_27_0}, - {3, arcs_27_1}, + {2, arcs_27_1}, {1, arcs_27_2}, {1, arcs_27_3}, }; static arc arcs_28_0[1] = { {12, 1}, }; -static arc arcs_28_1[3] = { +static arc arcs_28_1[2] = { {78, 2}, - {19, 2}, {0, 1}, }; static arc arcs_28_2[1] = { @@ -589,7 +587,7 @@ static arc arcs_28_3[1] = { }; static state states_28[4] = { {1, arcs_28_0}, - {3, arcs_28_1}, + {2, arcs_28_1}, {1, arcs_28_2}, {1, arcs_28_3}, }; @@ -917,9 +915,8 @@ static state states_40[6] = { {1, arcs_40_4}, {1, arcs_40_5}, }; -static arc arcs_41_0[2] = { +static arc arcs_41_0[1] = { {78, 1}, - {19, 1}, }; static arc arcs_41_1[1] = { {82, 2}, @@ -928,7 +925,7 @@ static arc arcs_41_2[1] = { {0, 2}, }; static state states_41[3] = { - {2, arcs_41_0}, + {1, arcs_41_0}, {1, arcs_41_1}, {1, arcs_41_2}, }; @@ -1870,7 +1867,7 @@ static dfa dfas[84] = { {296, "with_stmt", 0, 6, states_40, "\000\000\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000"}, {297, "with_var", 0, 3, states_41, - "\000\000\010\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000"}, {298, "except_clause", 0, 5, states_42, "\000\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, {299, "suite", 0, 5, states_43, diff --git a/Python/import.c b/Python/import.c index 73051a2..b64594d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -28,7 +28,7 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); a .pyc file in text mode the magic number will be wrong; also, the Apple MPW compiler swaps their values, botching string constants. - The magic numbers must be spaced apart atleast 2 values, as the + The magic numbers must be spaced apart at least 2 values, as the -U interpeter flag will cause MAGIC+1 being used. They have been odd numbers for some time now. @@ -56,9 +56,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); Python 2.5a0: 62081 (ast-branch) Python 2.5a0: 62091 (with) Python 2.5a0: 62092 (changed WITH_CLEANUP opcode) + Python 3000: 3000 . */ -#define MAGIC (62092 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3000 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 7b1f264..d04d111 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -696,9 +696,7 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flag /* compute parser flags based on compiler flags */ #define PARSER_FLAGS(flags) \ ((flags) ? ((((flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \ - PyPARSE_DONT_IMPLY_DEDENT : 0) \ - | ((flags)->cf_flags & CO_FUTURE_WITH_STATEMENT ? \ - PyPARSE_WITH_IS_KEYWORD : 0)) : 0) + PyPARSE_DONT_IMPLY_DEDENT : 0)) : 0) int PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) diff --git a/README b/README index cce60fa..70cb26d 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -This is Python version 2.5 alpha 0 -================================== +This is Python 3000 -- unversioned (branched off 2.5 pre alpha 1) +================================================================= Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation. All rights reserved. -- cgit v0.12 From e4993c7ac72842d18fb5689d7c77be2246b65708 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Thu, 16 Mar 2006 06:01:25 +0000 Subject: Get rid of compiler warning about with_msg and as_msg being unused --- Parser/parsetok.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Parser/parsetok.c b/Parser/parsetok.c index 07590c2..cb79fe4 100644 --- a/Parser/parsetok.c +++ b/Parser/parsetok.c @@ -92,11 +92,13 @@ PyParser_ParseFileFlags(FILE *fp, const char *filename, grammar *g, int start, /* Parse input coming from the given tokenizer structure. Return error code. */ +#ifdef PY_PARSER_REQUIRES_FUTURE_KEYWORD static char with_msg[] = "%s:%d: Warning: 'with' will become a reserved keyword in Python 2.6\n"; static char as_msg[] = "%s:%d: Warning: 'as' will become a reserved keyword in Python 2.6\n"; +#endif static void warn(const char *msg, const char *filename, int lineno) -- cgit v0.12 From c6d210ca7673c6ce0cb4b77ada22087f58372efc Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Thu, 16 Mar 2006 06:02:10 +0000 Subject: Get rid of last vestiges of BINARY_DIVIDE. --- Doc/lib/libdis.tex | 5 ----- Include/opcode.h | 2 +- Lib/compiler/pycodegen.py | 4 +--- Lib/opcode.py | 2 +- Python/ceval.c | 13 ------------- Python/compile.c | 6 ------ 6 files changed, 3 insertions(+), 29 deletions(-) diff --git a/Doc/lib/libdis.tex b/Doc/lib/libdis.tex index 67691b7..a5b2c2c 100644 --- a/Doc/lib/libdis.tex +++ b/Doc/lib/libdis.tex @@ -189,11 +189,6 @@ Implements \code{TOS = TOS1 ** TOS}. Implements \code{TOS = TOS1 * TOS}. \end{opcodedesc} -\begin{opcodedesc}{BINARY_DIVIDE}{} -Implements \code{TOS = TOS1 / TOS} when -\code{from __future__ import division} is not in effect. -\end{opcodedesc} - \begin{opcodedesc}{BINARY_FLOOR_DIVIDE}{} Implements \code{TOS = TOS1 // TOS}. \end{opcodedesc} diff --git a/Include/opcode.h b/Include/opcode.h index d8cb2cd..d05588a 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -26,7 +26,7 @@ extern "C" { #define BINARY_POWER 19 #define BINARY_MULTIPLY 20 -#define BINARY_DIVIDE 21 + #define BINARY_MODULO 22 #define BINARY_ADD 23 #define BINARY_SUBTRACT 24 diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index f25b3fb..e34120e 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -206,14 +206,12 @@ class CodeGenerator: self.setups = misc.Stack() self.last_lineno = None self._setupGraphDelegation() - self._div_op = "BINARY_DIVIDE" # XXX set flags based on future features futures = self.get_module().futures for feature in futures: if feature == "division": self.graph.setFlag(CO_FUTURE_DIVISION) - self._div_op = "BINARY_TRUE_DIVIDE" elif feature == "absolute_import": self.graph.setFlag(CO_FUTURE_ABSIMPORT) elif feature == "with_statement": @@ -1177,7 +1175,7 @@ class CodeGenerator: return self.binaryOp(node, 'BINARY_MULTIPLY') def visitDiv(self, node): - return self.binaryOp(node, self._div_op) + return self.binaryOp(node, 'BINARY_TRUE_DIVIDE') def visitFloorDiv(self, node): return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE') diff --git a/Lib/opcode.py b/Lib/opcode.py index 095ca42..2b9212f 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -61,7 +61,7 @@ def_op('UNARY_INVERT', 15) def_op('LIST_APPEND', 18) def_op('BINARY_POWER', 19) def_op('BINARY_MULTIPLY', 20) -def_op('BINARY_DIVIDE', 21) + def_op('BINARY_MODULO', 22) def_op('BINARY_ADD', 23) def_op('BINARY_SUBTRACT', 24) diff --git a/Python/ceval.c b/Python/ceval.c index c854fcf..1a35610 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1073,19 +1073,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) if (x != NULL) continue; break; - case BINARY_DIVIDE: - if (!_Py_QnewFlag) { - w = POP(); - v = TOP(); - x = PyNumber_Divide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - } - /* -Qnew is in effect: fall through to - BINARY_TRUE_DIVIDE */ case BINARY_TRUE_DIVIDE: w = POP(); v = TOP(); diff --git a/Python/compile.c b/Python/compile.c index cfc6ef1..9ce2bf7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -479,11 +479,6 @@ fold_binops_on_constants(unsigned char *codestr, PyObject *consts) case BINARY_MULTIPLY: newconst = PyNumber_Multiply(v, w); break; - case BINARY_DIVIDE: - /* Cannot fold this operation statically since - the result can depend on the run-time presence - of the -Qnew flag */ - return 0; case BINARY_TRUE_DIVIDE: newconst = PyNumber_TrueDivide(v, w); break; @@ -1302,7 +1297,6 @@ opcode_stack_effect(int opcode, int oparg) case BINARY_POWER: case BINARY_MULTIPLY: - case BINARY_DIVIDE: case BINARY_MODULO: case BINARY_ADD: case BINARY_SUBTRACT: -- cgit v0.12 From fc85c92a85e08d39ea769a07a3dc2a3c83c21477 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 05:44:46 +0000 Subject: Get rid of compiler warning --- Parser/parsetok.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parser/parsetok.c b/Parser/parsetok.c index cb79fe4..d877fc9 100644 --- a/Parser/parsetok.c +++ b/Parser/parsetok.c @@ -98,7 +98,6 @@ static char with_msg[] = static char as_msg[] = "%s:%d: Warning: 'as' will become a reserved keyword in Python 2.6\n"; -#endif static void warn(const char *msg, const char *filename, int lineno) @@ -107,6 +106,7 @@ warn(const char *msg, const char *filename, int lineno) filename = ""; PySys_WriteStderr(msg, filename, lineno); } +#endif static node * parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, -- cgit v0.12 From ac3625fcb95c2c54e40e1a27f2395811adbed03e Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 05:49:33 +0000 Subject: Remove sys.exc_type, sys.exc_value, sys.exc_traceback --- Demo/classes/Complex.py | 2 +- Demo/pdist/server.py | 2 +- Demo/sockets/gopher.py | 3 ++- Doc/api/exceptions.tex | 9 +++------ Doc/api/intro.tex | 7 ++----- Doc/ext/extending.tex | 5 ++--- Doc/lib/libtraceback.tex | 11 +++-------- Doc/ref/ref7.tex | 20 +++++++------------- Lib/SimpleXMLRPCServer.py | 4 ++-- Lib/idlelib/WindowList.py | 4 ++-- Lib/lib-tk/Tkinter.py | 2 +- Lib/traceback.py | 4 +--- Mac/Tools/IDE/PyDebugger.py | 11 +++++++---- Mac/Tools/IDE/PyEdit.py | 4 +--- Python/sysmodule.c | 4 ---- Tools/faqwiz/faqw.py | 2 +- 16 files changed, 36 insertions(+), 58 deletions(-) diff --git a/Demo/classes/Complex.py b/Demo/classes/Complex.py index 2b306ad..a779897 100755 --- a/Demo/classes/Complex.py +++ b/Demo/classes/Complex.py @@ -233,7 +233,7 @@ def checkop(expr, a, b, value, fuzz = 1e-6): try: result = eval(expr) except: - result = sys.exc_type + result = sys.exc_info()[0] print '->', result if isinstance(result, str) or isinstance(value, str): ok = (result == value) diff --git a/Demo/pdist/server.py b/Demo/pdist/server.py index e692eea..01b3249 100755 --- a/Demo/pdist/server.py +++ b/Demo/pdist/server.py @@ -83,7 +83,7 @@ class Server: method = getattr(self, methodname) reply = (None, apply(method, args), id) except: - reply = (sys.exc_type, sys.exc_value, id) + reply = (sys.exc_info()[:2], id) if id < 0 and reply[:2] == (None, None): if self._verbose > 1: print "Suppress reply" return 1 diff --git a/Demo/sockets/gopher.py b/Demo/sockets/gopher.py index cd76659..34bcdf0 100755 --- a/Demo/sockets/gopher.py +++ b/Demo/sockets/gopher.py @@ -191,7 +191,8 @@ def browse_menu(selector, host, port): try: browserfunc(i_selector, i_host, i_port) except (IOError, socket.error): - print '***', sys.exc_type, ':', sys.exc_value + t, v, tb = sys.exc_info() + print '***', t, ':', v else: print 'Unsupported object type' diff --git a/Doc/api/exceptions.tex b/Doc/api/exceptions.tex index c4727f2..62f713b 100644 --- a/Doc/api/exceptions.tex +++ b/Doc/api/exceptions.tex @@ -23,12 +23,9 @@ carefully propagated, additional calls into the Python/C API may not behave as intended and may fail in mysterious ways. The error indicator consists of three Python objects corresponding to -\withsubitem{(in module sys)}{ - \ttindex{exc_type}\ttindex{exc_value}\ttindex{exc_traceback}} -the Python variables \code{sys.exc_type}, \code{sys.exc_value} and -\code{sys.exc_traceback}. API functions exist to interact with the -error indicator in various ways. There is a separate error indicator -for each thread. +the result of \code{sys.exc_info()}. API functions exist to interact +with the error indicator in various ways. There is a separate +error indicator for each thread. % XXX Order of these should be more thoughtful. % Either alphabetical or some kind of structure. diff --git a/Doc/api/intro.tex b/Doc/api/intro.tex index d84b654..608d562 100644 --- a/Doc/api/intro.tex +++ b/Doc/api/intro.tex @@ -400,15 +400,12 @@ exception state. The full exception state consists of three objects (all of which can be \NULL): the exception type, the corresponding exception value, and the traceback. These have the same meanings as the Python -\withsubitem{(in module sys)}{ - \ttindex{exc_type}\ttindex{exc_value}\ttindex{exc_traceback}} -objects \code{sys.exc_type}, \code{sys.exc_value}, and -\code{sys.exc_traceback}; however, they are not the same: the Python +result of \code{sys.exc_info()}; however, they are not the same: the Python objects represent the last exception being handled by a Python \keyword{try} \ldots\ \keyword{except} statement, while the C level exception state only exists while an exception is being passed on between C functions until it reaches the Python bytecode interpreter's -main loop, which takes care of transferring it to \code{sys.exc_type} +main loop, which takes care of transferring it to \code{sys.exc_info()} and friends. Note that starting with Python 1.5, the preferred, thread-safe way to diff --git a/Doc/ext/extending.tex b/Doc/ext/extending.tex index 7016f94..0e2fd14 100644 --- a/Doc/ext/extending.tex +++ b/Doc/ext/extending.tex @@ -120,9 +120,8 @@ variable is \NULL{} no exception has occurred. A second global variable stores the ``associated value'' of the exception (the second argument to \keyword{raise}). A third variable contains the stack traceback in case the error originated in Python code. These three -variables are the C equivalents of the Python variables -\code{sys.exc_type}, \code{sys.exc_value} and \code{sys.exc_traceback} (see -the section on module \module{sys} in the +variables are the C equivalents of the result in Python of +\method{sys.exc_info()} (see the section on module \module{sys} in the \citetitle[../lib/lib.html]{Python Library Reference}). It is important to know about them to understand how errors are passed around. diff --git a/Doc/lib/libtraceback.tex b/Doc/lib/libtraceback.tex index b7f61ac..80dc423 100644 --- a/Doc/lib/libtraceback.tex +++ b/Doc/lib/libtraceback.tex @@ -12,9 +12,8 @@ when you want to print stack traces under program control, such as in a ``wrapper'' around the interpreter. The module uses traceback objects --- this is the object type that is -stored in the variables \code{sys.exc_traceback} (deprecated) and -\code{sys.last_traceback} and returned as the third item from -\function{sys.exc_info()}. +stored in the \code{sys.last_traceback} variable and returned +as the third item from \function{sys.exc_info()}. \obindex{traceback} The module defines the following functions: @@ -41,11 +40,7 @@ with a caret indicating the approximate position of the error. \end{funcdesc} \begin{funcdesc}{print_exc}{\optional{limit\optional{, file}}} -This is a shorthand for \code{print_exception(sys.exc_type, -sys.exc_value, sys.exc_traceback, \var{limit}, \var{file})}. (In -fact, it uses \function{sys.exc_info()} to retrieve the same -information in a thread-safe way instead of using the deprecated -variables.) +This is a shorthand for \code{print_exception(*\function{sys.exc_info()}}. \end{funcdesc} \begin{funcdesc}{format_exc}{\optional{limit}} diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index 4ae6040..90627a4 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -250,21 +250,15 @@ occurs in the try clause of the inner handler, the outer handler will not handle the exception.) Before an except clause's suite is executed, details about the -exception are assigned to three variables in the -\module{sys}\refbimodindex{sys} module: \code{sys.exc_type} receives -the object identifying the exception; \code{sys.exc_value} receives -the exception's parameter; \code{sys.exc_traceback} receives a +exception are stored in the \module{sys}\refbimodindex{sys} module +and can be access via \function{sys.exc_info()}. \function{sys.exc_info()} +returns a 3-tuple consisting of: \code{exc_type} receives +the object identifying the exception; \code{exc_value} receives +the exception's parameter; \code{exc_traceback} receives a traceback object\obindex{traceback} (see section~\ref{traceback}) identifying the point in the program where the exception occurred. -These details are also available through the \function{sys.exc_info()} -function, which returns a tuple \code{(\var{exc_type}, \var{exc_value}, -\var{exc_traceback})}. Use of the corresponding variables is -deprecated in favor of this function, since their use is unsafe in a -threaded program. As of Python 1.5, the variables are restored to -their previous values (before the call) when returning from a function -that handled an exception. -\withsubitem{(in module sys)}{\ttindex{exc_type} - \ttindex{exc_value}\ttindex{exc_traceback}} +\function{sys.exc_info()} values are restored to their previous values +(before the call) when returning from a function that handled an exception. The optional \keyword{else} clause is executed if and when control flows off the end of the \keyword{try} clause.\footnote{ diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/SimpleXMLRPCServer.py index 052a8e4..156c2ba 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/SimpleXMLRPCServer.py @@ -261,7 +261,7 @@ class SimpleXMLRPCDispatcher: except: # report exception back to server response = xmlrpclib.dumps( - xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)), + xmlrpclib.Fault(1, "%s:%s" % sys.exc_info()[:2]), encoding=self.encoding, allow_none=self.allow_none, ) @@ -362,7 +362,7 @@ class SimpleXMLRPCDispatcher: except: results.append( {'faultCode' : 1, - 'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)} + 'faultString' : "%s:%s" % sys.exc_info()[:2]} ) return results diff --git a/Lib/idlelib/WindowList.py b/Lib/idlelib/WindowList.py index 658502b..d0123d8 100644 --- a/Lib/idlelib/WindowList.py +++ b/Lib/idlelib/WindowList.py @@ -45,8 +45,8 @@ class WindowList: try: callback() except: - print "warning: callback failed in WindowList", \ - sys.exc_type, ":", sys.exc_value + t, v, tb = sys.exc_info() + print "warning: callback failed in WindowList", t, ":", v registry = WindowList() diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py index 37ddd3a..d600cd7 100644 --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -1108,7 +1108,7 @@ class Misc: def _report_exception(self): """Internal function.""" import sys - exc, val, tb = sys.exc_type, sys.exc_value, sys.exc_traceback + exc, val, tb = sys.exc_info() root = self._root() root.report_callback_exception(exc, val, tb) def _configure(self, cmd, cnf, kw): diff --git a/Lib/traceback.py b/Lib/traceback.py index 4971906..93a64b7 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -203,9 +203,7 @@ def _some_str(value): def print_exc(limit=None, file=None): - """Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'. - (In fact, it uses sys.exc_info() to retrieve the same information - in a thread-safe way.)""" + """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'.""" if file is None: file = sys.stderr try: diff --git a/Mac/Tools/IDE/PyDebugger.py b/Mac/Tools/IDE/PyDebugger.py index 5ee92d5..7fbc0f0 100644 --- a/Mac/Tools/IDE/PyDebugger.py +++ b/Mac/Tools/IDE/PyDebugger.py @@ -105,7 +105,8 @@ class Debugger(bdb.Bdb): raise 'spam' except: pass - frame = sys.exc_traceback.tb_frame + tb = sys.exc_info()[2] + frame = tb.tb_frame while frame is not None: del frame.f_trace frame = frame.f_back @@ -527,7 +528,7 @@ class Debugger(bdb.Bdb): raise bdb.BdbQuit except: print 'XXX Exception during debugger interaction.', \ - self.formatexception(sys.exc_type, sys.exc_value) + self.formatexception(sys.exc_info[:2]) import traceback traceback.print_exc() return self.trace_dispatch @@ -855,7 +856,8 @@ def startfromhere(): try: raise 'spam' except: - frame = sys.exc_traceback.tb_frame.f_back + tb = sys.exc_info()[2] + frame = tb.tb_frame.f_back d.start(frame) def startfrombottom(): @@ -876,7 +878,8 @@ def _getbottomframe(): raise 'spam' except: pass - frame = sys.exc_traceback.tb_frame + tb = sys.exc_info()[2] + frame = tb.tb_frame while 1: if frame.f_code.co_name == 'mainloop' or frame.f_back is None: break diff --git a/Mac/Tools/IDE/PyEdit.py b/Mac/Tools/IDE/PyEdit.py index 88c72ac..56f3f8d 100644 --- a/Mac/Tools/IDE/PyEdit.py +++ b/Mac/Tools/IDE/PyEdit.py @@ -1212,7 +1212,7 @@ def execstring(pytext, globals, locals, filename="", debugging=0, except: if debugging: sys.settrace(None) - PyDebugger.postmortem(sys.exc_type, sys.exc_value, sys.exc_traceback) + PyDebugger.postmortem(*sys.exc_info()) return else: tracebackwindow.traceback(1, filename) @@ -1289,7 +1289,6 @@ class _EditorDefaultSettings: settings = FontSettings.FontDialog(self.fontsettings, self.tabsettings) if settings: self.fontsettings, self.tabsettings = settings - sys.exc_traceback = None self.w.fonttext.set(self.template % (self.fontsettings[0], self.fontsettings[2])) def close(self): @@ -1327,7 +1326,6 @@ def geteditorprefs(): fontsettings = prefs.pyedit.fontsettings = ("Geneva", 0, 10, (0, 0, 0)) tabsettings = prefs.pyedit.tabsettings = (8, 1) windowsize = prefs.pyedit.windowsize = (500, 250) - sys.exc_traceback = None return fontsettings, tabsettings, windowsize def seteditorprefs(fontsettings, tabsettings, windowsize): diff --git a/Python/sysmodule.c b/Python/sysmodule.c index dfa6ac8..875f73f 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -179,10 +179,6 @@ sys_exc_clear(PyObject *self, PyObject *noargs) Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); - /* For b/w compatibility */ - PySys_SetObject("exc_type", Py_None); - PySys_SetObject("exc_value", Py_None); - PySys_SetObject("exc_traceback", Py_None); Py_INCREF(Py_None); return Py_None; } diff --git a/Tools/faqwiz/faqw.py b/Tools/faqwiz/faqw.py index 36c52e9..a26e0d6 100755 --- a/Tools/faqwiz/faqw.py +++ b/Tools/faqwiz/faqw.py @@ -27,7 +27,7 @@ try: except SystemExit, n: sys.exit(n) except: - t, v, tb = sys.exc_type, sys.exc_value, sys.exc_traceback + t, v, tb = sys.exc_info() print import cgi cgi.print_exception(t, v, tb) -- cgit v0.12 From cd65e3fc7d3280defdb14d75ffe71e665ed85989 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 05:59:16 +0000 Subject: raw_input() -> input(). old input behavior is history (and test_builtin passes again). It was failing due to future division. --- Lib/test/test_builtin.py | 31 +++----- Python/bltinmodule.c | 182 +++++++++++++++++++---------------------------- 2 files changed, 81 insertions(+), 132 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 7fdc063..c97a7f1 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -658,8 +658,6 @@ class BuiltinTest(unittest.TestCase): id([0,1,2,3]) id({'spam': 1, 'eggs': 2, 'ham': 3}) - # Test input() later, together with raw_input - def test_int(self): self.assertEqual(int(314), 314) self.assertEqual(int(3.14), 3) @@ -1108,7 +1106,7 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, oct, ()) def write_testfile(self): - # NB the first 4 lines are also used to test input and raw_input, below + # NB the first 4 lines are also used to test input, below fp = open(TESTFN, 'w') try: fp.write('1+1\n') @@ -1267,7 +1265,7 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint) self.assertRaises(OverflowError, range, 0, 2*sys.maxint) - def test_input_and_raw_input(self): + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r') savestdin = sys.stdin @@ -1275,29 +1273,18 @@ class BuiltinTest(unittest.TestCase): try: sys.stdin = fp sys.stdout = BitBucket() - self.assertEqual(input(), 2) - self.assertEqual(input('testing\n'), 2) - self.assertEqual(raw_input(), 'The quick brown fox jumps over the lazy dog.') - self.assertEqual(raw_input('testing\n'), 'Dear John') + self.assertEqual(input(), '1+1') + self.assertEqual(input('testing\n'), '1+1') + self.assertEqual(input(), 'The quick brown fox jumps over the lazy dog.') + self.assertEqual(input('testing\n'), 'Dear John') sys.stdin = cStringIO.StringIO("NULL\0") self.assertRaises(TypeError, input, 42, 42) - sys.stdin = cStringIO.StringIO(" 'whitespace'") - self.assertEqual(input(), 'whitespace') + whitespace = " 'whitespace'" + sys.stdin = cStringIO.StringIO(whitespace) + self.assertEqual(input(), whitespace) sys.stdin = cStringIO.StringIO() self.assertRaises(EOFError, input) - # SF 876178: make sure input() respect future options. - sys.stdin = cStringIO.StringIO('1/2') - sys.stdout = cStringIO.StringIO() - exec compile('print input()', 'test_builtin_tmp', 'exec') - sys.stdin.seek(0, 0) - exec compile('from __future__ import division;print input()', - 'test_builtin_tmp', 'exec') - sys.stdin.seek(0, 0) - exec compile('print input()', 'test_builtin_tmp', 'exec') - self.assertEqual(sys.stdout.getvalue().splitlines(), - ['0', '0.5', '0']) - del sys.stdout self.assertRaises(RuntimeError, input, 'prompt') del sys.stdin diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index c9da78c..a5dc0e3 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1073,42 +1073,89 @@ PyDoc_STRVAR(hex_doc, Return the hexadecimal representation of an integer or long integer."); -static PyObject *builtin_raw_input(PyObject *, PyObject *); - static PyObject * builtin_input(PyObject *self, PyObject *args) { - PyObject *line; - char *str; - PyObject *res; - PyObject *globals, *locals; - PyCompilerFlags cf; + PyObject *v = NULL; + PyObject *fin = PySys_GetObject("stdin"); + PyObject *fout = PySys_GetObject("stdout"); - line = builtin_raw_input(self, args); - if (line == NULL) - return line; - if (!PyArg_Parse(line, "s;embedded '\\0' in input line", &str)) + if (!PyArg_UnpackTuple(args, "input", 0, 1, &v)) return NULL; - while (*str == ' ' || *str == '\t') - str++; - globals = PyEval_GetGlobals(); - locals = PyEval_GetLocals(); - if (PyDict_GetItemString(globals, "__builtins__") == NULL) { - if (PyDict_SetItemString(globals, "__builtins__", - PyEval_GetBuiltins()) != 0) + + if (fin == NULL) { + PyErr_SetString(PyExc_RuntimeError, "input: lost sys.stdin"); + return NULL; + } + if (fout == NULL) { + PyErr_SetString(PyExc_RuntimeError, "input: lost sys.stdout"); + return NULL; + } + if (PyFile_SoftSpace(fout, 0)) { + if (PyFile_WriteString(" ", fout) != 0) return NULL; } - cf.cf_flags = 0; - PyEval_MergeCompilerFlags(&cf); - res = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf); - Py_DECREF(line); - return res; + if (PyFile_Check(fin) && PyFile_Check(fout) + && isatty(fileno(PyFile_AsFile(fin))) + && isatty(fileno(PyFile_AsFile(fout)))) { + PyObject *po; + char *prompt; + char *s; + PyObject *result; + if (v != NULL) { + po = PyObject_Str(v); + if (po == NULL) + return NULL; + prompt = PyString_AsString(po); + if (prompt == NULL) + return NULL; + } + else { + po = NULL; + prompt = ""; + } + s = PyOS_Readline(PyFile_AsFile(fin), PyFile_AsFile(fout), + prompt); + Py_XDECREF(po); + if (s == NULL) { + if (!PyErr_Occurred()) + PyErr_SetNone(PyExc_KeyboardInterrupt); + return NULL; + } + if (*s == '\0') { + PyErr_SetNone(PyExc_EOFError); + result = NULL; + } + else { /* strip trailing '\n' */ + size_t len = strlen(s); + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "[raw_]input: input too long"); + result = NULL; + } + else { + result = PyString_FromStringAndSize(s, + (int)(len-1)); + } + } + PyMem_FREE(s); + return result; + } + if (v != NULL) { + if (PyFile_WriteObject(v, fout, Py_PRINT_RAW) != 0) + return NULL; + } + return PyFile_GetLine(fin, -1); } PyDoc_STRVAR(input_doc, -"input([prompt]) -> value\n\ +"input([prompt]) -> string\n\ \n\ -Equivalent to eval(raw_input(prompt))."); +Read a string from standard input. The trailing newline is stripped.\n\ +If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError.\n\ +On Unix, GNU readline is used if enabled. The prompt string, if given,\n\ +is printed without a trailing newline before reading."); + static PyObject * @@ -1687,90 +1734,6 @@ These are exactly the valid indices for a list of 4 elements."); static PyObject * -builtin_raw_input(PyObject *self, PyObject *args) -{ - PyObject *v = NULL; - PyObject *fin = PySys_GetObject("stdin"); - PyObject *fout = PySys_GetObject("stdout"); - - if (!PyArg_UnpackTuple(args, "[raw_]input", 0, 1, &v)) - return NULL; - - if (fin == NULL) { - PyErr_SetString(PyExc_RuntimeError, "[raw_]input: lost sys.stdin"); - return NULL; - } - if (fout == NULL) { - PyErr_SetString(PyExc_RuntimeError, "[raw_]input: lost sys.stdout"); - return NULL; - } - if (PyFile_SoftSpace(fout, 0)) { - if (PyFile_WriteString(" ", fout) != 0) - return NULL; - } - if (PyFile_Check(fin) && PyFile_Check(fout) - && isatty(fileno(PyFile_AsFile(fin))) - && isatty(fileno(PyFile_AsFile(fout)))) { - PyObject *po; - char *prompt; - char *s; - PyObject *result; - if (v != NULL) { - po = PyObject_Str(v); - if (po == NULL) - return NULL; - prompt = PyString_AsString(po); - if (prompt == NULL) - return NULL; - } - else { - po = NULL; - prompt = ""; - } - s = PyOS_Readline(PyFile_AsFile(fin), PyFile_AsFile(fout), - prompt); - Py_XDECREF(po); - if (s == NULL) { - if (!PyErr_Occurred()) - PyErr_SetNone(PyExc_KeyboardInterrupt); - return NULL; - } - if (*s == '\0') { - PyErr_SetNone(PyExc_EOFError); - result = NULL; - } - else { /* strip trailing '\n' */ - size_t len = strlen(s); - if (len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "[raw_]input: input too long"); - result = NULL; - } - else { - result = PyString_FromStringAndSize(s, - (int)(len-1)); - } - } - PyMem_FREE(s); - return result; - } - if (v != NULL) { - if (PyFile_WriteObject(v, fout, Py_PRINT_RAW) != 0) - return NULL; - } - return PyFile_GetLine(fin, -1); -} - -PyDoc_STRVAR(raw_input_doc, -"raw_input([prompt]) -> string\n\ -\n\ -Read a string from standard input. The trailing newline is stripped.\n\ -If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError.\n\ -On Unix, GNU readline is used if enabled. The prompt string, if given,\n\ -is printed without a trailing newline before reading."); - - -static PyObject * builtin_reduce(PyObject *self, PyObject *args) { PyObject *seq, *func, *result = NULL, *it; @@ -2244,7 +2207,6 @@ static PyMethodDef builtin_methods[] = { {"ord", builtin_ord, METH_O, ord_doc}, {"pow", builtin_pow, METH_VARARGS, pow_doc}, {"range", builtin_range, METH_VARARGS, range_doc}, - {"raw_input", builtin_raw_input, METH_VARARGS, raw_input_doc}, {"reduce", builtin_reduce, METH_VARARGS, reduce_doc}, {"reload", builtin_reload, METH_O, reload_doc}, {"repr", builtin_repr, METH_O, repr_doc}, -- cgit v0.12 From 9e2b9665ae9f94a07da54156c48e2cd411a23746 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 06:04:34 +0000 Subject: Whoops, input *and* raw_input are slated for removal, and now both are gone. --- Lib/test/test_builtin.py | 31 ----------------- Python/bltinmodule.c | 86 ------------------------------------------------ 2 files changed, 117 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index c97a7f1..4f10d92 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1106,7 +1106,6 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, oct, ()) def write_testfile(self): - # NB the first 4 lines are also used to test input, below fp = open(TESTFN, 'w') try: fp.write('1+1\n') @@ -1265,36 +1264,6 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint) self.assertRaises(OverflowError, range, 0, 2*sys.maxint) - def test_input(self): - self.write_testfile() - fp = open(TESTFN, 'r') - savestdin = sys.stdin - savestdout = sys.stdout # Eats the echo - try: - sys.stdin = fp - sys.stdout = BitBucket() - self.assertEqual(input(), '1+1') - self.assertEqual(input('testing\n'), '1+1') - self.assertEqual(input(), 'The quick brown fox jumps over the lazy dog.') - self.assertEqual(input('testing\n'), 'Dear John') - sys.stdin = cStringIO.StringIO("NULL\0") - self.assertRaises(TypeError, input, 42, 42) - whitespace = " 'whitespace'" - sys.stdin = cStringIO.StringIO(whitespace) - self.assertEqual(input(), whitespace) - sys.stdin = cStringIO.StringIO() - self.assertRaises(EOFError, input) - - del sys.stdout - self.assertRaises(RuntimeError, input, 'prompt') - del sys.stdin - self.assertRaises(RuntimeError, input, 'prompt') - finally: - sys.stdin = savestdin - sys.stdout = savestdout - fp.close() - unlink(TESTFN) - def test_reduce(self): self.assertEqual(reduce(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc') self.assertEqual( diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index a5dc0e3..4c168eb 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1074,91 +1074,6 @@ Return the hexadecimal representation of an integer or long integer."); static PyObject * -builtin_input(PyObject *self, PyObject *args) -{ - PyObject *v = NULL; - PyObject *fin = PySys_GetObject("stdin"); - PyObject *fout = PySys_GetObject("stdout"); - - if (!PyArg_UnpackTuple(args, "input", 0, 1, &v)) - return NULL; - - if (fin == NULL) { - PyErr_SetString(PyExc_RuntimeError, "input: lost sys.stdin"); - return NULL; - } - if (fout == NULL) { - PyErr_SetString(PyExc_RuntimeError, "input: lost sys.stdout"); - return NULL; - } - if (PyFile_SoftSpace(fout, 0)) { - if (PyFile_WriteString(" ", fout) != 0) - return NULL; - } - if (PyFile_Check(fin) && PyFile_Check(fout) - && isatty(fileno(PyFile_AsFile(fin))) - && isatty(fileno(PyFile_AsFile(fout)))) { - PyObject *po; - char *prompt; - char *s; - PyObject *result; - if (v != NULL) { - po = PyObject_Str(v); - if (po == NULL) - return NULL; - prompt = PyString_AsString(po); - if (prompt == NULL) - return NULL; - } - else { - po = NULL; - prompt = ""; - } - s = PyOS_Readline(PyFile_AsFile(fin), PyFile_AsFile(fout), - prompt); - Py_XDECREF(po); - if (s == NULL) { - if (!PyErr_Occurred()) - PyErr_SetNone(PyExc_KeyboardInterrupt); - return NULL; - } - if (*s == '\0') { - PyErr_SetNone(PyExc_EOFError); - result = NULL; - } - else { /* strip trailing '\n' */ - size_t len = strlen(s); - if (len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "[raw_]input: input too long"); - result = NULL; - } - else { - result = PyString_FromStringAndSize(s, - (int)(len-1)); - } - } - PyMem_FREE(s); - return result; - } - if (v != NULL) { - if (PyFile_WriteObject(v, fout, Py_PRINT_RAW) != 0) - return NULL; - } - return PyFile_GetLine(fin, -1); -} - -PyDoc_STRVAR(input_doc, -"input([prompt]) -> string\n\ -\n\ -Read a string from standard input. The trailing newline is stripped.\n\ -If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError.\n\ -On Unix, GNU readline is used if enabled. The prompt string, if given,\n\ -is printed without a trailing newline before reading."); - - - -static PyObject * builtin_intern(PyObject *self, PyObject *args) { PyObject *s; @@ -2193,7 +2108,6 @@ static PyMethodDef builtin_methods[] = { {"hash", builtin_hash, METH_O, hash_doc}, {"hex", builtin_hex, METH_O, hex_doc}, {"id", builtin_id, METH_O, id_doc}, - {"input", builtin_input, METH_VARARGS, input_doc}, {"intern", builtin_intern, METH_VARARGS, intern_doc}, {"isinstance", builtin_isinstance, METH_VARARGS, isinstance_doc}, {"issubclass", builtin_issubclass, METH_VARARGS, issubclass_doc}, -- cgit v0.12 From ce96f69d69a6020c780145c89a17a8391b63624b Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 06:49:51 +0000 Subject: Get rid of a bunch more raw_input references --- Demo/pdist/cmptree.py | 5 +++++ Demo/pdist/mac.py | 11 ++++++++--- Demo/pdist/rcvs.py | 7 +++---- Demo/pdist/sumtree.py | 5 ++++- Demo/scripts/unbirthday.py | 5 +++++ Demo/sockets/ftp.py | 8 +++++++- Demo/sockets/gopher.py | 26 ++++++++++++++----------- Demo/tkinter/guido/wish.py | 5 ++++- Doc/lib/libcmd.tex | 10 ---------- Doc/lib/libcode.tex | 4 ++-- Doc/lib/libcrypt.tex | 6 ++++++ Doc/lib/libexcs.tex | 7 +------ Doc/lib/libfuncs.tex | 35 ---------------------------------- Doc/lib/libsmtplib.tex | 6 ++++++ Doc/lib/libsys.tex | 7 ++----- Doc/lib/libtelnetlib.tex | 5 +++++ Doc/lib/libtermios.tex | 6 ++++++ Doc/ref/ref8.tex | 5 +---- Doc/tools/keywords.py | 6 ++++++ Doc/tut/tut.tex | 33 +++++++++++++++++++++++++++----- Lib/cmd.py | 10 ++++++---- Lib/code.py | 10 ++++++---- Lib/distutils/command/register.py | 5 +++++ Lib/getpass.py | 3 +-- Lib/idlelib/PyShell.py | 2 +- Lib/pdb.py | 5 +++++ Lib/plat-mac/aetools.py | 5 +++++ Lib/pydoc.py | 5 +++++ Lib/rlcompleter.py | 6 ------ Lib/site.py | 4 +++- Lib/test/test_exceptions.py | 4 ++-- Lib/urllib.py | 7 ++++--- Mac/Demo/resources/copyres.py | 6 ++++++ Mac/Demo/sound/morselib.py | 40 ++++++++++++++++++++++++++------------- Misc/Vim/python.vim | 10 +++++----- Misc/cheatsheet | 10 ---------- Misc/python-mode.el | 8 ++++---- Tools/compiler/regrtest.py | 5 +++++ Tools/scripts/ftpmirror.py | 5 +++++ Tools/scripts/treesync.py | 5 +++++ Tools/scripts/xxci.py | 5 +++++ Tools/webchecker/wcmac.py | 4 +++- 42 files changed, 222 insertions(+), 144 deletions(-) diff --git a/Demo/pdist/cmptree.py b/Demo/pdist/cmptree.py index f6c611f..fa06f5f 100755 --- a/Demo/pdist/cmptree.py +++ b/Demo/pdist/cmptree.py @@ -6,6 +6,11 @@ import FSProxy import time import os +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def main(): pwd = os.getcwd() s = raw_input("chdir [%s] " % pwd) diff --git a/Demo/pdist/mac.py b/Demo/pdist/mac.py index 107113c..61cff09 100755 --- a/Demo/pdist/mac.py +++ b/Demo/pdist/mac.py @@ -1,14 +1,18 @@ import sys -import string import rcvs +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def main(): while 1: try: line = raw_input('$ ') except EOFError: break - words = string.split(line) + words = line.split() if not words: continue if words[0] != 'rcvs': @@ -16,4 +20,5 @@ def main(): sys.argv = words rcvs.main() -main() +if __name__ == '__main__': + main() diff --git a/Demo/pdist/rcvs.py b/Demo/pdist/rcvs.py index 8b8bae6..ab1fd87 100755 --- a/Demo/pdist/rcvs.py +++ b/Demo/pdist/rcvs.py @@ -35,7 +35,6 @@ from cvslib import CVS, File import md5 import os -import string import sys from cmdfw import CommandFrameWork @@ -269,13 +268,13 @@ class RCVS(CVS): def mailinfo(self, files, message = ""): towhom = "sjoerd@cwi.nl, jack@cwi.nl" # XXX - mailtext = MAILFORM % (towhom, string.join(files), - string.join(files), message) + mailtext = MAILFORM % (towhom, ' '.join(files), + ' '.join(files), message) print '-'*70 print mailtext print '-'*70 ok = raw_input("OK to mail to %s? " % towhom) - if string.lower(string.strip(ok)) in ('y', 'ye', 'yes'): + if ok.lower().strip() in ('y', 'ye', 'yes'): p = os.popen(SENDMAIL, "w") p.write(mailtext) sts = p.close() diff --git a/Demo/pdist/sumtree.py b/Demo/pdist/sumtree.py index 9291a56..68224df 100755 --- a/Demo/pdist/sumtree.py +++ b/Demo/pdist/sumtree.py @@ -1,4 +1,5 @@ import time +import sys import FSProxy def main(): @@ -9,7 +10,9 @@ def main(): proxy._close() t2 = time.time() print t2-t1, "seconds" - raw_input("[Return to exit] ") + sys.stdout.write("[Return to exit] ") + sys.stdout.flush() + sys.stdin.readline() def sumtree(proxy): print "PWD =", proxy.pwd() diff --git a/Demo/scripts/unbirthday.py b/Demo/scripts/unbirthday.py index 2d0b8e5..94ad448 100755 --- a/Demo/scripts/unbirthday.py +++ b/Demo/scripts/unbirthday.py @@ -9,6 +9,11 @@ import sys import time import calendar +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def main(): # Note that the range checks below also check for bad types, # e.g. 3.14 or (). However syntactically invalid replies diff --git a/Demo/sockets/ftp.py b/Demo/sockets/ftp.py index 6e9282a..eed45be 100755 --- a/Demo/sockets/ftp.py +++ b/Demo/sockets/ftp.py @@ -130,6 +130,11 @@ def getdata(r): sys.stdout.write(data) print '(end of data connection)' +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + # Get a command from the user. # def getcommand(): @@ -143,4 +148,5 @@ def getcommand(): # Call the main program. # -main() +if __name__ == '__main__': + main() diff --git a/Demo/sockets/gopher.py b/Demo/sockets/gopher.py index 34bcdf0..2488c81 100755 --- a/Demo/sockets/gopher.py +++ b/Demo/sockets/gopher.py @@ -4,7 +4,6 @@ # # Usage: gopher [ [selector] host [port] ] -import string import sys import os import socket @@ -42,7 +41,7 @@ def open_socket(host, port): if not port: port = DEF_PORT elif type(port) == type(''): - port = string.atoi(port) + port = int(port) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) return s @@ -73,7 +72,7 @@ def get_menu(selector, host, port): print '(Empty line from server)' continue typechar = line[0] - parts = string.splitfields(line[1:], TAB) + parts = line[1:].split(TAB) if len(parts) < 4: print '(Bad line from server: %r)' % (line,) continue @@ -160,7 +159,7 @@ def browse_menu(selector, host, port): for i in range(len(list)): item = list[i] typechar, description = item[0], item[1] - print string.rjust(repr(i+1), 3) + ':', description, + print repr(i+1).rjust(3) + ':', description, if typename.has_key(typechar): print typename[typechar] else: @@ -175,8 +174,8 @@ def browse_menu(selector, host, port): if not str: return try: - choice = string.atoi(str) - except string.atoi_error: + choice = int(str) + except ValueError: print 'Choice must be a number; try again:' continue if not 0 < choice <= len(list): @@ -218,6 +217,11 @@ def browse_textfile(selector, host, port): print 'IOError:', msg x.close() +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + # Browse a search index def browse_search(selector, host, port): while 1: @@ -230,7 +234,7 @@ def browse_search(selector, host, port): except EOFError: print break - query = string.strip(query) + query = query.strip() if not query: break if '\t' in query: @@ -300,11 +304,11 @@ def open_savefile(): except EOFError: print return None - savefile = string.strip(savefile) + savefile = savefile.strip() if not savefile: return None if savefile[0] == '|': - cmd = string.strip(savefile[1:]) + cmd = savefile[1:].strip() try: p = os.popen(cmd, 'w') except IOError, msg: @@ -331,10 +335,10 @@ def test(): browser(sys.argv[1], sys.argv[2], sys.argv[3]) elif sys.argv[2:]: try: - port = string.atoi(sys.argv[2]) + port = int(sys.argv[2]) selector = '' host = sys.argv[1] - except string.atoi_error: + except ValueError: selector = sys.argv[1] host = sys.argv[2] port = '' diff --git a/Demo/tkinter/guido/wish.py b/Demo/tkinter/guido/wish.py index 0a61ad8..35e012c 100755 --- a/Demo/tkinter/guido/wish.py +++ b/Demo/tkinter/guido/wish.py @@ -2,6 +2,7 @@ import _tkinter import os +import sys tk = _tkinter.create(os.environ['DISPLAY'], 'wish', 'Tk', 1) tk.call('update') @@ -12,7 +13,9 @@ while 1: if cmd: prompt = '' else: prompt = '% ' try: - line = raw_input(prompt) + sys.stdout.write(prompt) + sys.stdout.flush() + line = sys.stdin.readline() except EOFError: break cmd = cmd + (line + '\n') diff --git a/Doc/lib/libcmd.tex b/Doc/lib/libcmd.tex index 661eb9e..9fe8123 100644 --- a/Doc/lib/libcmd.tex +++ b/Doc/lib/libcmd.tex @@ -186,13 +186,3 @@ The character used to draw separator lines under the help-message headers. If empty, no ruler line is drawn. It defaults to \character{=}. \end{memberdesc} - -\begin{memberdesc}{use_rawinput} -A flag, defaulting to true. If true, \method{cmdloop()} uses -\function{raw_input()} to display a prompt and read the next command; -if false, \method{sys.stdout.write()} and -\method{sys.stdin.readline()} are used. (This means that by -importing \refmodule{readline}, on systems that support it, the -interpreter will automatically support \program{Emacs}-like line editing -and command-history keystrokes.) -\end{memberdesc} diff --git a/Doc/lib/libcode.tex b/Doc/lib/libcode.tex index dc4c717..628a1eb 100644 --- a/Doc/lib/libcode.tex +++ b/Doc/lib/libcode.tex @@ -167,7 +167,7 @@ Remove any unhandled source text from the input buffer. \begin{methoddesc}{raw_input}{\optional{prompt}} Write a prompt and read a line. The returned line does not include the trailing newline. When the user enters the \EOF{} key sequence, -\exception{EOFError} is raised. The base implementation uses the -built-in function \function{raw_input()}; a subclass may replace this +\exception{EOFError} is raised. The base implementation reads from +\code{sys.stdin}; a subclass may replace this with a different implementation. \end{methoddesc} diff --git a/Doc/lib/libcrypt.tex b/Doc/lib/libcrypt.tex index b6a1463..55e7163 100644 --- a/Doc/lib/libcrypt.tex +++ b/Doc/lib/libcrypt.tex @@ -41,6 +41,12 @@ A simple example illustrating typical use: \begin{verbatim} import crypt, getpass, pwd +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def login(): username = raw_input('Python login:') cryptedpasswd = pwd.getpwnam(username)[1] diff --git a/Doc/lib/libexcs.tex b/Doc/lib/libexcs.tex index f52ff0a..85058a4 100644 --- a/Doc/lib/libexcs.tex +++ b/Doc/lib/libexcs.tex @@ -153,9 +153,7 @@ Raised when an \keyword{assert} statement fails. \begin{excdesc}{EOFError} % XXXJH xrefs here - Raised when one of the built-in functions (\function{input()} or - \function{raw_input()}) hits an end-of-file condition (\EOF) without - reading any data. + Raised when attempting to read beyond the end of a file. % XXXJH xrefs here (N.B.: the \method{read()} and \method{readline()} methods of file objects return an empty string when they hit \EOF.) @@ -213,9 +211,6 @@ Raised when an \keyword{assert} statement fails. \kbd{Control-C} or \kbd{Delete}). During execution, a check for interrupts is made regularly. % XXX(hylton) xrefs here - Interrupts typed when a built-in function \function{input()} or - \function{raw_input()} is waiting for input also raise this - exception. The exception inherits from \exception{BaseException} so as to not be accidentally caught by code that catches \exception{Exception} and thus prevent the interpreter from exiting. diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex index 9b6bfe9..c75c172 100644 --- a/Doc/lib/libfuncs.tex +++ b/Doc/lib/libfuncs.tex @@ -551,23 +551,6 @@ class C: note: this is the address of the object.) \end{funcdesc} -\begin{funcdesc}{input}{\optional{prompt}} - Equivalent to \code{eval(raw_input(\var{prompt}))}. - \warning{This function is not safe from user errors! It - expects a valid Python expression as input; if the input is not - syntactically valid, a \exception{SyntaxError} will be raised. - Other exceptions may be raised if there is an error during - evaluation. (On the other hand, sometimes this is exactly what you - need when writing a quick script for expert use.)} - - If the \refmodule{readline} module was loaded, then - \function{input()} will use it to provide elaborate line editing and - history features. - - Consider using the \function{raw_input()} function for general input - from users. -\end{funcdesc} - \begin{funcdesc}{int}{\optional{x\optional{, radix}}} Convert a string or number to a plain integer. If the argument is a string, it must contain a possibly signed decimal number @@ -811,24 +794,6 @@ class C(object): \end{verbatim} \end{funcdesc} -\begin{funcdesc}{raw_input}{\optional{prompt}} - If the \var{prompt} argument is present, it is written to standard output - without a trailing newline. The function then reads a line from input, - converts it to a string (stripping a trailing newline), and returns that. - When \EOF{} is read, \exception{EOFError} is raised. Example: - -\begin{verbatim} ->>> s = raw_input('--> ') ---> Monty Python's Flying Circus ->>> s -"Monty Python's Flying Circus" -\end{verbatim} - - If the \refmodule{readline} module was loaded, then - \function{raw_input()} will use it to provide elaborate - line editing and history features. -\end{funcdesc} - \begin{funcdesc}{reduce}{function, sequence\optional{, initializer}} Apply \var{function} of two arguments cumulatively to the items of \var{sequence}, from left to right, so as to reduce the sequence to diff --git a/Doc/lib/libsmtplib.tex b/Doc/lib/libsmtplib.tex index 2f87bc4..ddf1764 100644 --- a/Doc/lib/libsmtplib.tex +++ b/Doc/lib/libsmtplib.tex @@ -267,6 +267,12 @@ processing of the \rfc{822} headers. In particular, the `To' and \begin{verbatim} import smtplib +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def prompt(prompt): return raw_input(prompt).strip() diff --git a/Doc/lib/libsys.tex b/Doc/lib/libsys.tex index ea8950a..1a57da4 100644 --- a/Doc/lib/libsys.tex +++ b/Doc/lib/libsys.tex @@ -511,11 +511,8 @@ else: \dataline{stderr} File objects corresponding to the interpreter's standard input, output and error streams. \code{stdin} is used for all interpreter - input except for scripts but including calls to - \function{input()}\bifuncindex{input} and - \function{raw_input()}\bifuncindex{raw_input}. \code{stdout} is - used for the output of \keyword{print} and expression statements and - for the prompts of \function{input()} and \function{raw_input()}. + input except for scripts. \code{stdout} is + used for the output of \keyword{print} and expression statements. The interpreter's own prompts and (almost all of) its error messages go to \code{stderr}. \code{stdout} and \code{stderr} needn't be built-in file objects: any object is acceptable as long as it has a diff --git a/Doc/lib/libtelnetlib.tex b/Doc/lib/libtelnetlib.tex index c7a4226..b8dfeee 100644 --- a/Doc/lib/libtelnetlib.tex +++ b/Doc/lib/libtelnetlib.tex @@ -196,6 +196,11 @@ import getpass import sys import telnetlib +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + HOST = "localhost" user = raw_input("Enter your remote account: ") password = getpass.getpass() diff --git a/Doc/lib/libtermios.tex b/Doc/lib/libtermios.tex index ef99cf9..64f3438 100644 --- a/Doc/lib/libtermios.tex +++ b/Doc/lib/libtermios.tex @@ -91,6 +91,12 @@ and a \keyword{try} ... \keyword{finally} statement to ensure that the old tty attributes are restored exactly no matter what happens: \begin{verbatim} +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def getpass(prompt = "Password: "): import termios, sys fd = sys.stdin.fileno() diff --git a/Doc/ref/ref8.tex b/Doc/ref/ref8.tex index d10c87f..801ab58 100644 --- a/Doc/ref/ref8.tex +++ b/Doc/ref/ref8.tex @@ -103,10 +103,7 @@ The input line read by \function{input()} must have the following form: \end{productionlist} Note: to read `raw' input line without interpretation, you can use the -built-in function \function{raw_input()} or the \method{readline()} method -of file objects. +the \method{readline()} method of file objects, including \code{sys.stdin}. \obindex{file} \index{input!raw} -\index{raw input} -\bifuncindex{raw_input} \withsubitem{(file method)}{\ttindex{readline()}} diff --git a/Doc/tools/keywords.py b/Doc/tools/keywords.py index 9f32056..3344021 100644 --- a/Doc/tools/keywords.py +++ b/Doc/tools/keywords.py @@ -2,6 +2,12 @@ # This Python program sorts and reformats the table of keywords in ref2.tex +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + l = [] try: while 1: diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex index efbc08d..054985b 100644 --- a/Doc/tut/tut.tex +++ b/Doc/tut/tut.tex @@ -231,7 +231,7 @@ full name on the command line. Note that there is a difference between \samp{python file} and \samp{python >> def raw_input(prompt): +... import sys +... sys.stdout.write(prompt) +... sys.stdout.flush() +... return sys.stdin.readline() +... >>> x = int(raw_input("Please enter an integer: ")) >>> if x < 0: ... x = 0 @@ -1453,6 +1459,12 @@ arguments. This creates a function that can be called with fewer arguments than it is defined to allow. For example: \begin{verbatim} +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): while True: ok = raw_input(prompt) @@ -2711,15 +2723,15 @@ standard module \module{__builtin__}\refbimodindex{__builtin__}: 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', - '__name__', 'abs', 'apply', 'basestring', 'bool', 'buffer', + '__name__', 'abs', 'basestring', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', - 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', + 'id', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', - 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', + 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'] \end{verbatim} @@ -3412,6 +3424,12 @@ supports); note that a user-generated interruption is signalled by raising the \exception{KeyboardInterrupt} exception. \begin{verbatim} +>>> def raw_input(prompt): +... import sys +... sys.stdout.write(prompt) +... sys.stdout.flush() +... return sys.stdin.readline() +... >>> while True: ... try: ... x = int(raw_input("Please enter a number: ")) @@ -4983,7 +5001,12 @@ renaming utility for a photo browser may elect to use percent signs for placeholders such as the current date, image sequence number, or file format: \begin{verbatim} ->>> import time, os.path +>>> import time, os.path, sys +>>> def raw_input(prompt): +... sys.stdout.write(prompt) +... sys.stdout.flush() +... return sys.stdin.readline() +... >>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg'] >>> class BatchRename(Template): ... delimiter = '%' diff --git a/Lib/cmd.py b/Lib/cmd.py index 3f82b48..23dc5b2 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -40,18 +40,20 @@ The data members `self.doc_header', `self.misc_header', and `self.undoc_header' set the headers used for the help function's listings of documented functions, miscellaneous topics, and undocumented functions respectively. - -These interpreters use raw_input; thus, if the readline module is loaded, -they automatically support Emacs-like command history and editing features. """ -import string +import string, sys __all__ = ["Cmd"] PROMPT = '(Cmd) ' IDENTCHARS = string.ascii_letters + string.digits + '_' +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + class Cmd: """A simple framework for writing line-oriented command interpreters. diff --git a/Lib/code.py b/Lib/code.py index 6bdc658..b67009b 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -269,12 +269,14 @@ class InteractiveConsole(InteractiveInterpreter): The returned line does not include the trailing newline. When the user enters the EOF key sequence, EOFError is raised. - The base implementation uses the built-in function - raw_input(); a subclass may replace this with a different - implementation. + The base implementation uses sys.stdin.readline(); a subclass + may replace this with a different implementation. """ - return raw_input(prompt) + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def interact(banner=None, readfunc=None, local=None): diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py index dec9aa2..f891262 100644 --- a/Lib/distutils/command/register.py +++ b/Lib/distutils/command/register.py @@ -13,6 +13,11 @@ import StringIO, ConfigParser from distutils.core import Command from distutils.errors import * +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + class register(Command): description = ("register the distribution with the Python package index") diff --git a/Lib/getpass.py b/Lib/getpass.py index e96491f..a30d3a1 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -69,8 +69,7 @@ def default_getpass(prompt='Password: '): def _raw_input(prompt=""): - # A raw_input() replacement that doesn't save the string in the - # GNU readline history. + # This doesn't save the string in the GNU readline history. prompt = str(prompt) if prompt: sys.stdout.write(prompt) diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index f81091b..b6abe40 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -1122,7 +1122,7 @@ class PyShell(OutputWindow): self.text.tag_add("stdin", "iomark", "end-1c") self.text.update_idletasks() if self.reading: - self.top.quit() # Break out of recursive mainloop() in raw_input() + self.top.quit() # Break out of recursive mainloop() else: self.runit() return "break" diff --git a/Lib/pdb.py b/Lib/pdb.py index b00f68b..1aa2eae 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -22,6 +22,11 @@ _saferepr = _repr.repr __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"] +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def find_function(funcname, filename): cre = re.compile(r'def\s+%s\s*[(]' % funcname) try: diff --git a/Lib/plat-mac/aetools.py b/Lib/plat-mac/aetools.py index 79f3978..861dd2f 100644 --- a/Lib/plat-mac/aetools.py +++ b/Lib/plat-mac/aetools.py @@ -342,6 +342,11 @@ _application_file._elemdict = { # XXXX Should test more, really... def test(): + def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + target = AE.AECreateDesc('sign', 'quil') ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0) print unpackevent(ae) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index ee45643..b6afc7f 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1505,6 +1505,11 @@ def writedocs(dir, pkgpath='', done=None): done[modname] = 1 writedoc(modname) +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + class Helper: keywords = { 'and': 'BOOLEAN', diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 1d29167..6eb77f9 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -28,12 +28,6 @@ application (or the user) to enable this feature, I consider this an acceptable risk. More complicated expressions (e.g. function calls or indexing operations) are *not* evaluated. -- GNU readline is also used by the built-in functions input() and -raw_input(), and thus these also benefit/suffer from the completer -features. Clearly an interactive application can benefit by -specifying its own completer function and using raw_input() for all -its input. - - When the original stdin is not a tty device, GNU readline is never used, and this module (and the readline module) are silently inactive. diff --git a/Lib/site.py b/Lib/site.py index 6818e85..5e7ff7b 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -299,7 +299,9 @@ class _Printer(object): lineno += self.MAXLINES key = None while key is None: - key = raw_input(prompt) + sys.stdout.write(prompt) + sys.stdout.flush() + key = sys.stdin.readline() if key not in ('', 'q'): key = None if key == 'q': diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index fdef876..65f7876 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -44,8 +44,8 @@ fp = open(TESTFN, 'r') savestdin = sys.stdin try: try: - sys.stdin = fp - x = raw_input() + import marshal + marshal.loads('') except EOFError: pass finally: diff --git a/Lib/urllib.py b/Lib/urllib.py index aeca3f1..136f42e 100644 --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -768,10 +768,11 @@ class FancyURLopener(URLopener): def prompt_user_passwd(self, host, realm): """Override this in a GUI environment!""" - import getpass + import getpass, sys try: - user = raw_input("Enter username for %s at %s: " % (realm, - host)) + sys.stdout.write("Enter username for %s at %s: " % (realm, host)) + sys.stdout.flush() + user = sys.stdin.readline() passwd = getpass.getpass("Enter password for %s in %s at %s: " % (user, realm, host)) return user, passwd diff --git a/Mac/Demo/resources/copyres.py b/Mac/Demo/resources/copyres.py index 528ff16..cb1fa8e 100644 --- a/Mac/Demo/resources/copyres.py +++ b/Mac/Demo/resources/copyres.py @@ -6,6 +6,12 @@ READ = 1 WRITE = 2 smAllScripts = -3 +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def copyres(src, dst): """Copy resource from src file to dst file.""" diff --git a/Mac/Demo/sound/morselib.py b/Mac/Demo/sound/morselib.py index 98e2590..3c7493f 100644 --- a/Mac/Demo/sound/morselib.py +++ b/Mac/Demo/sound/morselib.py @@ -61,11 +61,10 @@ morsetab = { } def morsecode(s): - from string import lower m = '' for c in s: - c = lower(c) - if morsetab.has_key(c): + c = c.lower() + if c in morsetab: c = morsetab[c] + ' ' else: c = '? ' @@ -107,9 +106,12 @@ class BaseMorse: def sendmorse(self, s): for c in s: - if c == '.': self.dot() - elif c == '-': self.dah() - else: self.pdah() + if c == '.': + self.dot() + elif c == '-': + self.dah() + else: + self.pdah() self.pdot() def sendascii(self, s): @@ -122,8 +124,9 @@ class BaseMorse: import Audio_mac class MyAudio(Audio_mac.Play_Audio_mac): def _callback(self, *args): - if hasattr(self, 'usercallback'): self.usercallback() - apply(Audio_mac.Play_Audio_mac._callback, (self,) + args) + if hasattr(self, 'usercallback'): + self.usercallback() + Audio_mac.Play_Audio_mac._callback(self, args) class MacMorse(BaseMorse): @@ -169,12 +172,21 @@ class MacMorse(BaseMorse): def usercallback(self): if self.morsequeue: c, self.morsequeue = self.morsequeue[0], self.morsequeue[1:] - if c == '.': self.dot() - elif c == '-': self.dah() - else: self.pdah() + if c == '.': + self.dot() + elif c == '-': + self.dah() + else: + self.pdah() self.pdot() +def raw_input(prompt): + import sys + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def test(): m = MacMorse() while 1: @@ -183,6 +195,8 @@ def test(): except (EOFError, KeyboardInterrupt): break m.send(line) - while m.morsequeue: pass + while m.morsequeue: + pass -test() +if __name__ == '__main__': + test() diff --git a/Misc/Vim/python.vim b/Misc/Vim/python.vim index 0d5e6d0..a31fa30 100644 --- a/Misc/Vim/python.vim +++ b/Misc/Vim/python.vim @@ -63,16 +63,16 @@ endif if exists("python_highlight_builtins") syn keyword pythonBuiltin unichr all set abs vars int __import__ unicode - syn keyword pythonBuiltin enumerate reduce coerce intern exit issubclass - syn keyword pythonBuiltin divmod file Ellipsis apply isinstance open any + syn keyword pythonBuiltin enumerate reduce exit issubclass + syn keyword pythonBuiltin divmod file Ellipsis isinstance open any syn keyword pythonBuiltin locals help filter basestring slice copyright min - syn keyword pythonBuiltin super sum tuple hex execfile long id xrange chr + syn keyword pythonBuiltin super sum tuple hex execfile long id chr syn keyword pythonBuiltin complex bool zip pow dict True oct NotImplemented syn keyword pythonBuiltin map None float hash getattr buffer max reversed syn keyword pythonBuiltin object quit len repr callable credits setattr syn keyword pythonBuiltin eval frozenset sorted ord __debug__ hasattr - syn keyword pythonBuiltin delattr False input license classmethod type - syn keyword pythonBuiltin raw_input list iter compile reload range globals + syn keyword pythonBuiltin delattr False license classmethod type + syn keyword pythonBuiltin list iter reload range globals syn keyword pythonBuiltin staticmethod str property round dir cmp endif diff --git a/Misc/cheatsheet b/Misc/cheatsheet index d50ed2e..b8de4281 100644 --- a/Misc/cheatsheet +++ b/Misc/cheatsheet @@ -925,8 +925,6 @@ __import__(name[, Imports module within the given context (see lib ref for globals[, locals[, more details) fromlist]]]) abs(x) Return the absolute value of number x. -apply(f, args[, Calls func/method f with arguments args and optional -keywords]) keywords. bool(x) Returns True when the argument x is true and False otherwise. buffer(obj) Creates a buffer reference to an object. callable(x) Returns True if x callable, else False. @@ -934,10 +932,6 @@ chr(i) Returns one-character string whose ASCII code isinteger i classmethod(f) Converts a function f, into a method with the class as the first argument. Useful for creating alternative constructors. cmp(x,y) Returns negative, 0, positive if x <, ==, > to y -coerce(x,y) Returns a tuple of the two numeric arguments converted to a - common type. - Compiles string into a code object.filename is used in - error message, can be any string. It isusually the file compile(string, from which the code was read, or eg. ''if not read filename, kind) from file.kind can be 'eval' if string is a single stmt, or 'single' which prints the output of expression statements @@ -971,8 +965,6 @@ hash(object) Returns the hash value of the object (if it has one) help(f) Display documentation on object f. hex(x) Converts a number x to a hexadecimal string. id(object) Returns a unique 'identity' integer for an object. -input([prompt]) Prints prompt if given. Reads input and evaluates it. - Converts a number or a string to a plain integer. Optional int(x[, base]) base paramenter specifies base from which to convert string values. intern(aString) Enters aString in the table of "interned strings" @@ -1013,8 +1005,6 @@ property() Created a property with access controlled by functions. range(start [,end Returns list of ints from >= start and < end.With 1 arg, [, step]]) list from 0..arg-1With 2 args, list from start..end-1With 3 args, list from start up to end by step -raw_input([prompt]) Prints prompt if given, then reads string from stdinput (no - trailing \n). See also input(). reduce(f, list [, Applies the binary function f to the items oflist so as to init]) reduce the list to a single value.If init given, it is "prepended" to list. diff --git a/Misc/python-mode.el b/Misc/python-mode.el index 202541c..1392c84 100644 --- a/Misc/python-mode.el +++ b/Misc/python-mode.el @@ -378,18 +378,18 @@ support for features needed by `python-mode'.") "ZeroDivisionError" "__debug__" "__import__" "__name__" "abs" "apply" "basestring" "bool" "buffer" "callable" "chr" "classmethod" - "cmp" "coerce" "compile" "complex" "copyright" + "cmp" "compile" "complex" "copyright" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "execfile" "exit" "file" "filter" "float" "getattr" "globals" "hasattr" - "hash" "hex" "id" "input" "int" "intern" + "hash" "hex" "id" "int" "intern" "isinstance" "issubclass" "iter" "len" "license" "list" "locals" "long" "map" "max" "min" "object" "oct" "open" "ord" "pow" "property" "range" - "raw_input" "reduce" "reload" "repr" "round" + "reduce" "reload" "repr" "round" "setattr" "slice" "staticmethod" "str" "sum" "super" "tuple" "type" "unichr" "unicode" "vars" - "xrange" "zip") + "zip") "\\|")) ) (list diff --git a/Tools/compiler/regrtest.py b/Tools/compiler/regrtest.py index 50d06e7..4244d62 100644 --- a/Tools/compiler/regrtest.py +++ b/Tools/compiler/regrtest.py @@ -67,6 +67,11 @@ def run_regrtest(lib_dir): def cleanup(dir): os.system("rm -rf %s" % dir) +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def main(): lib_dir = copy_library() compile_files(lib_dir) diff --git a/Tools/scripts/ftpmirror.py b/Tools/scripts/ftpmirror.py index 0f918b8..caade16 100755 --- a/Tools/scripts/ftpmirror.py +++ b/Tools/scripts/ftpmirror.py @@ -352,6 +352,11 @@ class LoggingFile: def close(self): self.outfp.write('\n') +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + # Ask permission to download a file. def askabout(filetype, filename, pwd): prompt = 'Retrieve %s %s from %s ? [ny] ' % (filetype, filename, pwd) diff --git a/Tools/scripts/treesync.py b/Tools/scripts/treesync.py index ab9324b..4fb1798 100755 --- a/Tools/scripts/treesync.py +++ b/Tools/scripts/treesync.py @@ -187,6 +187,11 @@ def copy(src, dst, rmode="rb", wmode="wb", answer='ask'): f.close() g.close() +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def okay(prompt, answer='ask'): answer = answer.strip().lower() if not answer or answer[0] not in 'ny': diff --git a/Tools/scripts/xxci.py b/Tools/scripts/xxci.py index c6a7d08..1e77c87 100755 --- a/Tools/scripts/xxci.py +++ b/Tools/scripts/xxci.py @@ -105,6 +105,11 @@ def showdiffs(file): cmd = 'rcsdiff ' + file + ' 2>&1 | ${PAGER-more}' sts = os.system(cmd) +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + def askyesno(prompt): s = raw_input(prompt) return s in ['y', 'yes'] diff --git a/Tools/webchecker/wcmac.py b/Tools/webchecker/wcmac.py index 9c8a199..9edcd5d 100644 --- a/Tools/webchecker/wcmac.py +++ b/Tools/webchecker/wcmac.py @@ -4,4 +4,6 @@ webchecker.MAXPAGE = 50000 webchecker.verbose = 2 sys.argv.append('-x') webchecker.main() -raw_input("\nCR to exit: ") +sys.stdout.write("\nCR to exit: ") +sys.stdout.flush() +sys.stdin.readline() -- cgit v0.12 From fe55464f393fc002fd0911a4d8dba6694723d408 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 06:58:45 +0000 Subject: Whitespace changes. Also remove apply from a few comments --- Include/abstract.h | 108 +++++++++++------------------------------------------ 1 file changed, 21 insertions(+), 87 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h index 9ec18fa..b76f257 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -290,35 +290,30 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ object is callable and 0 otherwise. This function always succeeds. - */ - - PyAPI_FUNC(PyObject *) PyObject_Call(PyObject *callable_object, - PyObject *args, PyObject *kw); + PyObject *args, PyObject *kw); /* Call a callable Python object, callable_object, with arguments and keywords arguments. The 'args' argument can not be NULL, but the 'kw' argument can be NULL. - */ PyAPI_FUNC(PyObject *) PyObject_CallObject(PyObject *callable_object, - PyObject *args); + PyObject *args); /* Call a callable Python object, callable_object, with arguments given by the tuple, args. If no arguments are needed, then args may be NULL. Returns the result of the call on success, or NULL on failure. This is the equivalent - of the Python expression: apply(o,args). - + of the Python expression: o(*args). */ PyAPI_FUNC(PyObject *) PyObject_CallFunction(PyObject *callable_object, - char *format, ...); + char *format, ...); /* Call a callable Python object, callable_object, with a @@ -326,13 +321,12 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ using a mkvalue-style format string. The format may be NULL, indicating that no arguments are provided. Returns the result of the call on success, or NULL on failure. This is - the equivalent of the Python expression: apply(o,args). - + the equivalent of the Python expression: o(*args). */ - PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *o, char *m, - char *format, ...); + PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *o, char *method, + char *format, ...); /* Call the method named m of object o with a variable number of @@ -345,19 +339,19 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, - ...); + ...); /* Call a callable Python object, callable_object, with a variable number of C arguments. The C arguments are provided as PyObject * values, terminated by a NULL. Returns the result of the call on success, or NULL on failure. This is - the equivalent of the Python expression: apply(o,args). + the equivalent of the Python expression: o(*args). */ PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs(PyObject *o, - PyObject *m, ...); + PyObject *method, ...); /* Call the method named m of object o with a variable number of @@ -375,7 +369,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Compute and return the hash, hash_value, of an object, o. On failure, return -1. This is the equivalent of the Python expression: hash(o). - */ @@ -386,7 +379,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns 1 if the object, o, is considered to be true, 0 if o is considered to be false and -1 on failure. This is equivalent to the Python expression: not not o - */ /* Implemented elsewhere: @@ -396,7 +388,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns 0 if the object, o, is considered to be true, 1 if o is considered to be false and -1 on failure. This is equivalent to the Python expression: not o - */ PyAPI_FUNC(PyObject *) PyObject_Type(PyObject *o); @@ -414,7 +405,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ both sequence and mapping protocols, the sequence size is returned. On error, -1 is returned. This is the equivalent to the Python expression: len(o). - */ /* For DLL compatibility */ @@ -449,7 +439,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Return element of o corresponding to the object, key, or NULL on failure. This is the equivalent of the Python expression: o[key]. - */ PyAPI_FUNC(int) PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v); @@ -476,8 +465,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ */ PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj, - const char **buffer, - Py_ssize_t *buffer_len); + const char **buffer, + Py_ssize_t *buffer_len); /* Takes an arbitrary object which must support the (character, @@ -488,7 +477,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ 0 is returned on success. buffer and buffer_len are only set in case no error occurs. Otherwise, -1 is returned and an exception set. - */ PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *obj); @@ -497,12 +485,11 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Checks whether an arbitrary object supports the (character, single segment) buffer interface. Returns 1 on success, 0 on failure. - */ PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *obj, - const void **buffer, - Py_ssize_t *buffer_len); + const void **buffer, + Py_ssize_t *buffer_len); /* Same as PyObject_AsCharBuffer() except that this API expects @@ -513,12 +500,11 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ 0 is returned on success. buffer and buffer_len are only set in case no error occurrs. Otherwise, -1 is returned and an exception set. - */ PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *obj, - void **buffer, - Py_ssize_t *buffer_len); + void **buffer, + Py_ssize_t *buffer_len); /* Takes an arbitrary object which must support the (writeable, @@ -528,7 +514,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ 0 is returned on success. buffer and buffer_len are only set in case no error occurrs. Otherwise, -1 is returned and an exception set. - */ /* Iterators */ @@ -557,7 +542,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ false otherwise. This function always succeeds. - */ PyAPI_FUNC(PyObject *) PyNumber_Add(PyObject *o1, PyObject *o2); @@ -565,8 +549,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* Returns the result of adding o1 and o2, or null on failure. This is the equivalent of the Python expression: o1+o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Subtract(PyObject *o1, PyObject *o2); @@ -575,7 +557,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of subtracting o2 from o1, or null on failure. This is the equivalent of the Python expression: o1-o2. - */ PyAPI_FUNC(PyObject *) PyNumber_Multiply(PyObject *o1, PyObject *o2); @@ -584,8 +565,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of multiplying o1 and o2, or null on failure. This is the equivalent of the Python expression: o1*o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Divide(PyObject *o1, PyObject *o2); @@ -593,8 +572,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* Returns the result of dividing o1 by o2, or null on failure. This is the equivalent of the Python expression: o1/o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_FloorDivide(PyObject *o1, PyObject *o2); @@ -603,8 +580,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of dividing o1 by o2 giving an integral result, or null on failure. This is the equivalent of the Python expression: o1//o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_TrueDivide(PyObject *o1, PyObject *o2); @@ -613,8 +588,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of dividing o1 by o2 giving a float result, or null on failure. This is the equivalent of the Python expression: o1/o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Remainder(PyObject *o1, PyObject *o2); @@ -623,8 +596,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the remainder of dividing o1 by o2, or null on failure. This is the equivalent of the Python expression: o1%o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Divmod(PyObject *o1, PyObject *o2); @@ -633,18 +604,15 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ See the built-in function divmod. Returns NULL on failure. This is the equivalent of the Python expression: divmod(o1,o2). - - */ PyAPI_FUNC(PyObject *) PyNumber_Power(PyObject *o1, PyObject *o2, - PyObject *o3); + PyObject *o3); /* See the built-in function pow. Returns NULL on failure. This is the equivalent of the Python expression: pow(o1,o2,o3), where o3 is optional. - */ PyAPI_FUNC(PyObject *) PyNumber_Negative(PyObject *o); @@ -652,7 +620,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* Returns the negation of o on success, or null on failure. This is the equivalent of the Python expression: -o. - */ PyAPI_FUNC(PyObject *) PyNumber_Positive(PyObject *o); @@ -660,7 +627,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* Returns the (what?) of o on success, or NULL on failure. This is the equivalent of the Python expression: +o. - */ PyAPI_FUNC(PyObject *) PyNumber_Absolute(PyObject *o); @@ -668,7 +634,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ /* Returns the absolute value of o, or null on failure. This is the equivalent of the Python expression: abs(o). - */ PyAPI_FUNC(PyObject *) PyNumber_Invert(PyObject *o); @@ -677,8 +642,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the bitwise negation of o on success, or NULL on failure. This is the equivalent of the Python expression: ~o. - - */ PyAPI_FUNC(PyObject *) PyNumber_Lshift(PyObject *o1, PyObject *o2); @@ -687,8 +650,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of left shifting o1 by o2 on success, or NULL on failure. This is the equivalent of the Python expression: o1 << o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Rshift(PyObject *o1, PyObject *o2); @@ -697,7 +658,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of right shifting o1 by o2 on success, or NULL on failure. This is the equivalent of the Python expression: o1 >> o2. - */ PyAPI_FUNC(PyObject *) PyNumber_And(PyObject *o1, PyObject *o2); @@ -707,7 +667,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ NULL on failure. This is the equivalent of the Python expression: o1&o2. - */ PyAPI_FUNC(PyObject *) PyNumber_Xor(PyObject *o1, PyObject *o2); @@ -716,8 +675,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the bitwise exclusive or of o1 by o2 on success, or NULL on failure. This is the equivalent of the Python expression: o1^o2. - - */ PyAPI_FUNC(PyObject *) PyNumber_Or(PyObject *o1, PyObject *o2); @@ -726,7 +683,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of bitwise or on o1 and o2 on success, or NULL on failure. This is the equivalent of the Python expression: o1|o2. - */ /* Implemented elsewhere: @@ -745,7 +701,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ return -1 (failure) and don't increment the reference counts. The call PyNumber_Coerce(&o1, &o2) is equivalent to the Python statement o1, o2 = coerce(o1, o2). - */ PyAPI_FUNC(Py_ssize_t) PyNumber_Index(PyObject *); @@ -762,7 +717,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the o converted to an integer object on success, or NULL on failure. This is the equivalent of the Python expression: int(o). - */ PyAPI_FUNC(PyObject *) PyNumber_Long(PyObject *o); @@ -771,7 +725,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the o converted to a long integer object on success, or NULL on failure. This is the equivalent of the Python expression: long(o). - */ PyAPI_FUNC(PyObject *) PyNumber_Float(PyObject *o); @@ -790,7 +743,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of adding o2 to o1, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 += o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceSubtract(PyObject *o1, PyObject *o2); @@ -799,7 +751,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of subtracting o2 from o1, possibly in-place or null on failure. This is the equivalent of the Python expression: o1 -= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceMultiply(PyObject *o1, PyObject *o2); @@ -808,7 +759,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of multiplying o1 by o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 *= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceDivide(PyObject *o1, PyObject *o2); @@ -817,29 +767,26 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of dividing o1 by o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 /= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceFloorDivide(PyObject *o1, - PyObject *o2); + PyObject *o2); /* Returns the result of dividing o1 by o2 giving an integral result, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 /= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceTrueDivide(PyObject *o1, - PyObject *o2); + PyObject *o2); /* Returns the result of dividing o1 by o2 giving a float result, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 /= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceRemainder(PyObject *o1, PyObject *o2); @@ -848,17 +795,15 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the remainder of dividing o1 by o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 %= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlacePower(PyObject *o1, PyObject *o2, - PyObject *o3); + PyObject *o3); /* Returns the result of raising o1 to the power of o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 **= o2, or pow(o1, o2, o3) if o3 is present. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceLshift(PyObject *o1, PyObject *o2); @@ -867,7 +812,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of left shifting o1 by o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 <<= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceRshift(PyObject *o1, PyObject *o2); @@ -876,7 +820,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of right shifting o1 by o2, possibly in-place or null on failure. This is the equivalent of the Python expression: o1 >>= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceAnd(PyObject *o1, PyObject *o2); @@ -885,7 +828,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of bitwise and of o1 and o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 &= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceXor(PyObject *o1, PyObject *o2); @@ -894,7 +836,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the bitwise exclusive or of o1 by o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 ^= o2. - */ PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); @@ -903,7 +844,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Returns the result of bitwise or of o1 and o2, possibly in-place, or null on failure. This is the equivalent of the Python expression: o1 |= o2. - */ @@ -916,14 +856,12 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ otherwise. This function always succeeds. - */ PyAPI_FUNC(Py_ssize_t) PySequence_Size(PyObject *o); /* Return the size of sequence object o, or -1 on failure. - */ /* For DLL compatibility */ @@ -938,7 +876,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Return the concatenation of o1 and o2 on success, and NULL on failure. This is the equivalent of the Python expression: o1+o2. - */ PyAPI_FUNC(PyObject *) PySequence_Repeat(PyObject *o, Py_ssize_t count); @@ -947,7 +884,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Return the result of repeating sequence object o count times, or NULL on failure. This is the equivalent of the Python expression: o1*count. - */ PyAPI_FUNC(PyObject *) PySequence_GetItem(PyObject *o, Py_ssize_t i); @@ -963,7 +899,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Return the slice of sequence object o between i1 and i2, or NULL on failure. This is the equivalent of the Python expression: o[i1:i2]. - */ PyAPI_FUNC(int) PySequence_SetItem(PyObject *o, Py_ssize_t i, PyObject *v); @@ -972,7 +907,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Assign object v to the ith element of o. Returns -1 on failure. This is the equivalent of the Python statement: o[i]=v. - */ PyAPI_FUNC(int) PySequence_DelItem(PyObject *o, Py_ssize_t i); @@ -984,7 +918,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ */ PyAPI_FUNC(int) PySequence_SetSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2, - PyObject *v); + PyObject *v); /* Assign the sequence object, v, to the slice in sequence -- cgit v0.12 From d91085598f5185b267ea51a3f615da9527af2ed2 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:00:19 +0000 Subject: Remove apply() --- Demo/classes/bitvec.py | 12 +- Demo/metaclasses/Eiffel.py | 6 +- Demo/metaclasses/Meta.py | 4 +- Demo/metaclasses/Simple.py | 2 +- Demo/metaclasses/Synch.py | 4 +- Demo/metaclasses/Trace.py | 6 +- Demo/pdist/RCSProxy.py | 2 +- Demo/pdist/client.py | 5 +- Demo/pdist/server.py | 4 +- Demo/threads/Coroutine.py | 2 +- Demo/threads/Generator.py | 2 +- Demo/threads/find.py | 5 +- Demo/tix/tixwidgets.py | 3 +- Demo/tkinter/guido/AttrDialog.py | 3 +- Demo/tkinter/guido/ManPage.py | 4 +- Demo/tkinter/guido/ShellWindow.py | 2 +- Demo/tkinter/guido/kill.py | 2 +- Demo/tkinter/guido/optionmenu.py | 2 +- Demo/tkinter/guido/sortvisu.py | 3 +- Demo/tkinter/guido/svkill.py | 4 +- Demo/tkinter/matt/window-creation-w-location.py | 2 +- Doc/api/abstract.tex | 3 - Doc/lib/libfuncs.tex | 20 --- Lib/bsddb/dbobj.py | 158 ++++++++++++------------ Lib/bsddb/dbshelve.py | 4 +- Lib/bsddb/test/test_basics.py | 2 +- Lib/bsddb/test/test_dbobj.py | 2 +- Lib/bsddb/test/test_join.py | 4 +- Lib/compiler/transformer.py | 2 +- Lib/distutils/archive_util.py | 2 +- Lib/distutils/command/build_ext.py | 4 +- Lib/distutils/command/build_py.py | 8 +- Lib/distutils/dir_util.py | 2 +- Lib/distutils/filelist.py | 2 +- Lib/distutils/util.py | 4 +- Lib/idlelib/MultiCall.py | 2 +- Lib/logging/__init__.py | 28 ++--- Lib/logging/config.py | 2 +- Lib/plat-mac/gensuitemodule.py | 4 +- Lib/subprocess.py | 2 +- Lib/test/crashers/infinite_rec_4.py | 7 -- Lib/test/test_builtin.py | 26 ---- Mac/Demo/sound/morse.py | 2 +- Mac/Tools/IDE/ProfileBrowser.py | 2 +- Mac/Tools/IDE/PyConsole.py | 2 +- Mac/Tools/IDE/PyDebugger.py | 4 +- Mac/Tools/IDE/Wapplication.py | 2 +- Mac/Tools/IDE/Wbase.py | 20 +-- Mac/Tools/macfreeze/macgen_bin.py | 2 +- Mac/scripts/buildpkg.py | 4 +- PCbuild/readme.txt | 2 +- Python/bltinmodule.c | 45 ------- Tools/freeze/freeze.py | 2 +- Tools/pynche/pyColorChooser.py | 2 +- Tools/unicode/gencodec.py | 4 +- Tools/webchecker/webchecker.py | 4 +- 56 files changed, 179 insertions(+), 285 deletions(-) delete mode 100644 Lib/test/crashers/infinite_rec_4.py diff --git a/Demo/classes/bitvec.py b/Demo/classes/bitvec.py index 2894a56..934d33a 100755 --- a/Demo/classes/bitvec.py +++ b/Demo/classes/bitvec.py @@ -172,7 +172,7 @@ class BitVec: def __cmp__(self, other, *rest): #rprt('%r.__cmp__%r\n' % (self, (other,) + rest)) if type(other) != type(self): - other = apply(bitvec, (other, ) + rest) + other = bitvec(other, *rest) #expensive solution... recursive binary, with slicing length = self._len if length == 0 or other._len == 0: @@ -237,7 +237,7 @@ class BitVec: #rprt('%s.__setslice__%r\n' % (self, (i, j, sequence) + rest)) i, j = _check_slice(self._len, i, j) if type(sequence) != type(self): - sequence = apply(bitvec, (sequence, ) + rest) + sequence = bitvec(sequence, *rest) #sequence is now of our own type ls_part = self[:i] ms_part = self[j:] @@ -283,7 +283,7 @@ class BitVec: def __and__(self, otherseq, *rest): #rprt('%r.__and__%r\n' % (self, (otherseq,) + rest)) if type(otherseq) != type(self): - otherseq = apply(bitvec, (otherseq, ) + rest) + otherseq = bitvec(otherseq, *rest) #sequence is now of our own type return BitVec(self._data & otherseq._data, \ min(self._len, otherseq._len)) @@ -292,7 +292,7 @@ class BitVec: def __xor__(self, otherseq, *rest): #rprt('%r.__xor__%r\n' % (self, (otherseq,) + rest)) if type(otherseq) != type(self): - otherseq = apply(bitvec, (otherseq, ) + rest) + otherseq = bitvec(otherseq, *rest) #sequence is now of our own type return BitVec(self._data ^ otherseq._data, \ max(self._len, otherseq._len)) @@ -301,7 +301,7 @@ class BitVec: def __or__(self, otherseq, *rest): #rprt('%r.__or__%r\n' % (self, (otherseq,) + rest)) if type(otherseq) != type(self): - otherseq = apply(bitvec, (otherseq, ) + rest) + otherseq = bitvec(otherseq, *rest) #sequence is now of our own type return BitVec(self._data | otherseq._data, \ max(self._len, otherseq._len)) @@ -316,7 +316,7 @@ class BitVec: #needed for *some* of the arithmetic operations #rprt('%r.__coerce__%r\n' % (self, (otherseq,) + rest)) if type(otherseq) != type(self): - otherseq = apply(bitvec, (otherseq, ) + rest) + otherseq = bitvec(otherseq, *rest) return self, otherseq def __int__(self): diff --git a/Demo/metaclasses/Eiffel.py b/Demo/metaclasses/Eiffel.py index 24fac14..8c39746 100644 --- a/Demo/metaclasses/Eiffel.py +++ b/Demo/metaclasses/Eiffel.py @@ -82,10 +82,10 @@ class EiffelMethodWrapper(MetaMethodWrapper): def __call__(self, *args, **kw): if self.pre: - apply(self.pre, args, kw) - Result = apply(self.func, (self.inst,) + args, kw) + self.pre(*args, **kw) + Result = self.func(self.inst, *args, **kw) if self.post: - apply(self.post, (Result,) + args, kw) + self.post(Result, *args, **kw) return Result class EiffelHelper(MetaHelper): diff --git a/Demo/metaclasses/Meta.py b/Demo/metaclasses/Meta.py index 580f582..9529e0f 100644 --- a/Demo/metaclasses/Meta.py +++ b/Demo/metaclasses/Meta.py @@ -14,7 +14,7 @@ class MetaMethodWrapper: self.__name__ = self.func.__name__ def __call__(self, *args, **kw): - return apply(self.func, (self.inst,) + args, kw) + return self.func(self.inst, *args, **kw) class MetaHelper: @@ -86,7 +86,7 @@ class MetaClass: init = inst.__getattr__('__init__') except AttributeError: init = lambda: None - apply(init, args, kw) + init(*args, **kw) return inst diff --git a/Demo/metaclasses/Simple.py b/Demo/metaclasses/Simple.py index 03ed259..e3e54f7 100644 --- a/Demo/metaclasses/Simple.py +++ b/Demo/metaclasses/Simple.py @@ -28,7 +28,7 @@ class BoundMethod: self.instance = instance def __call__(self, *args): print "calling", self.function, "for", self.instance, "with", args - return apply(self.function, (self.instance,) + args) + return self.function(self.instance, *args) Trace = Tracing('Trace', (), {}) diff --git a/Demo/metaclasses/Synch.py b/Demo/metaclasses/Synch.py index 80e52d9..cd13e86 100644 --- a/Demo/metaclasses/Synch.py +++ b/Demo/metaclasses/Synch.py @@ -148,10 +148,10 @@ from Meta import MetaClass, MetaHelper, MetaMethodWrapper class LockingMethodWrapper(MetaMethodWrapper): def __call__(self, *args, **kw): if self.__name__[:1] == '_' and self.__name__[1:] != '_': - return apply(self.func, (self.inst,) + args, kw) + return self.func(self.inst, *args, **kw) self.inst.__lock__.acquire() try: - return apply(self.func, (self.inst,) + args, kw) + return self.func(self.inst, *args, **kw) finally: self.inst.__lock__.release() diff --git a/Demo/metaclasses/Trace.py b/Demo/metaclasses/Trace.py index 69b9fab..97fda56 100644 --- a/Demo/metaclasses/Trace.py +++ b/Demo/metaclasses/Trace.py @@ -50,7 +50,7 @@ class TraceMetaClass: init = inst.__getattr__('__init__') except AttributeError: init = lambda: None - apply(init, args, kw) + init(*args, **kw) return inst __trace_output__ = None @@ -85,7 +85,7 @@ class NotTracingWrapper: self.func = func self.inst = inst def __call__(self, *args, **kw): - return apply(self.func, (self.inst,) + args, kw) + return self.func(self.inst, *args, **kw) class TracingWrapper(NotTracingWrapper): def __call__(self, *args, **kw): @@ -93,7 +93,7 @@ class TracingWrapper(NotTracingWrapper): "calling %s, inst=%s, args=%s, kw=%s", self.__name__, self.inst, args, kw) try: - rv = apply(self.func, (self.inst,) + args, kw) + rv = self.func(self.inst, *args, **kw) except: t, v, tb = sys.exc_info() self.inst.__trace_call__(self.inst.__trace_output__, diff --git a/Demo/pdist/RCSProxy.py b/Demo/pdist/RCSProxy.py index 87c65cc..ff3f0ce 100755 --- a/Demo/pdist/RCSProxy.py +++ b/Demo/pdist/RCSProxy.py @@ -186,7 +186,7 @@ def test(): if hasattr(proxy, what): attr = getattr(proxy, what) if callable(attr): - print apply(attr, tuple(sys.argv[2:])) + print attr(*sys.argv[2:]) else: print repr(attr) else: diff --git a/Demo/pdist/client.py b/Demo/pdist/client.py index 3e97d84..664c41b 100755 --- a/Demo/pdist/client.py +++ b/Demo/pdist/client.py @@ -132,12 +132,11 @@ from security import Security class SecureClient(Client, Security): def __init__(self, *args): - import string - apply(self._pre_init, args) + self._pre_init(*args) Security.__init__(self) self._wf.flush() line = self._rf.readline() - challenge = string.atoi(string.strip(line)) + challenge = int(line.strip()) response = self._encode_challenge(challenge) line = repr(long(response)) if line[-1] in 'Ll': line = line[:-1] diff --git a/Demo/pdist/server.py b/Demo/pdist/server.py index 01b3249..79afa8b 100755 --- a/Demo/pdist/server.py +++ b/Demo/pdist/server.py @@ -81,7 +81,7 @@ class Server: raise NameError, "illegal method name %s" % repr(methodname) else: method = getattr(self, methodname) - reply = (None, apply(method, args), id) + reply = (None, method(*args), id) except: reply = (sys.exc_info()[:2], id) if id < 0 and reply[:2] == (None, None): @@ -117,7 +117,7 @@ from security import Security class SecureServer(Server, Security): def __init__(self, *args): - apply(Server.__init__, (self,) + args) + Server.__init__(self, *args) Security.__init__(self) def _verify(self, conn, address): diff --git a/Demo/threads/Coroutine.py b/Demo/threads/Coroutine.py index 4cc65f7..10fa303 100644 --- a/Demo/threads/Coroutine.py +++ b/Demo/threads/Coroutine.py @@ -115,7 +115,7 @@ class Coroutine: if not self.killed: try: try: - apply(me.f, args) + me.f(*args) except Killed: pass finally: diff --git a/Demo/threads/Generator.py b/Demo/threads/Generator.py index a2713af..63bed9b 100644 --- a/Demo/threads/Generator.py +++ b/Demo/threads/Generator.py @@ -22,7 +22,7 @@ class Generator: self.putlock.acquire() if not self.killed: try: - apply(self.func, (self,) + self.args) + self.func(self, *self.args) except Killed: pass finally: diff --git a/Demo/threads/find.py b/Demo/threads/find.py index 7d5edc1..14148b8 100644 --- a/Demo/threads/find.py +++ b/Demo/threads/find.py @@ -17,7 +17,6 @@ import sys import getopt -import string import time import os from stat import * @@ -85,7 +84,7 @@ class WorkQ: if not job: break func, args = job - apply(func, args) + func(*args) self._donework() def run(self, nworkers): @@ -104,7 +103,7 @@ def main(): opts, args = getopt.getopt(sys.argv[1:], '-w:') for opt, arg in opts: if opt == '-w': - nworkers = string.atoi(arg) + nworkers = int(arg) if not args: args = [os.curdir] diff --git a/Demo/tix/tixwidgets.py b/Demo/tix/tixwidgets.py index de2e22e..bf7102a 100644 --- a/Demo/tix/tixwidgets.py +++ b/Demo/tix/tixwidgets.py @@ -71,8 +71,7 @@ class Demo: hm.add_checkbutton(label='BalloonHelp', underline=0, command=ToggleHelp, variable=self.useBalloons) # The trace variable option doesn't seem to work, instead I use 'command' - #apply(w.tk.call, ('trace', 'variable', self.useBalloons, 'w', - # ToggleHelp)) + #w.tk.call('trace', 'variable', self.useBalloons, 'w', ToggleHelp)) return w diff --git a/Demo/tkinter/guido/AttrDialog.py b/Demo/tkinter/guido/AttrDialog.py index 86333ad..9fa699e 100755 --- a/Demo/tkinter/guido/AttrDialog.py +++ b/Demo/tkinter/guido/AttrDialog.py @@ -155,8 +155,7 @@ class PackDialog(Dialog): def set(self, e=None): self.current = self.var.get() try: - apply(self.dialog.widget.pack, (), - {self.option: self.current}) + self.dialog.widget.pack(**{self.option: self.current}) except TclError, msg: print msg self.refresh() diff --git a/Demo/tkinter/guido/ManPage.py b/Demo/tkinter/guido/ManPage.py index 7d6fe00..911961e 100755 --- a/Demo/tkinter/guido/ManPage.py +++ b/Demo/tkinter/guido/ManPage.py @@ -22,7 +22,7 @@ class EditableManPage(ScrolledText): # Initialize instance def __init__(self, master=None, **cnf): # Initialize base class - apply(ScrolledText.__init__, (self, master), cnf) + ScrolledText.__init__(self, master, **cnf) # Define tags for formatting styles self.tag_config('X', underline=1) @@ -178,7 +178,7 @@ class ReadonlyManPage(EditableManPage): # Initialize instance def __init__(self, master=None, **cnf): cnf['state'] = DISABLED - apply(EditableManPage.__init__, (self, master), cnf) + EditableManPage.__init__(self, master, **cnf) # Alias ManPage = ReadonlyManPage diff --git a/Demo/tkinter/guido/ShellWindow.py b/Demo/tkinter/guido/ShellWindow.py index 609101b..6cdce0b 100755 --- a/Demo/tkinter/guido/ShellWindow.py +++ b/Demo/tkinter/guido/ShellWindow.py @@ -20,7 +20,7 @@ class ShellWindow(ScrolledText): args = string.split(shell) shell = args[0] - apply(ScrolledText.__init__, (self, master), cnf) + ScrolledText.__init__(self, master, **cnf) self.pos = '1.0' self.bind('', self.inputhandler) self.bind('', self.sigint) diff --git a/Demo/tkinter/guido/kill.py b/Demo/tkinter/guido/kill.py index e7df261..dd0dbf4 100755 --- a/Demo/tkinter/guido/kill.py +++ b/Demo/tkinter/guido/kill.py @@ -9,7 +9,7 @@ import os class BarButton(Menubutton): def __init__(self, master=None, **cnf): - apply(Menubutton.__init__, (self, master), cnf) + Menubutton.__init__(self, master, **cnf) self.pack(side=LEFT) self.menu = Menu(self, name='menu') self['menu'] = self.menu diff --git a/Demo/tkinter/guido/optionmenu.py b/Demo/tkinter/guido/optionmenu.py index be9d3ac..7365fa6 100644 --- a/Demo/tkinter/guido/optionmenu.py +++ b/Demo/tkinter/guido/optionmenu.py @@ -21,7 +21,7 @@ CHOICES = "Aah", "Bee", "Cee", "Dee", "Eff" var2 = StringVar() var2.set(CHOICES[0]) -menu2 = apply(OptionMenu, (root, var2) + tuple(CHOICES)) +menu2 = OptionMenu(root, var2, *CHOICES) menu2.pack() root.mainloop() diff --git a/Demo/tkinter/guido/sortvisu.py b/Demo/tkinter/guido/sortvisu.py index f18f2c1..3e4454f 100644 --- a/Demo/tkinter/guido/sortvisu.py +++ b/Demo/tkinter/guido/sortvisu.py @@ -523,8 +523,7 @@ class SortDemo: if self.size not in sizes: sizes.append(self.size) sizes.sort() - self.m_size = apply(OptionMenu, - (self.botleftframe, self.v_size) + tuple(sizes)) + self.m_size = OptionMenu(self.botleftframe, self.v_size, *sizes) self.m_size.pack(fill=X) self.v_speed = StringVar(self.master) diff --git a/Demo/tkinter/guido/svkill.py b/Demo/tkinter/guido/svkill.py index 69f7f3b..95f61b8 100755 --- a/Demo/tkinter/guido/svkill.py +++ b/Demo/tkinter/guido/svkill.py @@ -16,7 +16,7 @@ user = os.environ['LOGNAME'] class BarButton(Menubutton): def __init__(self, master=None, **cnf): - apply(Menubutton.__init__, (self, master), cnf) + Menubutton.__init__(self, master, **cnf) self.pack(side=LEFT) self.menu = Menu(self, name='menu') self['menu'] = self.menu @@ -61,7 +61,7 @@ class Kill(Frame): def do_1(self, e): self.kill(e.widget.get(e.widget.nearest(e.y))) def __init__(self, master=None, **cnf): - apply(Frame.__init__, (self, master), cnf) + Frame.__init__(self, master, **cnf) self.pack(expand=1, fill=BOTH) self.bar = Frame(self, name='bar', relief=RAISED, borderwidth=2) diff --git a/Demo/tkinter/matt/window-creation-w-location.py b/Demo/tkinter/matt/window-creation-w-location.py index 3f2b5b0..9f23bac 100644 --- a/Demo/tkinter/matt/window-creation-w-location.py +++ b/Demo/tkinter/matt/window-creation-w-location.py @@ -13,7 +13,7 @@ class QuitButton(Button): kwargs["text"] = "QUIT" if not kwargs.has_key("command"): kwargs["command"] = master.quit - apply(Button.__init__, (self, master) + args, kwargs) + Button.__init__(self, master, *args, **kwargs) class Test(Frame): def makeWindow(self, *args): diff --git a/Doc/api/abstract.tex b/Doc/api/abstract.tex index f01512c..e1c3901 100644 --- a/Doc/api/abstract.tex +++ b/Doc/api/abstract.tex @@ -235,7 +235,6 @@ determination. or \NULL{} on failure. This is the equivalent of the Python expression \samp{apply(\var{callable_object}, \var{args}, \var{kw})} or \samp{\var{callable_object}(*\var{args}, **\var{kw})}. - \bifuncindex{apply} \versionadded{2.2} \end{cfuncdesc} @@ -248,7 +247,6 @@ determination. success, or \NULL{} on failure. This is the equivalent of the Python expression \samp{apply(\var{callable_object}, \var{args})} or \samp{\var{callable_object}(*\var{args})}. - \bifuncindex{apply} \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyObject_CallFunction}{PyObject *callable, @@ -260,7 +258,6 @@ determination. result of the call on success, or \NULL{} on failure. This is the equivalent of the Python expression \samp{apply(\var{callable}, \var{args})} or \samp{\var{callable}(*\var{args})}. - \bifuncindex{apply} \end{cfuncdesc} diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex index c75c172..0be3aa9 100644 --- a/Doc/lib/libfuncs.tex +++ b/Doc/lib/libfuncs.tex @@ -1169,26 +1169,6 @@ bypass these functions without concerns about missing something important. \setindexsubitem{(non-essential built-in functions)} -\begin{funcdesc}{apply}{function, args\optional{, keywords}} - The \var{function} argument must be a callable object (a - user-defined or built-in function or method, or a class object) and - the \var{args} argument must be a sequence. The \var{function} is - called with \var{args} as the argument list; the number of arguments - is the length of the tuple. - If the optional \var{keywords} argument is present, it must be a - dictionary whose keys are strings. It specifies keyword arguments - to be added to the end of the argument list. - Calling \function{apply()} is different from just calling - \code{\var{function}(\var{args})}, since in that case there is always - exactly one argument. The use of \function{apply()} is equivalent - to \code{\var{function}(*\var{args}, **\var{keywords})}. - Use of \function{apply()} is not necessary since the ``extended call - syntax,'' as used in the last example, is completely equivalent. - - \deprecated{2.3}{Use the extended call syntax instead, as described - above.} -\end{funcdesc} - \begin{funcdesc}{buffer}{object\optional{, offset\optional{, size}}} The \var{object} argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers). A new buffer diff --git a/Lib/bsddb/dbobj.py b/Lib/bsddb/dbobj.py index 5bcf84b..4a75dd2 100644 --- a/Lib/bsddb/dbobj.py +++ b/Lib/bsddb/dbobj.py @@ -31,82 +31,82 @@ except ImportError: class DBEnv: def __init__(self, *args, **kwargs): - self._cobj = apply(db.DBEnv, args, kwargs) + self._cobj = db.DBEnv(*args, **kwargs) def close(self, *args, **kwargs): - return apply(self._cobj.close, args, kwargs) + return self._cobj.close(*args, **kwargs) def open(self, *args, **kwargs): - return apply(self._cobj.open, args, kwargs) + return self._cobj.open(*args, **kwargs) def remove(self, *args, **kwargs): - return apply(self._cobj.remove, args, kwargs) + return self._cobj.remove(*args, **kwargs) def set_shm_key(self, *args, **kwargs): - return apply(self._cobj.set_shm_key, args, kwargs) + return self._cobj.set_shm_key(*args, **kwargs) def set_cachesize(self, *args, **kwargs): - return apply(self._cobj.set_cachesize, args, kwargs) + return self._cobj.set_cachesize(*args, **kwargs) def set_data_dir(self, *args, **kwargs): - return apply(self._cobj.set_data_dir, args, kwargs) + return self._cobj.set_data_dir(*args, **kwargs) def set_flags(self, *args, **kwargs): - return apply(self._cobj.set_flags, args, kwargs) + return self._cobj.set_flags(*args, **kwargs) def set_lg_bsize(self, *args, **kwargs): - return apply(self._cobj.set_lg_bsize, args, kwargs) + return self._cobj.set_lg_bsize(*args, **kwargs) def set_lg_dir(self, *args, **kwargs): - return apply(self._cobj.set_lg_dir, args, kwargs) + return self._cobj.set_lg_dir(*args, **kwargs) def set_lg_max(self, *args, **kwargs): - return apply(self._cobj.set_lg_max, args, kwargs) + return self._cobj.set_lg_max(*args, **kwargs) def set_lk_detect(self, *args, **kwargs): - return apply(self._cobj.set_lk_detect, args, kwargs) + return self._cobj.set_lk_detect(*args, **kwargs) def set_lk_max(self, *args, **kwargs): - return apply(self._cobj.set_lk_max, args, kwargs) + return self._cobj.set_lk_max(*args, **kwargs) def set_lk_max_locks(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_locks, args, kwargs) + return self._cobj.set_lk_max_locks(*args, **kwargs) def set_lk_max_lockers(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_lockers, args, kwargs) + return self._cobj.set_lk_max_lockers(*args, **kwargs) def set_lk_max_objects(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_objects, args, kwargs) + return self._cobj.set_lk_max_objects(*args, **kwargs) def set_mp_mmapsize(self, *args, **kwargs): - return apply(self._cobj.set_mp_mmapsize, args, kwargs) + return self._cobj.set_mp_mmapsize(*args, **kwargs) def set_timeout(self, *args, **kwargs): - return apply(self._cobj.set_timeout, args, kwargs) + return self._cobj.set_timeout(*args, **kwargs) def set_tmp_dir(self, *args, **kwargs): - return apply(self._cobj.set_tmp_dir, args, kwargs) + return self._cobj.set_tmp_dir(*args, **kwargs) def txn_begin(self, *args, **kwargs): - return apply(self._cobj.txn_begin, args, kwargs) + return self._cobj.txn_begin(*args, **kwargs) def txn_checkpoint(self, *args, **kwargs): - return apply(self._cobj.txn_checkpoint, args, kwargs) + return self._cobj.txn_checkpoint(*args, **kwargs) def txn_stat(self, *args, **kwargs): - return apply(self._cobj.txn_stat, args, kwargs) + return self._cobj.txn_stat(*args, **kwargs) def set_tx_max(self, *args, **kwargs): - return apply(self._cobj.set_tx_max, args, kwargs) + return self._cobj.set_tx_max(*args, **kwargs) def set_tx_timestamp(self, *args, **kwargs): - return apply(self._cobj.set_tx_timestamp, args, kwargs) + return self._cobj.set_tx_timestamp(*args, **kwargs) def lock_detect(self, *args, **kwargs): - return apply(self._cobj.lock_detect, args, kwargs) + return self._cobj.lock_detect(*args, **kwargs) def lock_get(self, *args, **kwargs): - return apply(self._cobj.lock_get, args, kwargs) + return self._cobj.lock_get(*args, **kwargs) def lock_id(self, *args, **kwargs): - return apply(self._cobj.lock_id, args, kwargs) + return self._cobj.lock_id(*args, **kwargs) def lock_put(self, *args, **kwargs): - return apply(self._cobj.lock_put, args, kwargs) + return self._cobj.lock_put(*args, **kwargs) def lock_stat(self, *args, **kwargs): - return apply(self._cobj.lock_stat, args, kwargs) + return self._cobj.lock_stat(*args, **kwargs) def log_archive(self, *args, **kwargs): - return apply(self._cobj.log_archive, args, kwargs) + return self._cobj.log_archive(*args, **kwargs) def set_get_returns_none(self, *args, **kwargs): - return apply(self._cobj.set_get_returns_none, args, kwargs) + return self._cobj.set_get_returns_none(*args, **kwargs) if db.version() >= (4,1): def dbremove(self, *args, **kwargs): - return apply(self._cobj.dbremove, args, kwargs) + return self._cobj.dbremove(*args, **kwargs) def dbrename(self, *args, **kwargs): - return apply(self._cobj.dbrename, args, kwargs) + return self._cobj.dbrename(*args, **kwargs) def set_encrypt(self, *args, **kwargs): - return apply(self._cobj.set_encrypt, args, kwargs) + return self._cobj.set_encrypt(*args, **kwargs) class DB(DictMixin): def __init__(self, dbenv, *args, **kwargs): # give it the proper DBEnv C object that its expecting - self._cobj = apply(db.DB, (dbenv._cobj,) + args, kwargs) + self._cobj = db.DB(dbenv._cobj, *args, **kwargs) # TODO are there other dict methods that need to be overridden? def __len__(self): @@ -119,92 +119,92 @@ class DB(DictMixin): del self._cobj[arg] def append(self, *args, **kwargs): - return apply(self._cobj.append, args, kwargs) + return self._cobj.append(*args, **kwargs) def associate(self, *args, **kwargs): - return apply(self._cobj.associate, args, kwargs) + return self._cobj.associate(*args, **kwargs) def close(self, *args, **kwargs): - return apply(self._cobj.close, args, kwargs) + return self._cobj.close(*args, **kwargs) def consume(self, *args, **kwargs): - return apply(self._cobj.consume, args, kwargs) + return self._cobj.consume(*args, **kwargs) def consume_wait(self, *args, **kwargs): - return apply(self._cobj.consume_wait, args, kwargs) + return self._cobj.consume_wait(*args, **kwargs) def cursor(self, *args, **kwargs): - return apply(self._cobj.cursor, args, kwargs) + return self._cobj.cursor(*args, **kwargs) def delete(self, *args, **kwargs): - return apply(self._cobj.delete, args, kwargs) + return self._cobj.delete(*args, **kwargs) def fd(self, *args, **kwargs): - return apply(self._cobj.fd, args, kwargs) + return self._cobj.fd(*args, **kwargs) def get(self, *args, **kwargs): - return apply(self._cobj.get, args, kwargs) + return self._cobj.get(*args, **kwargs) def pget(self, *args, **kwargs): - return apply(self._cobj.pget, args, kwargs) + return self._cobj.pget(*args, **kwargs) def get_both(self, *args, **kwargs): - return apply(self._cobj.get_both, args, kwargs) + return self._cobj.get_both(*args, **kwargs) def get_byteswapped(self, *args, **kwargs): - return apply(self._cobj.get_byteswapped, args, kwargs) + return self._cobj.get_byteswapped(*args, **kwargs) def get_size(self, *args, **kwargs): - return apply(self._cobj.get_size, args, kwargs) + return self._cobj.get_size(*args, **kwargs) def get_type(self, *args, **kwargs): - return apply(self._cobj.get_type, args, kwargs) + return self._cobj.get_type(*args, **kwargs) def join(self, *args, **kwargs): - return apply(self._cobj.join, args, kwargs) + return self._cobj.join(*args, **kwargs) def key_range(self, *args, **kwargs): - return apply(self._cobj.key_range, args, kwargs) + return self._cobj.key_range(*args, **kwargs) def has_key(self, *args, **kwargs): - return apply(self._cobj.has_key, args, kwargs) + return self._cobj.has_key(*args, **kwargs) def items(self, *args, **kwargs): - return apply(self._cobj.items, args, kwargs) + return self._cobj.items(*args, **kwargs) def keys(self, *args, **kwargs): - return apply(self._cobj.keys, args, kwargs) + return self._cobj.keys(*args, **kwargs) def open(self, *args, **kwargs): - return apply(self._cobj.open, args, kwargs) + return self._cobj.open(*args, **kwargs) def put(self, *args, **kwargs): - return apply(self._cobj.put, args, kwargs) + return self._cobj.put(*args, **kwargs) def remove(self, *args, **kwargs): - return apply(self._cobj.remove, args, kwargs) + return self._cobj.remove(*args, **kwargs) def rename(self, *args, **kwargs): - return apply(self._cobj.rename, args, kwargs) + return self._cobj.rename(*args, **kwargs) def set_bt_minkey(self, *args, **kwargs): - return apply(self._cobj.set_bt_minkey, args, kwargs) + return self._cobj.set_bt_minkey(*args, **kwargs) def set_bt_compare(self, *args, **kwargs): - return apply(self._cobj.set_bt_compare, args, kwargs) + return self._cobj.set_bt_compare(*args, **kwargs) def set_cachesize(self, *args, **kwargs): - return apply(self._cobj.set_cachesize, args, kwargs) + return self._cobj.set_cachesize(*args, **kwargs) def set_flags(self, *args, **kwargs): - return apply(self._cobj.set_flags, args, kwargs) + return self._cobj.set_flags(*args, **kwargs) def set_h_ffactor(self, *args, **kwargs): - return apply(self._cobj.set_h_ffactor, args, kwargs) + return self._cobj.set_h_ffactor(*args, **kwargs) def set_h_nelem(self, *args, **kwargs): - return apply(self._cobj.set_h_nelem, args, kwargs) + return self._cobj.set_h_nelem(*args, **kwargs) def set_lorder(self, *args, **kwargs): - return apply(self._cobj.set_lorder, args, kwargs) + return self._cobj.set_lorder(*args, **kwargs) def set_pagesize(self, *args, **kwargs): - return apply(self._cobj.set_pagesize, args, kwargs) + return self._cobj.set_pagesize(*args, **kwargs) def set_re_delim(self, *args, **kwargs): - return apply(self._cobj.set_re_delim, args, kwargs) + return self._cobj.set_re_delim(*args, **kwargs) def set_re_len(self, *args, **kwargs): - return apply(self._cobj.set_re_len, args, kwargs) + return self._cobj.set_re_len(*args, **kwargs) def set_re_pad(self, *args, **kwargs): - return apply(self._cobj.set_re_pad, args, kwargs) + return self._cobj.set_re_pad(*args, **kwargs) def set_re_source(self, *args, **kwargs): - return apply(self._cobj.set_re_source, args, kwargs) + return self._cobj.set_re_source(*args, **kwargs) def set_q_extentsize(self, *args, **kwargs): - return apply(self._cobj.set_q_extentsize, args, kwargs) + return self._cobj.set_q_extentsize(*args, **kwargs) def stat(self, *args, **kwargs): - return apply(self._cobj.stat, args, kwargs) + return self._cobj.stat(*args, **kwargs) def sync(self, *args, **kwargs): - return apply(self._cobj.sync, args, kwargs) + return self._cobj.sync(*args, **kwargs) def type(self, *args, **kwargs): - return apply(self._cobj.type, args, kwargs) + return self._cobj.type(*args, **kwargs) def upgrade(self, *args, **kwargs): - return apply(self._cobj.upgrade, args, kwargs) + return self._cobj.upgrade(*args, **kwargs) def values(self, *args, **kwargs): - return apply(self._cobj.values, args, kwargs) + return self._cobj.values(*args, **kwargs) def verify(self, *args, **kwargs): - return apply(self._cobj.verify, args, kwargs) + return self._cobj.verify(*args, **kwargs) def set_get_returns_none(self, *args, **kwargs): - return apply(self._cobj.set_get_returns_none, args, kwargs) + return self._cobj.set_get_returns_none(*args, **kwargs) if db.version() >= (4,1): def set_encrypt(self, *args, **kwargs): - return apply(self._cobj.set_encrypt, args, kwargs) + return self._cobj.set_encrypt(*args, **kwargs) diff --git a/Lib/bsddb/dbshelve.py b/Lib/bsddb/dbshelve.py index d341ab7..5cd4a53 100644 --- a/Lib/bsddb/dbshelve.py +++ b/Lib/bsddb/dbshelve.py @@ -169,7 +169,7 @@ class DBShelf(DictMixin): # given nothing is passed to the extension module. That way # an exception can be raised if set_get_returns_none is turned # off. - data = apply(self.db.get, args, kw) + data = self.db.get(*args, **kw) try: return cPickle.loads(data) except (TypeError, cPickle.UnpicklingError): @@ -236,7 +236,7 @@ class DBShelfCursor: def get(self, *args): count = len(args) # a method overloading hack method = getattr(self, 'get_%d' % count) - apply(method, args) + method(*args) def get_1(self, flags): rec = self.dbc.get(flags) diff --git a/Lib/bsddb/test/test_basics.py b/Lib/bsddb/test/test_basics.py index 24c4038..7e8f835 100644 --- a/Lib/bsddb/test/test_basics.py +++ b/Lib/bsddb/test/test_basics.py @@ -444,7 +444,7 @@ class BasicTestCase(unittest.TestCase): print "attempting to use a closed cursor's %s method" % \ method # a bug may cause a NULL pointer dereference... - apply(getattr(c, method), args) + getattr(c, method)(*args) except db.DBError, val: assert val[0] == 0 if verbose: print val diff --git a/Lib/bsddb/test/test_dbobj.py b/Lib/bsddb/test/test_dbobj.py index 6799fc9..1305883 100644 --- a/Lib/bsddb/test/test_dbobj.py +++ b/Lib/bsddb/test/test_dbobj.py @@ -39,7 +39,7 @@ class dbobjTestCase(unittest.TestCase): def put(self, key, *args, **kwargs): key = string.upper(key) # call our parent classes put method with an upper case key - return apply(dbobj.DB.put, (self, key) + args, kwargs) + return dbobj.DB.put(self, key, *args, **kwargs) self.env = TestDBEnv() self.env.open(self.homeDir, db.DB_CREATE | db.DB_INIT_MPOOL) self.db = TestDB(self.env) diff --git a/Lib/bsddb/test/test_join.py b/Lib/bsddb/test/test_join.py index 73edd11..69a1e9d 100644 --- a/Lib/bsddb/test/test_join.py +++ b/Lib/bsddb/test/test_join.py @@ -72,13 +72,13 @@ class JoinTestCase(unittest.TestCase): # create and populate primary index priDB = db.DB(self.env) priDB.open(self.filename, "primary", db.DB_BTREE, db.DB_CREATE) - map(lambda t, priDB=priDB: apply(priDB.put, t), ProductIndex) + map(lambda t, priDB=priDB: priDB.put(*t), ProductIndex) # create and populate secondary index secDB = db.DB(self.env) secDB.set_flags(db.DB_DUP | db.DB_DUPSORT) secDB.open(self.filename, "secondary", db.DB_BTREE, db.DB_CREATE) - map(lambda t, secDB=secDB: apply(secDB.put, t), ColorIndex) + map(lambda t, secDB=secDB: secDB.put(*t), ColorIndex) sCursor = None jCursor = None diff --git a/Lib/compiler/transformer.py b/Lib/compiler/transformer.py index cc91b4f..504e283 100644 --- a/Lib/compiler/transformer.py +++ b/Lib/compiler/transformer.py @@ -90,7 +90,7 @@ def Node(*args): raise else: raise WalkerError, "Can't find appropriate Node type: %s" % str(args) - #return apply(ast.Node, args) + #return ast.Node(*args) class Transformer: """Utility object for transforming Python parse trees. diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 6aa5e63..b725a14 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -162,7 +162,7 @@ def make_archive (base_name, format, func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - filename = apply(func, (base_name, base_dir), kwargs) + filename = func(base_name, base_dir, **kwargs) if root_dir is not None: log.debug("changing back to '%s'", save_cwd) diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index 4191c76..6ea5d57 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py @@ -613,8 +613,8 @@ class build_ext (Command): # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext - return apply(os.path.join, ext_path) + so_ext + return os.path.join(*ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py index 621bcb4..3b7ec62 100644 --- a/Lib/distutils/command/build_py.py +++ b/Lib/distutils/command/build_py.py @@ -154,7 +154,7 @@ class build_py (Command): if not self.package_dir: if path: - return apply(os.path.join, path) + return os.path.join(*path) else: return '' else: @@ -167,7 +167,7 @@ class build_py (Command): del path[-1] else: tail.insert(0, pdir) - return apply(os.path.join, tail) + return os.path.join(*tail) else: # Oops, got all the way through 'path' without finding a # match in package_dir. If package_dir defines a directory @@ -181,7 +181,7 @@ class build_py (Command): tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' @@ -335,7 +335,7 @@ class build_py (Command): def get_module_outfile (self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] - return apply(os.path.join, outfile_path) + return os.path.join(*outfile_path) def get_outputs (self, include_bytecode=1): diff --git a/Lib/distutils/dir_util.py b/Lib/distutils/dir_util.py index 43994db..a4aff58 100644 --- a/Lib/distutils/dir_util.py +++ b/Lib/distutils/dir_util.py @@ -204,7 +204,7 @@ def remove_tree (directory, verbose=0, dry_run=0): _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: - apply(cmd[0], (cmd[1],)) + cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if _path_created.has_key(abspath): diff --git a/Lib/distutils/filelist.py b/Lib/distutils/filelist.py index 43f9aaa..4bbdd1f 100644 --- a/Lib/distutils/filelist.py +++ b/Lib/distutils/filelist.py @@ -69,7 +69,7 @@ class FileList: sortable_files.sort() self.files = [] for sort_tuple in sortable_files: - self.files.append(apply(os.path.join, sort_tuple)) + self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 387e9bd..889bf13 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -95,7 +95,7 @@ def convert_path (pathname): paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) # convert_path () @@ -295,7 +295,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - apply(func, args) + func(*args) def strtobool (val): diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py index ea8b140..4f53115 100644 --- a/Lib/idlelib/MultiCall.py +++ b/Lib/idlelib/MultiCall.py @@ -296,7 +296,7 @@ def MultiCallCreator(widget): assert issubclass(widget, Tkinter.Misc) def __init__(self, *args, **kwargs): - apply(widget.__init__, (self,)+args, kwargs) + widget.__init__(self, *args, **kwargs) # a dictionary which maps a virtual event to a tuple with: # 0. the function binded # 1. a list of triplets - the sequences it is binded to diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index d82d667..862f7ca 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -965,7 +965,7 @@ class Logger(Filterer): if self.manager.disable >= DEBUG: return if DEBUG >= self.getEffectiveLevel(): - apply(self._log, (DEBUG, msg, args), kwargs) + self._log(DEBUG, msg, args, **kwargs) def info(self, msg, *args, **kwargs): """ @@ -979,7 +979,7 @@ class Logger(Filterer): if self.manager.disable >= INFO: return if INFO >= self.getEffectiveLevel(): - apply(self._log, (INFO, msg, args), kwargs) + self._log(INFO, msg, args, **kwargs) def warning(self, msg, *args, **kwargs): """ @@ -993,7 +993,7 @@ class Logger(Filterer): if self.manager.disable >= WARNING: return if self.isEnabledFor(WARNING): - apply(self._log, (WARNING, msg, args), kwargs) + self._log(WARNING, msg, args, **kwargs) warn = warning @@ -1009,13 +1009,13 @@ class Logger(Filterer): if self.manager.disable >= ERROR: return if self.isEnabledFor(ERROR): - apply(self._log, (ERROR, msg, args), kwargs) + self._log(ERROR, msg, args, **kwargs) def exception(self, msg, *args): """ Convenience method for logging an ERROR with exception information. """ - apply(self.error, (msg,) + args, {'exc_info': 1}) + self.error(msg, *args, exc_info=1) def critical(self, msg, *args, **kwargs): """ @@ -1029,7 +1029,7 @@ class Logger(Filterer): if self.manager.disable >= CRITICAL: return if CRITICAL >= self.getEffectiveLevel(): - apply(self._log, (CRITICAL, msg, args), kwargs) + self._log(CRITICAL, msg, args, **kwargs) fatal = critical @@ -1050,7 +1050,7 @@ class Logger(Filterer): if self.manager.disable >= level: return if self.isEnabledFor(level): - apply(self._log, (level, msg, args), kwargs) + self._log(level, msg, args, **kwargs) def findCaller(self): """ @@ -1275,7 +1275,7 @@ def critical(msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.critical, (msg,)+args, kwargs) + root.critical(msg, *args, **kwargs) fatal = critical @@ -1285,14 +1285,14 @@ def error(msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.error, (msg,)+args, kwargs) + root.error(msg, *args, **kwargs) def exception(msg, *args): """ Log a message with severity 'ERROR' on the root logger, with exception information. """ - apply(error, (msg,)+args, {'exc_info': 1}) + error(msg, *args, exc_info=1) def warning(msg, *args, **kwargs): """ @@ -1300,7 +1300,7 @@ def warning(msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.warning, (msg,)+args, kwargs) + root.warning(msg, *args, **kwargs) warn = warning @@ -1310,7 +1310,7 @@ def info(msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.info, (msg,)+args, kwargs) + root.info(msg, *args, **kwargs) def debug(msg, *args, **kwargs): """ @@ -1318,7 +1318,7 @@ def debug(msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.debug, (msg,)+args, kwargs) + root.debug(msg, *args, **kwargs) def log(level, msg, *args, **kwargs): """ @@ -1326,7 +1326,7 @@ def log(level, msg, *args, **kwargs): """ if len(root.handlers) == 0: basicConfig() - apply(root.log, (level, msg)+args, kwargs) + root.log(level, msg, *args, **kwargs) def disable(level): """ diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 5adfe4d..457ec5c 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -148,7 +148,7 @@ def _install_handlers(cp, formatters): klass = eval(klass, vars(logging)) args = cp.get(sectname, "args") args = eval(args, vars(logging)) - h = apply(klass, args) + h = klass(*args) if "level" in opts: level = cp.get(sectname, "level") h.setLevel(logging._levelNames[level]) diff --git a/Lib/plat-mac/gensuitemodule.py b/Lib/plat-mac/gensuitemodule.py index 87132c5..03d38f6 100644 --- a/Lib/plat-mac/gensuitemodule.py +++ b/Lib/plat-mac/gensuitemodule.py @@ -351,11 +351,11 @@ def alt_generic(what, f, *args): def generic(what, f, *args): if type(what) == types.FunctionType: - return apply(what, (f,) + args) + return what(f, *args) if type(what) == types.ListType: record = [] for thing in what: - item = apply(generic, thing[:1] + (f,) + thing[1:]) + item = generic(thing[:1], f, *thing[1:]) record.append((thing[1], item)) return record return "BAD GENERIC ARGS: %r" % (what,) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 9cb03bc..6827244 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -995,7 +995,7 @@ class Popen(object): os.chdir(cwd) if preexec_fn: - apply(preexec_fn) + preexec_fn() if env is None: os.execvp(executable, args) diff --git a/Lib/test/crashers/infinite_rec_4.py b/Lib/test/crashers/infinite_rec_4.py deleted file mode 100644 index 14f1520..0000000 --- a/Lib/test/crashers/infinite_rec_4.py +++ /dev/null @@ -1,7 +0,0 @@ - -# http://python.org/sf/1202533 - -if __name__ == '__main__': - lst = [apply] - lst.append(lst) - apply(*lst) # segfault: infinite recursion in C diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 4f10d92..6f11fdd 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -153,32 +153,6 @@ class BuiltinTest(unittest.TestCase): S = [10, 20, 30] self.assertEqual(any(x > 42 for x in S), False) - def test_apply(self): - def f0(*args): - self.assertEqual(args, ()) - def f1(a1): - self.assertEqual(a1, 1) - def f2(a1, a2): - self.assertEqual(a1, 1) - self.assertEqual(a2, 2) - def f3(a1, a2, a3): - self.assertEqual(a1, 1) - self.assertEqual(a2, 2) - self.assertEqual(a3, 3) - apply(f0, ()) - apply(f1, (1,)) - apply(f2, (1, 2)) - apply(f3, (1, 2, 3)) - - # A PyCFunction that takes only positional parameters should allow an - # empty keyword dictionary to pass without a complaint, but raise a - # TypeError if the dictionary is non-empty. - apply(id, (1,), {}) - self.assertRaises(TypeError, apply, id, (1,), {"foo": 1}) - self.assertRaises(TypeError, apply) - self.assertRaises(TypeError, apply, id, 42) - self.assertRaises(TypeError, apply, id, (42,), 42) - def test_callable(self): self.assert_(callable(len)) def f(): pass diff --git a/Mac/Demo/sound/morse.py b/Mac/Demo/sound/morse.py index b26d554..79ec6f5 100644 --- a/Mac/Demo/sound/morse.py +++ b/Mac/Demo/sound/morse.py @@ -78,7 +78,7 @@ mkwave(OCTAVE) class BufferedAudioDev: def __init__(self, *args): import audiodev - self._base = apply(audiodev.AudioDev, args) + self._base = audiodev.AudioDev(*args) self._buffer = [] self._filled = 0 self._addmethods(self._base, self._base.__class__) diff --git a/Mac/Tools/IDE/ProfileBrowser.py b/Mac/Tools/IDE/ProfileBrowser.py index a2dafdd..1056010 100644 --- a/Mac/Tools/IDE/ProfileBrowser.py +++ b/Mac/Tools/IDE/ProfileBrowser.py @@ -65,7 +65,7 @@ class ProfileBrowser: def displaystats(self): W.SetCursor('watch') - apply(self.stats.sort_stats, self.sortkeys) + self.stats.sort_stats(*self.sortkeys) saveout = sys.stdout try: s = sys.stdout = StringIO.StringIO() diff --git a/Mac/Tools/IDE/PyConsole.py b/Mac/Tools/IDE/PyConsole.py index b8d6489..14312d5 100644 --- a/Mac/Tools/IDE/PyConsole.py +++ b/Mac/Tools/IDE/PyConsole.py @@ -26,7 +26,7 @@ def inspect(foo): # JJS 1/25/99 class ConsoleTextWidget(W.EditText): def __init__(self, *args, **kwargs): - apply(W.EditText.__init__, (self,) + args, kwargs) + W.EditText.__init__(self, *args, **kwargs) self._inputstart = 0 self._buf = '' self.pyinteractive = PyInteractive.PyInteractive() diff --git a/Mac/Tools/IDE/PyDebugger.py b/Mac/Tools/IDE/PyDebugger.py index 7fbc0f0..55f0d74 100644 --- a/Mac/Tools/IDE/PyDebugger.py +++ b/Mac/Tools/IDE/PyDebugger.py @@ -652,7 +652,7 @@ class Debugger(bdb.Bdb): class SourceViewer(W.PyEditor): def __init__(self, *args, **kwargs): - apply(W.PyEditor.__init__, (self,) + args, kwargs) + W.PyEditor.__init__(self, *args, **kwargs) self.bind('', self.clickintercept) def clickintercept(self, point, modifiers): @@ -815,7 +815,7 @@ class BreakpointsViewer: class TracingMonitor(W.Widget): def __init__(self, *args, **kwargs): - apply(W.Widget.__init__, (self,) + args, kwargs) + W.Widget.__init__(self, *args, **kwargs) self.state = 0 def toggle(self): diff --git a/Mac/Tools/IDE/Wapplication.py b/Mac/Tools/IDE/Wapplication.py index 4cfc77b..0869269 100644 --- a/Mac/Tools/IDE/Wapplication.py +++ b/Mac/Tools/IDE/Wapplication.py @@ -129,7 +129,7 @@ class Application(FrameWork.Application): window = self._windows[wid] if hasattr(window, attr): handler = getattr(window, attr) - apply(handler, args) + handler(*args) return 1 def getfrontwindow(self): diff --git a/Mac/Tools/IDE/Wbase.py b/Mac/Tools/IDE/Wbase.py index 93e499f..606b237 100644 --- a/Mac/Tools/IDE/Wbase.py +++ b/Mac/Tools/IDE/Wbase.py @@ -78,7 +78,7 @@ class Widget: if type(args[0]) == FunctionType or type(args[0]) == MethodType: self._possize = args[0] else: - apply(self.resize, args[0]) + self.resize(*args[0]) elif len(args) == 2: self._possize = (0, 0) + args elif len(args) == 4: @@ -175,37 +175,37 @@ class Widget: def forall(self, methodname, *args): for w in self._widgets: - rv = apply(w.forall, (methodname,) + args) + rv = w.forall(methodname, *args) if rv: return rv if self._bindings.has_key("<" + methodname + ">"): callback = self._bindings["<" + methodname + ">"] - rv = apply(callback, args) + rv = callback(*args) if rv: return rv if hasattr(self, methodname): method = getattr(self, methodname) - return apply(method, args) + return method(*args) def forall_butself(self, methodname, *args): for w in self._widgets: - rv = apply(w.forall, (methodname,) + args) + rv = w.forall(methodname, *args) if rv: return rv def forall_frombottom(self, methodname, *args): if self._bindings.has_key("<" + methodname + ">"): callback = self._bindings["<" + methodname + ">"] - rv = apply(callback, args) + rv = callback(*args) if rv: return rv if hasattr(self, methodname): method = getattr(self, methodname) - rv = apply(method, args) + rv = method(*args) if rv: return rv for w in self._widgets: - rv = apply(w.forall_frombottom, (methodname,) + args) + rv = w.forall_frombottom(methodname, *args) if rv: return rv @@ -670,7 +670,7 @@ def CallbackCall(callback, mustfit, *args): maxargs = func.func_code.co_argcount - 1 else: if callable(callback): - return apply(callback, args) + return callback(*args) else: raise TypeError, "uncallable callback object" @@ -679,7 +679,7 @@ def CallbackCall(callback, mustfit, *args): else: minargs = maxargs if minargs <= len(args) <= maxargs: - return apply(callback, args) + return callback(*args) elif not mustfit and minargs == 0: return callback() else: diff --git a/Mac/Tools/macfreeze/macgen_bin.py b/Mac/Tools/macfreeze/macgen_bin.py index bfcdc8b..f52e37e 100644 --- a/Mac/Tools/macfreeze/macgen_bin.py +++ b/Mac/Tools/macfreeze/macgen_bin.py @@ -180,7 +180,7 @@ def copyres(input, output, *args, **kwargs): output = Res.FSpOpenResFile(output, 3) openedout = 1 try: - apply(buildtools.copyres, (input, output) + args, kwargs) + buildtools.copyres(input, output, *args, **kwargs) finally: if openedin: Res.CloseResFile(input) diff --git a/Mac/scripts/buildpkg.py b/Mac/scripts/buildpkg.py index 7f635a0..e6dc474 100644 --- a/Mac/scripts/buildpkg.py +++ b/Mac/scripts/buildpkg.py @@ -374,7 +374,7 @@ def buildPackage(*args, **options): o = options title, version, desc = o["Title"], o["Version"], o["Description"] pm = PackageMaker(title, version, desc) - apply(pm.build, list(args), options) + pm.build(*args, **options) ###################################################################### @@ -468,7 +468,7 @@ def main(): "Description" in ok): print "Missing mandatory option!" else: - apply(buildPackage, args, optsDict) + buildPackage(*args, **optsDict) return printUsage() diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 94ea702..76c314d 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -204,7 +204,7 @@ _bsddb XXX File "C:\Code\python\lib\threading.py", line 411, in __bootstrap XXX self.run() XXX File "C:\Code\python\lib\threading.py", line 399, in run - XXX apply(self.__target, self.__args, self.__kwargs) + XXX self.__target(*self.__args, **self.__kwargs) XXX File "C:\Code\python\lib\bsddb\test\test_thread.py", line 268, in XXX readerThread XXX rec = c.next() diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 4c168eb..342c2db 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -133,50 +133,6 @@ PyDoc_STRVAR(any_doc, \n\ Return True if bool(x) is True for any x in the iterable."); -static PyObject * -builtin_apply(PyObject *self, PyObject *args) -{ - PyObject *func, *alist = NULL, *kwdict = NULL; - PyObject *t = NULL, *retval = NULL; - - if (!PyArg_UnpackTuple(args, "apply", 1, 3, &func, &alist, &kwdict)) - return NULL; - if (alist != NULL) { - if (!PyTuple_Check(alist)) { - if (!PySequence_Check(alist)) { - PyErr_Format(PyExc_TypeError, - "apply() arg 2 expected sequence, found %s", - alist->ob_type->tp_name); - return NULL; - } - t = PySequence_Tuple(alist); - if (t == NULL) - return NULL; - alist = t; - } - } - if (kwdict != NULL && !PyDict_Check(kwdict)) { - PyErr_Format(PyExc_TypeError, - "apply() arg 3 expected dictionary, found %s", - kwdict->ob_type->tp_name); - goto finally; - } - retval = PyEval_CallObjectWithKeywords(func, alist, kwdict); - finally: - Py_XDECREF(t); - return retval; -} - -PyDoc_STRVAR(apply_doc, -"apply(object[, args[, kwargs]]) -> value\n\ -\n\ -Call a callable object with positional arguments taken from the tuple args,\n\ -and keyword arguments taken from the optional dictionary kwargs.\n\ -Note that classes are callable, as are instances with a __call__() method.\n\ -\n\ -Deprecated since release 2.3. Instead, use the extended call syntax:\n\ - function(*args, **keywords)."); - static PyObject * builtin_callable(PyObject *self, PyObject *v) @@ -2090,7 +2046,6 @@ static PyMethodDef builtin_methods[] = { {"abs", builtin_abs, METH_O, abs_doc}, {"all", builtin_all, METH_O, all_doc}, {"any", builtin_any, METH_O, any_doc}, - {"apply", builtin_apply, METH_VARARGS, apply_doc}, {"callable", builtin_callable, METH_O, callable_doc}, {"chr", builtin_chr, METH_VARARGS, chr_doc}, {"cmp", builtin_cmp, METH_VARARGS, cmp_doc}, diff --git a/Tools/freeze/freeze.py b/Tools/freeze/freeze.py index 836f532..3e6a580 100755 --- a/Tools/freeze/freeze.py +++ b/Tools/freeze/freeze.py @@ -194,7 +194,7 @@ def main(): if o == '-l': addn_link.append(a) if o == '-a': - apply(modulefinder.AddPackagePath, tuple(a.split("=", 2))) + modulefinder.AddPackagePath(*a.split("=", 2)) if o == '-r': f,r = a.split("=", 2) replace_paths.append( (f,r) ) diff --git a/Tools/pynche/pyColorChooser.py b/Tools/pynche/pyColorChooser.py index 56f6940..d2ad61a 100644 --- a/Tools/pynche/pyColorChooser.py +++ b/Tools/pynche/pyColorChooser.py @@ -81,7 +81,7 @@ def askcolor(color = None, **options): """Ask for a color""" global _chooser if not _chooser: - _chooser = apply(Chooser, (), options) + _chooser = Chooser(**options) return _chooser.show(color, options) def save(): diff --git a/Tools/unicode/gencodec.py b/Tools/unicode/gencodec.py index 9b4ae16..494164a 100644 --- a/Tools/unicode/gencodec.py +++ b/Tools/unicode/gencodec.py @@ -399,6 +399,6 @@ if __name__ == '__main__': import sys if 1: - apply(convertdir,tuple(sys.argv[1:])) + convertdir(*sys.argv[1:]) else: - apply(rewritepythondir,tuple(sys.argv[1:])) + rewritepythondir(*sys.argv[1:]) diff --git a/Tools/webchecker/webchecker.py b/Tools/webchecker/webchecker.py index d918a0c..990159c 100755 --- a/Tools/webchecker/webchecker.py +++ b/Tools/webchecker/webchecker.py @@ -684,7 +684,7 @@ class Page: def note(self, level, msg, *args): if self.checker: - apply(self.checker.note, (level, msg) + args) + self.checker.note(level, msg, *args) else: if self.verbose >= level: if args: @@ -741,7 +741,7 @@ class MyURLopener(urllib.FancyURLopener): def __init__(*args): self = args[0] - apply(urllib.FancyURLopener.__init__, args) + urllib.FancyURLopener.__init__(*args) self.addheaders = [ ('User-agent', 'Python-webchecker/%s' % __version__), ] -- cgit v0.12 From 28ad48ee0a048e0f3a0d9a8462c29956f943110f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:04:59 +0000 Subject: Remove apply() --- Doc/api/abstract.tex | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Doc/api/abstract.tex b/Doc/api/abstract.tex index e1c3901..f50ebc4 100644 --- a/Doc/api/abstract.tex +++ b/Doc/api/abstract.tex @@ -233,8 +233,7 @@ determination. be \NULL{}. \var{args} must not be \NULL{}, use an empty tuple if no arguments are needed. Returns the result of the call on success, or \NULL{} on failure. This is the equivalent of the Python - expression \samp{apply(\var{callable_object}, \var{args}, \var{kw})} - or \samp{\var{callable_object}(*\var{args}, **\var{kw})}. + expression \samp{\var{callable_object}(*\var{args}, **\var{kw})}. \versionadded{2.2} \end{cfuncdesc} @@ -245,8 +244,7 @@ determination. given by the tuple \var{args}. If no arguments are needed, then \var{args} may be \NULL. Returns the result of the call on success, or \NULL{} on failure. This is the equivalent of the - Python expression \samp{apply(\var{callable_object}, \var{args})} or - \samp{\var{callable_object}(*\var{args})}. + Python expression \samp{\var{callable_object}(*\var{args})}. \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyObject_CallFunction}{PyObject *callable, @@ -256,8 +254,7 @@ determination. \cfunction{Py_BuildValue()} style format string. The format may be \NULL, indicating that no arguments are provided. Returns the result of the call on success, or \NULL{} on failure. This is the - equivalent of the Python expression \samp{apply(\var{callable}, - \var{args})} or \samp{\var{callable}(*\var{args})}. + equivalent of the Python expression \samp{\var{callable}(*\var{args})}. \end{cfuncdesc} -- cgit v0.12 From b2eaa44646088af26434f2eeede3e00f99b03f69 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:09:39 +0000 Subject: Remove some old types for int based functions (Py_ssize_t is da thing now.) --- Include/object.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Include/object.h b/Include/object.h index 131812f..184d4c6 100644 --- a/Include/object.h +++ b/Include/object.h @@ -130,21 +130,12 @@ typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *); typedef int (*inquiry)(PyObject *); typedef Py_ssize_t (*lenfunc)(PyObject *); typedef int (*coercion)(PyObject **, PyObject **); -typedef PyObject *(*intargfunc)(PyObject *, int) Py_DEPRECATED(2.5); -typedef PyObject *(*intintargfunc)(PyObject *, int, int) Py_DEPRECATED(2.5); typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); -typedef int(*intobjargproc)(PyObject *, int, PyObject *); -typedef int(*intintobjargproc)(PyObject *, int, int, PyObject *); typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *); -/* int-based buffer interface */ -typedef int (*getreadbufferproc)(PyObject *, int, void **); -typedef int (*getwritebufferproc)(PyObject *, int, void **); -typedef int (*getsegcountproc)(PyObject *, int *); -typedef int (*getcharbufferproc)(PyObject *, int, char **); /* ssize_t-based buffer interface */ typedef Py_ssize_t (*readbufferproc)(PyObject *, Py_ssize_t, void **); typedef Py_ssize_t (*writebufferproc)(PyObject *, Py_ssize_t, void **); -- cgit v0.12 From ade612be2f8b2a3569fb010d260c8cff1f93c486 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:27:50 +0000 Subject: This gets at least test_cmd_line working again and a few more modules built. --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 56cf762..2b7f6f2 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -15,7 +15,7 @@ /* Higher for patch releases */ /* Version parsed out into numeric values */ -#define PY_MAJOR_VERSION 0 +#define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 0 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -- cgit v0.12 From 7c3072437a8f2e4cd487e38d46ea99d0026bd4c2 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:28:24 +0000 Subject: More apply() cleanup --- Lib/logging/__init__.py | 4 ++-- Lib/stringold.py | 17 +++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 862f7ca..4b2f8a6 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1015,7 +1015,7 @@ class Logger(Filterer): """ Convenience method for logging an ERROR with exception information. """ - self.error(msg, *args, exc_info=1) + self.error(msg, exc_info=1, *args) def critical(self, msg, *args, **kwargs): """ @@ -1292,7 +1292,7 @@ def exception(msg, *args): Log a message with severity 'ERROR' on the root logger, with exception information. """ - error(msg, *args, exc_info=1) + error(msg, exc_info=1, *args) def warning(msg, *args, **kwargs): """ diff --git a/Lib/stringold.py b/Lib/stringold.py index 213a04c..69656be 100644 --- a/Lib/stringold.py +++ b/Lib/stringold.py @@ -126,9 +126,6 @@ def join(words, sep = ' '): return sep.join(words) joinfields = join -# for a little bit of speed -_apply = apply - # Find substring, raise exception if not found def index(s, *args): """index(s, sub [,start [,end]]) -> int @@ -136,7 +133,7 @@ def index(s, *args): Like find but raises ValueError when the substring is not found. """ - return _apply(s.index, args) + return s.index(*args) # Find last substring, raise exception if not found def rindex(s, *args): @@ -145,7 +142,7 @@ def rindex(s, *args): Like rfind but raises ValueError when the substring is not found. """ - return _apply(s.rindex, args) + return s.rindex(*args) # Count non-overlapping occurrences of substring def count(s, *args): @@ -156,7 +153,7 @@ def count(s, *args): interpreted as in slice notation. """ - return _apply(s.count, args) + return s.count(*args) # Find substring, return -1 if not found def find(s, *args): @@ -169,7 +166,7 @@ def find(s, *args): Return -1 on failure. """ - return _apply(s.find, args) + return s.find(*args) # Find last substring, return -1 if not found def rfind(s, *args): @@ -182,7 +179,7 @@ def rfind(s, *args): Return -1 on failure. """ - return _apply(s.rfind, args) + return s.rfind(*args) # for a bit of speed _float = float @@ -224,7 +221,7 @@ def atoi(*args): # error message isn't compatible but the error type is, and this function # is complicated enough already. if type(s) == _StringType: - return _apply(_int, args) + return _int(*args) else: raise TypeError('argument 1: expected string, %s found' % type(s).__name__) @@ -252,7 +249,7 @@ def atol(*args): # error message isn't compatible but the error type is, and this function # is complicated enough already. if type(s) == _StringType: - return _apply(_long, args) + return _long(*args) else: raise TypeError('argument 1: expected string, %s found' % type(s).__name__) -- cgit v0.12 From 7096760b2511df9b0e2ec846df9eb72c59e785fc Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:29:44 +0000 Subject: Get rid of xreadlines() (methods). --- Doc/lib/libbz2.tex | 9 --------- Doc/lib/libstdtypes.tex | 6 ------ Doc/tools/undoc_symbols.py | 2 +- Lib/rexec.py | 2 +- Lib/test/test_bz2.py | 10 +--------- Lib/test/test_decimal.py | 2 +- Lib/test/test_file.py | 2 +- Misc/python.man | 2 +- Modules/bz2module.c | 8 -------- Objects/fileobject.c | 7 ------- README | 6 +++--- 11 files changed, 9 insertions(+), 47 deletions(-) diff --git a/Doc/lib/libbz2.tex b/Doc/lib/libbz2.tex index f40b66f..11801fe 100644 --- a/Doc/lib/libbz2.tex +++ b/Doc/lib/libbz2.tex @@ -79,15 +79,6 @@ Return a list of lines read. The optional \var{size} argument, if given, is an approximate bound on the total number of bytes in the lines returned. \end{methoddesc} -\begin{methoddesc}[BZ2File]{xreadlines}{} -For backward compatibility. \class{BZ2File} objects now include the -performance optimizations previously implemented in the -\module{xreadlines} module. -\deprecated{2.3}{This exists only for compatibility with the method by - this name on \class{file} objects, which is - deprecated. Use \code{for line in file} instead.} -\end{methoddesc} - \begin{methoddesc}[BZ2File]{seek}{offset\optional{, whence}} Move to new file position. Argument \var{offset} is a byte count. Optional argument \var{whence} defaults to \code{0} (offset from start of file, diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index 5d15375..d2a0425 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -1583,12 +1583,6 @@ flush the read-ahead buffer. implemented, or cannot be implemented efficiently. \end{methoddesc} -\begin{methoddesc}[file]{xreadlines}{} - This method returns the same thing as \code{iter(f)}. - \versionadded{2.1} - \deprecated{2.3}{Use \samp{for \var{line} in \var{file}} instead.} -\end{methoddesc} - \begin{methoddesc}[file]{seek}{offset\optional{, whence}} Set the file's current position, like \code{stdio}'s \cfunction{fseek()}. The \var{whence} argument is optional and defaults to \code{0} diff --git a/Doc/tools/undoc_symbols.py b/Doc/tools/undoc_symbols.py index 3d776fa..782ab6c 100644 --- a/Doc/tools/undoc_symbols.py +++ b/Doc/tools/undoc_symbols.py @@ -50,7 +50,7 @@ import os, glob, re, sys def findnames(file, prefixes=()): names = {} - for line in file.xreadlines(): + for line in file: if line[0] == '!': continue fields = line.split() diff --git a/Lib/rexec.py b/Lib/rexec.py index 89ff509..ed01d24 100644 --- a/Lib/rexec.py +++ b/Lib/rexec.py @@ -29,7 +29,7 @@ __all__ = ["RExec"] class FileBase: ok_file_methods = ('fileno', 'flush', 'isatty', 'read', 'readline', - 'readlines', 'seek', 'tell', 'write', 'writelines', 'xreadlines', + 'readlines', 'seek', 'tell', 'write', 'writelines', '__iter__') diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 504a6d7..356c2e3 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -110,14 +110,6 @@ class BZ2FileTest(BaseTest): self.assertEqual(list(iter(bz2f)), sio.readlines()) bz2f.close() - def testXReadLines(self): - # "Test BZ2File.xreadlines()" - self.createTempFile() - bz2f = BZ2File(self.filename) - sio = StringIO(self.TEXT) - self.assertEqual(list(bz2f.xreadlines()), sio.readlines()) - bz2f.close() - def testUniversalNewlinesLF(self): # "Test BZ2File.read() with universal newlines (\\n)" self.createTempFile() @@ -256,7 +248,7 @@ class BZ2FileTest(BaseTest): bz2f.close() self.assertEqual(lines, ['Test']) bz2f = BZ2File(self.filename) - xlines = list(bz2f.xreadlines()) + xlines = list(bz2f.readlines()) bz2f.close() self.assertEqual(lines, ['Test']) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 34f034b..7d0addf 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -132,7 +132,7 @@ class DecimalTest(unittest.TestCase): if skip_expected: raise TestSkipped return - for line in open(file).xreadlines(): + for line in open(file): line = line.replace('\r\n', '').replace('\n', '') #print line try: diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index efb06f4..fd5670a 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -179,7 +179,7 @@ for s in (-1, 0, 1, 512): methods = ['fileno', 'flush', 'isatty', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'tell', 'truncate', 'write', - 'xreadlines', '__iter__'] + '__iter__'] if sys.platform.startswith('atheos'): methods.remove('truncate') diff --git a/Misc/python.man b/Misc/python.man index 705f7bc..42f0123 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -152,7 +152,7 @@ expressed in spaces. Issue an error when the option is given twice. .B \-u Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode. -Note that there is internal buffering in xreadlines(), readlines() and +Note that there is internal buffering in readlines() and file-object iterators ("for line in sys.stdin") which is not influenced by this option. To work around this, you will want to use "sys.stdin.readline()" inside a "while 1:" loop. diff --git a/Modules/bz2module.c b/Modules/bz2module.c index ed329b8..9576895 100644 --- a/Modules/bz2module.c +++ b/Modules/bz2module.c @@ -778,13 +778,6 @@ BZ2File_readlines(BZ2FileObject *self, PyObject *args) return list; } -PyDoc_STRVAR(BZ2File_xreadlines__doc__, -"xreadlines() -> self\n\ -\n\ -For backward compatibility. BZ2File objects now include the performance\n\ -optimizations previously implemented in the xreadlines module.\n\ -"); - PyDoc_STRVAR(BZ2File_write__doc__, "write(data) -> None\n\ \n\ @@ -1183,7 +1176,6 @@ static PyMethodDef BZ2File_methods[] = { {"read", (PyCFunction)BZ2File_read, METH_VARARGS, BZ2File_read__doc__}, {"readline", (PyCFunction)BZ2File_readline, METH_VARARGS, BZ2File_readline__doc__}, {"readlines", (PyCFunction)BZ2File_readlines, METH_VARARGS, BZ2File_readlines__doc__}, - {"xreadlines", (PyCFunction)BZ2File_getiter, METH_VARARGS, BZ2File_xreadlines__doc__}, {"write", (PyCFunction)BZ2File_write, METH_VARARGS, BZ2File_write__doc__}, {"writelines", (PyCFunction)BZ2File_writelines, METH_O, BZ2File_writelines__doc__}, {"seek", (PyCFunction)BZ2File_seek, METH_VARARGS, BZ2File_seek__doc__}, diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 57a9e9d..f96ee7b 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -1675,12 +1675,6 @@ PyDoc_STRVAR(readlines_doc, "The optional size argument, if given, is an approximate bound on the\n" "total number of bytes in the lines returned."); -PyDoc_STRVAR(xreadlines_doc, -"xreadlines() -> returns self.\n" -"\n" -"For backward compatibility. File objects now include the performance\n" -"optimizations previously implemented in the xreadlines module."); - PyDoc_STRVAR(writelines_doc, "writelines(sequence_of_strings) -> None. Write the strings to the file.\n" "\n" @@ -1719,7 +1713,6 @@ static PyMethodDef file_methods[] = { {"tell", (PyCFunction)file_tell, METH_NOARGS, tell_doc}, {"readinto", (PyCFunction)file_readinto, METH_VARARGS, readinto_doc}, {"readlines", (PyCFunction)file_readlines,METH_VARARGS, readlines_doc}, - {"xreadlines",(PyCFunction)file_self, METH_NOARGS, xreadlines_doc}, {"writelines",(PyCFunction)file_writelines, METH_O, writelines_doc}, {"flush", (PyCFunction)file_flush, METH_NOARGS, flush_doc}, {"close", (PyCFunction)file_close, METH_NOARGS, close_doc}, diff --git a/README b/README index 70cb26d..919af4c 100644 --- a/README +++ b/README @@ -504,9 +504,9 @@ Cray T3E: Mark Hadfield (m.hadfield@niwa.co.nz) writes: _codecs, _locale, _socket, _symtable, _testcapi, _weakref array, binascii, cmath, cPickle, crypt, cStringIO, dbm - errno, fcntl, grp, math, md5, operator, parser, pcre, pwd - regex, rotor, select, struct, strop, syslog, termios - time, timing, xreadlines + errno, fcntl, grp, math, md5, operator, parser, pwd + rotor, select, struct, strop, syslog, termios, + time, timing 4) Once the python executable and library have been built, make will execute setup.py, which will attempt to build remaining -- cgit v0.12 From bd2835c6ae7e707b98ace515ef1764ffe6bff267 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:54:15 +0000 Subject: as is a keyword now :-) --- Lib/test/test_array.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 078d727..87d395d 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -205,7 +205,7 @@ class BaseTest(unittest.TestCase): self.assert_((a > a) is False) self.assert_((a >= a) is True) - as = array.array(self.typecode, self.smallerexample) + al = array.array(self.typecode, self.smallerexample) ab = array.array(self.typecode, self.biggerexample) self.assert_((a == 2*a) is False) @@ -215,12 +215,12 @@ class BaseTest(unittest.TestCase): self.assert_((a > 2*a) is False) self.assert_((a >= 2*a) is False) - self.assert_((a == as) is False) - self.assert_((a != as) is True) - self.assert_((a < as) is False) - self.assert_((a <= as) is False) - self.assert_((a > as) is True) - self.assert_((a >= as) is True) + self.assert_((a == al) is False) + self.assert_((a != al) is True) + self.assert_((a < al) is False) + self.assert_((a <= al) is False) + self.assert_((a > al) is True) + self.assert_((a >= al) is True) self.assert_((a == ab) is False) self.assert_((a != ab) is True) -- cgit v0.12 From 712ce454156496e9bcd32cffde78d1626b102010 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:57:43 +0000 Subject: _Py_QnewFlag and INPLACE_DIVIDE are not necessary any longer --- Include/pydebug.h | 4 ---- Python/ceval.c | 13 ------------- Python/pythonrun.c | 4 ---- 3 files changed, 21 deletions(-) diff --git a/Include/pydebug.h b/Include/pydebug.h index f49a98e..f6c7718 100644 --- a/Include/pydebug.h +++ b/Include/pydebug.h @@ -16,10 +16,6 @@ PyAPI_DATA(int) Py_TabcheckFlag; PyAPI_DATA(int) Py_UnicodeFlag; PyAPI_DATA(int) Py_IgnoreEnvironmentFlag; PyAPI_DATA(int) Py_DivisionWarningFlag; -/* _XXX Py_QnewFlag should go away in 3.0. It's true iff -Qnew is passed, - on the command line, and is used in 2.2 by ceval.c to make all "/" divisions - true divisions (which they will be in 3.0). */ -PyAPI_DATA(int) _Py_QnewFlag; /* this is a wrapper around getenv() that pays attention to Py_IgnoreEnvironmentFlag. It should be used for getting variables like diff --git a/Python/ceval.c b/Python/ceval.c index 1a35610..6f76781 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1262,19 +1262,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) if (x != NULL) continue; break; - case INPLACE_DIVIDE: - if (!_Py_QnewFlag) { - w = POP(); - v = TOP(); - x = PyNumber_InPlaceDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - } - /* -Qnew is in effect: fall through to - INPLACE_TRUE_DIVIDE */ case INPLACE_TRUE_DIVIDE: w = POP(); v = TOP(); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index d04d111..2c5400d 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -71,10 +71,6 @@ int Py_UseClassExceptionsFlag = 1; /* Needed by bltinmodule.c: deprecated */ int Py_FrozenFlag; /* Needed by getpath.c */ int Py_UnicodeFlag = 0; /* Needed by compile.c */ int Py_IgnoreEnvironmentFlag; /* e.g. PYTHONPATH, PYTHONHOME */ -/* _XXX Py_QnewFlag should go away in 2.3. It's true iff -Qnew is passed, - on the command line, and is used in 2.2 by ceval.c to make all "/" divisions - true divisions (which they will be in 2.3). */ -int _Py_QnewFlag = 0; /* Reference to 'warnings' module, to avoid importing it on the fly when the import lock may be held. See 683658/771097 -- cgit v0.12 From e7086d409e846254df5cc89b505cbd0804d45c5e Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:59:09 +0000 Subject: INPLACE_DIVIDE is no longer necessary (INPLACE_TRUE_DIVIDE is used). --- Doc/lib/libdis.tex | 5 ----- Include/opcode.h | 2 +- Lib/compiler/pycodegen.py | 2 +- Lib/opcode.py | 2 +- Python/compile.c | 1 - 5 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Doc/lib/libdis.tex b/Doc/lib/libdis.tex index a5b2c2c..19fda5b 100644 --- a/Doc/lib/libdis.tex +++ b/Doc/lib/libdis.tex @@ -247,11 +247,6 @@ Implements in-place \code{TOS = TOS1 ** TOS}. Implements in-place \code{TOS = TOS1 * TOS}. \end{opcodedesc} -\begin{opcodedesc}{INPLACE_DIVIDE}{} -Implements in-place \code{TOS = TOS1 / TOS} when -\code{from __future__ import division} is not in effect. -\end{opcodedesc} - \begin{opcodedesc}{INPLACE_FLOOR_DIVIDE}{} Implements in-place \code{TOS = TOS1 // TOS}. \end{opcodedesc} diff --git a/Include/opcode.h b/Include/opcode.h index d05588a..e8a7c7f 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -48,7 +48,7 @@ extern "C" { #define INPLACE_ADD 55 #define INPLACE_SUBTRACT 56 #define INPLACE_MULTIPLY 57 -#define INPLACE_DIVIDE 58 + #define INPLACE_MODULO 59 #define STORE_SUBSCR 60 #define DELETE_SUBSCR 61 diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index e34120e..aac2dda 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -999,7 +999,7 @@ class CodeGenerator: '+=' : 'INPLACE_ADD', '-=' : 'INPLACE_SUBTRACT', '*=' : 'INPLACE_MULTIPLY', - '/=' : 'INPLACE_DIVIDE', + '/=' : 'INPLACE_TRUE_DIVIDE', '//=': 'INPLACE_FLOOR_DIVIDE', '%=' : 'INPLACE_MODULO', '**=': 'INPLACE_POWER', diff --git a/Lib/opcode.py b/Lib/opcode.py index 2b9212f..573a7b0 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -88,7 +88,7 @@ def_op('DELETE_SLICE+3', 53) def_op('INPLACE_ADD', 55) def_op('INPLACE_SUBTRACT', 56) def_op('INPLACE_MULTIPLY', 57) -def_op('INPLACE_DIVIDE', 58) + def_op('INPLACE_MODULO', 59) def_op('STORE_SUBSCR', 60) def_op('DELETE_SUBSCR', 61) diff --git a/Python/compile.c b/Python/compile.c index 9ce2bf7..b92fb62 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1338,7 +1338,6 @@ opcode_stack_effect(int opcode, int oparg) case INPLACE_ADD: case INPLACE_SUBTRACT: case INPLACE_MULTIPLY: - case INPLACE_DIVIDE: case INPLACE_MODULO: return -1; case STORE_SUBSCR: -- cgit v0.12 From a10f52e3b2f028bb9d5e232f067d42cd74b95184 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 09:00:00 +0000 Subject: Remove PyArg_NoArgs() and PyArg_GetInt() --- Include/Python.h | 7 ------- Modules/cstubs | 2 -- RISCOS/Modules/riscosmodule.c | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Include/Python.h b/Include/Python.h index 8df7dbc..161c838 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -131,13 +131,6 @@ /* _Py_Mangle is defined in compile.c */ PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); -/* PyArg_GetInt is deprecated and should not be used, use PyArg_Parse(). */ -#define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) - -/* PyArg_NoArgs should not be necessary. - Set ml_flags in the PyMethodDef to METH_NOARGS. */ -#define PyArg_NoArgs(v) PyArg_Parse(v, "") - /* Convert a possibly signed character to a nonnegative int */ /* XXX This assumes characters are 8 bits wide */ #ifdef __CHAR_UNSIGNED__ diff --git a/Modules/cstubs b/Modules/cstubs index 53bd4ab..c8c7dd5 100644 --- a/Modules/cstubs +++ b/Modules/cstubs @@ -472,8 +472,6 @@ endpick_select(args, func) { PyObject *v, *w; int i, nhits, n; - if (!PyArg_NoArgs(args)) - return NULL; if (pickbuffer == NULL) { PyErr_SetString(PyExc_RuntimeError, "endpick/endselect: not in pick/select mode"); diff --git a/RISCOS/Modules/riscosmodule.c b/RISCOS/Modules/riscosmodule.c index 55f189d..2ad4499 100644 --- a/RISCOS/Modules/riscosmodule.c +++ b/RISCOS/Modules/riscosmodule.c @@ -79,7 +79,7 @@ static PyObject *canon(char *path) } static PyObject *riscos_getcwd(PyObject *self,PyObject *args) -{ if(!PyArg_NoArgs(args)) return NULL; +{ return canon("@"); } @@ -354,7 +354,7 @@ static PyMethodDef riscos_methods[] = { {"system", riscos_system}, {"rmdir", riscos_remove}, {"chdir", riscos_chdir}, - {"getcwd", riscos_getcwd}, + {"getcwd", riscos_getcwd, METH_NOARGS}, {"expand", riscos_expand}, {"mkdir", riscos_mkdir,1}, {"listdir", riscos_listdir}, -- cgit v0.12 From 398b7351ac7240f1c127646d72fc606ee08a7b83 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 20 Mar 2006 04:35:06 +0000 Subject: Some more TODO items of things I'd like to start with. Mention the builtins that Neal killed. --- Misc/NEWS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS b/Misc/NEWS index 111112b..1595649 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,7 +12,21 @@ What's New in Python 3000? TO DO ----- -- Make strings all Unicode. (First have to introduce the bytes type.) +- See PEP 3000. + +- Test merging certain changes from the 2.5 HEAD code. + +- Weed really old/weird stuff from the library. + +- Unify range() and xrange(). + +- Revamp the dict API: keys(), values(), items() return iterators, etc. + +- Add the bytes type. + +- Rework the standard I/O library to use bytes for binary files. + +- Make strings all Unicode. - Get rid of classic class implementation. @@ -31,6 +45,8 @@ Core and Builtins - Absolute import is the default behavior for 'import foo' etc. +- input(), raw_input() and apply() are gone. + Extension Modules ----------------- -- cgit v0.12 From ff80579f2eeabb3b5fd12512c7a32814fc0d0f29 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 18:05:50 +0000 Subject: testing svn emails --- Misc/ignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/ignore diff --git a/Misc/ignore b/Misc/ignore new file mode 100644 index 0000000..be9bd12 --- /dev/null +++ b/Misc/ignore @@ -0,0 +1 @@ +ignore -- cgit v0.12 From e63f01e44efa5496abc5a6b7a57b1bd6263dc525 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 18:13:08 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index be9bd12..4fad7fa 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ignore +ingore -- cgit v0.12 From c6718808c3f8d9e13da99a3932563c6df90eaead Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 18:18:20 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index 4fad7fa..be9bd12 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ingore +ignore -- cgit v0.12 From 05eb9a315122526d95fbc5dd562b7f8a76accaae Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 18:21:43 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index be9bd12..4fad7fa 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ignore +ingore -- cgit v0.12 From e3c8ebbff6e91e89bff69b3c06204a92f26697d3 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 18:29:19 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index 4fad7fa..be9bd12 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ingore +ignore -- cgit v0.12 From 72a786e4f3ee71265d76f1608ac94c405433dc0b Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 19:21:14 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index be9bd12..4fad7fa 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ignore +ingore -- cgit v0.12 From cf028c1c7d70eb0b788594cb8fe28eddc641e823 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 19:35:05 +0000 Subject: testing svn emails --- Misc/ignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ignore b/Misc/ignore index 4fad7fa..be9bd12 100644 --- a/Misc/ignore +++ b/Misc/ignore @@ -1 +1 @@ -ingore +ignore -- cgit v0.12 From 3114608c518866ffda706fb9bf466ed980f69356 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 21 Mar 2006 19:47:07 +0000 Subject: remove test file --- Misc/ignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/ignore diff --git a/Misc/ignore b/Misc/ignore deleted file mode 100644 index be9bd12..0000000 --- a/Misc/ignore +++ /dev/null @@ -1 +0,0 @@ -ignore -- cgit v0.12 From f84c38a39ad9b870a43d35fba7d0f3586a492176 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 22 Mar 2006 07:12:41 +0000 Subject: Damn another occurrence of using as as a keywordf --- Lib/plat-mac/applesingle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/plat-mac/applesingle.py b/Lib/plat-mac/applesingle.py index 56d73a7..b035d9e 100644 --- a/Lib/plat-mac/applesingle.py +++ b/Lib/plat-mac/applesingle.py @@ -119,8 +119,8 @@ def decode(infile, outpath, resonly=False, verbose=False): infile = infile.as_pathname() infile = open(infile, 'rb') - as = AppleSingle(infile, verbose=verbose) - as.tofile(outpath, resonly=resonly) + asfile = AppleSingle(infile, verbose=verbose) + asfile.tofile(outpath, resonly=resonly) def _test(): if len(sys.argv) < 3 or sys.argv[1] == '-r' and len(sys.argv) != 4: -- cgit v0.12 From 57c179c5b92f1cbe21e23b514f34f75e0d32bbfb Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 22 Mar 2006 07:18:02 +0000 Subject: Remove staticforward and statichere --- Include/object.h | 15 --------------- Misc/NEWS | 2 ++ Modules/_sre.c | 6 +++--- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Include/object.h b/Include/object.h index 184d4c6..5739651 100644 --- a/Include/object.h +++ b/Include/object.h @@ -685,21 +685,6 @@ PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */ */ PyAPI_DATA(int) _Py_SwappedOp[]; -/* -Define staticforward and statichere for source compatibility with old -C extensions. - -The staticforward define was needed to support certain broken C -compilers (notably SCO ODT 3.0, perhaps early AIX as well) botched the -static keyword when it was used with a forward declaration of a static -initialized structure. Standard C allows the forward declaration with -static, and we've decided to stop catering to broken C compilers. -(In fact, we expect that the compilers are all fixed eight years later.) -*/ - -#define staticforward static -#define statichere static - /* More conventions diff --git a/Misc/NEWS b/Misc/NEWS index 1595649..3dce99d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -47,6 +47,8 @@ Core and Builtins - input(), raw_input() and apply() are gone. +- Removed staticforward and statichere macros. + Extension Modules ----------------- diff --git a/Modules/_sre.c b/Modules/_sre.c index 413ae09..c167210 100644 --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -2770,7 +2770,7 @@ pattern_getattr(PatternObject* self, char* name) return NULL; } -statichere PyTypeObject Pattern_Type = { +static PyTypeObject Pattern_Type = { PyObject_HEAD_INIT(NULL) 0, "_" SRE_MODULE ".SRE_Pattern", sizeof(PatternObject), sizeof(SRE_CODE), @@ -3251,7 +3251,7 @@ match_getattr(MatchObject* self, char* name) /* FIXME: implement setattr("string", None) as a special case (to detach the associated string, if any */ -statichere PyTypeObject Match_Type = { +static PyTypeObject Match_Type = { PyObject_HEAD_INIT(NULL) 0, "_" SRE_MODULE ".SRE_Match", sizeof(MatchObject), sizeof(int), @@ -3361,7 +3361,7 @@ scanner_getattr(ScannerObject* self, char* name) return NULL; } -statichere PyTypeObject Scanner_Type = { +static PyTypeObject Scanner_Type = { PyObject_HEAD_INIT(NULL) 0, "_" SRE_MODULE ".SRE_Scanner", sizeof(ScannerObject), 0, -- cgit v0.12 From 914a81841f881122f95eb5a9330a3ef96de174cd Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 22 Mar 2006 09:20:30 +0000 Subject: Get doctest to pass. The problem was int/int -> float now. There were some cases where an int was assumed. Also had to change the string of the exception when dividing and int by zero. Not sure what the best error message should be. Currently 5 / 0 yields the message: ZeroDivisionError: float division That isn't entirely correct. But I'm not sure what else to do. --- Lib/test/test_doctest.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 9c39ee8..1f89ac2 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -605,7 +605,7 @@ DocTestFinder finds the line number of each example: ... ... print x, ... 0 1 2 3 4 5 6 7 8 9 ... >>> x/2 - ... 6 + ... 6.0 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> [e.lineno for e in test.examples] @@ -680,7 +680,7 @@ statistics. Here's a simple DocTest case we can use: ... >>> print x ... 12 ... >>> x/2 - ... 6 + ... 6.0 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] @@ -701,7 +701,7 @@ the failure and proceeds to the next example: ... >>> print x ... 14 ... >>> x/2 - ... 6 + ... 6.0 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=True).run(test) @@ -725,7 +725,7 @@ the failure and proceeds to the next example: Trying: x/2 Expecting: - 6 + 6.0 ok (1, 3) """ @@ -739,7 +739,7 @@ output: ... >>> print x ... 12 ... >>> x/2 - ... 6 + ... 6.0 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] @@ -756,7 +756,7 @@ output: Trying: x/2 Expecting: - 6 + 6.0 ok (0, 3) @@ -786,7 +786,7 @@ iff `-v` appears in sys.argv: Trying: x/2 Expecting: - 6 + 6.0 ok (0, 3) @@ -808,7 +808,7 @@ replaced with any other string: ... >>> x = 12 ... >>> print x/0 ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero + ... ZeroDivisionError: float division ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -825,7 +825,7 @@ unexpected exception: ... >>> print 'pre-exception output', x/0 ... pre-exception output ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero + ... ZeroDivisionError: float division ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -836,7 +836,7 @@ unexpected exception: print 'pre-exception output', x/0 Exception raised: ... - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: float division (1, 2) Exception messages may contain newlines: @@ -933,7 +933,7 @@ unexpected exception: Exception raised: Traceback (most recent call last): ... - ZeroDivisionError: integer division or modulo by zero + ZeroDivisionError: float division (1, 1) """ def optionflags(): r""" -- cgit v0.12 From 227b5339edcdcc9ca6322a2217b75db11b69048f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 22 Mar 2006 09:28:35 +0000 Subject: Finish getting rid of statichere/staticforward --- Modules/_bsddb.c | 12 ++++++------ Modules/_csv.c | 6 +++--- Modules/_ctypes/_ctypes.c | 2 +- Modules/_elementtree.c | 12 ++++++------ Modules/_lsprof.c | 4 ++-- Modules/_tkinter.c | 4 ++-- Modules/datetimemodule.c | 6 +++--- PC/_subprocess.c | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c index 9d0893e..8e9ec0f 100644 --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -283,7 +283,7 @@ typedef struct { -staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLock_Type; +static PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLock_Type; #define DBObject_Check(v) ((v)->ob_type == &DB_Type) #define DBCursorObject_Check(v) ((v)->ob_type == &DBCursor_Type) @@ -4823,7 +4823,7 @@ DBLock_getattr(DBLockObject* self, char *name) return NULL; } -statichere PyTypeObject DB_Type = { +static PyTypeObject DB_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "DB", /*tp_name*/ @@ -4856,7 +4856,7 @@ statichere PyTypeObject DB_Type = { }; -statichere PyTypeObject DBCursor_Type = { +static PyTypeObject DBCursor_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "DBCursor", /*tp_name*/ @@ -4889,7 +4889,7 @@ statichere PyTypeObject DBCursor_Type = { }; -statichere PyTypeObject DBEnv_Type = { +static PyTypeObject DBEnv_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "DBEnv", /*tp_name*/ @@ -4921,7 +4921,7 @@ statichere PyTypeObject DBEnv_Type = { #endif }; -statichere PyTypeObject DBTxn_Type = { +static PyTypeObject DBTxn_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "DBTxn", /*tp_name*/ @@ -4954,7 +4954,7 @@ statichere PyTypeObject DBTxn_Type = { }; -statichere PyTypeObject DBLock_Type = { +static PyTypeObject DBLock_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "DBLock", /*tp_name*/ diff --git a/Modules/_csv.c b/Modules/_csv.c index da5ae0d..469cd64 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -83,7 +83,7 @@ typedef struct { int strict; /* raise exception on bad CSV */ } DialectObj; -staticforward PyTypeObject Dialect_Type; +static PyTypeObject Dialect_Type; typedef struct { PyObject_HEAD @@ -101,7 +101,7 @@ typedef struct { unsigned long line_num; /* Source-file line number */ } ReaderObj; -staticforward PyTypeObject Reader_Type; +static PyTypeObject Reader_Type; #define ReaderObject_Check(v) ((v)->ob_type == &Reader_Type) @@ -118,7 +118,7 @@ typedef struct { int num_fields; /* number of fields in record */ } WriterObj; -staticforward PyTypeObject Writer_Type; +static PyTypeObject Writer_Type; /* * DIALECT class diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index bf88af8..3a7d3ef 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -591,7 +591,7 @@ PointerType_set_type(PyTypeObject *self, PyObject *type) return Py_None; } -staticforward PyObject *_byref(PyObject *); +static PyObject *_byref(PyObject *); static PyObject * PointerType_from_param(PyObject *type, PyObject *value) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 06ae24c..7e36ea3 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -267,7 +267,7 @@ typedef struct { } ElementObject; -staticforward PyTypeObject Element_Type; +static PyTypeObject Element_Type; #define Element_CheckExact(op) ((op)->ob_type == &Element_Type) @@ -1396,7 +1396,7 @@ static PySequenceMethods element_as_sequence = { element_setslice, }; -statichere PyTypeObject Element_Type = { +static PyTypeObject Element_Type = { PyObject_HEAD_INIT(NULL) 0, "Element", sizeof(ElementObject), 0, /* methods */ @@ -1435,7 +1435,7 @@ typedef struct { } TreeBuilderObject; -staticforward PyTypeObject TreeBuilder_Type; +static PyTypeObject TreeBuilder_Type; #define TreeBuilder_CheckExact(op) ((op)->ob_type == &TreeBuilder_Type) @@ -1805,7 +1805,7 @@ treebuilder_getattr(TreeBuilderObject* self, char* name) return Py_FindMethod(treebuilder_methods, (PyObject*) self, name); } -statichere PyTypeObject TreeBuilder_Type = { +static PyTypeObject TreeBuilder_Type = { PyObject_HEAD_INIT(NULL) 0, "TreeBuilder", sizeof(TreeBuilderObject), 0, /* methods */ @@ -1849,7 +1849,7 @@ typedef struct { } XMLParserObject; -staticforward PyTypeObject XMLParser_Type; +static PyTypeObject XMLParser_Type; /* helpers */ @@ -2580,7 +2580,7 @@ xmlparser_getattr(XMLParserObject* self, char* name) return res; } -statichere PyTypeObject XMLParser_Type = { +static PyTypeObject XMLParser_Type = { PyObject_HEAD_INIT(NULL) 0, "XMLParser", sizeof(XMLParserObject), 0, /* methods */ diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index dddab8e..17c71e9 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -117,7 +117,7 @@ typedef struct { #define POF_BUILTINS 0x004 #define POF_NOMEMORY 0x100 -staticforward PyTypeObject PyProfiler_Type; +static PyTypeObject PyProfiler_Type; #define PyProfiler_Check(op) PyObject_TypeCheck(op, &PyProfiler_Type) #define PyProfiler_CheckExact(op) ((op)->ob_type == &PyProfiler_Type) @@ -798,7 +798,7 @@ Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\ is, in seconds).\n\ "); -statichere PyTypeObject PyProfiler_Type = { +static PyTypeObject PyProfiler_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "_lsprof.Profiler", /* tp_name */ diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index b85e158..e8efaa7 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -720,7 +720,7 @@ typedef struct { PyObject *string; /* This cannot cause cycles. */ } PyTclObject; -staticforward PyTypeObject PyTclObject_Type; +static PyTypeObject PyTclObject_Type; #define PyTclObject_Check(v) ((v)->ob_type == &PyTclObject_Type) static PyObject * @@ -858,7 +858,7 @@ static PyMethodDef PyTclObject_methods[] = { {0} }; -statichere PyTypeObject PyTclObject_Type = { +static PyTypeObject PyTclObject_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_tkinter.Tcl_Obj", /*tp_name*/ diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 199ee65..b011729 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -2931,7 +2931,7 @@ static PyMethodDef tzinfo_methods[] = { static char tzinfo_doc[] = PyDoc_STR("Abstract base class for time zone info objects."); -statichere PyTypeObject PyDateTime_TZInfoType = { +static PyTypeObject PyDateTime_TZInfoType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "datetime.tzinfo", /* tp_name */ @@ -3451,7 +3451,7 @@ static PyNumberMethods time_as_number = { (inquiry)time_nonzero, /* nb_nonzero */ }; -statichere PyTypeObject PyDateTime_TimeType = { +static PyTypeObject PyDateTime_TimeType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "datetime.time", /* tp_name */ @@ -4536,7 +4536,7 @@ static PyNumberMethods datetime_as_number = { 0, /* nb_nonzero */ }; -statichere PyTypeObject PyDateTime_DateTimeType = { +static PyTypeObject PyDateTime_DateTimeType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "datetime.datetime", /* tp_name */ diff --git a/PC/_subprocess.c b/PC/_subprocess.c index b675b88..1ca4ece 100644 --- a/PC/_subprocess.c +++ b/PC/_subprocess.c @@ -53,7 +53,7 @@ typedef struct { HANDLE handle; } sp_handle_object; -staticforward PyTypeObject sp_handle_type; +static PyTypeObject sp_handle_type; static PyObject* sp_handle_new(HANDLE handle) @@ -127,7 +127,7 @@ sp_handle_as_int(sp_handle_object* self) static PyNumberMethods sp_handle_as_number; -statichere PyTypeObject sp_handle_type = { +static PyTypeObject sp_handle_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_subprocess_handle", sizeof(sp_handle_object), 0, -- cgit v0.12 From a0bc30f36fbcf010420f902612b62f65c0105d09 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 22 Mar 2006 09:34:44 +0000 Subject: Remove another use of as as a keyword --- Lib/plat-mac/aetools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/plat-mac/aetools.py b/Lib/plat-mac/aetools.py index 861dd2f..3fca7a2 100644 --- a/Lib/plat-mac/aetools.py +++ b/Lib/plat-mac/aetools.py @@ -233,7 +233,7 @@ class TalkTo: """Send 'activate' command""" self.send('misc', 'actv') - def _get(self, _object, as=None, _attributes={}): + def _get(self, _object, asfile=None, _attributes={}): """_get: get data from an object Required argument: the object Keyword argument _attributes: AppleEvent attribute dictionary @@ -243,8 +243,8 @@ class TalkTo: _subcode = 'getd' _arguments = {'----':_object} - if as: - _arguments['rtyp'] = mktype(as) + if asfile: + _arguments['rtyp'] = mktype(asfile) _reply, _arguments, _attributes = self.send(_code, _subcode, _arguments, _attributes) @@ -253,8 +253,8 @@ class TalkTo: if _arguments.has_key('----'): return _arguments['----'] - if as: - item.__class__ = as + if asfile: + item.__class__ = asfile return item get = _get -- cgit v0.12 From abdbeff36eabfe2e8a1978f014a9d6e7c6f51260 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 06:57:41 +0000 Subject: Use relative imports in compiler package now that it is required. (Should this go into 2.5 or should we do compiler.XXX?) --- Lib/compiler/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/compiler/__init__.py b/Lib/compiler/__init__.py index 13b05bf..3dc62f0 100644 --- a/Lib/compiler/__init__.py +++ b/Lib/compiler/__init__.py @@ -21,6 +21,6 @@ compileFile(filename) Generates a .pyc file by compiling filename. """ -from transformer import parse, parseFile -from visitor import walk -from pycodegen import compile, compileFile +from .transformer import parse, parseFile +from .visitor import walk +from .pycodegen import compile, compileFile -- cgit v0.12 From ef4f7f0298f3a7eca6369b71e950957ad27ef3b5 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 06:59:24 +0000 Subject: Use relative import now that it is required. (Should this go into 2.5?) --- Lib/test/test_sets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_sets.py b/Lib/test/test_sets.py index ff834e0..85e4a22 100644 --- a/Lib/test/test_sets.py +++ b/Lib/test/test_sets.py @@ -819,7 +819,8 @@ Set(['Jack', 'Jane', 'Janice', 'John', 'Marvin', 'Sam', 'Zack']) __test__ = {'libreftest' : libreftest} def test_main(verbose=None): - import test_sets, doctest + import doctest + from test import test_sets test_support.run_unittest( TestSetOfSets, TestExceptionPropagation, -- cgit v0.12 From 0fb43762d5e0991da443887a27b277f199041b75 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:02:16 +0000 Subject: Must inherit from Exception now. --- Lib/test/test_richcmp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_richcmp.py b/Lib/test/test_richcmp.py index 687298d..f412a89 100644 --- a/Lib/test/test_richcmp.py +++ b/Lib/test/test_richcmp.py @@ -211,7 +211,7 @@ class MiscTest(unittest.TestCase): # Check that exceptions in __nonzero__ are properly # propagated by the not operator import operator - class Exc: + class Exc(Exception): pass class Bad: def __nonzero__(self): @@ -305,7 +305,7 @@ class ListTest(unittest.TestCase): def test_badentry(self): # make sure that exceptions for item comparison are properly # propagated in list comparisons - class Exc: + class Exc(Exception): pass class Bad: def __eq__(self, other): -- cgit v0.12 From 2b499436b54cbd7c2f9ba71d1303f401bd929127 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:07:34 +0000 Subject: Ok, compiler.transformer can really be imported now --- Lib/compiler/__init__.py | 6 +++--- Lib/compiler/ast.py | 2 +- Lib/compiler/transformer.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/compiler/__init__.py b/Lib/compiler/__init__.py index 3dc62f0..ce89144 100644 --- a/Lib/compiler/__init__.py +++ b/Lib/compiler/__init__.py @@ -21,6 +21,6 @@ compileFile(filename) Generates a .pyc file by compiling filename. """ -from .transformer import parse, parseFile -from .visitor import walk -from .pycodegen import compile, compileFile +from compiler.transformer import parse, parseFile +from compiler.visitor import walk +from compiler.pycodegen import compile, compileFile diff --git a/Lib/compiler/ast.py b/Lib/compiler/ast.py index 08e0c6a..8dcdf68 100644 --- a/Lib/compiler/ast.py +++ b/Lib/compiler/ast.py @@ -2,7 +2,7 @@ This file is automatically generated by Tools/compiler/astgen.py """ -from consts import CO_VARARGS, CO_VARKEYWORDS +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS def flatten(seq): l = [] diff --git a/Lib/compiler/transformer.py b/Lib/compiler/transformer.py index 504e283..604c57a 100644 --- a/Lib/compiler/transformer.py +++ b/Lib/compiler/transformer.py @@ -34,8 +34,8 @@ import sys class WalkerError(StandardError): pass -from consts import CO_VARARGS, CO_VARKEYWORDS -from consts import OP_ASSIGN, OP_DELETE, OP_APPLY +from compiler.consts import CO_VARARGS, CO_VARKEYWORDS +from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY def parseFile(path): f = open(path, "U") -- cgit v0.12 From 2a0c7801a51932c172a83ad889157da50e7e892c Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:10:31 +0000 Subject: Use relative import now that it is required. (Should this go into 2.5?) --- Lib/test/test_urllib2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 5710444..2843138 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -626,7 +626,7 @@ class HandlerTests(unittest.TestCase): from urllib2 import build_opener, HTTPHandler, HTTPError, \ HTTPCookieProcessor - from test_cookielib import interact_netscape + from test.test_cookielib import interact_netscape cj = CookieJar() interact_netscape(cj, "http://www.example.com/", "spam=eggs") -- cgit v0.12 From 16d651042510e2014dfd552d1c67edf61b1cb99b Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:35:29 +0000 Subject: Use relative import now that it is required. (Should this go into 2.5?) --- Lib/test/test_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index 72f27fa..a72b8bd 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -15,7 +15,7 @@ else: raise TestFailed("import of RAnDoM should have failed (case mismatch)") # Another brief digression to test the accuracy of manifest float constants. -import double_const # don't blink -- that *was* the test +from test import double_const # don't blink -- that *was* the test def remove_files(name): for f in (name + os.extsep + "py", -- cgit v0.12 From c3e54b848000e8cd2fc28ea29f3f8a0163aa075f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:38:37 +0000 Subject: Use *absolute* import now that it is required. (Should this go into 2.5? Hopefully not the bogus comment about using relative imports. That was just to see if anyone was paying attention.) --- Lib/test/test_dict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index f3f78e7..bbca798 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -445,7 +445,7 @@ class DictTest(unittest.TestCase): self.fail_("g[42] didn't raise KeyError") -import mapping_tests +from test import mapping_tests class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = dict -- cgit v0.12 From 2def11a90d1ea89af46c32ccb7e5dbe0bcaff73a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 07:47:46 +0000 Subject: Use *absolute* imports now that they are required. (Should this go into 2.5?) --- Lib/xmlcore/sax/__init__.py | 10 +++++----- Lib/xmlcore/sax/saxutils.py | 4 ++-- Lib/xmlcore/sax/xmlreader.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Lib/xmlcore/sax/__init__.py b/Lib/xmlcore/sax/__init__.py index f1e467c..8afbdb0 100644 --- a/Lib/xmlcore/sax/__init__.py +++ b/Lib/xmlcore/sax/__init__.py @@ -19,11 +19,11 @@ xmlreader -- Base classes and constants which define the SAX 2 API for expatreader -- Driver that allows use of the Expat parser with SAX. """ -from xmlreader import InputSource -from handler import ContentHandler, ErrorHandler -from _exceptions import SAXException, SAXNotRecognizedException, \ - SAXParseException, SAXNotSupportedException, \ - SAXReaderNotAvailable +from .xmlreader import InputSource +from .handler import ContentHandler, ErrorHandler +from ._exceptions import (SAXException, SAXNotRecognizedException, + SAXParseException, SAXNotSupportedException, + SAXReaderNotAvailable) def parse(source, handler, errorHandler=ErrorHandler()): diff --git a/Lib/xmlcore/sax/saxutils.py b/Lib/xmlcore/sax/saxutils.py index 582b008..880de80 100644 --- a/Lib/xmlcore/sax/saxutils.py +++ b/Lib/xmlcore/sax/saxutils.py @@ -4,8 +4,8 @@ convenience of application and driver writers. """ import os, urlparse, urllib, types -import handler -import xmlreader +from . import handler +from . import xmlreader try: _StringTypes = [types.StringType, types.UnicodeType] diff --git a/Lib/xmlcore/sax/xmlreader.py b/Lib/xmlcore/sax/xmlreader.py index 9a2361e..6b37d37 100644 --- a/Lib/xmlcore/sax/xmlreader.py +++ b/Lib/xmlcore/sax/xmlreader.py @@ -1,9 +1,9 @@ """An XML Reader is the SAX 2 name for an XML parser. XML Parsers should be based on this code. """ -import handler +from . import handler -from _exceptions import SAXNotSupportedException, SAXNotRecognizedException +from ._exceptions import SAXNotSupportedException, SAXNotRecognizedException # ===== XMLREADER ===== -- cgit v0.12 From 1e32b6927f33050c99dcee2bd07a92bfab1e6e2f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 08:02:35 +0000 Subject: Must inherit from Exception now. --- Lib/test/test_normalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_normalization.py b/Lib/test/test_normalization.py index 8a361fd..81bdfbd 100644 --- a/Lib/test/test_normalization.py +++ b/Lib/test/test_normalization.py @@ -7,7 +7,7 @@ from unicodedata import normalize TESTDATAFILE = "NormalizationTest" + os.extsep + "txt" TESTDATAURL = "http://www.unicode.org/Public/4.1.0/ucd/" + TESTDATAFILE -class RangeError: +class RangeError(Exception): pass def NFC(str): -- cgit v0.12 From 28c31982eb24f93017d4859e71ce97cb7c463678 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 08:04:47 +0000 Subject: Use *absolute* imports now that they are required. (Should this go into 2.5?) --- Lib/xmlcore/dom/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/xmlcore/dom/__init__.py b/Lib/xmlcore/dom/__init__.py index 6363d00..002cdb7 100644 --- a/Lib/xmlcore/dom/__init__.py +++ b/Lib/xmlcore/dom/__init__.py @@ -136,4 +136,4 @@ XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml" EMPTY_NAMESPACE = None EMPTY_PREFIX = None -from domreg import getDOMImplementation,registerDOMImplementation +from .domreg import getDOMImplementation,registerDOMImplementation -- cgit v0.12 From ed483ba63b9c03845386976bccff5d95df5b570a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 08:08:49 +0000 Subject: String exceptions are gone and so are classic classes. --- Lib/test/test_pep352.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py index 251e0be..73cffd2 100644 --- a/Lib/test/test_pep352.py +++ b/Lib/test/test_pep352.py @@ -120,22 +120,6 @@ class UsageTests(unittest.TestCase): def tearDown(self): warnings.filters = self._filters[:] - def test_raise_classic(self): - class ClassicClass: - pass - try: - raise ClassicClass - except ClassicClass: - pass - except: - self.fail("unable to raise classic class") - try: - raise ClassicClass() - except ClassicClass: - pass - except: - self.fail("unable to raise class class instance") - def test_raise_new_style_non_exception(self): class NewStyleClass(object): pass @@ -152,32 +136,8 @@ class UsageTests(unittest.TestCase): except: self.fail("unable to raise new-style class instance") - def test_raise_string(self): - warnings.resetwarnings() - warnings.filterwarnings("error") - try: - raise "spam" - except DeprecationWarning: - pass - except: - self.fail("raising a string did not cause a DeprecationWarning") - - def test_catch_string(self): - # Test will be pertinent when catching exceptions raises a - # DeprecationWarning - warnings.filterwarnings("ignore", "raising") - str_exc = "spam" - try: - raise str_exc - except str_exc: - pass - except: - self.fail("catching a string exception failed") - def test_main(): run_unittest(ExceptionClassTests, UsageTests) - - if __name__ == '__main__': test_main() -- cgit v0.12 From bcc0db82dc9cb474d56a4cc63748583232d9524f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 08:14:36 +0000 Subject: Get rid of remnants of integer division --- Include/object.h | 3 --- Lib/decimal.py | 20 +++++++++----------- Lib/test/test_augassign.py | 30 +++++++++--------------------- Lib/test/test_binop.py | 4 ---- Lib/test/test_class.py | 16 ++++------------ Lib/test/test_coercion.py | 4 ++-- Lib/test/test_complex.py | 8 ++------ Lib/test/test_decimal.py | 4 +--- Lib/test/test_descr.py | 9 ++------- Lib/test/test_operator.py | 8 -------- Misc/NEWS | 8 ++++++++ Modules/_ctypes/_ctypes.c | 2 -- Modules/datetimemodule.c | 5 ----- Modules/mathmodule.c | 2 +- Modules/operator.c | 8 ++------ Objects/abstract.c | 2 -- Objects/boolobject.c | 2 -- Objects/classobject.c | 4 ---- Objects/complexobject.c | 23 ----------------------- Objects/floatobject.c | 21 --------------------- Objects/intobject.c | 27 ++------------------------- Objects/longobject.c | 18 ------------------ Objects/setobject.c | 3 --- Objects/stringobject.c | 1 - Objects/typeobject.c | 21 +++++---------------- Objects/unicodeobject.c | 1 - Objects/weakrefobject.c | 4 ---- PC/_winreg.c | 1 - 28 files changed, 47 insertions(+), 212 deletions(-) diff --git a/Include/object.h b/Include/object.h index 5739651..9198007 100644 --- a/Include/object.h +++ b/Include/object.h @@ -158,7 +158,6 @@ typedef struct { binaryfunc nb_add; binaryfunc nb_subtract; binaryfunc nb_multiply; - binaryfunc nb_divide; binaryfunc nb_remainder; binaryfunc nb_divmod; ternaryfunc nb_power; @@ -182,7 +181,6 @@ typedef struct { binaryfunc nb_inplace_add; binaryfunc nb_inplace_subtract; binaryfunc nb_inplace_multiply; - binaryfunc nb_inplace_divide; binaryfunc nb_inplace_remainder; ternaryfunc nb_inplace_power; binaryfunc nb_inplace_lshift; @@ -192,7 +190,6 @@ typedef struct { binaryfunc nb_inplace_or; /* Added in release 2.2 */ - /* The following require the Py_TPFLAGS_HAVE_CLASS flag */ binaryfunc nb_floor_divide; binaryfunc nb_true_divide; binaryfunc nb_inplace_floor_divide; diff --git a/Lib/decimal.py b/Lib/decimal.py index 967f101..9815ab3 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -1135,10 +1135,9 @@ class Decimal(object): return ans __rmul__ = __mul__ - def __div__(self, other, context=None): + def __truediv__(self, other, context=None): """Return self / other.""" return self._divide(other, context=context) - __truediv__ = __div__ def _divide(self, other, divmod = 0, context=None): """Return a / b, to context.prec precision. @@ -1306,13 +1305,12 @@ class Decimal(object): ans = ans._fix(context) return ans - def __rdiv__(self, other, context=None): - """Swaps self/other and returns __div__.""" + def __rtruediv__(self, other, context=None): + """Swaps self/other and returns __truediv__.""" other = _convert_other(other) if other is NotImplemented: return other - return other.__div__(self, context=context) - __rtruediv__ = __rdiv__ + return other.__truediv__(self, context=context) def __divmod__(self, other, context=None): """ @@ -1384,9 +1382,9 @@ class Decimal(object): rounding = context._set_rounding_decision(NEVER_ROUND) if other._sign: - comparison = other.__div__(Decimal(-2), context=context) + comparison = other.__truediv__(Decimal(-2), context=context) else: - comparison = other.__div__(Decimal(2), context=context) + comparison = other.__truediv__(Decimal(2), context=context) context._set_rounding_decision(rounding) context._regard_flags(*flags) @@ -1751,7 +1749,7 @@ class Decimal(object): if n < 0: #n is a long now, not Decimal instance n = -n - mul = Decimal(1).__div__(mul, context=context) + mul = Decimal(1).__truediv__(mul, context=context) spot = 1 while spot <= n: @@ -1972,7 +1970,7 @@ class Decimal(object): rounding = context._set_rounding(ROUND_HALF_EVEN) while 1: context.prec = min(2*context.prec - 2, maxp) - ans = half.__mul__(ans.__add__(tmp.__div__(ans, context=context), + ans = half.__mul__(ans.__add__(tmp.__truediv__(ans, context=context), context=context), context=context) if context.prec == maxp: break @@ -2454,7 +2452,7 @@ class Context(object): >>> ExtendedContext.divide(Decimal('2.40E+6'), Decimal('2')) Decimal("1.20E+6") """ - return a.__div__(b, context=self) + return a.__truediv__(b, context=self) def divide_int(self, a, b): """Divides two numbers and returns the integer part of the result. diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 8a8f00d..228e03a 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -5,7 +5,7 @@ x += 1 x *= 2 x **= 2 x -= 8 -x //= 2 +x /= 2 x //= 1 x %= 12 x &= 2 @@ -19,7 +19,7 @@ x[0] += 1 x[0] *= 2 x[0] **= 2 x[0] -= 8 -x[0] //= 2 +x[0] /= 2 x[0] //= 2 x[0] %= 12 x[0] &= 2 @@ -33,7 +33,7 @@ x[0] += 1 x[0] *= 2 x[0] **= 2 x[0] -= 8 -x[0] //= 2 +x[0] /= 2 x[0] //= 1 x[0] %= 12 x[0] &= 2 @@ -123,14 +123,6 @@ class testall: print "__imul__ called" return self - def __div__(self, val): - print "__div__ called" - def __rdiv__(self, val): - print "__rdiv__ called" - def __idiv__(self, val): - print "__idiv__ called" - return self - def __floordiv__(self, val): print "__floordiv__ called" return self @@ -147,6 +139,9 @@ class testall: def __itruediv__(self, val): print "__itruediv__ called" return self + def __rtruediv__(self, val): + print "__rtruediv__ called" + return self def __mod__(self, val): print "__mod__ called" @@ -217,16 +212,9 @@ x * 1 1 * x x *= 1 -if 1/2 == 0: - x / 1 - 1 / x - x /= 1 -else: - # True division is in effect, so "/" doesn't map to __div__ etc; - # but the canned expected-output file requires that those get called. - x.__div__(1) - x.__rdiv__(1) - x.__idiv__(1) +x / 1 +1 / x +x /= 1 x // 1 1 // x diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py index b3d9a62..719186b 100644 --- a/Lib/test/test_binop.py +++ b/Lib/test/test_binop.py @@ -140,8 +140,6 @@ class Rat(object): return float(self) / other return NotImplemented - __div__ = __truediv__ - def __rtruediv__(self, other): """Divide two Rats, or a Rat and a number (reversed args).""" if isRat(other): @@ -152,8 +150,6 @@ class Rat(object): return other / float(self) return NotImplemented - __rdiv__ = __rtruediv__ - def __floordiv__(self, other): """Divide two Rats, returning the floored result.""" if isint(other): diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 92c220e..b8b4cab 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -11,8 +11,8 @@ testmeths = [ "rsub", "mul", "rmul", - "div", - "rdiv", + "truediv", + "rtruediv", "mod", "rmod", "divmod", @@ -134,16 +134,8 @@ testme - 1 testme * 1 1 * testme -if 1/2 == 0: - testme / 1 - 1 / testme -else: - # True division is in effect, so "/" doesn't map to __div__ etc; but - # the canned expected-output file requires that __div__ etc get called. - testme.__coerce__(1) - testme.__div__(1) - testme.__coerce__(1) - testme.__rdiv__(1) +testme / 1 +1 / testme testme % 1 1 % testme diff --git a/Lib/test/test_coercion.py b/Lib/test/test_coercion.py index ceea17b..e12ef0d 100644 --- a/Lib/test/test_coercion.py +++ b/Lib/test/test_coercion.py @@ -44,10 +44,10 @@ class MethodNumber: def __rmul__(self,other): return other * self.arg - def __div__(self,other): + def __truediv__(self,other): return self.arg / other - def __rdiv__(self,other): + def __rtruediv__(self,other): return other / self.arg def __pow__(self,other): diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 0d42bd2b..035f524 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -55,19 +55,15 @@ class ComplexTest(unittest.TestCase): if x != 0: q = z / x self.assertClose(q, y) - q = z.__div__(x) - self.assertClose(q, y) q = z.__truediv__(x) self.assertClose(q, y) if y != 0: q = z / y self.assertClose(q, x) - q = z.__div__(y) - self.assertClose(q, x) q = z.__truediv__(y) self.assertClose(q, x) - def test_div(self): + def test_truediv(self): simple_real = [float(i) for i in xrange(-5, 6)] simple_complex = [complex(x, y) for x in simple_real for y in simple_real] for x in simple_complex: @@ -84,7 +80,7 @@ class ComplexTest(unittest.TestCase): self.check_div(complex(random(), random()), complex(random(), random())) - self.assertRaises(ZeroDivisionError, complex.__div__, 1+1j, 0+0j) + self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j) # FIXME: The following currently crashes on Alpha # self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 7d0addf..1d33ec4 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -507,7 +507,7 @@ class DecimalImplicitConstructionTest(unittest.TestCase): ('+', '__add__', '__radd__'), ('-', '__sub__', '__rsub__'), ('*', '__mul__', '__rmul__'), - ('/', '__div__', '__rdiv__'), + ('/', '__truediv__', '__rtruediv__'), ('%', '__mod__', '__rmod__'), ('//', '__floordiv__', '__rfloordiv__'), ('**', '__pow__', '__rpow__'), @@ -975,7 +975,6 @@ class DecimalUsabilityTest(unittest.TestCase): checkSameDec("__abs__") checkSameDec("__add__", True) - checkSameDec("__div__", True) checkSameDec("__divmod__", True) checkSameDec("__cmp__", True) checkSameDec("__float__") @@ -990,7 +989,6 @@ class DecimalUsabilityTest(unittest.TestCase): checkSameDec("__pos__") checkSameDec("__pow__", True) checkSameDec("__radd__", True) - checkSameDec("__rdiv__", True) checkSameDec("__rdivmod__", True) checkSameDec("__repr__") checkSameDec("__rfloordiv__", True) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 57a8f44..108d95e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -29,10 +29,6 @@ def testbinop(a, b, res, expr="a+b", meth="__add__"): if verbose: print "checking", expr dict = {'a': a, 'b': b} - # XXX Hack so this passes before 2.3 when -Qnew is specified. - if meth == "__div__" and 1/2 == 0.5: - meth = "__truediv__" - vereq(eval(expr, dict), res) t = type(a) m = getattr(t, meth) @@ -4044,9 +4040,8 @@ def notimplemented(): ('__add__', 'x + y', 'x += y'), ('__sub__', 'x - y', 'x -= y'), ('__mul__', 'x * y', 'x *= y'), - ('__truediv__', 'operator.truediv(x, y)', None), - ('__floordiv__', 'operator.floordiv(x, y)', None), - ('__div__', 'x / y', 'x /= y'), + ('__truediv__', 'x / y', None), + ('__floordiv__', 'x // y', None), ('__mod__', 'x % y', 'x %= y'), ('__divmod__', 'divmod(x, y)', None), ('__pow__', 'x ** y', 'x **= y'), diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index c1fe88c..50c3b0c 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -144,11 +144,6 @@ class OperatorTestCase(unittest.TestCase): self.failUnless(operator.delslice(a, 2, 8) is None) self.assert_(a == [0, 1, 8, 9]) - def test_div(self): - self.failUnlessRaises(TypeError, operator.div, 5) - self.failUnlessRaises(TypeError, operator.div, None, None) - self.failUnless(operator.floordiv(5, 2) == 2) - def test_floordiv(self): self.failUnlessRaises(TypeError, operator.floordiv, 5) self.failUnlessRaises(TypeError, operator.floordiv, None, None) @@ -416,7 +411,6 @@ class OperatorTestCase(unittest.TestCase): class C(object): def __iadd__ (self, other): return "iadd" def __iand__ (self, other): return "iand" - def __idiv__ (self, other): return "idiv" def __ifloordiv__(self, other): return "ifloordiv" def __ilshift__ (self, other): return "ilshift" def __imod__ (self, other): return "imod" @@ -431,7 +425,6 @@ class OperatorTestCase(unittest.TestCase): c = C() self.assertEqual(operator.iadd (c, 5), "iadd") self.assertEqual(operator.iand (c, 5), "iand") - self.assertEqual(operator.idiv (c, 5), "idiv") self.assertEqual(operator.ifloordiv(c, 5), "ifloordiv") self.assertEqual(operator.ilshift (c, 5), "ilshift") self.assertEqual(operator.imod (c, 5), "imod") @@ -446,7 +439,6 @@ class OperatorTestCase(unittest.TestCase): self.assertEqual(operator.irepeat (c, 5), "imul") self.assertEqual(operator.__iadd__ (c, 5), "iadd") self.assertEqual(operator.__iand__ (c, 5), "iand") - self.assertEqual(operator.__idiv__ (c, 5), "idiv") self.assertEqual(operator.__ifloordiv__(c, 5), "ifloordiv") self.assertEqual(operator.__ilshift__ (c, 5), "ilshift") self.assertEqual(operator.__imod__ (c, 5), "imod") diff --git a/Misc/NEWS b/Misc/NEWS index 3dce99d..e5d19ec 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -40,6 +40,14 @@ Core and Builtins - Exceptions *must* derive from BaseException. - Integer division always returns a float. The -Q option is no more. + All the following are gone: + * PyNumber_Divide and PyNumber_InPlaceDivide + * __div__, __rdiv__, and __idiv__ + * nb_divide, nb_inplace_divide + * operator.div, operator.idiv, operator.__div__, operator.__idiv__ + (Only __truediv__ and __floordiv__ remain, not sure how to handle them + if we want to re-use __div__ and friends. If we do, it will make + it harder to write code for both 2.x and 3.x.) - 'as' and 'with' are keywords. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3a7d3ef..7c5da64 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3812,7 +3812,6 @@ static PyNumberMethods Simple_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -4165,7 +4164,6 @@ static PyNumberMethods Pointer_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index b011729..c1a0cb3 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -2080,7 +2080,6 @@ static PyNumberMethods delta_as_number = { delta_add, /* nb_add */ delta_subtract, /* nb_subtract */ delta_multiply, /* nb_multiply */ - delta_divide, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -2103,7 +2102,6 @@ static PyNumberMethods delta_as_number = { 0, /*nb_inplace_add*/ 0, /*nb_inplace_subtract*/ 0, /*nb_inplace_multiply*/ - 0, /*nb_inplace_divide*/ 0, /*nb_inplace_remainder*/ 0, /*nb_inplace_power*/ 0, /*nb_inplace_lshift*/ @@ -2665,7 +2663,6 @@ static PyNumberMethods date_as_number = { date_add, /* nb_add */ date_subtract, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -3441,7 +3438,6 @@ static PyNumberMethods time_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -4526,7 +4522,6 @@ static PyNumberMethods datetime_as_number = { datetime_add, /* nb_add */ datetime_subtract, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index e7fc6dd..731b1d9 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -266,7 +266,7 @@ math_log(PyObject *self, PyObject *args) return NULL; } - ans = PyNumber_Divide(num, den); + ans = PyNumber_TrueDivide(num, den); Py_DECREF(num); Py_DECREF(den); return ans; diff --git a/Modules/operator.c b/Modules/operator.c index 53144f1..24f4e0a 100644 --- a/Modules/operator.c +++ b/Modules/operator.c @@ -65,7 +65,6 @@ spami(truth , PyObject_IsTrue) spam2(op_add , PyNumber_Add) spam2(op_sub , PyNumber_Subtract) spam2(op_mul , PyNumber_Multiply) -spam2(op_div , PyNumber_Divide) spam2(op_floordiv , PyNumber_FloorDivide) spam2(op_truediv , PyNumber_TrueDivide) spam2(op_mod , PyNumber_Remainder) @@ -83,7 +82,6 @@ spam2(op_or_ , PyNumber_Or) spam2(op_iadd , PyNumber_InPlaceAdd) spam2(op_isub , PyNumber_InPlaceSubtract) spam2(op_imul , PyNumber_InPlaceMultiply) -spam2(op_idiv , PyNumber_InPlaceDivide) spam2(op_ifloordiv , PyNumber_InPlaceFloorDivide) spam2(op_itruediv , PyNumber_InPlaceTrueDivide) spam2(op_imod , PyNumber_InPlaceRemainder) @@ -247,9 +245,8 @@ spam2(index, __index__, "index(a) -- Same as a.__index__()") spam2(add,__add__, "add(a, b) -- Same as a + b.") spam2(sub,__sub__, "sub(a, b) -- Same as a - b.") spam2(mul,__mul__, "mul(a, b) -- Same as a * b.") -spam2(div,__div__, "div(a, b) -- Same as a / b when __future__.division is not in effect.") spam2(floordiv,__floordiv__, "floordiv(a, b) -- Same as a // b.") -spam2(truediv,__truediv__, "truediv(a, b) -- Same as a / b when __future__.division is in effect.") +spam2(truediv,__truediv__, "truediv(a, b) -- Same as a / b.") spam2(mod,__mod__, "mod(a, b) -- Same as a % b.") spam2o(neg,__neg__, "neg(a) -- Same as -a.") spam2o(pos,__pos__, "pos(a) -- Same as +a.") @@ -265,9 +262,8 @@ spam2(or_,__or__, "or_(a, b) -- Same as a | b.") spam2(iadd,__iadd__, "iadd(a, b) -- Same as a += b.") spam2(isub,__isub__, "isub(a, b) -- Same as a -= b.") spam2(imul,__imul__, "imul(a, b) -- Same as a *= b.") -spam2(idiv,__idiv__, "idiv(a, b) -- Same as a /= b when __future__.division is not in effect.") spam2(ifloordiv,__ifloordiv__, "ifloordiv(a, b) -- Same as a //= b.") -spam2(itruediv,__itruediv__, "itruediv(a, b) -- Same as a /= b when __future__.division is in effect.") +spam2(itruediv,__itruediv__, "itruediv(a, b) -- Same as a /= b.") spam2(imod,__imod__, "imod(a, b) -- Same as a %= b.") spam2(ilshift,__ilshift__, "ilshift(a, b) -- Same as a <<= b.") spam2(irshift,__irshift__, "irshift(a, b) -- Same as a >>= b.") diff --git a/Objects/abstract.c b/Objects/abstract.c index 052e3ca..c755654 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -625,7 +625,6 @@ BINARY_FUNC(PyNumber_And, nb_and, "&") BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<") BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>") BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-") -BINARY_FUNC(PyNumber_Divide, nb_divide, "/") BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()") PyObject * @@ -765,7 +764,6 @@ INPLACE_BINOP(PyNumber_InPlaceAnd, nb_inplace_and, nb_and, "&=") INPLACE_BINOP(PyNumber_InPlaceLshift, nb_inplace_lshift, nb_lshift, "<<=") INPLACE_BINOP(PyNumber_InPlaceRshift, nb_inplace_rshift, nb_rshift, ">>=") INPLACE_BINOP(PyNumber_InPlaceSubtract, nb_inplace_subtract, nb_subtract, "-=") -INPLACE_BINOP(PyNumber_InPlaceDivide, nb_inplace_divide, nb_divide, "/=") PyObject * PyNumber_InPlaceFloorDivide(PyObject *v, PyObject *w) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index f2429fe..05784e5 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -106,7 +106,6 @@ static PyNumberMethods bool_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ - 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -129,7 +128,6 @@ static PyNumberMethods bool_as_number = { 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ diff --git a/Objects/classobject.c b/Objects/classobject.c index 037252d..93acb50 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -1551,7 +1551,6 @@ BINARY(instance_rshift, "rshift", PyNumber_Rshift) BINARY(instance_add, "add", PyNumber_Add) BINARY(instance_sub, "sub", PyNumber_Subtract) BINARY(instance_mul, "mul", PyNumber_Multiply) -BINARY(instance_div, "div", PyNumber_Divide) BINARY(instance_mod, "mod", PyNumber_Remainder) BINARY(instance_divmod, "divmod", PyNumber_Divmod) BINARY(instance_floordiv, "floordiv", PyNumber_FloorDivide) @@ -1565,7 +1564,6 @@ BINARY_INPLACE(instance_irshift, "rshift", PyNumber_InPlaceRshift) BINARY_INPLACE(instance_iadd, "add", PyNumber_InPlaceAdd) BINARY_INPLACE(instance_isub, "sub", PyNumber_InPlaceSubtract) BINARY_INPLACE(instance_imul, "mul", PyNumber_InPlaceMultiply) -BINARY_INPLACE(instance_idiv, "div", PyNumber_InPlaceDivide) BINARY_INPLACE(instance_imod, "mod", PyNumber_InPlaceRemainder) BINARY_INPLACE(instance_ifloordiv, "floordiv", PyNumber_InPlaceFloorDivide) BINARY_INPLACE(instance_itruediv, "truediv", PyNumber_InPlaceTrueDivide) @@ -2054,7 +2052,6 @@ static PyNumberMethods instance_as_number = { (binaryfunc)instance_add, /* nb_add */ (binaryfunc)instance_sub, /* nb_subtract */ (binaryfunc)instance_mul, /* nb_multiply */ - (binaryfunc)instance_div, /* nb_divide */ (binaryfunc)instance_mod, /* nb_remainder */ (binaryfunc)instance_divmod, /* nb_divmod */ (ternaryfunc)instance_pow, /* nb_power */ @@ -2077,7 +2074,6 @@ static PyNumberMethods instance_as_number = { (binaryfunc)instance_iadd, /* nb_inplace_add */ (binaryfunc)instance_isub, /* nb_inplace_subtract */ (binaryfunc)instance_imul, /* nb_inplace_multiply */ - (binaryfunc)instance_idiv, /* nb_inplace_divide */ (binaryfunc)instance_imod, /* nb_inplace_remainder */ (ternaryfunc)instance_ipow, /* nb_inplace_power */ (binaryfunc)instance_ilshift, /* nb_inplace_lshift */ diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 5c84eff..f0915dd 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -382,27 +382,6 @@ complex_div(PyComplexObject *v, PyComplexObject *w) } static PyObject * -complex_classic_div(PyComplexObject *v, PyComplexObject *w) -{ - Py_complex quot; - - if (Py_DivisionWarningFlag >= 2 && - PyErr_Warn(PyExc_DeprecationWarning, - "classic complex division") < 0) - return NULL; - - PyFPE_START_PROTECT("complex_classic_div", return 0) - errno = 0; - quot = c_quot(v->cval,w->cval); - PyFPE_END_PROTECT(quot) - if (errno == EDOM) { - PyErr_SetString(PyExc_ZeroDivisionError, "complex division"); - return NULL; - } - return PyComplex_FromCComplex(quot); -} - -static PyObject * complex_remainder(PyComplexObject *v, PyComplexObject *w) { Py_complex div, mod; @@ -948,7 +927,6 @@ static PyNumberMethods complex_as_number = { (binaryfunc)complex_add, /* nb_add */ (binaryfunc)complex_sub, /* nb_subtract */ (binaryfunc)complex_mul, /* nb_multiply */ - (binaryfunc)complex_classic_div, /* nb_divide */ (binaryfunc)complex_remainder, /* nb_remainder */ (binaryfunc)complex_divmod, /* nb_divmod */ (ternaryfunc)complex_pow, /* nb_power */ @@ -971,7 +949,6 @@ static PyNumberMethods complex_as_number = { 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply*/ - 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ diff --git a/Objects/floatobject.c b/Objects/floatobject.c index c27a41a..20ed86e 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -642,25 +642,6 @@ float_div(PyObject *v, PyObject *w) } static PyObject * -float_classic_div(PyObject *v, PyObject *w) -{ - double a,b; - CONVERT_TO_DOUBLE(v, a); - CONVERT_TO_DOUBLE(w, b); - if (Py_DivisionWarningFlag >= 2 && - PyErr_Warn(PyExc_DeprecationWarning, "classic float division") < 0) - return NULL; - if (b == 0.0) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - return NULL; - } - PyFPE_START_PROTECT("divide", return 0) - a = a / b; - PyFPE_END_PROTECT(a) - return PyFloat_FromDouble(a); -} - -static PyObject * float_rem(PyObject *v, PyObject *w) { double vx, wx; @@ -1128,7 +1109,6 @@ static PyNumberMethods float_as_number = { (binaryfunc)float_add, /*nb_add*/ (binaryfunc)float_sub, /*nb_subtract*/ (binaryfunc)float_mul, /*nb_multiply*/ - (binaryfunc)float_classic_div, /*nb_divide*/ (binaryfunc)float_rem, /*nb_remainder*/ (binaryfunc)float_divmod, /*nb_divmod*/ (ternaryfunc)float_pow, /*nb_power*/ @@ -1151,7 +1131,6 @@ static PyNumberMethods float_as_number = { 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ diff --git a/Objects/intobject.c b/Objects/intobject.c index 86e2e8c..c734840 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -580,29 +580,8 @@ int_div(PyIntObject *x, PyIntObject *y) case DIVMOD_OK: return PyInt_FromLong(d); case DIVMOD_OVERFLOW: - return PyLong_Type.tp_as_number->nb_divide((PyObject *)x, - (PyObject *)y); - default: - return NULL; - } -} - -static PyObject * -int_classic_div(PyIntObject *x, PyIntObject *y) -{ - long xi, yi; - long d, m; - CONVERT_TO_LONG(x, xi); - CONVERT_TO_LONG(y, yi); - if (Py_DivisionWarningFlag && - PyErr_Warn(PyExc_DeprecationWarning, "classic int division") < 0) - return NULL; - switch (i_divmod(xi, yi, &d, &m)) { - case DIVMOD_OK: - return PyInt_FromLong(d); - case DIVMOD_OVERFLOW: - return PyLong_Type.tp_as_number->nb_divide((PyObject *)x, - (PyObject *)y); + return PyLong_Type.tp_as_number->nb_floor_divide((PyObject *)x, + (PyObject *)y); default: return NULL; } @@ -1034,7 +1013,6 @@ static PyNumberMethods int_as_number = { (binaryfunc)int_add, /*nb_add*/ (binaryfunc)int_sub, /*nb_subtract*/ (binaryfunc)int_mul, /*nb_multiply*/ - (binaryfunc)int_classic_div, /*nb_divide*/ (binaryfunc)int_mod, /*nb_remainder*/ (binaryfunc)int_divmod, /*nb_divmod*/ (ternaryfunc)int_pow, /*nb_power*/ @@ -1057,7 +1035,6 @@ static PyNumberMethods int_as_number = { 0, /*nb_inplace_add*/ 0, /*nb_inplace_subtract*/ 0, /*nb_inplace_multiply*/ - 0, /*nb_inplace_divide*/ 0, /*nb_inplace_remainder*/ 0, /*nb_inplace_power*/ 0, /*nb_inplace_lshift*/ diff --git a/Objects/longobject.c b/Objects/longobject.c index e47c292..7c5ebc4 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -2355,22 +2355,6 @@ long_div(PyObject *v, PyObject *w) } static PyObject * -long_classic_div(PyObject *v, PyObject *w) -{ - PyLongObject *a, *b, *div; - - CONVERT_BINOP(v, w, &a, &b); - if (Py_DivisionWarningFlag && - PyErr_Warn(PyExc_DeprecationWarning, "classic long division") < 0) - div = NULL; - else if (l_divmod(a, b, &div, NULL) < 0) - div = NULL; - Py_DECREF(a); - Py_DECREF(b); - return (PyObject *)div; -} - -static PyObject * long_true_divide(PyObject *v, PyObject *w) { PyLongObject *a, *b; @@ -3130,7 +3114,6 @@ static PyNumberMethods long_as_number = { (binaryfunc) long_add, /*nb_add*/ (binaryfunc) long_sub, /*nb_subtract*/ (binaryfunc) long_mul, /*nb_multiply*/ - (binaryfunc) long_classic_div, /*nb_divide*/ (binaryfunc) long_mod, /*nb_remainder*/ (binaryfunc) long_divmod, /*nb_divmod*/ (ternaryfunc) long_pow, /*nb_power*/ @@ -3153,7 +3136,6 @@ static PyNumberMethods long_as_number = { 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ diff --git a/Objects/setobject.c b/Objects/setobject.c index ed3d190..89d574f 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1755,7 +1755,6 @@ static PyNumberMethods set_as_number = { 0, /*nb_add*/ (binaryfunc)set_sub, /*nb_subtract*/ 0, /*nb_multiply*/ - 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ @@ -1778,7 +1777,6 @@ static PyNumberMethods set_as_number = { 0, /*nb_inplace_add*/ (binaryfunc)set_isub, /*nb_inplace_subtract*/ 0, /*nb_inplace_multiply*/ - 0, /*nb_inplace_divide*/ 0, /*nb_inplace_remainder*/ 0, /*nb_inplace_power*/ 0, /*nb_inplace_lshift*/ @@ -1867,7 +1865,6 @@ static PyNumberMethods frozenset_as_number = { 0, /*nb_add*/ (binaryfunc)set_sub, /*nb_subtract*/ 0, /*nb_multiply*/ - 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 16d542a..32aacf5 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -3408,7 +3408,6 @@ static PyNumberMethods string_as_number = { 0, /*nb_add*/ 0, /*nb_subtract*/ 0, /*nb_multiply*/ - 0, /*nb_divide*/ string_mod, /*nb_remainder*/ }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 65bf404..c02f060 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3014,7 +3014,6 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) COPYNUM(nb_add); COPYNUM(nb_subtract); COPYNUM(nb_multiply); - COPYNUM(nb_divide); COPYNUM(nb_remainder); COPYNUM(nb_divmod); COPYNUM(nb_power); @@ -3037,7 +3036,6 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) COPYNUM(nb_inplace_add); COPYNUM(nb_inplace_subtract); COPYNUM(nb_inplace_multiply); - COPYNUM(nb_inplace_divide); COPYNUM(nb_inplace_remainder); COPYNUM(nb_inplace_power); COPYNUM(nb_inplace_lshift); @@ -3045,12 +3043,11 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) COPYNUM(nb_inplace_and); COPYNUM(nb_inplace_xor); COPYNUM(nb_inplace_or); - if (base->tp_flags & Py_TPFLAGS_CHECKTYPES) { - COPYNUM(nb_true_divide); - COPYNUM(nb_floor_divide); - COPYNUM(nb_inplace_true_divide); - COPYNUM(nb_inplace_floor_divide); - } + COPYNUM(nb_true_divide); + COPYNUM(nb_floor_divide); + COPYNUM(nb_inplace_true_divide); + COPYNUM(nb_inplace_floor_divide); + /* XXX(nnorwitz): we don't need to check flags do we? */ if (base->tp_flags & Py_TPFLAGS_HAVE_INDEX) { COPYNUM(nb_index); } @@ -4291,7 +4288,6 @@ slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value) SLOT1BIN(slot_nb_add, nb_add, "__add__", "__radd__") SLOT1BIN(slot_nb_subtract, nb_subtract, "__sub__", "__rsub__") SLOT1BIN(slot_nb_multiply, nb_multiply, "__mul__", "__rmul__") -SLOT1BIN(slot_nb_divide, nb_divide, "__div__", "__rdiv__") SLOT1BIN(slot_nb_remainder, nb_remainder, "__mod__", "__rmod__") SLOT1BIN(slot_nb_divmod, nb_divmod, "__divmod__", "__rdivmod__") @@ -4470,7 +4466,6 @@ SLOT0(slot_nb_hex, "__hex__") SLOT1(slot_nb_inplace_add, "__iadd__", PyObject *, "O") SLOT1(slot_nb_inplace_subtract, "__isub__", PyObject *, "O") SLOT1(slot_nb_inplace_multiply, "__imul__", PyObject *, "O") -SLOT1(slot_nb_inplace_divide, "__idiv__", PyObject *, "O") SLOT1(slot_nb_inplace_remainder, "__imod__", PyObject *, "O") SLOT1(slot_nb_inplace_power, "__ipow__", PyObject *, "O") SLOT1(slot_nb_inplace_lshift, "__ilshift__", PyObject *, "O") @@ -5077,10 +5072,6 @@ static slotdef slotdefs[] = { "*"), RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, "*"), - BINSLOT("__div__", nb_divide, slot_nb_divide, - "/"), - RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, - "/"), BINSLOT("__mod__", nb_remainder, slot_nb_remainder, "%"), RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, @@ -5130,8 +5121,6 @@ static slotdef slotdefs[] = { wrap_binaryfunc, "-"), IBSLOT("__imul__", nb_inplace_multiply, slot_nb_inplace_multiply, wrap_binaryfunc, "*"), - IBSLOT("__idiv__", nb_inplace_divide, slot_nb_inplace_divide, - wrap_binaryfunc, "/"), IBSLOT("__imod__", nb_inplace_remainder, slot_nb_inplace_remainder, wrap_binaryfunc, "%"), IBSLOT("__ipow__", nb_inplace_power, slot_nb_inplace_power, diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 52bff2d..7fbce14 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -6445,7 +6445,6 @@ static PyNumberMethods unicode_as_number = { 0, /*nb_add*/ 0, /*nb_subtract*/ 0, /*nb_multiply*/ - 0, /*nb_divide*/ unicode_mod, /*nb_remainder*/ }; diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 1d68bb5..39595ae 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -471,7 +471,6 @@ proxy_compare(PyObject *proxy, PyObject *v) WRAP_BINARY(proxy_add, PyNumber_Add) WRAP_BINARY(proxy_sub, PyNumber_Subtract) WRAP_BINARY(proxy_mul, PyNumber_Multiply) -WRAP_BINARY(proxy_div, PyNumber_Divide) WRAP_BINARY(proxy_mod, PyNumber_Remainder) WRAP_BINARY(proxy_divmod, PyNumber_Divmod) WRAP_TERNARY(proxy_pow, PyNumber_Power) @@ -490,7 +489,6 @@ WRAP_UNARY(proxy_float, PyNumber_Float) WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd) WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract) WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply) -WRAP_BINARY(proxy_idiv, PyNumber_InPlaceDivide) WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder) WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower) WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift) @@ -591,7 +589,6 @@ static PyNumberMethods proxy_as_number = { (binaryfunc)proxy_add, /*nb_add*/ (binaryfunc)proxy_sub, /*nb_subtract*/ (binaryfunc)proxy_mul, /*nb_multiply*/ - (binaryfunc)proxy_div, /*nb_divide*/ (binaryfunc)proxy_mod, /*nb_remainder*/ (binaryfunc)proxy_divmod, /*nb_divmod*/ (ternaryfunc)proxy_pow, /*nb_power*/ @@ -614,7 +611,6 @@ static PyNumberMethods proxy_as_number = { (binaryfunc)proxy_iadd, /*nb_inplace_add*/ (binaryfunc)proxy_isub, /*nb_inplace_subtract*/ (binaryfunc)proxy_imul, /*nb_inplace_multiply*/ - (binaryfunc)proxy_idiv, /*nb_inplace_divide*/ (binaryfunc)proxy_imod, /*nb_inplace_remainder*/ (ternaryfunc)proxy_ipow, /*nb_inplace_power*/ (binaryfunc)proxy_ilshift, /*nb_inplace_lshift*/ diff --git a/PC/_winreg.c b/PC/_winreg.c index 007885c..5ed58bb 100644 --- a/PC/_winreg.c +++ b/PC/_winreg.c @@ -431,7 +431,6 @@ static PyNumberMethods PyHKEY_NumberMethods = PyHKEY_binaryFailureFunc, /* nb_add */ PyHKEY_binaryFailureFunc, /* nb_subtract */ PyHKEY_binaryFailureFunc, /* nb_multiply */ - PyHKEY_binaryFailureFunc, /* nb_divide */ PyHKEY_binaryFailureFunc, /* nb_remainder */ PyHKEY_binaryFailureFunc, /* nb_divmod */ PyHKEY_ternaryFailureFunc, /* nb_power */ -- cgit v0.12 From c6fe059512c7c4fd82aa7eb5374c78be61041d85 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 24 Mar 2006 08:57:54 +0000 Subject: Use absolute import. (Should this go into 2.5?) --- Lib/test/test_cpickle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_cpickle.py b/Lib/test/test_cpickle.py index d87decd..78beda7 100644 --- a/Lib/test/test_cpickle.py +++ b/Lib/test/test_cpickle.py @@ -1,7 +1,7 @@ import cPickle import unittest from cStringIO import StringIO -from pickletester import AbstractPickleTests, AbstractPickleModuleTests +from test.pickletester import AbstractPickleTests, AbstractPickleModuleTests from test import test_support class cPickleTests(AbstractPickleTests, AbstractPickleModuleTests): -- cgit v0.12 From 0c4eb625656a8b8169e599452e271317863d0c7e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 30 Mar 2006 16:19:24 +0000 Subject: Fix the version number to be 3.0. --- configure | 22 +++++++++++----------- configure.in | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configure b/configure index aad20c8..3e322e6 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #! /bin/sh -# From configure.in Revision: 42437 . +# From configure.in Revision: 42563 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for python 2.5. +# Generated by GNU Autoconf 2.59 for python 3.0. # # Report bugs to . # @@ -270,8 +270,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='python' PACKAGE_TARNAME='python' -PACKAGE_VERSION='2.5' -PACKAGE_STRING='python 2.5' +PACKAGE_VERSION='3.0' +PACKAGE_STRING='python 3.0' PACKAGE_BUGREPORT='http://www.python.org/python-bugs' ac_unique_file="Include/object.h" @@ -781,7 +781,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures python 2.5 to adapt to many kinds of systems. +\`configure' configures python 3.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -838,7 +838,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of python 2.5:";; + short | recursive ) echo "Configuration of python 3.0:";; esac cat <<\_ACEOF @@ -986,7 +986,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -python configure 2.5 +python configure 3.0 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1000,7 +1000,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by python $as_me 2.5, which was +It was created by python $as_me 3.0, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1352,7 +1352,7 @@ rm confdefs.h mv confdefs.h.new confdefs.h -VERSION=2.5 +VERSION=3.0 SOVERSION=1.0 @@ -22193,7 +22193,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by python $as_me 2.5, which was +This file was extended by python $as_me 3.0, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22253,7 +22253,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -python config.status 2.5 +python config.status 3.0 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.in b/configure.in index d617108..f3525b1 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf 2.0 or later to make a configure script. # Set VERSION so we only need to edit in one place (i.e., here) -m4_define(PYTHON_VERSION, 2.5) +m4_define(PYTHON_VERSION, 3.0) AC_REVISION($Revision$) AC_PREREQ(2.59) -- cgit v0.12 From 6caa07b23d4f838d00a9d27efc76e412549b1de9 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 14 Apr 2006 11:33:28 +0000 Subject: Make 'python -tt' the default, meaning Python won't allow mixing tabs and spaces for indentation. Adds a '-ttt' option to turn the errors back into warnings; I'm not yet sure whether that's desireable for Py3K. Also remove the magic for setting the size of tabs based on emacs/vim-style comments. Python now always considers tabstops to be every-8-spaces. --- Parser/parsetok.c | 15 ++++----------- Parser/tokenizer.c | 39 ++++----------------------------------- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/Parser/parsetok.c b/Parser/parsetok.c index d877fc9..dbac6e3 100644 --- a/Parser/parsetok.c +++ b/Parser/parsetok.c @@ -47,11 +47,8 @@ PyParser_ParseStringFlagsFilename(const char *s, const char *filename, } tok->filename = filename ? filename : ""; - if (Py_TabcheckFlag || Py_VerboseFlag) { - tok->altwarning = (tok->filename != NULL); - if (Py_TabcheckFlag >= 2) - tok->alterror++; - } + if (Py_TabcheckFlag >= 3) + tok->alterror = 0; return parsetok(tok, g, start, err_ret, flags); } @@ -79,12 +76,8 @@ PyParser_ParseFileFlags(FILE *fp, const char *filename, grammar *g, int start, return NULL; } tok->filename = filename; - if (Py_TabcheckFlag || Py_VerboseFlag) { - tok->altwarning = (filename != NULL); - if (Py_TabcheckFlag >= 2) - tok->alterror++; - } - + if (Py_TabcheckFlag >= 3) + tok->alterror = 0; return parsetok(tok, g, start, err_ret, flags); } diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 3c82588..0631ca3 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -120,8 +120,8 @@ tok_new(void) tok->lineno = 0; tok->level = 0; tok->filename = NULL; - tok->altwarning = 0; - tok->alterror = 0; + tok->altwarning = 1; + tok->alterror = 1; tok->alttabsize = 1; tok->altindstack[0] = 0; tok->decoding_state = 0; @@ -1207,41 +1207,10 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) /* Set start of current token */ tok->start = tok->cur - 1; - /* Skip comment, while looking for tab-setting magic */ - if (c == '#') { - static char *tabforms[] = { - "tab-width:", /* Emacs */ - ":tabstop=", /* vim, full form */ - ":ts=", /* vim, abbreviated form */ - "set tabsize=", /* will vi never die? */ - /* more templates can be added here to support other editors */ - }; - char cbuf[80]; - char *tp, **cp; - tp = cbuf; - do { - *tp++ = c = tok_nextc(tok); - } while (c != EOF && c != '\n' && - tp - cbuf + 1 < sizeof(cbuf)); - *tp = '\0'; - for (cp = tabforms; - cp < tabforms + sizeof(tabforms)/sizeof(tabforms[0]); - cp++) { - if ((tp = strstr(cbuf, *cp))) { - int newsize = atoi(tp + strlen(*cp)); - - if (newsize >= 1 && newsize <= 40) { - tok->tabsize = newsize; - if (Py_VerboseFlag) - PySys_WriteStderr( - "Tab size set to %d\n", - newsize); - } - } - } + /* Skip comment */ + if (c == '#') while (c != EOF && c != '\n') c = tok_nextc(tok); - } /* Check for EOF and errors now */ if (c == EOF) { -- cgit v0.12 From 28bc76897757ed614dfdccbea7902043221fe141 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:03:16 +0000 Subject: - Fix doctest results to account for classes being new-style, and thus printing differently. - Fix doctest for classic-class behaviour, make it test new-style behaviour on an implicitly-new-style class instead. --- Lib/test/test_descrtut.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 9dcfca1..6a5310c 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -265,19 +265,19 @@ implicit first argument that is the *class* for which they are invoked. ... print "classmethod", cls, y >>> C.foo(1) - classmethod test.test_descrtut.C 1 + classmethod 1 >>> c = C() >>> c.foo(1) - classmethod test.test_descrtut.C 1 + classmethod 1 >>> class D(C): ... pass >>> D.foo(1) - classmethod test.test_descrtut.D 1 + classmethod 1 >>> d = D() >>> d.foo(1) - classmethod test.test_descrtut.D 1 + classmethod 1 This prints "classmethod __main__.D 1" both times; in other words, the class passed as the first argument of foo() is the class involved in the @@ -293,11 +293,11 @@ But notice this: >>> E.foo(1) E.foo() called - classmethod test.test_descrtut.C 1 + classmethod 1 >>> e = E() >>> e.foo(1) E.foo() called - classmethod test.test_descrtut.C 1 + classmethod 1 In this example, the call to C.foo() from E.foo() will see class C as its first argument, not class E. This is to be expected, since the call @@ -386,7 +386,7 @@ Method resolution order This example is implicit in the writeup. ->>> class A: # classic class +>>> class A: # implicit new-style class ... def save(self): ... print "called A.save()" >>> class B(A): @@ -398,9 +398,9 @@ This example is implicit in the writeup. ... pass >>> D().save() -called A.save() +called C.save() ->>> class A(object): # new class +>>> class A(object): # explicit new-style class ... def save(self): ... print "called A.save()" >>> class B(A): -- cgit v0.12 From 0725cf2127128654ccc54ac9155a8cf0233b7348 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:04:57 +0000 Subject: Remove tests for classic-class and mixed-classic-class/new-style behaviour. (New-style class behaviour was already thoroughly tested) --- Lib/test/test_descr.py | 148 +------------------------------------------------ 1 file changed, 2 insertions(+), 146 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 108d95e..185edb3 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -267,39 +267,8 @@ def test_dir(): for arg in 2, 2L, 2j, 2e0, [2], "2", u"2", (2,), {2:2}, type, test_dir: dir(arg) - # Try classic classes. - class C: - Cdata = 1 - def Cmethod(self): pass - - cstuff = ['Cdata', 'Cmethod', '__doc__', '__module__'] - vereq(dir(C), cstuff) - verify('im_self' in dir(C.Cmethod)) - - c = C() # c.__doc__ is an odd thing to see here; ditto c.__module__. - vereq(dir(c), cstuff) - - c.cdata = 2 - c.cmethod = lambda self: 0 - vereq(dir(c), cstuff + ['cdata', 'cmethod']) - verify('im_self' in dir(c.Cmethod)) - - class A(C): - Adata = 1 - def Amethod(self): pass - - astuff = ['Adata', 'Amethod'] + cstuff - vereq(dir(A), astuff) - verify('im_self' in dir(A.Amethod)) - a = A() - vereq(dir(a), astuff) - verify('im_self' in dir(a.Amethod)) - a.adata = 42 - a.amethod = lambda self: 3 - vereq(dir(a), astuff + ['adata', 'amethod']) - - # The same, but with new-style classes. Since these have object as a - # base class, a lot more gets sucked in. + # Test dir on custom classes. Since these have object as a + # base class, a lot of stuff gets sucked in. def interesting(strings): return [s for s in strings if not s.startswith('_')] @@ -882,96 +851,6 @@ def multi(): vereq(Frag().__int__(), 42) vereq(int(Frag()), 42) - # MI mixing classic and new-style classes. - - class A: - x = 1 - - class B(A): - pass - - class C(A): - x = 2 - - class D(B, C): - pass - vereq(D.x, 1) - - # Classic MRO is preserved for a classic base class. - class E(D, object): - pass - vereq(E.__mro__, (E, D, B, A, C, object)) - vereq(E.x, 1) - - # But with a mix of classic bases, their MROs are combined using - # new-style MRO. - class F(B, C, object): - pass - vereq(F.__mro__, (F, B, C, A, object)) - vereq(F.x, 2) - - # Try something else. - class C: - def cmethod(self): - return "C a" - def all_method(self): - return "C b" - - class M1(C, object): - def m1method(self): - return "M1 a" - def all_method(self): - return "M1 b" - - vereq(M1.__mro__, (M1, C, object)) - m = M1() - vereq(m.cmethod(), "C a") - vereq(m.m1method(), "M1 a") - vereq(m.all_method(), "M1 b") - - class D(C): - def dmethod(self): - return "D a" - def all_method(self): - return "D b" - - class M2(D, object): - def m2method(self): - return "M2 a" - def all_method(self): - return "M2 b" - - vereq(M2.__mro__, (M2, D, C, object)) - m = M2() - vereq(m.cmethod(), "C a") - vereq(m.dmethod(), "D a") - vereq(m.m2method(), "M2 a") - vereq(m.all_method(), "M2 b") - - class M3(M1, M2, object): - def m3method(self): - return "M3 a" - def all_method(self): - return "M3 b" - vereq(M3.__mro__, (M3, M1, M2, D, C, object)) - m = M3() - vereq(m.cmethod(), "C a") - vereq(m.dmethod(), "D a") - vereq(m.m1method(), "M1 a") - vereq(m.m2method(), "M2 a") - vereq(m.m3method(), "M3 a") - vereq(m.all_method(), "M3 b") - - class Classic: - pass - try: - class New(Classic): - __metaclass__ = type - except TypeError: - pass - else: - raise TestFailed, "new class with only classic bases - shouldn't be" - def diamond(): if verbose: print "Testing multiple inheritance special cases..." class A(object): @@ -3619,29 +3498,6 @@ def test_mutable_bases(): else: raise TestFailed, "shouldn't be able to create inheritance cycles" - # let's throw a classic class into the mix: - class Classic: - def meth2(self): - return 3 - - D.__bases__ = (C, Classic) - - vereq(d.meth2(), 3) - vereq(e.meth2(), 3) - try: - d.a - except AttributeError: - pass - else: - raise TestFailed, "attribute should have vanished" - - try: - D.__bases__ = (Classic,) - except TypeError: - pass - else: - raise TestFailed, "new-style class must have a new-style base" - def test_mutable_bases_with_failing_mro(): if verbose: print "Testing mutable bases with failing mro..." -- cgit v0.12 From 8690c4ed3fa1f1889459249c7e50e11c2052b340 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:07:20 +0000 Subject: Fix tests that were trying to make iteration blow up, on broken iterators. Since the broken iterators are now new-style classes, iter() was able to do the valid-iterator check sooner (on instantiation instead of on first call), making the tests blow up sooner than expected. --- Lib/test/test_itertools.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 635d156..c4ed3bc 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -560,7 +560,7 @@ class TestVariousIteratorArgs(unittest.TestCase): self.assertEqual(list(chain(g(s))), list(g(s))) self.assertEqual(list(chain(g(s), g(s))), list(g(s))+list(g(s))) self.assertRaises(TypeError, chain, X(s)) - self.assertRaises(TypeError, list, chain(N(s))) + self.assertRaises(TypeError, chain, N(s)) self.assertRaises(ZeroDivisionError, list, chain(E(s))) def test_cycle(self): @@ -571,7 +571,7 @@ class TestVariousIteratorArgs(unittest.TestCase): actual = list(islice(cycle(g(s)), tgtlen)) self.assertEqual(actual, expected) self.assertRaises(TypeError, cycle, X(s)) - self.assertRaises(TypeError, list, cycle(N(s))) + self.assertRaises(TypeError, cycle, N(s)) self.assertRaises(ZeroDivisionError, list, cycle(E(s))) def test_groupby(self): @@ -579,7 +579,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for g in (G, I, Ig, S, L, R): self.assertEqual([k for k, sb in groupby(g(s))], list(g(s))) self.assertRaises(TypeError, groupby, X(s)) - self.assertRaises(TypeError, list, groupby(N(s))) + self.assertRaises(TypeError, groupby, N(s)) self.assertRaises(ZeroDivisionError, list, groupby(E(s))) def test_ifilter(self): @@ -587,7 +587,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for g in (G, I, Ig, S, L, R): self.assertEqual(list(ifilter(isEven, g(s))), filter(isEven, g(s))) self.assertRaises(TypeError, ifilter, isEven, X(s)) - self.assertRaises(TypeError, list, ifilter(isEven, N(s))) + self.assertRaises(TypeError, ifilter, isEven, N(s)) self.assertRaises(ZeroDivisionError, list, ifilter(isEven, E(s))) def test_ifilterfalse(self): @@ -595,7 +595,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for g in (G, I, Ig, S, L, R): self.assertEqual(list(ifilterfalse(isEven, g(s))), filter(isOdd, g(s))) self.assertRaises(TypeError, ifilterfalse, isEven, X(s)) - self.assertRaises(TypeError, list, ifilterfalse(isEven, N(s))) + self.assertRaises(TypeError, ifilterfalse, isEven, N(s)) self.assertRaises(ZeroDivisionError, list, ifilterfalse(isEven, E(s))) def test_izip(self): @@ -604,7 +604,7 @@ class TestVariousIteratorArgs(unittest.TestCase): self.assertEqual(list(izip(g(s))), zip(g(s))) self.assertEqual(list(izip(g(s), g(s))), zip(g(s), g(s))) self.assertRaises(TypeError, izip, X(s)) - self.assertRaises(TypeError, list, izip(N(s))) + self.assertRaises(TypeError, izip, N(s)) self.assertRaises(ZeroDivisionError, list, izip(E(s))) def test_imap(self): @@ -613,7 +613,7 @@ class TestVariousIteratorArgs(unittest.TestCase): self.assertEqual(list(imap(onearg, g(s))), map(onearg, g(s))) self.assertEqual(list(imap(operator.pow, g(s), g(s))), map(operator.pow, g(s), g(s))) self.assertRaises(TypeError, imap, onearg, X(s)) - self.assertRaises(TypeError, list, imap(onearg, N(s))) + self.assertRaises(TypeError, imap, onearg, N(s)) self.assertRaises(ZeroDivisionError, list, imap(onearg, E(s))) def test_islice(self): @@ -621,7 +621,7 @@ class TestVariousIteratorArgs(unittest.TestCase): for g in (G, I, Ig, S, L, R): self.assertEqual(list(islice(g(s),1,None,2)), list(g(s))[1::2]) self.assertRaises(TypeError, islice, X(s), 10) - self.assertRaises(TypeError, list, islice(N(s), 10)) + self.assertRaises(TypeError, islice, N(s), 10) self.assertRaises(ZeroDivisionError, list, islice(E(s), 10)) def test_starmap(self): @@ -630,7 +630,7 @@ class TestVariousIteratorArgs(unittest.TestCase): ss = zip(s, s) self.assertEqual(list(starmap(operator.pow, g(ss))), map(operator.pow, g(s), g(s))) self.assertRaises(TypeError, starmap, operator.pow, X(ss)) - self.assertRaises(TypeError, list, starmap(operator.pow, N(ss))) + self.assertRaises(TypeError, starmap, operator.pow, N(ss)) self.assertRaises(ZeroDivisionError, list, starmap(operator.pow, E(ss))) def test_takewhile(self): @@ -642,7 +642,7 @@ class TestVariousIteratorArgs(unittest.TestCase): tgt.append(elem) self.assertEqual(list(takewhile(isEven, g(s))), tgt) self.assertRaises(TypeError, takewhile, isEven, X(s)) - self.assertRaises(TypeError, list, takewhile(isEven, N(s))) + self.assertRaises(TypeError, takewhile, isEven, N(s)) self.assertRaises(ZeroDivisionError, list, takewhile(isEven, E(s))) def test_dropwhile(self): @@ -654,7 +654,7 @@ class TestVariousIteratorArgs(unittest.TestCase): tgt.append(elem) self.assertEqual(list(dropwhile(isOdd, g(s))), tgt) self.assertRaises(TypeError, dropwhile, isOdd, X(s)) - self.assertRaises(TypeError, list, dropwhile(isOdd, N(s))) + self.assertRaises(TypeError, dropwhile, isOdd, N(s)) self.assertRaises(ZeroDivisionError, list, dropwhile(isOdd, E(s))) def test_tee(self): @@ -664,7 +664,7 @@ class TestVariousIteratorArgs(unittest.TestCase): self.assertEqual(list(it1), list(g(s))) self.assertEqual(list(it2), list(g(s))) self.assertRaises(TypeError, tee, X(s)) - self.assertRaises(TypeError, list, tee(N(s))[0]) + self.assertRaises(TypeError, tee, N(s)) self.assertRaises(ZeroDivisionError, list, tee(E(s))[0]) class LengthTransparency(unittest.TestCase): -- cgit v0.12 From 34729030a741bcbc469ce0e796182f85405a9b3d Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:10:43 +0000 Subject: Fix the superficial augmented-assignment tests to deal with true division. Add (equally superficial) >>=/<<= test in the process. Relies on floats that should be extremely close to the int '6' printing as '6.0', but I believe that's a valid assumption ;P --- Lib/test/output/test_augassign | 11 +++++++---- Lib/test/test_augassign.py | 21 +++++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Lib/test/output/test_augassign b/Lib/test/output/test_augassign index af840f8..b66b7e5 100644 --- a/Lib/test/output/test_augassign +++ b/Lib/test/output/test_augassign @@ -1,6 +1,9 @@ test_augassign +6.0 6 -[6] +[6.0] +6 +6.0 6 [1, 2, 3, 4, 1, 2, 3, 4] [1, 2, 1, 2, 3] @@ -22,9 +25,9 @@ __isub__ called __mul__ called __rmul__ called __imul__ called -__div__ called -__rdiv__ called -__idiv__ called +__truediv__ called +__rtruediv__ called +__itruediv__ called __floordiv__ called __rfloordiv__ called __ifloordiv__ called diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 228e03a..22cca44 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -5,42 +5,51 @@ x += 1 x *= 2 x **= 2 x -= 8 -x /= 2 -x //= 1 x %= 12 +x >>= 1 x &= 2 x |= 5 x ^= 1 +x <<= 2 +x /= 2 +x //= 2 print x +print int(x) x = [2] x[0] += 1 x[0] *= 2 x[0] **= 2 x[0] -= 8 -x[0] /= 2 -x[0] //= 2 x[0] %= 12 +x[0] >>= 1 x[0] &= 2 x[0] |= 5 x[0] ^= 1 +x[0] <<= 2 +x[0] /= 2 +x[0] //= 2 print x +print int(x[0]) x = {0: 2} x[0] += 1 x[0] *= 2 x[0] **= 2 x[0] -= 8 -x[0] /= 2 -x[0] //= 1 x[0] %= 12 +x[0] >>= 1 x[0] &= 2 x[0] |= 5 x[0] ^= 1 +x[0] <<= 2 +x[0] /= 2 +x[0] //= 2 print x[0] +print int(x[0]) x = [1,2] x += [3,4] -- cgit v0.12 From 1ae9afa829ff0b6372b9fd70365653ab6c565b3a Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:12:14 +0000 Subject: Fix test_bisect in the same way as test_itertools: iter() blows up a lot sooner for new-style broken-iterators, expect it to. --- Lib/test/test_bisect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py index f6e24a6..302ff63 100644 --- a/Lib/test/test_bisect.py +++ b/Lib/test/test_bisect.py @@ -185,11 +185,11 @@ class TestErrorHandling(unittest.TestCase): def test_len_only(self): for f in (bisect_left, bisect_right, insort_left, insort_right): - self.assertRaises(AttributeError, f, LenOnly(), 10) + self.assertRaises(TypeError, f, LenOnly(), 10) def test_get_only(self): for f in (bisect_left, bisect_right, insort_left, insort_right): - self.assertRaises(AttributeError, f, GetOnly(), 10) + self.assertRaises(TypeError, f, GetOnly(), 10) def test_cmp_err(self): seq = [CmpErr(), CmpErr(), CmpErr()] -- cgit v0.12 From 725af87d0a0041d108b493d206fc2f8d0ca75558 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:13:19 +0000 Subject: - Remove tests for classic class behaviour - Expect a new-style class tree in the getclasstree test. --- Lib/test/test_inspect.py | 84 ++++-------------------------------------------- 1 file changed, 7 insertions(+), 77 deletions(-) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index ce346b9..0bdf959 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -139,11 +139,13 @@ class TestRetrievingSourceCode(GetSourceBase): ('StupidGit', mod.StupidGit)]) tree = inspect.getclasstree([cls[1] for cls in classes], 1) self.assertEqual(tree, - [(mod.ParrotDroppings, ()), - (mod.StupidGit, ()), - [(mod.MalodorousPervert, (mod.StupidGit,)), - [(mod.FesteringGob, (mod.MalodorousPervert, - mod.ParrotDroppings)) + [(object, ()), + [(mod.ParrotDroppings, (object,)), + (mod.StupidGit, (object,)), + [(mod.MalodorousPervert, (mod.StupidGit,)), + [(mod.FesteringGob, (mod.MalodorousPervert, + mod.ParrotDroppings)) + ] ] ] ]) @@ -255,17 +257,6 @@ def attrs_wo_objs(cls): return [t[:3] for t in inspect.classify_class_attrs(cls)] class TestClassesAndFunctions(unittest.TestCase): - def test_classic_mro(self): - # Test classic-class method resolution order. - class A: pass - class B(A): pass - class C(A): pass - class D(B, C): pass - - expected = (D, B, A, C) - got = inspect.getmro(D) - self.assertEqual(expected, got) - def test_newstyle_mro(self): # The same w/ new-class MRO. class A(object): pass @@ -308,67 +299,6 @@ class TestClassesAndFunctions(unittest.TestCase): self.assertArgSpecEquals(sublistOfOne, [['foo']]) - def test_classify_oldstyle(self): - class A: - def s(): pass - s = staticmethod(s) - - def c(cls): pass - c = classmethod(c) - - def getp(self): pass - p = property(getp) - - def m(self): pass - - def m1(self): pass - - datablob = '1' - - attrs = attrs_wo_objs(A) - self.assert_(('s', 'static method', A) in attrs, 'missing static method') - self.assert_(('c', 'class method', A) in attrs, 'missing class method') - self.assert_(('p', 'property', A) in attrs, 'missing property') - self.assert_(('m', 'method', A) in attrs, 'missing plain method') - self.assert_(('m1', 'method', A) in attrs, 'missing plain method') - self.assert_(('datablob', 'data', A) in attrs, 'missing data') - - class B(A): - def m(self): pass - - attrs = attrs_wo_objs(B) - self.assert_(('s', 'static method', A) in attrs, 'missing static method') - self.assert_(('c', 'class method', A) in attrs, 'missing class method') - self.assert_(('p', 'property', A) in attrs, 'missing property') - self.assert_(('m', 'method', B) in attrs, 'missing plain method') - self.assert_(('m1', 'method', A) in attrs, 'missing plain method') - self.assert_(('datablob', 'data', A) in attrs, 'missing data') - - - class C(A): - def m(self): pass - def c(self): pass - - attrs = attrs_wo_objs(C) - self.assert_(('s', 'static method', A) in attrs, 'missing static method') - self.assert_(('c', 'method', C) in attrs, 'missing plain method') - self.assert_(('p', 'property', A) in attrs, 'missing property') - self.assert_(('m', 'method', C) in attrs, 'missing plain method') - self.assert_(('m1', 'method', A) in attrs, 'missing plain method') - self.assert_(('datablob', 'data', A) in attrs, 'missing data') - - class D(B, C): - def m1(self): pass - - attrs = attrs_wo_objs(D) - self.assert_(('s', 'static method', A) in attrs, 'missing static method') - self.assert_(('c', 'class method', A) in attrs, 'missing class method') - self.assert_(('p', 'property', A) in attrs, 'missing property') - self.assert_(('m', 'method', B) in attrs, 'missing plain method') - self.assert_(('m1', 'method', D) in attrs, 'missing plain method') - self.assert_(('datablob', 'data', A) in attrs, 'missing data') - - # Repeat all that, but w/ new-style classes. def test_classify_newstyle(self): class A(object): -- cgit v0.12 From c947123350b31d3fab7e4893eb87cb361125f549 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:15:11 +0000 Subject: Fix tests for PyArg_Parse*; The PyArg_Parse functions no longer (noisily) convert float arguments to integer-taking format characters, so fix the test to expect the failure. --- Lib/test/test_getargs2.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 47db73f..d4c681a 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -67,7 +67,7 @@ class Unsigned_TestCase(unittest.TestCase): def test_b(self): from _testcapi import getargs_b # b returns 'unsigned char', and does range checking (0 ... UCHAR_MAX) - self.failUnlessEqual(3, getargs_b(3.14)) + self.assertRaises(TypeError, getargs_b, 3.14) self.failUnlessEqual(99, getargs_b(Long())) self.failUnlessEqual(99, getargs_b(Int())) @@ -83,7 +83,7 @@ class Unsigned_TestCase(unittest.TestCase): def test_B(self): from _testcapi import getargs_B # B returns 'unsigned char', no range checking - self.failUnlessEqual(3, getargs_B(3.14)) + self.assertRaises(TypeError, getargs_B, 3.14) self.failUnlessEqual(99, getargs_B(Long())) self.failUnlessEqual(99, getargs_B(Int())) @@ -100,7 +100,7 @@ class Unsigned_TestCase(unittest.TestCase): def test_H(self): from _testcapi import getargs_H # H returns 'unsigned short', no range checking - self.failUnlessEqual(3, getargs_H(3.14)) + self.assertRaises(TypeError, getargs_H, 3.14) self.failUnlessEqual(99, getargs_H(Long())) self.failUnlessEqual(99, getargs_H(Int())) @@ -117,7 +117,7 @@ class Unsigned_TestCase(unittest.TestCase): def test_I(self): from _testcapi import getargs_I # I returns 'unsigned int', no range checking - self.failUnlessEqual(3, getargs_I(3.14)) + self.assertRaises(TypeError, getargs_I, 3.14) self.failUnlessEqual(99, getargs_I(Long())) self.failUnlessEqual(99, getargs_I(Int())) @@ -153,7 +153,7 @@ class Signed_TestCase(unittest.TestCase): def test_i(self): from _testcapi import getargs_i # i returns 'int', and does range checking (INT_MIN ... INT_MAX) - self.failUnlessEqual(3, getargs_i(3.14)) + self.assertRaises(TypeError, getargs_i, 3.14) self.failUnlessEqual(99, getargs_i(Long())) self.failUnlessEqual(99, getargs_i(Int())) @@ -169,7 +169,7 @@ class Signed_TestCase(unittest.TestCase): def test_l(self): from _testcapi import getargs_l # l returns 'long', and does range checking (LONG_MIN ... LONG_MAX) - self.failUnlessEqual(3, getargs_l(3.14)) + self.assertRaises(TypeError, getargs_l, 3.14) self.failUnlessEqual(99, getargs_l(Long())) self.failUnlessEqual(99, getargs_l(Int())) -- cgit v0.12 From 1034dadf1fb032612281f7559be2b71615e9fc9e Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:16:16 +0000 Subject: Adjust test_enumerate to accomodate for iter() blowing up sooner than expected, when dealing with new-style broken-iterators. --- Lib/test/test_enumerate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index e0a272e..10ff8f4 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -92,7 +92,7 @@ class EnumerateTestCase(unittest.TestCase): self.assertRaises(TypeError, self.enum, X(self.seq)) def test_illformediterable(self): - self.assertRaises(TypeError, list, self.enum(N(self.seq))) + self.assertRaises(TypeError, self.enum, N(self.seq)) def test_exception_propagation(self): self.assertRaises(ZeroDivisionError, list, self.enum(E(self.seq))) -- cgit v0.12 From 4cdada9af61caa842cc1b04bac1968356cda7b7d Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 09:19:16 +0000 Subject: Make test_class work (but still fail) even though class.__dict__ is now a 'dictproxy' (which is a read-only non-dict mapping type that can't be passed to exec.) The failures the test finds are behavioural differences between old- and new-style classes that may or may not be intended. --- Lib/test/test_class.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index b8b4cab..601b8b4 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -115,9 +115,12 @@ def __%(method)s__(self, *args): print "__%(method)s__:", args """ +d = {} for method in testmeths: - exec method_template % locals() in AllTests.__dict__ - + exec method_template % locals() in d +for k in d: + setattr(AllTests, k, d[k]) +del d, k del method, method_template # this also tests __init__ of course. -- cgit v0.12 From ba617af2989bd2eb625100b377e0cb745cae5f11 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 15 Apr 2006 09:35:38 +0000 Subject: Mention how __coerce__ has been removed. --- Misc/NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index e5d19ec..3e147fb 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -35,6 +35,8 @@ TO DO Core and Builtins ----------------- +- __coerce__ has been removed. + - Classic classes are a thing of the past. All classes are new style. - Exceptions *must* derive from BaseException. -- cgit v0.12 From 07519f8712241397bcb98efa19e2e7ec9257c18c Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 17 Apr 2006 13:16:50 +0000 Subject: Fix xrange tests now that xrange() (like many other places) no longer silently converts floats to integers when expecting integer values. --- Lib/test/test_xrange.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py index 9f9daa7..40590cd 100644 --- a/Lib/test/test_xrange.py +++ b/Lib/test/test_xrange.py @@ -43,7 +43,10 @@ class XrangeTest(unittest.TestCase): self.assertRaises(TypeError, xrange, 1, 2, 3, 4) self.assertRaises(ValueError, xrange, 1, 2, 0) - self.assertRaises(OverflowError, xrange, 1e100, 1e101, 1e101) + self.assertRaises(TypeError, xrange, 0.0, 2, 1) + self.assertRaises(TypeError, xrange, 1, 2.0, 1) + self.assertRaises(TypeError, xrange, 1, 2, 1.0) + self.assertRaises(TypeError, xrange, 1e100, 1e101, 1e101) self.assertRaises(TypeError, xrange, 0, "spam") self.assertRaises(TypeError, xrange, 0, 42, "spam") -- cgit v0.12 From 69e8084ad683bb57a2e5a874eabc714f1d3af644 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 17 Apr 2006 23:13:00 +0000 Subject: Fix two errors that prevented "make libinstall" from working: - a line indented with tabs; - a function named 'as'. --- Lib/idlelib/WindowList.py | 2 +- Lib/plat-mac/lib-scriptpackages/StdSuites/AppleScript_Suite.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/WindowList.py b/Lib/idlelib/WindowList.py index d0123d8..2fad998 100644 --- a/Lib/idlelib/WindowList.py +++ b/Lib/idlelib/WindowList.py @@ -45,7 +45,7 @@ class WindowList: try: callback() except: - t, v, tb = sys.exc_info() + t, v, tb = sys.exc_info() print "warning: callback failed in WindowList", t, ":", v registry = WindowList() diff --git a/Lib/plat-mac/lib-scriptpackages/StdSuites/AppleScript_Suite.py b/Lib/plat-mac/lib-scriptpackages/StdSuites/AppleScript_Suite.py index 574043d..5d02ea3 100644 --- a/Lib/plat-mac/lib-scriptpackages/StdSuites/AppleScript_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/StdSuites/AppleScript_Suite.py @@ -300,8 +300,8 @@ class AppleScript_Suite_Events: if _arguments.has_key('----'): return _arguments['----'] - def as(self, _object, _attributes={}, **_arguments): - """as: Coercion + def as_(self, _object, _attributes={}, **_arguments): + """as_: Coercion Required argument: an AE object reference Keyword argument _attributes: AppleEvent attribute dictionary Returns: anything -- cgit v0.12 From 7ea7d90dbe98788609949d21abeea0e5830f2ccf Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 17 Apr 2006 23:38:13 +0000 Subject: Fix the expected output file; new classes just behave differently... (There are some other problems with test_class.py that aren't as easily fixed. :-( ) --- Lib/test/output/test_class | 44 +++++--------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/Lib/test/output/test_class b/Lib/test/output/test_class index 93827f1..14d43a8 100644 --- a/Lib/test/output/test_class +++ b/Lib/test/output/test_class @@ -1,52 +1,28 @@ test_class __init__: () -__coerce__: (1,) __add__: (1,) -__coerce__: (1,) __radd__: (1,) -__coerce__: (1,) __sub__: (1,) -__coerce__: (1,) __rsub__: (1,) -__coerce__: (1,) __mul__: (1,) -__coerce__: (1,) __rmul__: (1,) -__coerce__: (1,) -__div__: (1,) -__coerce__: (1,) -__rdiv__: (1,) -__coerce__: (1,) +__truediv__: (1,) +__rtruediv__: (1,) __mod__: (1,) -__coerce__: (1,) __rmod__: (1,) -__coerce__: (1,) __divmod__: (1,) -__coerce__: (1,) __rdivmod__: (1,) -__coerce__: (1,) __pow__: (1,) -__coerce__: (1,) __rpow__: (1,) -__coerce__: (1,) __rshift__: (1,) -__coerce__: (1,) __rrshift__: (1,) -__coerce__: (1,) __lshift__: (1,) -__coerce__: (1,) __rlshift__: (1,) -__coerce__: (1,) __and__: (1,) -__coerce__: (1,) __rand__: (1,) -__coerce__: (1,) __or__: (1,) -__coerce__: (1,) __ror__: (1,) -__coerce__: (1,) __xor__: (1,) -__coerce__: (1,) __rxor__: (1,) __contains__: (1,) __getitem__: (1,) @@ -61,9 +37,9 @@ __delitem__: (slice(2, 1024, 10),) __getitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100),) __setitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100), 'Strange') __delitem__: ((slice(None, 42, None), Ellipsis, slice(None, 24, None), 24, 100),) -__getitem__: (slice(0, 42, None),) -__setitem__: (slice(0, 42, None), 'The Answer') -__delitem__: (slice(0, 42, None),) +__getitem__: (slice(None, 42, None),) +__setitem__: (slice(None, 42, None), 'The Answer') +__delitem__: (slice(None, 42, None),) __neg__: () __pos__: () __abs__: () @@ -75,25 +51,15 @@ __hex__: () __hash__: () __repr__: () __str__: () -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) -__coerce__: (1,) __cmp__: (1,) __del__: () __getattr__: ('spam',) -- cgit v0.12 From 08a1a9fac7d754207ac339e1828a789d0462dfd4 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Tue, 18 Apr 2006 21:41:36 +0000 Subject: Use explicit relative import for an, ehm, relative import. --- Lib/test/test_pkg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pkg.py b/Lib/test/test_pkg.py index 919a019..c365742 100644 --- a/Lib/test/test_pkg.py +++ b/Lib/test/test_pkg.py @@ -146,7 +146,7 @@ print "t4.sub.subsub.spam =", spam ("t5 __init__"+os.extsep+"py", "import t5.foo"), ("t5 string"+os.extsep+"py", "print __name__, 'loading'; spam = 1"), ("t5 foo"+os.extsep+"py", - "print __name__, 'loading'; import string; print string.spam"), + "print __name__, 'loading'; from . import string; print string.spam"), ], """ import t5 -- cgit v0.12 From a48a3b42dd78c0201010ed737fdc45244a526648 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 20 Apr 2006 16:07:39 +0000 Subject: Fix test failures for repr.py. But shouldn't we kill this module? How many pprint clones do we need? --- Lib/repr.py | 15 +++++---------- Lib/test/test_repr.py | 11 +++++------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Lib/repr.py b/Lib/repr.py index 53b5207..32544a2 100644 --- a/Lib/repr.py +++ b/Lib/repr.py @@ -18,7 +18,7 @@ class Repr: self.maxdeque = 6 self.maxstring = 30 self.maxlong = 40 - self.maxother = 20 + self.maxother = 30 def repr(self, x): return self.repr1(x, self.maxlevel) @@ -31,12 +31,7 @@ class Repr: if hasattr(self, 'repr_' + typename): return getattr(self, 'repr_' + typename)(x, level) else: - s = __builtin__.repr(x) - if len(s) > self.maxother: - i = max(0, (self.maxother-3)//2) - j = max(0, self.maxother-3-i) - s = s[:i] + '...' + s[len(s)-j:] - return s + return self.repr_instance(x, level) def _repr_iterable(self, x, level, left, right, maxiter, trail=''): n = len(x) @@ -112,9 +107,9 @@ class Repr: # exceptions -- then make up something except: return '<%s instance at %x>' % (x.__class__.__name__, id(x)) - if len(s) > self.maxstring: - i = max(0, (self.maxstring-3)//2) - j = max(0, self.maxstring-3-i) + if len(s) > self.maxother: + i = max(0, (self.maxother-3)//2) + j = max(0, self.maxother-3-i) s = s[:i] + '...' + s[len(s)-j:] return s diff --git a/Lib/test/test_repr.py b/Lib/test/test_repr.py index 4ded484..9128585 100644 --- a/Lib/test/test_repr.py +++ b/Lib/test/test_repr.py @@ -111,7 +111,7 @@ class ReprTests(unittest.TestCase): s = r(ClassWithFailingRepr) self.failUnless(s.startswith("")) - self.failUnless(s.find("...") == 8) + self.failUnless(s.find("...") in [12, 13]) def test_file(self): fp = open(unittest.__file__) @@ -249,8 +249,7 @@ class bar: ''') from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import bar # Module name may be prefixed with "test.", depending on how run. - self.failUnless(repr(bar.bar).startswith( - "" % bar.__name__) def test_instance(self): touch(os.path.join(self.subpkgname, 'baz'+os.extsep+'py'), '''\ @@ -260,7 +259,7 @@ class baz: from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import baz ibaz = baz.baz() self.failUnless(repr(ibaz).startswith( - "<%s.baz instance at 0x" % baz.__name__)) + "<%s.baz object at 0x" % baz.__name__)) def test_method(self): eq = self.assertEquals @@ -275,7 +274,7 @@ class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # Bound method next iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() self.failUnless(repr(iqux.amethod).startswith( - ' Date: Thu, 20 Apr 2006 22:36:57 +0000 Subject: Fix typical truedivision problem (using the result of division as an index.) --- Lib/xdrlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/xdrlib.py b/Lib/xdrlib.py index 47cc22b..b349eb9 100644 --- a/Lib/xdrlib.py +++ b/Lib/xdrlib.py @@ -80,7 +80,7 @@ class Packer: if n < 0: raise ValueError, 'fstring size must be nonnegative' data = s[:n] - n = ((n+3)/4)*4 + n = ((n+3)//4)*4 data = data + (n - len(data)) * '\0' self.__buf.write(data) @@ -192,7 +192,7 @@ class Unpacker: if n < 0: raise ValueError, 'fstring size must be nonnegative' i = self.__pos - j = i + (n+3)/4*4 + j = i + (n+3)//4*4 if j > len(self.__buf): raise EOFError self.__pos = j -- cgit v0.12 From 303de6a25b4dc4874eded29c34c719a3bd6a4f40 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Thu, 20 Apr 2006 22:42:37 +0000 Subject: Fix (and add test for) missing check for BaseException subclasses in the C API. --- Lib/test/test_exceptions.py | 20 +++++++++++++++++++- Python/errors.c | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 65f7876..7946142 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -171,10 +171,15 @@ except Exception, e: pass # test that setting an exception at the C level works even if the # exception object can't be constructed. -class BadException: +class BadException(Exception): def __init__(self): raise RuntimeError, "can't instantiate BadException" +# Exceptions must inherit from BaseException, raising invalid exception +# should instead raise SystemError +class InvalidException: + pass + def test_capi1(): import _testcapi try: @@ -201,8 +206,21 @@ def test_capi2(): else: print "Expected exception" +def test_capi3(): + import _testcapi + try: + _testcapi.raise_exception(InvalidException, 1) + except SystemError: + pass + except InvalidException: + raise AssertionError("Managed to raise InvalidException"); + else: + print "Expected SystemError exception" + + if not sys.platform.startswith('java'): test_capi1() test_capi2() + test_capi3() unlink(TESTFN) diff --git a/Python/errors.c b/Python/errors.c index a64900b..67f86ed 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -47,6 +47,15 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) void PyErr_SetObject(PyObject *exception, PyObject *value) { + if (exception != NULL && + !PyExceptionClass_Check(exception)) { + PyObject *excstr = PyObject_Repr(exception); + PyErr_Format(PyExc_SystemError, + "exception %s not a BaseException subclass", + PyString_AS_STRING(excstr)); + Py_DECREF(excstr); + return; + } Py_XINCREF(exception); Py_XINCREF(value); PyErr_Restore(exception, value, (PyObject *)NULL); -- cgit v0.12 From d858f70617a9df8456e89a898ad8f97bd57c09f9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 21 Apr 2006 09:17:15 +0000 Subject: Fix the problems in this test. Boy this is a painful thing to debug -- it's fundamentally unmaintainable. Should we throw away pyclbr, or rewrite it using the AST? --- Lib/test/test_pyclbr.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index ceb7a3d..0eb7d90 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -57,6 +57,8 @@ class PyclbrTest(TestCase): ignore are ignored. If no module is provided, the appropriate module is loaded with __import__.''' + ignore = set(ignore) | set(['object']) + if module == None: # Import it. # ('' is to work around an API silliness in __import__) @@ -164,7 +166,7 @@ class PyclbrTest(TestCase): 'getproxies_internetconfig',)) # not on all platforms cm('pickle') cm('aifc', ignore=('openfp',)) # set with = in module - cm('Cookie') + cm('Cookie', ignore=('Cookie',)) # Cookie is an alias for SmartCookie cm('sre_parse', ignore=('dump',)) # from sre_constants import * cm('pdb') cm('pydoc') -- cgit v0.12 From a977329b6fb0e4c95cabb9043794de69b27a1099 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 09:43:23 +0000 Subject: Merge part of the trunk changes into the p3yk branch. This merges from 43030 (branch-creation time) up to 43067. 43068 and 43069 contain a little swapping action between re.py and sre.py, and this mightily confuses svn merge, so later changes are going in separately. This merge should break no additional tests. The last-merged revision is going in a 'last_merge' property on '.' (the branch directory.) Arbitrarily chosen, really; if there's a BCP for this, I couldn't find it, but we can easily change it afterwards ;) --- Doc/lib/libcodecs.tex | 168 ++++++++- Doc/whatsnew/whatsnew25.tex | 6 + Include/codecs.h | 22 +- Lib/StringIO.py | 3 +- Lib/codecs.py | 183 ++++++++- Lib/encodings/__init__.py | 31 +- Lib/encodings/ascii.py | 19 +- Lib/encodings/base64_codec.py | 21 +- Lib/encodings/bz2_codec.py | 21 +- Lib/encodings/charmap.py | 31 +- Lib/encodings/cp037.py | 23 +- Lib/encodings/cp1006.py | 23 +- Lib/encodings/cp1026.py | 23 +- Lib/encodings/cp1140.py | 23 +- Lib/encodings/cp1250.py | 23 +- Lib/encodings/cp1251.py | 23 +- Lib/encodings/cp1252.py | 23 +- Lib/encodings/cp1253.py | 23 +- Lib/encodings/cp1254.py | 23 +- Lib/encodings/cp1255.py | 23 +- Lib/encodings/cp1256.py | 23 +- Lib/encodings/cp1257.py | 23 +- Lib/encodings/cp1258.py | 23 +- Lib/encodings/cp424.py | 23 +- Lib/encodings/cp437.py | 23 +- Lib/encodings/cp500.py | 23 +- Lib/encodings/cp737.py | 23 +- Lib/encodings/cp775.py | 24 +- Lib/encodings/cp850.py | 21 +- Lib/encodings/cp852.py | 21 +- Lib/encodings/cp855.py | 21 +- Lib/encodings/cp856.py | 23 +- Lib/encodings/cp857.py | 21 +- Lib/encodings/cp860.py | 21 +- Lib/encodings/cp861.py | 21 +- Lib/encodings/cp862.py | 21 +- Lib/encodings/cp863.py | 21 +- Lib/encodings/cp864.py | 21 +- Lib/encodings/cp865.py | 21 +- Lib/encodings/cp866.py | 21 +- Lib/encodings/cp869.py | 21 +- Lib/encodings/cp874.py | 23 +- Lib/encodings/cp875.py | 23 +- Lib/encodings/hex_codec.py | 21 +- Lib/encodings/hp_roman8.py | 21 +- Lib/encodings/idna.py | 19 +- Lib/encodings/iso8859_1.py | 23 +- Lib/encodings/iso8859_10.py | 23 +- Lib/encodings/iso8859_11.py | 23 +- Lib/encodings/iso8859_13.py | 23 +- Lib/encodings/iso8859_14.py | 23 +- Lib/encodings/iso8859_15.py | 23 +- Lib/encodings/iso8859_16.py | 23 +- Lib/encodings/iso8859_2.py | 23 +- Lib/encodings/iso8859_3.py | 23 +- Lib/encodings/iso8859_4.py | 23 +- Lib/encodings/iso8859_5.py | 23 +- Lib/encodings/iso8859_6.py | 23 +- Lib/encodings/iso8859_7.py | 23 +- Lib/encodings/iso8859_8.py | 23 +- Lib/encodings/iso8859_9.py | 23 +- Lib/encodings/koi8_r.py | 23 +- Lib/encodings/koi8_u.py | 23 +- Lib/encodings/latin_1.py | 19 +- Lib/encodings/mac_arabic.py | 21 +- Lib/encodings/mac_centeuro.py | 23 +- Lib/encodings/mac_croatian.py | 23 +- Lib/encodings/mac_cyrillic.py | 23 +- Lib/encodings/mac_farsi.py | 23 +- Lib/encodings/mac_greek.py | 23 +- Lib/encodings/mac_iceland.py | 23 +- Lib/encodings/mac_latin2.py | 21 +- Lib/encodings/mac_roman.py | 23 +- Lib/encodings/mac_romanian.py | 23 +- Lib/encodings/mac_turkish.py | 23 +- Lib/encodings/mbcs.py | 18 +- Lib/encodings/palmos.py | 18 +- Lib/encodings/ptcp154.py | 21 +- Lib/encodings/punycode.py | 24 +- Lib/encodings/quopri_codec.py | 18 +- Lib/encodings/raw_unicode_escape.py | 19 +- Lib/encodings/rot_13.py | 21 +- Lib/encodings/string_escape.py | 19 +- Lib/encodings/tis_620.py | 23 +- Lib/encodings/undefined.py | 23 +- Lib/encodings/unicode_escape.py | 19 +- Lib/encodings/unicode_internal.py | 19 +- Lib/encodings/utf_16.py | 52 ++- Lib/encodings/utf_16_be.py | 18 +- Lib/encodings/utf_16_le.py | 19 +- Lib/encodings/utf_7.py | 19 +- Lib/encodings/utf_8.py | 18 +- Lib/encodings/utf_8_sig.py | 47 ++- Lib/encodings/uu_codec.py | 20 +- Lib/encodings/zlib_codec.py | 21 +- Lib/logging/__init__.py | 7 +- Lib/runpy.py | 431 +++++++++++++++++++++ Lib/test/regrtest.py | 3 - Lib/test/test_StringIO.py | 9 + Lib/test/test___all__.py | 3 - Lib/test/test_codecs.py | 57 +++ Lib/test/test_hashlib_speed.py | 92 ----- Lib/test/test_runpy.py | 172 +++++++++ Lib/test/time_hashlib.py | 87 +++++ Makefile.pre.in | 2 + Modules/_ctypes/_ctypes.c | 8 + Modules/_ctypes/cfield.c | 18 +- Modules/_ctypes/ctypes.h | 13 + Modules/_testcapimodule.c | 13 + Modules/cStringIO.c | 3 +- Modules/main.c | 79 ++-- Modules/xxmodule.c | 14 - Objects/obmalloc.c | 730 ++++++++++++++++++++++++++---------- Python/codecs.c | 50 +++ Tools/buildbot/clean.bat | 5 +- Tools/unicode/Makefile | 4 +- Tools/unicode/gencodec.py | 61 ++- 117 files changed, 3489 insertions(+), 789 deletions(-) create mode 100755 Lib/runpy.py delete mode 100644 Lib/test/test_hashlib_speed.py create mode 100644 Lib/test/test_runpy.py create mode 100644 Lib/test/time_hashlib.py diff --git a/Doc/lib/libcodecs.tex b/Doc/lib/libcodecs.tex index 9e92217..1806ef0 100644 --- a/Doc/lib/libcodecs.tex +++ b/Doc/lib/libcodecs.tex @@ -24,8 +24,19 @@ It defines the following functions: \begin{funcdesc}{register}{search_function} Register a codec search function. Search functions are expected to take one argument, the encoding name in all lower case letters, and -return a tuple of functions \code{(\var{encoder}, \var{decoder}, \var{stream_reader}, -\var{stream_writer})} taking the following arguments: +return a \class{CodecInfo} object having the following attributes: + +\begin{itemize} + \item \code{name} The name of the encoding; + \item \code{encoder} The stateless encoding function; + \item \code{decoder} The stateless decoding function; + \item \code{incrementalencoder} An incremental encoder class or factory function; + \item \code{incrementaldecoder} An incremental decoder class or factory function; + \item \code{streamwriter} A stream writer class or factory function; + \item \code{streamreader} A stream reader class or factory function. +\end{itemize} + +The various functions or classes take the following arguments: \var{encoder} and \var{decoder}: These must be functions or methods which have the same interface as the @@ -33,7 +44,17 @@ return a tuple of functions \code{(\var{encoder}, \var{decoder}, \var{stream_rea Codec Interface). The functions/methods are expected to work in a stateless mode. - \var{stream_reader} and \var{stream_writer}: These have to be + \var{incrementalencoder} and \var{incrementalencoder}: These have to be + factory functions providing the following interface: + + \code{factory(\var{errors}='strict')} + + The factory functions must return objects providing the interfaces + defined by the base classes \class{IncrementalEncoder} and + \class{IncrementalEncoder}, respectively. Incremental codecs can maintain + state. + + \var{streamreader} and \var{streamwriter}: These have to be factory functions providing the following interface: \code{factory(\var{stream}, \var{errors}='strict')} @@ -58,13 +79,13 @@ return \code{None}. \end{funcdesc} \begin{funcdesc}{lookup}{encoding} -Looks up a codec tuple in the Python codec registry and returns the -function tuple as defined above. +Looks up the codec info in the Python codec registry and returns a +\class{CodecInfo} object as defined above. Encodings are first looked up in the registry's cache. If not found, -the list of registered search functions is scanned. If no codecs tuple -is found, a \exception{LookupError} is raised. Otherwise, the codecs -tuple is stored in the cache and returned to the caller. +the list of registered search functions is scanned. If no \class{CodecInfo} +object is found, a \exception{LookupError} is raised. Otherwise, the +\class{CodecInfo} object is stored in the cache and returned to the caller. \end{funcdesc} To simplify access to the various codecs, the module provides these @@ -85,6 +106,22 @@ function. Raises a \exception{LookupError} in case the encoding cannot be found. \end{funcdesc} +\begin{funcdesc}{getincrementalencoder}{encoding} +Lookup up the codec for the given encoding and return its incremental encoder +class or factory function. + +Raises a \exception{LookupError} in case the encoding cannot be found or the +codec doesn't support an incremental encoder. +\end{funcdesc} + +\begin{funcdesc}{getincrementaldecoder}{encoding} +Lookup up the codec for the given encoding and return its incremental decoder +class or factory function. + +Raises a \exception{LookupError} in case the encoding cannot be found or the +codec doesn't support an incremental decoder. +\end{funcdesc} + \begin{funcdesc}{getreader}{encoding} Lookup up the codec for the given encoding and return its StreamReader class or factory function. @@ -188,6 +225,18 @@ If \var{output} is not given, it defaults to \var{input}. an encoding error occurs. \end{funcdesc} +\begin{funcdesc}{iterencode}{iterable, encoding\optional{, errors}} +Uses an incremental encoder to iteratively encode the input provided by +\var{iterable}. This function is a generator. \var{errors} (as well as +any other keyword argument) is passed through to the incremental encoder. +\end{funcdesc} + +\begin{funcdesc}{iterdecode}{iterable, encoding\optional{, errors}} +Uses an incremental decoder to iteratively decode the input provided by +\var{iterable}. This function is a generator. \var{errors} (as well as +any other keyword argument) is passed through to the incremental encoder. +\end{funcdesc} + The module also provides the following constants which are useful for reading and writing to platform dependent files: @@ -292,6 +341,109 @@ function interfaces of the stateless encoder and decoder: empty object of the output object type in this situation. \end{methoddesc} +The \class{IncrementalEncoder} and \class{IncrementalDecoder} classes provide +the basic interface for incremental encoding and decoding. Encoding/decoding the +input isn't done with one call to the stateless encoder/decoder function, +but with multiple calls to the \method{encode}/\method{decode} method of the +incremental encoder/decoder. The incremental encoder/decoder keeps track of +the encoding/decoding process during method calls. + +The joined output of calls to the \method{encode}/\method{decode} method is the +same as if the all single inputs where joined into one, and this input was +encoded/decoded with the stateless encoder/decoder. + + +\subsubsection{IncrementalEncoder Objects \label{incremental-encoder-objects}} + +The \class{IncrementalEncoder} class is used for encoding an input in multiple +steps. It defines the following methods which every incremental encoder must +define in order to be compatible to the Python codec registry. + +\begin{classdesc}{IncrementalEncoder}{\optional{errors}} + Constructor for a \class{IncrementalEncoder} instance. + + All incremental encoders must provide this constructor interface. They are + free to add additional keyword arguments, but only the ones defined + here are used by the Python codec registry. + + The \class{IncrementalEncoder} may implement different error handling + schemes by providing the \var{errors} keyword argument. These + parameters are predefined: + + \begin{itemize} + \item \code{'strict'} Raise \exception{ValueError} (or a subclass); + this is the default. + \item \code{'ignore'} Ignore the character and continue with the next. + \item \code{'replace'} Replace with a suitable replacement character + \item \code{'xmlcharrefreplace'} Replace with the appropriate XML + character reference + \item \code{'backslashreplace'} Replace with backslashed escape sequences. + \end{itemize} + + The \var{errors} argument will be assigned to an attribute of the + same name. Assigning to this attribute makes it possible to switch + between different error handling strategies during the lifetime + of the \class{IncrementalEncoder} object. + + The set of allowed values for the \var{errors} argument can + be extended with \function{register_error()}. +\end{classdesc} + +\begin{methoddesc}{encode}{object\optional{, final}} + Encodes \var{object} (taking the current state of the encoder into account) + and returns the resulting encoded object. If this is the last call to + \method{encode} \var{final} must be true (the default is false). +\end{methoddesc} + +\begin{methoddesc}{reset}{} + Reset the encoder to the initial state. +\end{methoddesc} + + +\subsubsection{IncrementalDecoder Objects \label{incremental-decoder-objects}} + +The \class{IncrementalDecoder} class is used for decoding an input in multiple +steps. It defines the following methods which every incremental decoder must +define in order to be compatible to the Python codec registry. + +\begin{classdesc}{IncrementalDecoder}{\optional{errors}} + Constructor for a \class{IncrementalDecoder} instance. + + All incremental decoders must provide this constructor interface. They are + free to add additional keyword arguments, but only the ones defined + here are used by the Python codec registry. + + The \class{IncrementalDecoder} may implement different error handling + schemes by providing the \var{errors} keyword argument. These + parameters are predefined: + + \begin{itemize} + \item \code{'strict'} Raise \exception{ValueError} (or a subclass); + this is the default. + \item \code{'ignore'} Ignore the character and continue with the next. + \item \code{'replace'} Replace with a suitable replacement character. + \end{itemize} + + The \var{errors} argument will be assigned to an attribute of the + same name. Assigning to this attribute makes it possible to switch + between different error handling strategies during the lifetime + of the \class{IncrementalEncoder} object. + + The set of allowed values for the \var{errors} argument can + be extended with \function{register_error()}. +\end{classdesc} + +\begin{methoddesc}{decode}{object\optional{, final}} + Decodes \var{object} (taking the current state of the decoder into account) + and returns the resulting decoded object. If this is the last call to + \method{decode} \var{final} must be true (the default is false). +\end{methoddesc} + +\begin{methoddesc}{reset}{} + Reset the decoder to the initial state. +\end{methoddesc} + + The \class{StreamWriter} and \class{StreamReader} classes provide generic working interfaces which can be used to implement new encodings submodules very easily. See \module{encodings.utf_8} for an diff --git a/Doc/whatsnew/whatsnew25.tex b/Doc/whatsnew/whatsnew25.tex index ff64af0..5743285 100644 --- a/Doc/whatsnew/whatsnew25.tex +++ b/Doc/whatsnew/whatsnew25.tex @@ -210,6 +210,12 @@ implemented by Richard Jones and Fred Drake.} %====================================================================== +\section{PEP 338: Executing Modules as Scripts} + +% XXX write this + + +%====================================================================== \section{PEP 341: Unified try/except/finally} % XXX write this diff --git a/Include/codecs.h b/Include/codecs.h index 82f18cd..0d76241 100644 --- a/Include/codecs.h +++ b/Include/codecs.h @@ -29,15 +29,15 @@ PyAPI_FUNC(int) PyCodec_Register( /* Codec register lookup API. - Looks up the given encoding and returns a tuple (encoder, decoder, - stream reader, stream writer) of functions which implement the - different aspects of processing the encoding. + Looks up the given encoding and returns a CodecInfo object with + function attributes which implement the different aspects of + processing the encoding. The encoding string is looked up converted to all lower-case characters. This makes encodings looked up through this mechanism effectively case-insensitive. - If no codec is found, a KeyError is set and NULL returned. + If no codec is found, a KeyError is set and NULL returned. As side effect, this tries to load the encodings package, if not yet done. This is part of the lazy load strategy for the encodings @@ -101,6 +101,20 @@ PyAPI_FUNC(PyObject *) PyCodec_Decoder( const char *encoding ); +/* Get a IncrementalEncoder object for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_IncrementalEncoder( + const char *encoding, + const char *errors + ); + +/* Get a IncrementalDecoder object function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_IncrementalDecoder( + const char *encoding, + const char *errors + ); + /* Get a StreamReader factory function for the given encoding. */ PyAPI_FUNC(PyObject *) PyCodec_StreamReader( diff --git a/Lib/StringIO.py b/Lib/StringIO.py index 5c463fb..1e5f254 100644 --- a/Lib/StringIO.py +++ b/Lib/StringIO.py @@ -72,8 +72,7 @@ class StringIO: method is called repeatedly. This method returns the next input line, or raises StopIteration when EOF is hit. """ - if self.closed: - raise StopIteration + _complain_ifclosed(self.closed) r = self.readline() if not r: raise StopIteration diff --git a/Lib/codecs.py b/Lib/codecs.py index 6895a22..28856c7 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -73,6 +73,23 @@ BOM64_BE = BOM_UTF32_BE ### Codec base classes (defining the API) +class CodecInfo(tuple): + + def __new__(cls, encode, decode, streamreader=None, streamwriter=None, + incrementalencoder=None, incrementaldecoder=None, name=None): + self = tuple.__new__(cls, (encode, decode, streamreader, streamwriter)) + self.name = name + self.encode = encode + self.decode = decode + self.incrementalencoder = incrementalencoder + self.incrementaldecoder = incrementaldecoder + self.streamwriter = streamwriter + self.streamreader = streamreader + return self + + def __repr__(self): + return "<%s.%s object for encoding %s at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, id(self)) + class Codec: """ Defines the interface for stateless encoders/decoders. @@ -137,6 +154,88 @@ class Codec: """ raise NotImplementedError +class IncrementalEncoder(object): + """ + A IncrementalEncoder encodes an input in multiple steps. The input can be + passed piece by piece to the encode() method. The IncrementalEncoder remembers + the state of the Encoding process between calls to encode(). + """ + def __init__(self, errors='strict'): + """ + Creates a IncrementalEncoder instance. + + The IncrementalEncoder may use different error handling schemes by + providing the errors keyword argument. See the module docstring + for a list of possible values. + """ + self.errors = errors + self.buffer = "" + + def encode(self, input, final=False): + """ + Encodes input and returns the resulting object. + """ + raise NotImplementedError + + def reset(self): + """ + Resets the encoder to the initial state. + """ + +class IncrementalDecoder(object): + """ + An IncrementalDecoder decodes an input in multiple steps. The input can be + passed piece by piece to the decode() method. The IncrementalDecoder + remembers the state of the decoding process between calls to decode(). + """ + def __init__(self, errors='strict'): + """ + Creates a IncrementalDecoder instance. + + The IncrementalDecoder may use different error handling schemes by + providing the errors keyword argument. See the module docstring + for a list of possible values. + """ + self.errors = errors + + def decode(self, input, final=False): + """ + Decodes input and returns the resulting object. + """ + raise NotImplementedError + + def reset(self): + """ + Resets the decoder to the initial state. + """ + +class BufferedIncrementalDecoder(IncrementalDecoder): + """ + This subclass of IncrementalDecoder can be used as the baseclass for an + incremental decoder if the decoder must be able to handle incomplete byte + sequences. + """ + def __init__(self, errors='strict'): + IncrementalDecoder.__init__(self, errors) + self.buffer = "" # undecoded input that is kept between calls to decode() + + def _buffer_decode(self, input, errors, final): + # Overwrite this method in subclasses: It must decode input + # and return an (output, length consumed) tuple + raise NotImplementedError + + def decode(self, input, final=False): + # decode input (taking the buffer into account) + data = self.buffer + input + (result, consumed) = self._buffer_decode(data, self.errors, final) + # keep undecoded input until the next call + self.buffer = data[consumed:] + return result + + def reset(self): + IncrementalDecoder.reset(self) + self.bytebuffer = "" + # # The StreamWriter and StreamReader class provide generic working # interfaces which can be used to implement new encoding submodules @@ -666,8 +765,8 @@ def open(filename, mode='rb', encoding=None, errors='strict', buffering=1): file = __builtin__.open(filename, mode, buffering) if encoding is None: return file - (e, d, sr, sw) = lookup(encoding) - srw = StreamReaderWriter(file, sr, sw, errors) + info = lookup(encoding) + srw = StreamReaderWriter(file, info.streamreader, info.streamwriter, errors) # Add attributes to simplify introspection srw.encoding = encoding return srw @@ -699,11 +798,9 @@ def EncodedFile(file, data_encoding, file_encoding=None, errors='strict'): """ if file_encoding is None: file_encoding = data_encoding - encode, decode = lookup(data_encoding)[:2] - Reader, Writer = lookup(file_encoding)[2:] - sr = StreamRecoder(file, - encode, decode, Reader, Writer, - errors) + info = lookup(data_encoding) + sr = StreamRecoder(file, info.encode, info.decode, + info.streamreader, info.streamwriter, errors) # Add attributes to simplify introspection sr.data_encoding = data_encoding sr.file_encoding = file_encoding @@ -719,7 +816,7 @@ def getencoder(encoding): Raises a LookupError in case the encoding cannot be found. """ - return lookup(encoding)[0] + return lookup(encoding).encode def getdecoder(encoding): @@ -729,7 +826,35 @@ def getdecoder(encoding): Raises a LookupError in case the encoding cannot be found. """ - return lookup(encoding)[1] + return lookup(encoding).decode + +def getincrementalencoder(encoding): + + """ Lookup up the codec for the given encoding and return + its IncrementalEncoder class or factory function. + + Raises a LookupError in case the encoding cannot be found + or the codecs doesn't provide an incremental encoder. + + """ + encoder = lookup(encoding).incrementalencoder + if encoder is None: + raise LookupError(encoding) + return encoder + +def getincrementaldecoder(encoding): + + """ Lookup up the codec for the given encoding and return + its IncrementalDecoder class or factory function. + + Raises a LookupError in case the encoding cannot be found + or the codecs doesn't provide an incremental decoder. + + """ + decoder = lookup(encoding).incrementaldecoder + if decoder is None: + raise LookupError(encoding) + return decoder def getreader(encoding): @@ -739,7 +864,7 @@ def getreader(encoding): Raises a LookupError in case the encoding cannot be found. """ - return lookup(encoding)[2] + return lookup(encoding).streamreader def getwriter(encoding): @@ -749,7 +874,43 @@ def getwriter(encoding): Raises a LookupError in case the encoding cannot be found. """ - return lookup(encoding)[3] + return lookup(encoding).streamwriter + +def iterencode(iterator, encoding, errors='strict', **kwargs): + """ + Encoding iterator. + + Encodes the input strings from the iterator using a IncrementalEncoder. + + errors and kwargs are passed through to the IncrementalEncoder + constructor. + """ + encoder = getincrementalencoder(encoding)(errors, **kwargs) + for input in iterator: + output = encoder.encode(input) + if output: + yield output + output = encoder.encode("", True) + if output: + yield output + +def iterdecode(iterator, encoding, errors='strict', **kwargs): + """ + Decoding iterator. + + Decodes the input strings from the iterator using a IncrementalDecoder. + + errors and kwargs are passed through to the IncrementalDecoder + constructor. + """ + decoder = getincrementaldecoder(encoding)(errors, **kwargs) + for input in iterator: + output = decoder.decode(input) + if output: + yield output + output = decoder.decode("", True) + if output: + yield output ### Helpers for charmap-based codecs diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index ddaacb9..1f469bf 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -9,9 +9,10 @@ Each codec module must export the following interface: - * getregentry() -> (encoder, decoder, stream_reader, stream_writer) - The getregentry() API must return callable objects which adhere to - the Python Codec Interface Standard. + * getregentry() -> codecs.CodecInfo object + The getregentry() API must a CodecInfo object with encoder, decoder, + incrementalencoder, incrementaldecoder, streamwriter and streamreader + atttributes which adhere to the Python Codec Interface Standard. In addition, a module may optionally also define the following APIs which are then used by the package's codec search function: @@ -113,16 +114,24 @@ def search_function(encoding): return None # Now ask the module for the registry entry - entry = tuple(getregentry()) - if len(entry) != 4: - raise CodecRegistryError,\ - 'module "%s" (%s) failed to register' % \ - (mod.__name__, mod.__file__) - for obj in entry: - if not callable(obj): + entry = getregentry() + if not isinstance(entry, codecs.CodecInfo): + if not 4 <= len(entry) <= 7: raise CodecRegistryError,\ - 'incompatible codecs in module "%s" (%s)' % \ + 'module "%s" (%s) failed to register' % \ (mod.__name__, mod.__file__) + if not callable(entry[0]) or \ + not callable(entry[1]) or \ + (entry[2] is not None and not callable(entry[2])) or \ + (entry[3] is not None and not callable(entry[3])) or \ + (len(entry) > 4 and entry[4] is not None and not callable(entry[4])) or \ + (len(entry) > 5 and entry[5] is not None and not callable(entry[5])): + raise CodecRegistryError,\ + 'incompatible codecs in module "%s" (%s)' % \ + (mod.__name__, mod.__file__) + if len(entry)<7 or entry[6] is None: + entry += (None,)*(6-len(entry)) + (mod.__name__.split(".", 1)[1],) + entry = codecs.CodecInfo(*entry) # Cache the codec registry entry _cache[encoding] = entry diff --git a/Lib/encodings/ascii.py b/Lib/encodings/ascii.py index 05fc36a..2033cde 100644 --- a/Lib/encodings/ascii.py +++ b/Lib/encodings/ascii.py @@ -17,6 +17,14 @@ class Codec(codecs.Codec): encode = codecs.ascii_encode decode = codecs.ascii_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.ascii_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.ascii_decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -31,5 +39,12 @@ class StreamConverter(StreamWriter,StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='ascii', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/base64_codec.py b/Lib/encodings/base64_codec.py index 085ab14..f84e780 100644 --- a/Lib/encodings/base64_codec.py +++ b/Lib/encodings/base64_codec.py @@ -49,6 +49,16 @@ class Codec(codecs.Codec): def decode(self, input,errors='strict'): return base64_decode(input,errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + assert self.errors == 'strict' + return base64.encodestring(input) + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + assert self.errors == 'strict' + return base64.decodestring(input) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -58,5 +68,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (base64_encode,base64_decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='base64', + encode=base64_encode, + decode=base64_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/bz2_codec.py b/Lib/encodings/bz2_codec.py index 870474c..81e84b6 100644 --- a/Lib/encodings/bz2_codec.py +++ b/Lib/encodings/bz2_codec.py @@ -51,6 +51,16 @@ class Codec(codecs.Codec): def decode(self, input, errors='strict'): return bz2_decode(input, errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + assert self.errors == 'strict' + return bz2.compress(input) + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + assert self.errors == 'strict' + return bz2.decompress(input) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -60,5 +70,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (bz2_encode,bz2_decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name="bz2", + encode=bz2_encode, + decode=bz2_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/charmap.py b/Lib/encodings/charmap.py index 9bd93ec..81189b1 100644 --- a/Lib/encodings/charmap.py +++ b/Lib/encodings/charmap.py @@ -21,30 +21,49 @@ class Codec(codecs.Codec): encode = codecs.charmap_encode decode = codecs.charmap_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def __init__(self, errors='strict', mapping=None): + codecs.IncrementalEncoder.__init__(self, errors) + self.mapping = mapping + + def encode(self, input, final=False): + return codecs.charmap_encode(input, self.errors, self.mapping)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def __init__(self, errors='strict', mapping=None): + codecs.IncrementalDecoder.__init__(self, errors) + self.mapping = mapping + + def decode(self, input, final=False): + return codecs.charmap_decode(input, self.errors, self.mapping)[0] + class StreamWriter(Codec,codecs.StreamWriter): def __init__(self,stream,errors='strict',mapping=None): - codecs.StreamWriter.__init__(self,stream,errors) self.mapping = mapping def encode(self,input,errors='strict'): - return Codec.encode(input,errors,self.mapping) class StreamReader(Codec,codecs.StreamReader): def __init__(self,stream,errors='strict',mapping=None): - codecs.StreamReader.__init__(self,stream,errors) self.mapping = mapping def decode(self,input,errors='strict'): - return Codec.decode(input,errors,self.mapping) ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='charmap', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/cp037.py b/Lib/encodings/cp037.py index 5864c4b..7e589a9 100644 --- a/Lib/encodings/cp037.py +++ b/Lib/encodings/cp037.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP037.TXT' with gencodec.py. +""" Python Character Mapping Codec cp037 generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP037.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp037', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1006.py b/Lib/encodings/cp1006.py index d94563d..7829969 100644 --- a/Lib/encodings/cp1006.py +++ b/Lib/encodings/cp1006.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MISC/CP1006.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1006 generated from 'MAPPINGS/VENDORS/MISC/CP1006.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1006', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1026.py b/Lib/encodings/cp1026.py index 648bed0..01c8804 100644 --- a/Lib/encodings/cp1026.py +++ b/Lib/encodings/cp1026.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP1026.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1026 generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP1026.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1026', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1140.py b/Lib/encodings/cp1140.py index 6507552..ac8d41b 100644 --- a/Lib/encodings/cp1140.py +++ b/Lib/encodings/cp1140.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'python-mappings/CP1140.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1140 generated from 'python-mappings/CP1140.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1140', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1250.py b/Lib/encodings/cp1250.py index 73427bc..6e6f57c 100644 --- a/Lib/encodings/cp1250.py +++ b/Lib/encodings/cp1250.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1250 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1250', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1251.py b/Lib/encodings/cp1251.py index f3e107ef..ed835fe 100644 --- a/Lib/encodings/cp1251.py +++ b/Lib/encodings/cp1251.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1251 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1251', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1252.py b/Lib/encodings/cp1252.py index ea7561d..e5b6905 100644 --- a/Lib/encodings/cp1252.py +++ b/Lib/encodings/cp1252.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1252 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1252', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1253.py b/Lib/encodings/cp1253.py index 5feefb3..3ce70b25 100644 --- a/Lib/encodings/cp1253.py +++ b/Lib/encodings/cp1253.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1253.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1253 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1253.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1253', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1254.py b/Lib/encodings/cp1254.py index 6769e1b..31cd48c 100644 --- a/Lib/encodings/cp1254.py +++ b/Lib/encodings/cp1254.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1254.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1254 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1254.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1254', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1255.py b/Lib/encodings/cp1255.py index b994f9d..47c43ce 100644 --- a/Lib/encodings/cp1255.py +++ b/Lib/encodings/cp1255.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1255.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1255 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1255.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1255', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1256.py b/Lib/encodings/cp1256.py index 5da425e..e90393b 100644 --- a/Lib/encodings/cp1256.py +++ b/Lib/encodings/cp1256.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1256.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1256 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1256.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1256', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1257.py b/Lib/encodings/cp1257.py index 237a531..dcc81c0 100644 --- a/Lib/encodings/cp1257.py +++ b/Lib/encodings/cp1257.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1257.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1257 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1257.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1257', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp1258.py b/Lib/encodings/cp1258.py index 5de3fd8..d4d2271 100644 --- a/Lib/encodings/cp1258.py +++ b/Lib/encodings/cp1258.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1258.TXT' with gencodec.py. +""" Python Character Mapping Codec cp1258 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1258.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp1258', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp424.py b/Lib/encodings/cp424.py index d19c756..966aecb 100644 --- a/Lib/encodings/cp424.py +++ b/Lib/encodings/cp424.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MISC/CP424.TXT' with gencodec.py. +""" Python Character Mapping Codec cp424 generated from 'MAPPINGS/VENDORS/MISC/CP424.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp424', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp437.py b/Lib/encodings/cp437.py index e9cefaf..52cd882 100644 --- a/Lib/encodings/cp437.py +++ b/Lib/encodings/cp437.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'VENDORS/MICSFT/PC/CP437.TXT' with gencodec.py. +""" Python Character Mapping Codec cp437 generated from 'VENDORS/MICSFT/PC/CP437.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp437', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp500.py b/Lib/encodings/cp500.py index 3bbefa0..83af090 100644 --- a/Lib/encodings/cp500.py +++ b/Lib/encodings/cp500.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP500.TXT' with gencodec.py. +""" Python Character Mapping Codec cp500 generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP500.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp500', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp737.py b/Lib/encodings/cp737.py index 883a8ba..d654448 100644 --- a/Lib/encodings/cp737.py +++ b/Lib/encodings/cp737.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'VENDORS/MICSFT/PC/CP737.TXT' with gencodec.py. +""" Python Character Mapping Codec cp737 generated from 'VENDORS/MICSFT/PC/CP737.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp737', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp775.py b/Lib/encodings/cp775.py index 1b39188..6a456a5 100644 --- a/Lib/encodings/cp775.py +++ b/Lib/encodings/cp775.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'VENDORS/MICSFT/PC/CP775.TXT' with gencodec.py. +""" Python Character Mapping Codec cp775 generated from 'VENDORS/MICSFT/PC/CP775.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,9 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) - + return codecs.CodecInfo( + name='cp775', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map decoding_map = codecs.make_identity_dict(range(256)) diff --git a/Lib/encodings/cp850.py b/Lib/encodings/cp850.py index 6481ee0..0c8478c 100644 --- a/Lib/encodings/cp850.py +++ b/Lib/encodings/cp850.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp850', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp852.py b/Lib/encodings/cp852.py index 749b0da..069d547 100644 --- a/Lib/encodings/cp852.py +++ b/Lib/encodings/cp852.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp852', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp855.py b/Lib/encodings/cp855.py index 13146c9..241ef9d 100644 --- a/Lib/encodings/cp855.py +++ b/Lib/encodings/cp855.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp855', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp856.py b/Lib/encodings/cp856.py index 5823dff..c72fcad 100644 --- a/Lib/encodings/cp856.py +++ b/Lib/encodings/cp856.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MISC/CP856.TXT' with gencodec.py. +""" Python Character Mapping Codec cp856 generated from 'MAPPINGS/VENDORS/MISC/CP856.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp856', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp857.py b/Lib/encodings/cp857.py index 6c5cdbb..c24191b 100644 --- a/Lib/encodings/cp857.py +++ b/Lib/encodings/cp857.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp857', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp860.py b/Lib/encodings/cp860.py index cd7cdf4..4acb0cf 100644 --- a/Lib/encodings/cp860.py +++ b/Lib/encodings/cp860.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp860', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp861.py b/Lib/encodings/cp861.py index b140f2e..0939b5b 100644 --- a/Lib/encodings/cp861.py +++ b/Lib/encodings/cp861.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp861', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp862.py b/Lib/encodings/cp862.py index badec08..ea0405c 100644 --- a/Lib/encodings/cp862.py +++ b/Lib/encodings/cp862.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp862', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp863.py b/Lib/encodings/cp863.py index 812bc3d..62dfabf 100644 --- a/Lib/encodings/cp863.py +++ b/Lib/encodings/cp863.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp863', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp864.py b/Lib/encodings/cp864.py index 41f2ea3..02a0e73 100644 --- a/Lib/encodings/cp864.py +++ b/Lib/encodings/cp864.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp864', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp865.py b/Lib/encodings/cp865.py index 8e68c6b..e9f45f1 100644 --- a/Lib/encodings/cp865.py +++ b/Lib/encodings/cp865.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp865', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp866.py b/Lib/encodings/cp866.py index 4c0dd8c..29cd85a 100644 --- a/Lib/encodings/cp866.py +++ b/Lib/encodings/cp866.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp866', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp869.py b/Lib/encodings/cp869.py index 1747546..b4dc99b 100644 --- a/Lib/encodings/cp869.py +++ b/Lib/encodings/cp869.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp869', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/cp874.py b/Lib/encodings/cp874.py index 5e1fea1..591e8aa 100644 --- a/Lib/encodings/cp874.py +++ b/Lib/encodings/cp874.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP874.TXT' with gencodec.py. +""" Python Character Mapping Codec cp874 generated from 'MAPPINGS/VENDORS/MICSFT/WINDOWS/CP874.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp874', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/cp875.py b/Lib/encodings/cp875.py index ccebb84..879d5a4 100644 --- a/Lib/encodings/cp875.py +++ b/Lib/encodings/cp875.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP875.TXT' with gencodec.py. +""" Python Character Mapping Codec cp875 generated from 'MAPPINGS/VENDORS/MICSFT/EBCDIC/CP875.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='cp875', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/hex_codec.py b/Lib/encodings/hex_codec.py index 5c6e4a4..91b38d9 100644 --- a/Lib/encodings/hex_codec.py +++ b/Lib/encodings/hex_codec.py @@ -49,6 +49,16 @@ class Codec(codecs.Codec): def decode(self, input,errors='strict'): return hex_decode(input,errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + assert self.errors == 'strict' + return binascii.b2a_hex(input) + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + assert self.errors == 'strict' + return binascii.a2b_hex(input) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -58,5 +68,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (hex_encode,hex_decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='hex', + encode=hex_encode, + decode=hex_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/hp_roman8.py b/Lib/encodings/hp_roman8.py index 4f201b8..dbaaa72 100644 --- a/Lib/encodings/hp_roman8.py +++ b/Lib/encodings/hp_roman8.py @@ -14,13 +14,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_map) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_map)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -30,8 +36,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='hp-roman8', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) ### Decoding Map diff --git a/Lib/encodings/idna.py b/Lib/encodings/idna.py index 3d3ed23..8bdae32 100644 --- a/Lib/encodings/idna.py +++ b/Lib/encodings/idna.py @@ -194,6 +194,14 @@ class Codec(codecs.Codec): return u".".join(result)+trailing_dot, len(input) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return Codec().encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return Codec().decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -203,5 +211,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='idna', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/iso8859_1.py b/Lib/encodings/iso8859_1.py index 6aaea7c..b985585 100644 --- a/Lib/encodings/iso8859_1.py +++ b/Lib/encodings/iso8859_1.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-1.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_1 generated from 'MAPPINGS/ISO8859/8859-1.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-1', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_10.py b/Lib/encodings/iso8859_10.py index 26b55c9..8588430 100644 --- a/Lib/encodings/iso8859_10.py +++ b/Lib/encodings/iso8859_10.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-10.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_10 generated from 'MAPPINGS/ISO8859/8859-10.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-10', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_11.py b/Lib/encodings/iso8859_11.py index ef17167..fffe692 100644 --- a/Lib/encodings/iso8859_11.py +++ b/Lib/encodings/iso8859_11.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-11.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_11 generated from 'MAPPINGS/ISO8859/8859-11.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-11', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_13.py b/Lib/encodings/iso8859_13.py index 0e10c7d..a890580 100644 --- a/Lib/encodings/iso8859_13.py +++ b/Lib/encodings/iso8859_13.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-13.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_13 generated from 'MAPPINGS/ISO8859/8859-13.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-13', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_14.py b/Lib/encodings/iso8859_14.py index b981031..afa458c 100644 --- a/Lib/encodings/iso8859_14.py +++ b/Lib/encodings/iso8859_14.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-14.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_14 generated from 'MAPPINGS/ISO8859/8859-14.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-14', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_15.py b/Lib/encodings/iso8859_15.py index f50a904..4a8334e 100644 --- a/Lib/encodings/iso8859_15.py +++ b/Lib/encodings/iso8859_15.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-15.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_15 generated from 'MAPPINGS/ISO8859/8859-15.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-15', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_16.py b/Lib/encodings/iso8859_16.py index ce8bc5b..aeebfb6 100644 --- a/Lib/encodings/iso8859_16.py +++ b/Lib/encodings/iso8859_16.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-16.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_16 generated from 'MAPPINGS/ISO8859/8859-16.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-16', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_2.py b/Lib/encodings/iso8859_2.py index 417b451..845f322 100644 --- a/Lib/encodings/iso8859_2.py +++ b/Lib/encodings/iso8859_2.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-2.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_2 generated from 'MAPPINGS/ISO8859/8859-2.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-2', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_3.py b/Lib/encodings/iso8859_3.py index c3ad3a9..fbc8775 100644 --- a/Lib/encodings/iso8859_3.py +++ b/Lib/encodings/iso8859_3.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-3.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_3 generated from 'MAPPINGS/ISO8859/8859-3.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-3', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_4.py b/Lib/encodings/iso8859_4.py index b65685a..e705954 100644 --- a/Lib/encodings/iso8859_4.py +++ b/Lib/encodings/iso8859_4.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-4.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_4 generated from 'MAPPINGS/ISO8859/8859-4.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-4', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_5.py b/Lib/encodings/iso8859_5.py index 59fe372..93a4e90 100644 --- a/Lib/encodings/iso8859_5.py +++ b/Lib/encodings/iso8859_5.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-5.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_5 generated from 'MAPPINGS/ISO8859/8859-5.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-5', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_6.py b/Lib/encodings/iso8859_6.py index b07661d9..f911cc4 100644 --- a/Lib/encodings/iso8859_6.py +++ b/Lib/encodings/iso8859_6.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-6.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_6 generated from 'MAPPINGS/ISO8859/8859-6.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-6', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_7.py b/Lib/encodings/iso8859_7.py index 54d0997..4cce6e2 100644 --- a/Lib/encodings/iso8859_7.py +++ b/Lib/encodings/iso8859_7.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-7.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_7 generated from 'MAPPINGS/ISO8859/8859-7.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-7', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_8.py b/Lib/encodings/iso8859_8.py index 24ba1bb..8c29a87 100644 --- a/Lib/encodings/iso8859_8.py +++ b/Lib/encodings/iso8859_8.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-8.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_8 generated from 'MAPPINGS/ISO8859/8859-8.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-8', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/iso8859_9.py b/Lib/encodings/iso8859_9.py index 940d92b..9648e9f 100644 --- a/Lib/encodings/iso8859_9.py +++ b/Lib/encodings/iso8859_9.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/ISO8859/8859-9.TXT' with gencodec.py. +""" Python Character Mapping Codec iso8859_9 generated from 'MAPPINGS/ISO8859/8859-9.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-9', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/koi8_r.py b/Lib/encodings/koi8_r.py index f2723de..3efeb56 100644 --- a/Lib/encodings/koi8_r.py +++ b/Lib/encodings/koi8_r.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/MISC/KOI8-R.TXT' with gencodec.py. +""" Python Character Mapping Codec koi8_r generated from 'MAPPINGS/VENDORS/MISC/KOI8-R.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='koi8-r', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/koi8_u.py b/Lib/encodings/koi8_u.py index 9bbcf7c..5f46db1 100644 --- a/Lib/encodings/koi8_u.py +++ b/Lib/encodings/koi8_u.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'python-mappings/KOI8-U.TXT' with gencodec.py. +""" Python Character Mapping Codec koi8_u generated from 'python-mappings/KOI8-U.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='koi8-u', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/latin_1.py b/Lib/encodings/latin_1.py index 0e55917..370160c 100644 --- a/Lib/encodings/latin_1.py +++ b/Lib/encodings/latin_1.py @@ -17,6 +17,14 @@ class Codec(codecs.Codec): encode = codecs.latin_1_encode decode = codecs.latin_1_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.latin_1_encode(input,self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.latin_1_decode(input,self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -31,5 +39,12 @@ class StreamConverter(StreamWriter,StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='iso8859-1', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/mac_arabic.py b/Lib/encodings/mac_arabic.py index 6c096b4..7a7d3c5 100644 --- a/Lib/encodings/mac_arabic.py +++ b/Lib/encodings/mac_arabic.py @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-arabic', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/mac_centeuro.py b/Lib/encodings/mac_centeuro.py index d7d67ec..54a1510 100644 --- a/Lib/encodings/mac_centeuro.py +++ b/Lib/encodings/mac_centeuro.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/CENTEURO.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_centeuro generated from 'MAPPINGS/VENDORS/APPLE/CENTEURO.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-centeuro', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_croatian.py b/Lib/encodings/mac_croatian.py index 96e1187..9e93cdd 100644 --- a/Lib/encodings/mac_croatian.py +++ b/Lib/encodings/mac_croatian.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/CROATIAN.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_croatian generated from 'MAPPINGS/VENDORS/APPLE/CROATIAN.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-croatian', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_cyrillic.py b/Lib/encodings/mac_cyrillic.py index f1b1fb0..8ffd715 100644 --- a/Lib/encodings/mac_cyrillic.py +++ b/Lib/encodings/mac_cyrillic.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/CYRILLIC.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_cyrillic generated from 'MAPPINGS/VENDORS/APPLE/CYRILLIC.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-cyrillic', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_farsi.py b/Lib/encodings/mac_farsi.py index 2b61e27..6d26a42 100644 --- a/Lib/encodings/mac_farsi.py +++ b/Lib/encodings/mac_farsi.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/FARSI.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_farsi generated from 'MAPPINGS/VENDORS/APPLE/FARSI.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-farsi', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_greek.py b/Lib/encodings/mac_greek.py index c2dd26f..7264f9a 100644 --- a/Lib/encodings/mac_greek.py +++ b/Lib/encodings/mac_greek.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/GREEK.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_greek generated from 'MAPPINGS/VENDORS/APPLE/GREEK.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-greek', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_iceland.py b/Lib/encodings/mac_iceland.py index d305d29..5d8d9ad 100644 --- a/Lib/encodings/mac_iceland.py +++ b/Lib/encodings/mac_iceland.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/ICELAND.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_iceland generated from 'MAPPINGS/VENDORS/APPLE/ICELAND.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-iceland', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_latin2.py b/Lib/encodings/mac_latin2.py index f5d5225..e322be2 100644 --- a/Lib/encodings/mac_latin2.py +++ b/Lib/encodings/mac_latin2.py @@ -14,13 +14,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_map) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_map)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -30,8 +36,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-latin2', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/mac_roman.py b/Lib/encodings/mac_roman.py index d23aeba..9552e53 100644 --- a/Lib/encodings/mac_roman.py +++ b/Lib/encodings/mac_roman.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/ROMAN.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_roman generated from 'MAPPINGS/VENDORS/APPLE/ROMAN.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-roman', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_romanian.py b/Lib/encodings/mac_romanian.py index 5c35a64..51282c3 100644 --- a/Lib/encodings/mac_romanian.py +++ b/Lib/encodings/mac_romanian.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/ROMANIAN.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_romanian generated from 'MAPPINGS/VENDORS/APPLE/ROMANIAN.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-romanian', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mac_turkish.py b/Lib/encodings/mac_turkish.py index 79894ba..4e5641f 100644 --- a/Lib/encodings/mac_turkish.py +++ b/Lib/encodings/mac_turkish.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'MAPPINGS/VENDORS/APPLE/TURKISH.TXT' with gencodec.py. +""" Python Character Mapping Codec mac_turkish generated from 'MAPPINGS/VENDORS/APPLE/TURKISH.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mac-turkish', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/mbcs.py b/Lib/encodings/mbcs.py index c79f47c..ff77fde 100644 --- a/Lib/encodings/mbcs.py +++ b/Lib/encodings/mbcs.py @@ -18,6 +18,13 @@ class Codec(codecs.Codec): encode = codecs.mbcs_encode decode = codecs.mbcs_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.mbcs_encode(input,self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.mbcs_decode(input,self.errors)[0] class StreamWriter(Codec,codecs.StreamWriter): pass @@ -32,5 +39,12 @@ class StreamConverter(StreamWriter,StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='mbcs', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/palmos.py b/Lib/encodings/palmos.py index c0f0606..4b77e2b 100644 --- a/Lib/encodings/palmos.py +++ b/Lib/encodings/palmos.py @@ -15,6 +15,14 @@ class Codec(codecs.Codec): def decode(self,input,errors='strict'): return codecs.charmap_decode(input,errors,decoding_map) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_map)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -24,7 +32,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='palmos', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/ptcp154.py b/Lib/encodings/ptcp154.py index 5cdd98c..aef8975 100644 --- a/Lib/encodings/ptcp154.py +++ b/Lib/encodings/ptcp154.py @@ -14,13 +14,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_map) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_map)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -30,8 +36,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='ptcp154', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Map diff --git a/Lib/encodings/punycode.py b/Lib/encodings/punycode.py index 82fd458..2cde8b9 100644 --- a/Lib/encodings/punycode.py +++ b/Lib/encodings/punycode.py @@ -197,18 +197,27 @@ def punycode_decode(text, errors): ### Codec APIs class Codec(codecs.Codec): - def encode(self,input,errors='strict'): + def encode(self,input,errors='strict'): res = punycode_encode(input) return res, len(input) def decode(self,input,errors='strict'): - if errors not in ('strict', 'replace', 'ignore'): raise UnicodeError, "Unsupported error handling "+errors res = punycode_decode(input, errors) return res, len(input) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return punycode_encode(input) + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + if errors not in ('strict', 'replace', 'ignore'): + raise UnicodeError, "Unsupported error handling "+errors + return punycode_decode(input, errors) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -218,5 +227,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='punycode', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/quopri_codec.py b/Lib/encodings/quopri_codec.py index d98b5ed..b802ae6 100644 --- a/Lib/encodings/quopri_codec.py +++ b/Lib/encodings/quopri_codec.py @@ -46,6 +46,14 @@ class Codec(codecs.Codec): def decode(self, input,errors='strict'): return quopri_decode(input,errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return quopri_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return quopri_decode(input, self.errors)[0] + class StreamWriter(Codec, codecs.StreamWriter): pass @@ -55,4 +63,12 @@ class StreamReader(Codec,codecs.StreamReader): # encodings module API def getregentry(): - return (quopri_encode, quopri_decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='quopri', + encode=quopri_encode, + decode=quopri_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/raw_unicode_escape.py b/Lib/encodings/raw_unicode_escape.py index a2f3fff..2b919b4 100644 --- a/Lib/encodings/raw_unicode_escape.py +++ b/Lib/encodings/raw_unicode_escape.py @@ -17,6 +17,14 @@ class Codec(codecs.Codec): encode = codecs.raw_unicode_escape_encode decode = codecs.raw_unicode_escape_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.raw_unicode_escape_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.raw_unicode_escape_decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -26,5 +34,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='raw-unicode-escape', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/rot_13.py b/Lib/encodings/rot_13.py index c628181..52b6431 100644 --- a/Lib/encodings/rot_13.py +++ b/Lib/encodings/rot_13.py @@ -14,13 +14,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_map) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_map)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -30,8 +36,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='rot-13', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) ### Decoding Map diff --git a/Lib/encodings/string_escape.py b/Lib/encodings/string_escape.py index c02bfee..e329a26 100644 --- a/Lib/encodings/string_escape.py +++ b/Lib/encodings/string_escape.py @@ -12,6 +12,14 @@ class Codec(codecs.Codec): encode = codecs.escape_encode decode = codecs.escape_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.escape_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.escape_decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -19,5 +27,12 @@ class StreamReader(Codec,codecs.StreamReader): pass def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='string-escape', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/tis_620.py b/Lib/encodings/tis_620.py index aa1823e..166d932 100644 --- a/Lib/encodings/tis_620.py +++ b/Lib/encodings/tis_620.py @@ -1,4 +1,4 @@ -""" Python Character Mapping Codec generated from 'python-mappings/TIS-620.TXT' with gencodec.py. +""" Python Character Mapping Codec tis_620 generated from 'python-mappings/TIS-620.TXT' with gencodec.py. """#" @@ -9,13 +9,19 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) def decode(self,input,errors='strict'): - return codecs.charmap_decode(input,errors,decoding_table) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input,self.errors,decoding_table)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -25,8 +31,15 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='tis-620', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) ### Decoding Table diff --git a/Lib/encodings/undefined.py b/Lib/encodings/undefined.py index d2277ac..4690288 100644 --- a/Lib/encodings/undefined.py +++ b/Lib/encodings/undefined.py @@ -16,10 +16,18 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - raise UnicodeError, "undefined encoding" + raise UnicodeError("undefined encoding") def decode(self,input,errors='strict'): - raise UnicodeError, "undefined encoding" + raise UnicodeError("undefined encoding") + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + raise UnicodeError("undefined encoding") + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + raise UnicodeError("undefined encoding") class StreamWriter(Codec,codecs.StreamWriter): pass @@ -30,5 +38,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='undefined', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/unicode_escape.py b/Lib/encodings/unicode_escape.py index 8fb6293..817f932 100644 --- a/Lib/encodings/unicode_escape.py +++ b/Lib/encodings/unicode_escape.py @@ -17,6 +17,14 @@ class Codec(codecs.Codec): encode = codecs.unicode_escape_encode decode = codecs.unicode_escape_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.unicode_escape_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.unicode_escape_decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -26,5 +34,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='unicode-escape', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/unicode_internal.py b/Lib/encodings/unicode_internal.py index 3bd2fa0..df3e775 100644 --- a/Lib/encodings/unicode_internal.py +++ b/Lib/encodings/unicode_internal.py @@ -17,6 +17,14 @@ class Codec(codecs.Codec): encode = codecs.unicode_internal_encode decode = codecs.unicode_internal_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.unicode_internal_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.unicode_internal_decode(input, self.errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -26,5 +34,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='unicode-internal', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/Lib/encodings/utf_16.py b/Lib/encodings/utf_16.py index 95abb05..eff08f3 100644 --- a/Lib/encodings/utf_16.py +++ b/Lib/encodings/utf_16.py @@ -15,6 +15,47 @@ encode = codecs.utf_16_encode def decode(input, errors='strict'): return codecs.utf_16_decode(input, errors, True) +class IncrementalEncoder(codecs.IncrementalEncoder): + def __init__(self, errors='strict'): + codecs.IncrementalEncoder.__init__(self, errors) + self.encoder = None + + def encode(self, input, final=False): + if self.encoder is None: + result = codecs.utf_16_encode(input, self.errors)[0] + if sys.byteorder == 'little': + self.encoder = codecs.utf_16_le_encode + else: + self.encoder = codecs.utf_16_be_encode + return result + return self.encoder(input, self.errors)[0] + + def reset(self): + codecs.IncrementalEncoder.reset(self) + self.encoder = None + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def __init__(self, errors='strict'): + codecs.BufferedIncrementalDecoder.__init__(self, errors) + self.decoder = None + + def _buffer_decode(self, input, errors, final): + if self.decoder is None: + (output, consumed, byteorder) = \ + codecs.utf_16_ex_decode(input, errors, 0, final) + if byteorder == -1: + self.decoder = codecs.utf_16_le_decode + elif byteorder == 1: + self.decoder = codecs.utf_16_be_decode + elif consumed >= 2: + raise UnicodeError("UTF-16 stream does not start with BOM") + return (output, consumed) + return self.decoder(input, self.errors, final) + + def reset(self): + codecs.BufferedIncrementalDecoder.reset(self) + self.decoder = None + class StreamWriter(codecs.StreamWriter): def __init__(self, stream, errors='strict'): self.bom_written = False @@ -52,5 +93,12 @@ class StreamReader(codecs.StreamReader): ### encodings module API def getregentry(): - - return (encode,decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-16', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/utf_16_be.py b/Lib/encodings/utf_16_be.py index 9a51f8c..86b458e 100644 --- a/Lib/encodings/utf_16_be.py +++ b/Lib/encodings/utf_16_be.py @@ -15,6 +15,13 @@ encode = codecs.utf_16_be_encode def decode(input, errors='strict'): return codecs.utf_16_be_decode(input, errors, True) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.utf_16_be_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + _buffer_decode = codecs.utf_16_be_decode + class StreamWriter(codecs.StreamWriter): encode = codecs.utf_16_be_encode @@ -24,5 +31,12 @@ class StreamReader(codecs.StreamReader): ### encodings module API def getregentry(): - - return (encode,decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-16-be', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/utf_16_le.py b/Lib/encodings/utf_16_le.py index 95ca830..ec45414 100644 --- a/Lib/encodings/utf_16_le.py +++ b/Lib/encodings/utf_16_le.py @@ -15,15 +15,28 @@ encode = codecs.utf_16_le_encode def decode(input, errors='strict'): return codecs.utf_16_le_decode(input, errors, True) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.utf_16_le_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + _buffer_decode = codecs.utf_16_le_decode + class StreamWriter(codecs.StreamWriter): encode = codecs.utf_16_le_encode class StreamReader(codecs.StreamReader): decode = codecs.utf_16_le_decode - ### encodings module API def getregentry(): - - return (encode,decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-16-le', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/utf_7.py b/Lib/encodings/utf_7.py index ee78d09..d78d192 100644 --- a/Lib/encodings/utf_7.py +++ b/Lib/encodings/utf_7.py @@ -13,6 +13,14 @@ class Codec(codecs.Codec): encode = codecs.utf_7_encode decode = codecs.utf_7_decode +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.utf_7_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input, errors, final): + return codecs.utf_7_decode(input, self.errors) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -22,5 +30,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec.encode,Codec.decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-7', + encode=Codec.encode, + decode=Codec.decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/utf_8.py b/Lib/encodings/utf_8.py index 9cb0b4b..1bf6336 100644 --- a/Lib/encodings/utf_8.py +++ b/Lib/encodings/utf_8.py @@ -15,6 +15,13 @@ encode = codecs.utf_8_encode def decode(input, errors='strict'): return codecs.utf_8_decode(input, errors, True) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.utf_8_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + _buffer_decode = codecs.utf_8_decode + class StreamWriter(codecs.StreamWriter): encode = codecs.utf_8_encode @@ -24,5 +31,12 @@ class StreamReader(codecs.StreamReader): ### encodings module API def getregentry(): - - return (encode,decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-8', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/utf_8_sig.py b/Lib/encodings/utf_8_sig.py index fa437e6..cd14ab0 100644 --- a/Lib/encodings/utf_8_sig.py +++ b/Lib/encodings/utf_8_sig.py @@ -22,6 +22,42 @@ def decode(input, errors='strict'): (output, consumed) = codecs.utf_8_decode(input, errors, True) return (output, consumed+prefix) +class IncrementalEncoder(codecs.IncrementalEncoder): + def __init__(self, errors='strict'): + codecs.IncrementalEncoder.__init__(self, errors) + self.first = True + + def encode(self, input, final=False): + if self.first: + self.first = False + return codecs.BOM_UTF8 + codecs.utf_8_encode(input, errors)[0] + else: + return codecs.utf_8_encode(input, errors)[0] + + def reset(self): + codecs.IncrementalEncoder.reset(self) + self.first = True + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def __init__(self, errors='strict'): + codecs.BufferedIncrementalDecoder.__init__(self, errors) + self.first = True + + def _buffer_decode(self, input, errors, final): + if self.first and codecs.BOM_UTF8.startswith(input): # might be a BOM + if len(input) < 3: + # not enough data to decide if this really is a BOM + # => try again on the next call + return (u"", 0) + (output, consumed) = codecs.utf_8_decode(input[3:], errors, final) + self.first = False + return (output, consumed+3) + return codecs.utf_8_decode(input, errors, final) + + def reset(self): + codecs.BufferedIncrementalDecoder.reset(self) + self.first = True + class StreamWriter(codecs.StreamWriter): def reset(self): codecs.StreamWriter.reset(self) @@ -53,5 +89,12 @@ class StreamReader(codecs.StreamReader): ### encodings module API def getregentry(): - - return (encode,decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='utf-8-sig', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/uu_codec.py b/Lib/encodings/uu_codec.py index a70ff9e..0877fe1 100644 --- a/Lib/encodings/uu_codec.py +++ b/Lib/encodings/uu_codec.py @@ -96,9 +96,18 @@ class Codec(codecs.Codec): def encode(self,input,errors='strict'): return uu_encode(input,errors) + def decode(self,input,errors='strict'): return uu_decode(input,errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return uu_encode(input, errors)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return uu_decode(input, errors)[0] + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -108,5 +117,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (uu_encode,uu_decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='uu', + encode=uu_encode, + decode=uu_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/zlib_codec.py b/Lib/encodings/zlib_codec.py index 9b6e4d1..2694f15 100644 --- a/Lib/encodings/zlib_codec.py +++ b/Lib/encodings/zlib_codec.py @@ -50,6 +50,16 @@ class Codec(codecs.Codec): def decode(self, input, errors='strict'): return zlib_decode(input, errors) +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + assert self.errors == 'strict' + return zlib.compress(input) + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + assert self.errors == 'strict' + return zlib.decompress(input) + class StreamWriter(Codec,codecs.StreamWriter): pass @@ -59,5 +69,12 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (zlib_encode,zlib_decode,StreamReader,StreamWriter) + return codecs.CodecInfo( + name='zlib', + encode=zlib_encode, + decode=zlib_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 4b2f8a6..7db0dab 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1058,13 +1058,16 @@ class Logger(Filterer): file name, line number and function name. """ f = currentframe().f_back - while 1: + rv = "(unknown file)", 0, "(unknown function)" + while hasattr(f, "f_code"): co = f.f_code filename = os.path.normcase(co.co_filename) if filename == _srcfile: f = f.f_back continue - return filename, f.f_lineno, co.co_name + rv = (filename, f.f_lineno, co.co_name) + break + return rv def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None): """ diff --git a/Lib/runpy.py b/Lib/runpy.py new file mode 100755 index 0000000..afb0098 --- /dev/null +++ b/Lib/runpy.py @@ -0,0 +1,431 @@ +"""runpy.py - locating and running Python code using the module namespace + +Provides support for locating and running Python scripts using the Python +module namespace instead of the native filesystem. + +This allows Python code to play nicely with non-filesystem based PEP 302 +importers when locating support scripts as well as when importing modules. +""" +# Written by Nick Coghlan +# to implement PEP 338 (Executing Modules as Scripts) + +import sys +import imp + +__all__ = [ + "run_module", +] + +try: + _get_loader = imp.get_loader +except AttributeError: + # get_loader() is not provided by the imp module, so emulate it + # as best we can using the PEP 302 import machinery exposed since + # Python 2.3. The emulation isn't perfect, but the differences + # in the way names are shadowed shouldn't matter in practice. + import os.path + import marshal # Handle compiled Python files + + # This helper is needed in order for the PEP 302 emulation to + # correctly handle compiled files + def _read_compiled_file(compiled_file): + magic = compiled_file.read(4) + if magic != imp.get_magic(): + return None + try: + compiled_file.read(4) # Skip timestamp + return marshal.load(compiled_file) + except Exception: + return None + + class _AbsoluteImporter(object): + """PEP 302 importer wrapper for top level import machinery""" + def find_module(self, mod_name, path=None): + if path is not None: + return None + try: + file, filename, mod_info = imp.find_module(mod_name) + except ImportError: + return None + suffix, mode, mod_type = mod_info + if mod_type == imp.PY_SOURCE: + loader = _SourceFileLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.PY_COMPILED: + loader = _CompiledFileLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.PKG_DIRECTORY: + loader = _PackageDirLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.C_EXTENSION: + loader = _FileSystemLoader(mod_name, file, + filename, mod_info) + else: + loader = _BasicLoader(mod_name, file, + filename, mod_info) + return loader + + + class _FileSystemImporter(object): + """PEP 302 importer wrapper for filesystem based imports""" + def __init__(self, path_item=None): + if path_item is not None: + if path_item != '' and not os.path.isdir(path_item): + raise ImportError("%s is not a directory" % path_item) + self.path_dir = path_item + else: + raise ImportError("Filesystem importer requires " + "a directory name") + + def find_module(self, mod_name, path=None): + if path is not None: + return None + path_dir = self.path_dir + if path_dir == '': + path_dir = os.getcwd() + sub_name = mod_name.rsplit(".", 1)[-1] + try: + file, filename, mod_info = imp.find_module(sub_name, + [path_dir]) + except ImportError: + return None + if not filename.startswith(path_dir): + return None + suffix, mode, mod_type = mod_info + if mod_type == imp.PY_SOURCE: + loader = _SourceFileLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.PY_COMPILED: + loader = _CompiledFileLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.PKG_DIRECTORY: + loader = _PackageDirLoader(mod_name, file, + filename, mod_info) + elif mod_type == imp.C_EXTENSION: + loader = _FileSystemLoader(mod_name, file, + filename, mod_info) + else: + loader = _BasicLoader(mod_name, file, + filename, mod_info) + return loader + + + class _BasicLoader(object): + """PEP 302 loader wrapper for top level import machinery""" + def __init__(self, mod_name, file, filename, mod_info): + self.mod_name = mod_name + self.file = file + self.filename = filename + self.mod_info = mod_info + + def _fix_name(self, mod_name): + if mod_name is None: + mod_name = self.mod_name + elif mod_name != self.mod_name: + raise ImportError("Loader for module %s cannot handle " + "module %s" % (self.mod_name, mod_name)) + return mod_name + + def load_module(self, mod_name=None): + mod_name = self._fix_name(mod_name) + mod = imp.load_module(mod_name, self.file, + self.filename, self.mod_info) + mod.__loader__ = self # for introspection + return mod + + def get_code(self, mod_name=None): + return None + + def get_source(self, mod_name=None): + return None + + def is_package(self, mod_name=None): + return False + + def close(self): + if self.file: + self.file.close() + + def __del__(self): + self.close() + + + class _FileSystemLoader(_BasicLoader): + """PEP 302 loader wrapper for filesystem based imports""" + def get_code(self, mod_name=None): + mod_name = self._fix_name(mod_name) + return self._get_code(mod_name) + + def get_data(self, pathname): + return open(pathname, "rb").read() + + def get_filename(self, mod_name=None): + mod_name = self._fix_name(mod_name) + return self._get_filename(mod_name) + + def get_source(self, mod_name=None): + mod_name = self._fix_name(mod_name) + return self._get_source(mod_name) + + def is_package(self, mod_name=None): + mod_name = self._fix_name(mod_name) + return self._is_package(mod_name) + + def _get_code(self, mod_name): + return None + + def _get_filename(self, mod_name): + return self.filename + + def _get_source(self, mod_name): + return None + + def _is_package(self, mod_name): + return False + + class _PackageDirLoader(_FileSystemLoader): + """PEP 302 loader wrapper for PKG_DIRECTORY directories""" + def _is_package(self, mod_name): + return True + + + class _SourceFileLoader(_FileSystemLoader): + """PEP 302 loader wrapper for PY_SOURCE modules""" + def _get_code(self, mod_name): + return compile(self._get_source(mod_name), + self.filename, 'exec') + + def _get_source(self, mod_name): + f = self.file + f.seek(0) + return f.read() + + + class _CompiledFileLoader(_FileSystemLoader): + """PEP 302 loader wrapper for PY_COMPILED modules""" + def _get_code(self, mod_name): + f = self.file + f.seek(0) + return _read_compiled_file(f) + + + def _get_importer(path_item): + """Retrieve a PEP 302 importer for the given path item + + 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 cache (or part of it) can be cleared manually if a + rescan of sys.path_hooks is necessary. + """ + try: + importer = sys.path_importer_cache[path_item] + except KeyError: + for path_hook in sys.path_hooks: + try: + importer = path_hook(path_item) + break + except ImportError: + pass + else: + importer = None + sys.path_importer_cache[path_item] = importer + if importer is None: + try: + importer = _FileSystemImporter(path_item) + except ImportError: + pass + return importer + + + def _get_path_loader(mod_name, path=None): + """Retrieve a PEP 302 loader using a path importer""" + if path is None: + path = sys.path + absolute_loader = _AbsoluteImporter().find_module(mod_name) + if isinstance(absolute_loader, _FileSystemLoader): + # Found in filesystem, so scan path hooks + # before accepting this one as the right one + loader = None + else: + # Not found in filesystem, so use top-level loader + loader = absolute_loader + else: + loader = absolute_loader = None + if loader is None: + for path_item in path: + importer = _get_importer(path_item) + if importer is not None: + loader = importer.find_module(mod_name) + if loader is not None: + # Found a loader for our module + break + else: + # No path hook found, so accept the top level loader + loader = absolute_loader + return loader + + def _get_package(pkg_name): + """Retrieve a named package""" + pkg = __import__(pkg_name) + sub_pkg_names = pkg_name.split(".") + for sub_pkg in sub_pkg_names[1:]: + pkg = getattr(pkg, sub_pkg) + return pkg + + def _get_loader(mod_name, path=None): + """Retrieve a PEP 302 loader for the given module or package + + If the module or package is accessible via the normal import + mechanism, a wrapper around the relevant part of that machinery + is returned. + + 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 + 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, + 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 + """ + try: + loader = sys.modules[mod_name].__loader__ + except (KeyError, AttributeError): + loader = None + if loader is None: + imp.acquire_lock() + try: + # Module not in sys.modules, or uses an unhooked loader + parts = mod_name.rsplit(".", 1) + if len(parts) == 2: + # Sub package, so use parent package's path + pkg_name, sub_name = parts + if pkg_name and pkg_name[0] != '.': + if path is not None: + raise ImportError("Path argument must be None " + "for a dotted module name") + pkg = _get_package(pkg_name) + try: + path = pkg.__path__ + except AttributeError: + raise ImportError(pkg_name + + " is not a package") + else: + raise ImportError("Relative import syntax is not " + "supported by _get_loader()") + else: + # Top level module, so stick with default path + sub_name = mod_name + + for importer in sys.meta_path: + loader = importer.find_module(mod_name, path) + if loader is not None: + # Found a metahook to handle the module + break + else: + # Handling via the standard path mechanism + loader = _get_path_loader(mod_name, path) + finally: + imp.release_lock() + return loader + + +# This helper is needed due to a missing component in the PEP 302 +# loader protocol (specifically, "get_filename" is non-standard) +def _get_filename(loader, mod_name): + try: + get_filename = loader.get_filename + except AttributeError: + return None + else: + return get_filename(mod_name) + +# ------------------------------------------------------------ +# Done with the import machinery emulation, on with the code! + +def _run_code(code, run_globals, init_globals, + mod_name, mod_fname, mod_loader): + """Helper for _run_module_code""" + if init_globals is not None: + run_globals.update(init_globals) + run_globals.update(__name__ = mod_name, + __file__ = mod_fname, + __loader__ = mod_loader) + exec code in run_globals + return run_globals + +def _run_module_code(code, init_globals=None, + mod_name=None, mod_fname=None, + mod_loader=None, alter_sys=False): + """Helper for run_module""" + # Set up the top level namespace dictionary + if alter_sys: + # Modify sys.argv[0] and sys.module[mod_name] + temp_module = imp.new_module(mod_name) + mod_globals = temp_module.__dict__ + saved_argv0 = sys.argv[0] + restore_module = mod_name in sys.modules + if restore_module: + saved_module = sys.modules[mod_name] + imp.acquire_lock() + try: + sys.argv[0] = mod_fname + sys.modules[mod_name] = temp_module + try: + _run_code(code, mod_globals, init_globals, + mod_name, mod_fname, mod_loader) + finally: + sys.argv[0] = saved_argv0 + if restore_module: + sys.modules[mod_name] = saved_module + else: + del sys.modules[mod_name] + finally: + imp.release_lock() + # Copy the globals of the temporary module, as they + # may be cleared when the temporary module goes away + return mod_globals.copy() + else: + # Leave the sys module alone + return _run_code(code, {}, init_globals, + mod_name, mod_fname, mod_loader) + + +def run_module(mod_name, init_globals=None, + run_name=None, alter_sys=False): + """Execute a module's code without importing it + + Returns the resulting top level namespace dictionary + """ + loader = _get_loader(mod_name) + if loader is None: + raise ImportError("No module named " + mod_name) + code = loader.get_code(mod_name) + if code is None: + raise ImportError("No code object available for " + mod_name) + filename = _get_filename(loader, mod_name) + if run_name is None: + run_name = mod_name + return _run_module_code(code, init_globals, run_name, + filename, loader, alter_sys) + + +if __name__ == "__main__": + # Run the module specified as the next command line argument + if len(sys.argv) < 2: + print >> sys.stderr, "No module specified for execution" + else: + del sys.argv[0] # Make the requested module sys.argv[0] + run_module(sys.argv[0], run_name="__main__", alter_sys=True) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index b4c229a..b850912 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1136,9 +1136,6 @@ class _ExpectedSkips: s = _expectations[sys.platform] self.expected = set(s.split()) - # this isn't a regularly run unit test, it is always skipped - self.expected.add('test_hashlib_speed') - if not os.path.supports_unicode_filenames: self.expected.add('test_pep277') diff --git a/Lib/test/test_StringIO.py b/Lib/test/test_StringIO.py index c61f7cc..cc3367f 100644 --- a/Lib/test/test_StringIO.py +++ b/Lib/test/test_StringIO.py @@ -75,6 +75,13 @@ class TestGenericStringIO(unittest.TestCase): f.close() self.assertEqual(f.closed, True) + def test_isatty(self): + f = self.MODULE.StringIO() + self.assertRaises(TypeError, f.isatty, None) + self.assertEqual(f.isatty(), False) + f.close() + self.assertRaises(ValueError, f.isatty) + def test_iterator(self): eq = self.assertEqual unless = self.failUnless @@ -87,6 +94,8 @@ class TestGenericStringIO(unittest.TestCase): eq(line, self._line + '\n') i += 1 eq(i, 5) + self._fp.close() + self.assertRaises(ValueError, self._fp.next) class TestStringIO(TestGenericStringIO): MODULE = StringIO diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 7bf5314..74a9cdf 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -5,8 +5,6 @@ from test.test_support import verify, verbose import sys import warnings -warnings.filterwarnings("ignore", ".* 'pre' .*", DeprecationWarning, - r'pre$') warnings.filterwarnings("ignore", ".* regsub .*", DeprecationWarning, r'^regsub$') warnings.filterwarnings("ignore", @@ -122,7 +120,6 @@ class AllTest(unittest.TestCase): self.check_all("poplib") self.check_all("posixpath") self.check_all("pprint") - self.check_all("pre") # deprecated self.check_all("profile") self.check_all("pstats") self.check_all("pty") diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 3944d65..913aa91 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -41,6 +41,33 @@ class ReadTest(unittest.TestCase): self.assertEqual(r.bytebuffer, "") self.assertEqual(r.charbuffer, u"") + # do the check again, this time using a incremental decoder + d = codecs.getincrementaldecoder(self.encoding)() + result = u"" + for (c, partialresult) in zip(input.encode(self.encoding), partialresults): + result += d.decode(c) + self.assertEqual(result, partialresult) + # check that there's nothing left in the buffers + self.assertEqual(d.decode("", True), u"") + self.assertEqual(d.buffer, "") + + # Check whether the rest method works properly + d.reset() + result = u"" + for (c, partialresult) in zip(input.encode(self.encoding), partialresults): + result += d.decode(c) + self.assertEqual(result, partialresult) + # check that there's nothing left in the buffers + self.assertEqual(d.decode("", True), u"") + self.assertEqual(d.buffer, "") + + # check iterdecode() + encoded = input.encode(self.encoding) + self.assertEqual( + input, + u"".join(codecs.iterdecode(encoded, self.encoding)) + ) + def test_readline(self): def getreader(input): stream = StringIO.StringIO(input.encode(self.encoding)) @@ -977,6 +1004,12 @@ class BasicUnicodeTest(unittest.TestCase): def test_basics(self): s = u"abc123" # all codecs should be able to encode these for encoding in all_unicode_encodings: + name = codecs.lookup(encoding).name + if encoding.endswith("_codec"): + name += "_codec" + elif encoding == "latin_1": + name = "latin_1" + self.assertEqual(encoding.replace("_", "-"), name.replace("_", "-")) (bytes, size) = codecs.getencoder(encoding)(s) if encoding != "unicode_internal": self.assertEqual(size, len(s), "%r != %r (encoding=%r)" % (size, len(s), encoding)) @@ -999,6 +1032,30 @@ class BasicUnicodeTest(unittest.TestCase): decodedresult += reader.read() self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + # check incremental decoder/encoder and iterencode()/iterdecode() + try: + encoder = codecs.getincrementalencoder(encoding)() + except LookupError: # no IncrementalEncoder + pass + else: + # check incremental decoder/encoder + encodedresult = "" + for c in s: + encodedresult += encoder.encode(c) + decoder = codecs.getincrementaldecoder(encoding)() + decodedresult = u"" + for c in encodedresult: + decodedresult += decoder.decode(c) + self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + + # check iterencode()/iterdecode() + result = u"".join(codecs.iterdecode(codecs.iterencode(s, encoding), encoding)) + self.assertEqual(result, s, "%r != %r (encoding=%r)" % (result, s, encoding)) + + # check iterencode()/iterdecode() with empty string + result = u"".join(codecs.iterdecode(codecs.iterencode(u"", encoding), encoding)) + self.assertEqual(result, u"") + def test_seek(self): # all codecs should be able to encode these s = u"%s\n%s\n" % (100*u"abc123", 100*u"def456") diff --git a/Lib/test/test_hashlib_speed.py b/Lib/test/test_hashlib_speed.py deleted file mode 100644 index c84a62b..0000000 --- a/Lib/test/test_hashlib_speed.py +++ /dev/null @@ -1,92 +0,0 @@ - -import sys, time -import hashlib -from test import test_support - - -def creatorFunc(): - raise RuntimeError, "eek, creatorFunc not overridden" - - -def test_scaled_msg(scale, name): - - iterations = 106201/scale * 20 - longStr = 'Z'*scale - - localCF = creatorFunc - start = time.time() - for f in xrange(iterations): - x = localCF(longStr).digest() - end = time.time() - - print ('%2.2f' % (end-start)), "seconds", iterations, "x", len(longStr), "bytes", name - -def test_create(): - start = time.time() - for f in xrange(20000): - d = creatorFunc() - end = time.time() - - print ('%2.2f' % (end-start)), "seconds", '[20000 creations]' - -def test_zero(): - start = time.time() - for f in xrange(20000): - x = creatorFunc().digest() - end = time.time() - - print ('%2.2f' % (end-start)), "seconds", '[20000 "" digests]' - - - -### this 'test' is not normally run. skip it if the test runner finds it -if __name__ != '__main__': - raise test_support.TestSkipped, "not a unit test (stand alone benchmark)" - -hName = sys.argv[1] - -# -# setup our creatorFunc to test the requested hash -# -if hName in ('_md5', '_sha'): - exec 'import '+hName - exec 'creatorFunc = '+hName+'.new' - print "testing speed of old", hName, "legacy interface" -elif hName == '_hashlib' and len(sys.argv) > 3: - import _hashlib - exec 'creatorFunc = _hashlib.%s' % sys.argv[2] - print "testing speed of _hashlib.%s" % sys.argv[2], getattr(_hashlib, sys.argv[2]) -elif hName == '_hashlib' and len(sys.argv) == 3: - import _hashlib - exec 'creatorFunc = lambda x=_hashlib.new : x(%r)' % sys.argv[2] - print "testing speed of _hashlib.new(%r)" % sys.argv[2] -elif hasattr(hashlib, hName) and callable(getattr(hashlib, hName)): - creatorFunc = getattr(hashlib, hName) - print "testing speed of hashlib."+hName, getattr(hashlib, hName) -else: - exec "creatorFunc = lambda x=hashlib.new : x(%r)" % hName - print "testing speed of hashlib.new(%r)" % hName - -try: - test_create() -except ValueError: - print - print "pass argument(s) naming the hash to run a speed test on:" - print " '_md5' and '_sha' test the legacy builtin md5 and sha" - print " '_hashlib' 'openssl_hName' 'fast' tests the builtin _hashlib" - print " '_hashlib' 'hName' tests builtin _hashlib.new(shaFOO)" - print " 'hName' tests the hashlib.hName() implementation if it exists" - print " otherwise it uses hashlib.new(hName)." - print - raise - -test_zero() -test_scaled_msg(scale=106201, name='[huge data]') -test_scaled_msg(scale=10620, name='[large data]') -test_scaled_msg(scale=1062, name='[medium data]') -test_scaled_msg(scale=424, name='[4*small data]') -test_scaled_msg(scale=336, name='[3*small data]') -test_scaled_msg(scale=212, name='[2*small data]') -test_scaled_msg(scale=106, name='[small data]') -test_scaled_msg(scale=creatorFunc().digest_size, name='[digest_size data]') -test_scaled_msg(scale=10, name='[tiny data]') diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py new file mode 100644 index 0000000..88e9900 --- /dev/null +++ b/Lib/test/test_runpy.py @@ -0,0 +1,172 @@ +# Test the runpy module +import unittest +import os +import os.path +import sys +import tempfile +from test.test_support import verbose, run_unittest +from runpy import _run_module_code, run_module + +# Set up the test code and expected results + +class RunModuleCodeTest(unittest.TestCase): + + expected_result = ["Top level assignment", "Lower level reference"] + test_source = ( + "# Check basic code execution\n" + "result = ['Top level assignment']\n" + "def f():\n" + " result.append('Lower level reference')\n" + "f()\n" + "# Check the sys module\n" + "import sys\n" + "run_argv0 = sys.argv[0]\n" + "if __name__ in sys.modules:\n" + " run_name = sys.modules[__name__].__name__\n" + "# Check nested operation\n" + "import runpy\n" + "nested = runpy._run_module_code('x=1\\n', mod_name='',\n" + " alter_sys=True)\n" + ) + + + def test_run_module_code(self): + initial = object() + name = "" + file = "Some other nonsense" + loader = "Now you're just being silly" + d1 = dict(initial=initial) + saved_argv0 = sys.argv[0] + d2 = _run_module_code(self.test_source, + d1, + name, + file, + loader, + True) + self.failUnless("result" not in d1) + self.failUnless(d2["initial"] is initial) + self.failUnless(d2["result"] == self.expected_result) + self.failUnless(d2["nested"]["x"] == 1) + self.failUnless(d2["__name__"] is name) + self.failUnless(d2["run_name"] is name) + self.failUnless(d2["__file__"] is file) + self.failUnless(d2["run_argv0"] is file) + self.failUnless(d2["__loader__"] is loader) + self.failUnless(sys.argv[0] is saved_argv0) + self.failUnless(name not in sys.modules) + + def test_run_module_code_defaults(self): + saved_argv0 = sys.argv[0] + d = _run_module_code(self.test_source) + self.failUnless(d["result"] == self.expected_result) + self.failUnless(d["__name__"] is None) + self.failUnless(d["__file__"] is None) + self.failUnless(d["__loader__"] is None) + self.failUnless(d["run_argv0"] is saved_argv0) + self.failUnless("run_name" not in d) + self.failUnless(sys.argv[0] is saved_argv0) + +class RunModuleTest(unittest.TestCase): + + def expect_import_error(self, mod_name): + try: + run_module(mod_name) + except ImportError: + pass + else: + self.fail("Expected import error for " + mod_name) + + def test_invalid_names(self): + self.expect_import_error("sys") + self.expect_import_error("sys.imp.eric") + self.expect_import_error("os.path.half") + self.expect_import_error("a.bee") + self.expect_import_error(".howard") + self.expect_import_error("..eaten") + + def test_library_module(self): + run_module("runpy") + + def _make_pkg(self, source, depth): + pkg_name = "__runpy_pkg__" + init_fname = "__init__"+os.extsep+"py" + test_fname = "runpy_test"+os.extsep+"py" + pkg_dir = sub_dir = tempfile.mkdtemp() + if verbose: print " Package tree in:", sub_dir + sys.path.insert(0, pkg_dir) + if verbose: print " Updated sys.path:", sys.path[0] + for i in range(depth): + sub_dir = os.path.join(sub_dir, pkg_name) + os.mkdir(sub_dir) + if verbose: print " Next level in:", sub_dir + pkg_fname = os.path.join(sub_dir, init_fname) + pkg_file = open(pkg_fname, "w") + pkg_file.close() + if verbose: print " Created:", pkg_fname + mod_fname = os.path.join(sub_dir, test_fname) + mod_file = open(mod_fname, "w") + mod_file.write(source) + mod_file.close() + if verbose: print " Created:", mod_fname + mod_name = (pkg_name+".")*depth + "runpy_test" + return pkg_dir, mod_fname, mod_name + + def _del_pkg(self, top, depth, mod_name): + for i in range(depth+1): # Don't forget the module itself + parts = mod_name.rsplit(".", i) + entry = parts[0] + try: + del sys.modules[entry] + except KeyError, ex: + if verbose: print ex # Persist with cleaning up + if verbose: print " Removed sys.modules entries" + del sys.path[0] + if verbose: print " Removed sys.path entry" + for root, dirs, files in os.walk(top, topdown=False): + for name in files: + try: + os.remove(os.path.join(root, name)) + except OSError, ex: + if verbose: print ex # Persist with cleaning up + for name in dirs: + fullname = os.path.join(root, name) + try: + os.rmdir(fullname) + except OSError, ex: + if verbose: print ex # Persist with cleaning up + try: + os.rmdir(top) + if verbose: print " Removed package tree" + except OSError, ex: + if verbose: print ex # Persist with cleaning up + + def _check_module(self, depth): + pkg_dir, mod_fname, mod_name = ( + self._make_pkg("x=1\n", depth)) + try: + if verbose: print "Running from source:", mod_name + d1 = run_module(mod_name) # Read from source + self.failUnless(d1["x"] == 1) + del d1 # Ensure __loader__ entry doesn't keep file open + __import__(mod_name) + os.remove(mod_fname) + if verbose: print "Running from compiled:", mod_name + d2 = run_module(mod_name) # Read from bytecode + self.failUnless(d2["x"] == 1) + del d2 # Ensure __loader__ entry doesn't keep file open + finally: + self._del_pkg(pkg_dir, depth, mod_name) + if verbose: print "Module executed successfully" + + def test_run_module(self): + for depth in range(4): + if verbose: print "Testing package depth:", depth + self._check_module(depth) + + +def test_main(): + run_unittest(RunModuleCodeTest) + run_unittest(RunModuleTest) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/time_hashlib.py b/Lib/test/time_hashlib.py new file mode 100644 index 0000000..1bf707d --- /dev/null +++ b/Lib/test/time_hashlib.py @@ -0,0 +1,87 @@ +# It's intended that this script be run by hand. It runs speed tests on +# hashlib functions; it does not test for correctness. + +import sys, time +import hashlib + + +def creatorFunc(): + raise RuntimeError, "eek, creatorFunc not overridden" + +def test_scaled_msg(scale, name): + iterations = 106201/scale * 20 + longStr = 'Z'*scale + + localCF = creatorFunc + start = time.time() + for f in xrange(iterations): + x = localCF(longStr).digest() + end = time.time() + + print ('%2.2f' % (end-start)), "seconds", iterations, "x", len(longStr), "bytes", name + +def test_create(): + start = time.time() + for f in xrange(20000): + d = creatorFunc() + end = time.time() + + print ('%2.2f' % (end-start)), "seconds", '[20000 creations]' + +def test_zero(): + start = time.time() + for f in xrange(20000): + x = creatorFunc().digest() + end = time.time() + + print ('%2.2f' % (end-start)), "seconds", '[20000 "" digests]' + + + +hName = sys.argv[1] + +# +# setup our creatorFunc to test the requested hash +# +if hName in ('_md5', '_sha'): + exec 'import '+hName + exec 'creatorFunc = '+hName+'.new' + print "testing speed of old", hName, "legacy interface" +elif hName == '_hashlib' and len(sys.argv) > 3: + import _hashlib + exec 'creatorFunc = _hashlib.%s' % sys.argv[2] + print "testing speed of _hashlib.%s" % sys.argv[2], getattr(_hashlib, sys.argv[2]) +elif hName == '_hashlib' and len(sys.argv) == 3: + import _hashlib + exec 'creatorFunc = lambda x=_hashlib.new : x(%r)' % sys.argv[2] + print "testing speed of _hashlib.new(%r)" % sys.argv[2] +elif hasattr(hashlib, hName) and callable(getattr(hashlib, hName)): + creatorFunc = getattr(hashlib, hName) + print "testing speed of hashlib."+hName, getattr(hashlib, hName) +else: + exec "creatorFunc = lambda x=hashlib.new : x(%r)" % hName + print "testing speed of hashlib.new(%r)" % hName + +try: + test_create() +except ValueError: + print + print "pass argument(s) naming the hash to run a speed test on:" + print " '_md5' and '_sha' test the legacy builtin md5 and sha" + print " '_hashlib' 'openssl_hName' 'fast' tests the builtin _hashlib" + print " '_hashlib' 'hName' tests builtin _hashlib.new(shaFOO)" + print " 'hName' tests the hashlib.hName() implementation if it exists" + print " otherwise it uses hashlib.new(hName)." + print + raise + +test_zero() +test_scaled_msg(scale=106201, name='[huge data]') +test_scaled_msg(scale=10620, name='[large data]') +test_scaled_msg(scale=1062, name='[medium data]') +test_scaled_msg(scale=424, name='[4*small data]') +test_scaled_msg(scale=336, name='[3*small data]') +test_scaled_msg(scale=212, name='[2*small data]') +test_scaled_msg(scale=106, name='[small data]') +test_scaled_msg(scale=creatorFunc().digest_size, name='[digest_size data]') +test_scaled_msg(scale=10, name='[tiny data]') diff --git a/Makefile.pre.in b/Makefile.pre.in index 5f2e984..056b578 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -974,6 +974,8 @@ clean: find . -name '*.o' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' + find $(srcdir) -name 'fficonfig.h' -exec rm -f {} ';' + find $(srcdir) -name 'fficonfig.py' -exec rm -f {} ';' clobber: clean -rm -f $(BUILDPYTHON) $(PGEN) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 7c5da64..926c85b 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3674,7 +3674,11 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length) if (cache == NULL) return NULL; } +#if (PY_VERSION_HEX < 0x02050000) + key = Py_BuildValue("(Oi)", itemtype, length); +#else key = Py_BuildValue("(On)", itemtype, length); +#endif if (!key) return NULL; result = PyDict_GetItem(cache, key); @@ -3698,7 +3702,11 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length) #endif result = PyObject_CallFunction((PyObject *)&ArrayType_Type, +#if (PY_VERSION_HEX < 0x02050000) + "s(O){s:i,s:O}", +#else "s(O){s:n,s:O}", +#endif name, &Array_Type, "_length_", diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index de41571..336f265 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -250,11 +250,21 @@ CField_repr(CFieldObject *self) name = ((PyTypeObject *)self->proto)->tp_name; if (bits) - result = PyString_FromFormat("", - name, (int)self->offset, size, bits); + result = PyString_FromFormat( +#if (PY_VERSION_HEX < 0x02050000) + "", +#else + "", +#endif + name, self->offset, size, bits); else - result = PyString_FromFormat("", - name, (int)self->offset, size); + result = PyString_FromFormat( +#if (PY_VERSION_HEX < 0x02050000) + "", +#else + "", +#endif + name, self->offset, size); return result; } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 9347c99..179dcf1 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -1,5 +1,18 @@ /******************************************************************/ +#if (PY_VERSION_HEX < 0x02050000) +typedef int Py_ssize_t; +#define lenfunc inquiry +#define readbufferproc getreadbufferproc +#define writebufferproc getwritebufferproc +#define segcountproc getsegcountproc +#define charbufferproc getcharbufferproc +#define ssizeargfunc intargfunc +#define ssizessizeargfunc intintargfunc +#define ssizeobjargproc intobjargproc +#define ssizessizeobjargproc intintobjargproc +#endif + #ifndef MS_WIN32 #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 913c49a..6d8ea3c 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -522,6 +522,18 @@ test_long_numbits(PyObject *self) return Py_None; } +/* Example passing NULLs to PyObject_Str(NULL) and PyObject_Unicode(NULL). */ + +static PyObject * +test_null_strings(PyObject *self) +{ + PyObject *o1 = PyObject_Str(NULL), *o2 = PyObject_Unicode(NULL); + PyObject *tuple = PyTuple_Pack(2, o1, o2); + Py_XDECREF(o1); + Py_XDECREF(o2); + return tuple; +} + static PyObject * raise_exception(PyObject *self, PyObject *args) { @@ -597,6 +609,7 @@ static PyMethodDef TestMethods[] = { {"test_long_api", (PyCFunction)test_long_api, METH_NOARGS}, {"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS}, {"test_k_code", (PyCFunction)test_k_code, METH_NOARGS}, + {"test_null_strings", (PyCFunction)test_null_strings, METH_NOARGS}, {"getargs_b", (PyCFunction)getargs_b, METH_VARARGS}, {"getargs_B", (PyCFunction)getargs_B, METH_VARARGS}, diff --git a/Modules/cStringIO.c b/Modules/cStringIO.c index fd28aa9..bdc9f00 100644 --- a/Modules/cStringIO.c +++ b/Modules/cStringIO.c @@ -144,7 +144,8 @@ PyDoc_STRVAR(IO_isatty__doc__, "isatty(): always returns 0"); static PyObject * IO_isatty(IOobject *self, PyObject *unused) { - Py_INCREF(Py_False); + if (!IO__opencheck(self)) return NULL; + Py_INCREF(Py_False); return Py_False; } diff --git a/Modules/main.c b/Modules/main.c index c8298fb..b3ce16e 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -130,27 +130,42 @@ static void RunStartupFile(PyCompilerFlags *cf) } } -/* Get the path to a top-level module */ -static struct filedescr * FindModule(const char *module, - FILE **fp, char **filename) -{ - struct filedescr *fdescr = NULL; - *fp = NULL; - *filename = malloc(MAXPATHLEN); - - if (*filename == NULL) - return NULL; - - /* Find the actual module source code */ - fdescr = _PyImport_FindModule(module, NULL, - *filename, MAXPATHLEN, fp, NULL); - if (fdescr == NULL) { - free(*filename); - *filename = NULL; +static int RunModule(char *module) +{ + PyObject *runpy, *runmodule, *runargs, *result; + runpy = PyImport_ImportModule("runpy"); + if (runpy == NULL) { + fprintf(stderr, "Could not import runpy module\n"); + return -1; } - - return fdescr; + runmodule = PyObject_GetAttrString(runpy, "run_module"); + if (runmodule == NULL) { + fprintf(stderr, "Could not access runpy.run_module\n"); + Py_DECREF(runpy); + return -1; + } + runargs = Py_BuildValue("sOsO", module, + Py_None, "__main__", Py_True); + if (runargs == NULL) { + fprintf(stderr, + "Could not create arguments for runpy.run_module\n"); + Py_DECREF(runpy); + Py_DECREF(runmodule); + return -1; + } + result = PyObject_Call(runmodule, runargs, NULL); + if (result == NULL) { + PyErr_Print(); + } + Py_DECREF(runpy); + Py_DECREF(runmodule); + Py_DECREF(runargs); + if (result == NULL) { + return -1; + } + Py_DECREF(result); + return 0; } /* Main program */ @@ -410,28 +425,9 @@ Py_Main(int argc, char **argv) } if (module != NULL) { - /* Backup _PyOS_optind and find the real file */ - struct filedescr *fdescr = NULL; + /* Backup _PyOS_optind and force sys.arv[0] = module */ _PyOS_optind--; - if ((fdescr = FindModule(module, &fp, &filename))) { - argv[_PyOS_optind] = filename; - } else { - fprintf(stderr, "%s: module %s not found\n", - argv[0], module); - return 2; - } - if (!fp) { - fprintf(stderr, - "%s: module %s has no associated file\n", - argv[0], module); - return 2; - } - if (!_PyImport_IsScript(fdescr)) { - fprintf(stderr, - "%s: module %s not usable as script\n (%s)\n", - argv[0], module, filename); - return 2; - } + argv[_PyOS_optind] = module; } PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); @@ -450,9 +446,8 @@ Py_Main(int argc, char **argv) sts = PyRun_SimpleStringFlags(command, &cf) != 0; free(command); } else if (module) { - sts = PyRun_AnyFileExFlags(fp, filename, 1, &cf) != 0; + sts = RunModule(module); free(module); - free(filename); } else { if (filename == NULL && stdin_is_interactive) { diff --git a/Modules/xxmodule.c b/Modules/xxmodule.c index df312eb..ea66eef 100644 --- a/Modules/xxmodule.c +++ b/Modules/xxmodule.c @@ -197,18 +197,6 @@ xx_bug(PyObject *self, PyObject *args) return Py_None; } -/* Example passing NULLs to PyObject_Str(NULL) and PyObject_Unicode(NULL). */ - -static PyObject * -xx_null(PyObject *self, PyObject *noargs) -{ - PyObject *o1 = PyObject_Str(NULL), *o2 = PyObject_Unicode(NULL); - PyObject *tuple = PyTuple_Pack(2, o1, o2); - Py_XDECREF(o1); - Py_XDECREF(o2); - return tuple; -} - /* Test bad format character */ static PyObject * @@ -343,8 +331,6 @@ static PyMethodDef xx_methods[] = { PyDoc_STR("new() -> new Xx object")}, {"bug", xx_bug, METH_VARARGS, PyDoc_STR("bug(o) -> None")}, - {"null", xx_null, METH_NOARGS, - PyDoc_STR("null(o) -> ('NULL', u'NULL')")}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 3ee21e4..870f93c 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -217,16 +217,16 @@ * I don't care if these are defined in or elsewhere. Axiom. */ #undef uchar -#define uchar unsigned char /* assuming == 8 bits */ +#define uchar unsigned char /* assuming == 8 bits */ #undef uint -#define uint unsigned int /* assuming >= 16 bits */ +#define uint unsigned int /* assuming >= 16 bits */ #undef ulong -#define ulong unsigned long /* assuming >= 32 bits */ +#define ulong unsigned long /* assuming >= 32 bits */ #undef uptr -#define uptr Py_uintptr_t +#define uptr Py_uintptr_t /* When you say memory, my mind reasons in terms of (pointers to) blocks */ typedef uchar block; @@ -246,6 +246,47 @@ struct pool_header { typedef struct pool_header *poolp; +/* Record keeping for arenas. */ +struct arena_object { + /* The address of the arena, as returned by malloc. Note that 0 + * will never be returned by a successful malloc, and is used + * here to mark an arena_object that doesn't correspond to an + * allocated arena. + */ + uptr address; + + /* Pool-aligned pointer to the next pool to be carved off. */ + block* pool_address; + + /* The number of available pools in the arena: free pools + never- + * allocated pools. + */ + uint nfreepools; + + /* The total number of pools in the arena, whether or not available. */ + uint ntotalpools; + + /* Singly-linked list of available pools. */ + struct pool_header* freepools; + + /* Whenever this arena_object is not associated with an allocated + * arena, the nextarena member is used to link all unassociated + * arena_objects in the singly-linked `unused_arena_objects` list. + * The prevarena member is unused in this case. + * + * When this arena_object is associated with an allocated arena + * with at least one available pool, both members are used in the + * doubly-linked `usable_arenas` list, which is maintained in + * increasing order of `nfreepools` values. + * + * Else this arena_object is associated with an allocated arena + * all of whose pools are in use. `nextarena` and `prevarena` + * are both meaningless in this case. + */ + struct arena_object* nextarena; + struct arena_object* prevarena; +}; + #undef ROUNDUP #define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK) #define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header)) @@ -277,8 +318,9 @@ all partially used pools holding small blocks with "size class idx" i. So usedpools[0] corresponds to blocks of size 8, usedpools[2] to blocks of size 16, and so on: index 2*i <-> blocks of size (i+1)< 8 */ }; -/* - * Free (cached) pools +/*========================================================================== +Arena management. + +`arenas` is a vector of arena_objects. It contains maxarenas entries, some of +which may not be currently used (== they're arena_objects that aren't +currently associated with an allocated arena). Note that arenas proper are +separately malloc'ed. + +Prior to Python 2.5, arenas were never free()'ed. Starting with Python 2.5, +we do try to free() arenas, and use some mild heuristic strategies to increase +the likelihood that arenas eventually can be freed. + +unused_arena_objects + + This is a singly-linked list of the arena_objects that are currently not + being used (no arena is associated with them). Objects are taken off the + head of the list in new_arena(), and are pushed on the head of the list in + PyObject_Free() when the arena is empty. Key invariant: an arena_object + is on this list if and only if its .address member is 0. + +usable_arenas + + This is a doubly-linked list of the arena_objects associated with arenas + that have pools available. These pools are either waiting to be reused, + or have not been used before. The list is sorted to have the most- + allocated arenas first (ascending order based on the nfreepools member). + This means that the next allocation will come from a heavily used arena, + which gives the nearly empty arenas a chance to be returned to the system. + In my unscientific tests this dramatically improved the number of arenas + that could be freed. + +Note that an arena_object associated with an arena all of whose pools are +currently in use isn't on either list. +*/ + +/* Array of objects used to track chunks of memory (arenas). */ +static struct arena_object* arenas = NULL; +/* Number of slots currently allocated in the `arenas` vector. */ +static uint maxarenas = 0; + +/* The head of the singly-linked, NULL-terminated list of available + * arena_objects. */ -static poolp freepools = NULL; /* free list for cached pools */ +static struct arena_object* unused_arena_objects = NULL; -/*==========================================================================*/ -/* Arena management. */ +/* The head of the doubly-linked, NULL-terminated at each end, list of + * arena_objects associated with arenas that have pools available. + */ +static struct arena_object* usable_arenas = NULL; -/* arenas is a vector of arena base addresses, in order of allocation time. - * arenas currently contains narenas entries, and has space allocated - * for at most maxarenas entries. - * - * CAUTION: See the long comment block about thread safety in new_arena(): - * the code currently relies in deep ways on that this vector only grows, - * and only grows by appending at the end. For now we never return an arena - * to the OS. +/* How many arena_objects do we initially allocate? + * 16 = can allocate 16 arenas = 16 * ARENA_SIZE = 4MB before growing the + * `arenas` vector. */ -static uptr *volatile arenas = NULL; /* the pointer itself is volatile */ -static volatile uint narenas = 0; -static uint maxarenas = 0; +#define INITIAL_ARENA_OBJECTS 16 -/* Number of pools still available to be allocated in the current arena. */ -static uint nfreepools = 0; +/* Number of arenas allocated that haven't been free()'d. */ +static ulong narenas_currently_allocated = 0; -/* Free space start address in current arena. This is pool-aligned. */ -static block *arenabase = NULL; +#ifdef PYMALLOC_DEBUG +/* Total number of times malloc() called to allocate an arena. */ +static ulong ntimes_arena_allocated = 0; +/* High water mark (max value ever seen) for narenas_currently_allocated. */ +static ulong narenas_highwater = 0; +#endif -/* Allocate a new arena and return its base address. If we run out of - * memory, return NULL. +/* Allocate a new arena. If we run out of memory, return NULL. Else + * allocate a new arena, and return the address of an arena_object + * describing the new arena. It's expected that the caller will set + * `usable_arenas` to the return value. */ -static block * +static struct arena_object* new_arena(void) { + struct arena_object* arenaobj; uint excess; /* number of bytes above pool alignment */ - block *bp = (block *)malloc(ARENA_SIZE); - if (bp == NULL) - return NULL; #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) _PyObject_DebugMallocStats(); #endif + if (unused_arena_objects == NULL) { + uint i; + uint numarenas; + size_t nbytes; - /* arenabase <- first pool-aligned address in the arena - nfreepools <- number of whole pools that fit after alignment */ - arenabase = bp; - nfreepools = ARENA_SIZE / POOL_SIZE; - assert(POOL_SIZE * nfreepools == ARENA_SIZE); - excess = (uint) ((Py_uintptr_t)bp & POOL_SIZE_MASK); - if (excess != 0) { - --nfreepools; - arenabase += POOL_SIZE - excess; - } + /* Double the number of arena objects on each allocation. + * Note that it's possible for `numarenas` to overflow. + */ + numarenas = maxarenas ? maxarenas << 1 : INITIAL_ARENA_OBJECTS; + if (numarenas <= maxarenas) + return NULL; /* overflow */ + nbytes = numarenas * sizeof(*arenas); + if (nbytes / sizeof(*arenas) != numarenas) + return NULL; /* overflow */ + arenaobj = realloc(arenas, nbytes); + if (arenaobj == NULL) + return NULL; + arenas = arenaobj; + + /* We might need to fix pointers that were copied. However, + * new_arena only gets called when all the pages in the + * previous arenas are full. Thus, there are *no* pointers + * into the old array. Thus, we don't have to worry about + * invalid pointers. Just to be sure, some asserts: + */ + assert(usable_arenas == NULL); + assert(unused_arena_objects == NULL); + + /* Put the new arenas on the unused_arena_objects list. */ + for (i = maxarenas; i < numarenas; ++i) { + arenas[i].address = 0; /* mark as unassociated */ + arenas[i].nextarena = i < numarenas - 1 ? + &arenas[i+1] : NULL; + } - /* Make room for a new entry in the arenas vector. */ - if (arenas == NULL) { - assert(narenas == 0 && maxarenas == 0); - arenas = (uptr *)malloc(16 * sizeof(*arenas)); - if (arenas == NULL) - goto error; - maxarenas = 16; + /* Update globals. */ + unused_arena_objects = &arenas[maxarenas]; + maxarenas = numarenas; } - else if (narenas == maxarenas) { - /* Grow arenas. - * - * Exceedingly subtle: Someone may be calling the pymalloc - * free via PyMem_{DEL, Del, FREE, Free} without holding the - *.GIL. Someone else may simultaneously be calling the - * pymalloc malloc while holding the GIL via, e.g., - * PyObject_New. Now the pymalloc free may index into arenas - * for an address check, while the pymalloc malloc calls - * new_arena and we end up here to grow a new arena *and* - * grow the arenas vector. If the value for arenas pymalloc - * free picks up "vanishes" during this resize, anything may - * happen, and it would be an incredibly rare bug. Therefore - * the code here takes great pains to make sure that, at every - * moment, arenas always points to an intact vector of - * addresses. It doesn't matter whether arenas points to a - * wholly up-to-date vector when pymalloc free checks it in - * this case, because the only legal (and that even this is - * legal is debatable) way to call PyMem_{Del, etc} while not - * holding the GIL is if the memory being released is not - * object memory, i.e. if the address check in pymalloc free - * is supposed to fail. Having an incomplete vector can't - * make a supposed-to-fail case succeed by mistake (it could - * only make a supposed-to-succeed case fail by mistake). - * - * In addition, without a lock we can't know for sure when - * an old vector is no longer referenced, so we simply let - * old vectors leak. - * - * And on top of that, since narenas and arenas can't be - * changed as-a-pair atomically without a lock, we're also - * careful to declare them volatile and ensure that we change - * arenas first. This prevents another thread from picking - * up an narenas value too large for the arenas value it - * reads up (arenas never shrinks). - * - * Read the above 50 times before changing anything in this - * block. + + /* Take the next available arena object off the head of the list. */ + assert(unused_arena_objects != NULL); + arenaobj = unused_arena_objects; + unused_arena_objects = arenaobj->nextarena; + assert(arenaobj->address == 0); + arenaobj->address = (uptr)malloc(ARENA_SIZE); + if (arenaobj->address == 0) { + /* The allocation failed: return NULL after putting the + * arenaobj back. */ - uptr *p; - uint newmax = maxarenas << 1; - if (newmax <= maxarenas) /* overflow */ - goto error; - p = (uptr *)malloc(newmax * sizeof(*arenas)); - if (p == NULL) - goto error; - memcpy(p, arenas, narenas * sizeof(*arenas)); - arenas = p; /* old arenas deliberately leaked */ - maxarenas = newmax; + arenaobj->nextarena = unused_arena_objects; + unused_arena_objects = arenaobj; + return NULL; } - /* Append the new arena address to arenas. */ - assert(narenas < maxarenas); - arenas[narenas] = (uptr)bp; - ++narenas; /* can't overflow, since narenas < maxarenas before */ - return bp; + ++narenas_currently_allocated; +#ifdef PYMALLOC_DEBUG + ++ntimes_arena_allocated; + if (narenas_currently_allocated > narenas_highwater) + narenas_highwater = narenas_currently_allocated; +#endif + arenaobj->freepools = NULL; + /* pool_address <- first pool-aligned address in the arena + nfreepools <- number of whole pools that fit after alignment */ + arenaobj->pool_address = (block*)arenaobj->address; + arenaobj->nfreepools = ARENA_SIZE / POOL_SIZE; + assert(POOL_SIZE * arenaobj->nfreepools == ARENA_SIZE); + excess = (uint)(arenaobj->address & POOL_SIZE_MASK); + if (excess != 0) { + --arenaobj->nfreepools; + arenaobj->pool_address += POOL_SIZE - excess; + } + arenaobj->ntotalpools = arenaobj->nfreepools; -error: - free(bp); - nfreepools = 0; - return NULL; + return arenaobj; } -/* Return true if and only if P is an address that was allocated by - * pymalloc. I must be the index into arenas that the address claims - * to come from. - * - * Tricky: Letting B be the arena base address in arenas[I], P belongs to the - * arena if and only if - * B <= P < B + ARENA_SIZE - * Subtracting B throughout, this is true iff - * 0 <= P-B < ARENA_SIZE - * By using unsigned arithmetic, the "0 <=" half of the test can be skipped. - * - * Obscure: A PyMem "free memory" function can call the pymalloc free or - * realloc before the first arena has been allocated. arenas is still - * NULL in that case. We're relying on that narenas is also 0 in that case, - * so the (I) < narenas must be false, saving us from trying to index into - * a NULL arenas. - */ -#define Py_ADDRESS_IN_RANGE(P, POOL) \ - ((POOL)->arenaindex < narenas && \ - (uptr)(P) - arenas[(POOL)->arenaindex] < (uptr)ARENA_SIZE) +/* +Py_ADDRESS_IN_RANGE(P, POOL) + +Return true if and only if P is an address that was allocated by pymalloc. +POOL must be the pool address associated with P, i.e., POOL = POOL_ADDR(P) +(the caller is asked to compute this because the macro expands POOL more than +once, and for efficiency it's best for the caller to assign POOL_ADDR(P) to a +variable and pass the latter to the macro; because Py_ADDRESS_IN_RANGE is +called on every alloc/realloc/free, micro-efficiency is important here). + +Tricky: Let B be the arena base address associated with the pool, B = +arenas[(POOL)->arenaindex].address. Then P belongs to the arena if and only if + + B <= P < B + ARENA_SIZE + +Subtracting B throughout, this is true iff + + 0 <= P-B < ARENA_SIZE + +By using unsigned arithmetic, the "0 <=" half of the test can be skipped. + +Obscure: A PyMem "free memory" function can call the pymalloc free or realloc +before the first arena has been allocated. `arenas` is still NULL in that +case. We're relying on that maxarenas is also 0 in that case, so that +(POOL)->arenaindex < maxarenas must be false, saving us from trying to index +into a NULL arenas. + +Details: given P and POOL, the arena_object corresponding to P is AO = +arenas[(POOL)->arenaindex]. Suppose obmalloc controls P. Then (barring wild +stores, etc), POOL is the correct address of P's pool, AO.address is the +correct base address of the pool's arena, and P must be within ARENA_SIZE of +AO.address. In addition, AO.address is not 0 (no arena can start at address 0 +(NULL)). Therefore Py_ADDRESS_IN_RANGE correctly reports that obmalloc +controls P. + +Now suppose obmalloc does not control P (e.g., P was obtained via a direct +call to the system malloc() or realloc()). (POOL)->arenaindex may be anything +in this case -- it may even be uninitialized trash. If the trash arenaindex +is >= maxarenas, the macro correctly concludes at once that obmalloc doesn't +control P. + +Else arenaindex is < maxarena, and AO is read up. If AO corresponds to an +allocated arena, obmalloc controls all the memory in slice AO.address : +AO.address+ARENA_SIZE. By case assumption, P is not controlled by obmalloc, +so P doesn't lie in that slice, so the macro correctly reports that P is not +controlled by obmalloc. + +Finally, if P is not controlled by obmalloc and AO corresponds to an unused +arena_object (one not currently associated with an allocated arena), +AO.address is 0, and the second test in the macro reduces to: + + P < ARENA_SIZE + +If P >= ARENA_SIZE (extremely likely), the macro again correctly concludes +that P is not controlled by obmalloc. However, if P < ARENA_SIZE, this part +of the test still passes, and the third clause (AO.address != 0) is necessary +to get the correct result: AO.address is 0 in this case, so the macro +correctly reports that P is not controlled by obmalloc (despite that P lies in +slice AO.address : AO.address + ARENA_SIZE). + +Note: The third (AO.address != 0) clause was added in Python 2.5. Before +2.5, arenas were never free()'ed, and an arenaindex < maxarena always +corresponded to a currently-allocated arena, so the "P is not controlled by +obmalloc, AO corresponds to an unused arena_object, and P < ARENA_SIZE" case +was impossible. + +Note that the logic is excruciating, and reading up possibly uninitialized +memory when P is not controlled by obmalloc (to get at (POOL)->arenaindex) +creates problems for some memory debuggers. The overwhelming advantage is +that this test determines whether an arbitrary address is controlled by +obmalloc in a small constant time, independent of the number of arenas +obmalloc controls. Since this test is needed at every entry point, it's +extremely desirable that it be this fast. +*/ +#define Py_ADDRESS_IN_RANGE(P, POOL) \ + ((POOL)->arenaindex < maxarenas && \ + (uptr)(P) - arenas[(POOL)->arenaindex].address < (uptr)ARENA_SIZE && \ + arenas[(POOL)->arenaindex].address != 0) + /* This is only useful when running memory debuggers such as * Purify or Valgrind. Uncomment to use. @@ -599,7 +733,7 @@ PyObject_Malloc(size_t nbytes) /* * Most frequent paths first */ - size = (uint )(nbytes - 1) >> ALIGNMENT_SHIFT; + size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; pool = usedpools[size + size]; if (pool != pool->nextpool) { /* @@ -614,22 +748,18 @@ PyObject_Malloc(size_t nbytes) return (void *)bp; } /* - * Reached the end of the free list, try to extend it + * Reached the end of the free list, try to extend it. */ if (pool->nextoffset <= pool->maxnextoffset) { - /* - * There is room for another block - */ - pool->freeblock = (block *)pool + + /* There is room for another block. */ + pool->freeblock = (block*)pool + pool->nextoffset; pool->nextoffset += INDEX2SIZE(size); *(block **)(pool->freeblock) = NULL; UNLOCK(); return (void *)bp; } - /* - * Pool is full, unlink from used pools - */ + /* Pool is full, unlink from used pools. */ next = pool->nextpool; pool = pool->prevpool; next->prevpool = pool; @@ -637,19 +767,68 @@ PyObject_Malloc(size_t nbytes) UNLOCK(); return (void *)bp; } - /* - * Try to get a cached free pool + + /* There isn't a pool of the right size class immediately + * available: use a free pool. */ - pool = freepools; + if (usable_arenas == NULL) { + /* No arena has a free pool: allocate a new arena. */ +#ifdef WITH_MEMORY_LIMITS + if (narenas_currently_allocated >= MAX_ARENAS) { + UNLOCK(); + goto redirect; + } +#endif + usable_arenas = new_arena(); + if (usable_arenas == NULL) { + UNLOCK(); + goto redirect; + } + usable_arenas->nextarena = + usable_arenas->prevarena = NULL; + } + assert(usable_arenas->address != 0); + + /* Try to get a cached free pool. */ + pool = usable_arenas->freepools; if (pool != NULL) { - /* - * Unlink from cached pools + /* Unlink from cached pools. */ + usable_arenas->freepools = pool->nextpool; + + /* This arena already had the smallest nfreepools + * value, so decreasing nfreepools doesn't change + * that, and we don't need to rearrange the + * usable_arenas list. However, if the arena has + * become wholly allocated, we need to remove its + * arena_object from usable_arenas. */ - freepools = pool->nextpool; + --usable_arenas->nfreepools; + if (usable_arenas->nfreepools == 0) { + /* Wholly allocated: remove. */ + assert(usable_arenas->freepools == NULL); + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } + } + else { + /* nfreepools > 0: it must be that freepools + * isn't NULL, or that we haven't yet carved + * off all the arena's pools for the first + * time. + */ + assert(usable_arenas->freepools != NULL || + usable_arenas->pool_address <= + (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + } init_pool: - /* - * Frontlink to used pools - */ + /* Frontlink to used pools. */ next = usedpools[size + size]; /* == prev */ pool->nextpool = next; pool->prevpool = next; @@ -657,8 +836,7 @@ PyObject_Malloc(size_t nbytes) next->prevpool = pool; pool->ref.count = 1; if (pool->szidx == size) { - /* - * Luckily, this pool last contained blocks + /* Luckily, this pool last contained blocks * of the same size class, so its header * and free list are already initialized. */ @@ -682,39 +860,38 @@ PyObject_Malloc(size_t nbytes) UNLOCK(); return (void *)bp; } - /* - * Allocate new pool - */ - if (nfreepools) { - commit_pool: - --nfreepools; - pool = (poolp)arenabase; - arenabase += POOL_SIZE; - pool->arenaindex = narenas - 1; - pool->szidx = DUMMY_SIZE_IDX; - goto init_pool; - } - /* - * Allocate new arena - */ -#ifdef WITH_MEMORY_LIMITS - if (!(narenas < MAX_ARENAS)) { - UNLOCK(); - goto redirect; + + /* Carve off a new pool. */ + assert(usable_arenas->nfreepools > 0); + assert(usable_arenas->freepools == NULL); + pool = (poolp)usable_arenas->pool_address; + assert((block*)pool <= (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + pool->arenaindex = usable_arenas - arenas; + assert(&arenas[pool->arenaindex] == usable_arenas); + pool->szidx = DUMMY_SIZE_IDX; + usable_arenas->pool_address += POOL_SIZE; + --usable_arenas->nfreepools; + + if (usable_arenas->nfreepools == 0) { + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + /* Unlink the arena: it is completely allocated. */ + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } } -#endif - bp = new_arena(); - if (bp != NULL) - goto commit_pool; - UNLOCK(); - goto redirect; + + goto init_pool; } /* The small block allocator ends here. */ redirect: - /* - * Redirect the original request to the underlying (libc) allocator. + /* Redirect the original request to the underlying (libc) allocator. * We jump here on bigger requests, on error in the code above (as a * last chance to serve the request) or when the max memory limit * has been reached. @@ -742,8 +919,7 @@ PyObject_Free(void *p) if (Py_ADDRESS_IN_RANGE(p, pool)) { /* We allocated this address. */ LOCK(); - /* - * Link p to the start of the pool's freeblock list. Since + /* Link p to the start of the pool's freeblock list. Since * the pool had at least the p block outstanding, the pool * wasn't empty (so it's already in a usedpools[] list, or * was full and is in no list -- it's not in the freeblocks @@ -753,8 +929,10 @@ PyObject_Free(void *p) *(block **)p = lastfree = pool->freeblock; pool->freeblock = (block *)p; if (lastfree) { - /* - * freeblock wasn't NULL, so the pool wasn't full, + struct arena_object* ao; + uint nf; /* ao->nfreepools */ + + /* freeblock wasn't NULL, so the pool wasn't full, * and the pool is in a usedpools[] list. */ if (--pool->ref.count != 0) { @@ -762,8 +940,7 @@ PyObject_Free(void *p) UNLOCK(); return; } - /* - * Pool is now empty: unlink from usedpools, and + /* Pool is now empty: unlink from usedpools, and * link to the front of freepools. This ensures that * previously freed pools will be allocated later * (being not referenced, they are perhaps paged out). @@ -772,16 +949,147 @@ PyObject_Free(void *p) prev = pool->prevpool; next->prevpool = prev; prev->nextpool = next; - /* Link to freepools. This is a singly-linked list, - * and pool->prevpool isn't used there. + + /* Link the pool to freepools. This is a singly-linked + * list, and pool->prevpool isn't used there. + */ + ao = &arenas[pool->arenaindex]; + pool->nextpool = ao->freepools; + ao->freepools = pool; + nf = ++ao->nfreepools; + + /* All the rest is arena management. We just freed + * a pool, and there are 4 cases for arena mgmt: + * 1. If all the pools are free, return the arena to + * the system free(). + * 2. If this is the only free pool in the arena, + * add the arena back to the `usable_arenas` list. + * 3. If the "next" arena has a smaller count of free + * pools, we have to "slide this arena right" to + * restore that usable_arenas is sorted in order of + * nfreepools. + * 4. Else there's nothing more to do. + */ + if (nf == ao->ntotalpools) { + /* Case 1. First unlink ao from usable_arenas. + */ + assert(ao->prevarena == NULL || + ao->prevarena->address != 0); + assert(ao ->nextarena == NULL || + ao->nextarena->address != 0); + + /* Fix the pointer in the prevarena, or the + * usable_arenas pointer. + */ + if (ao->prevarena == NULL) { + usable_arenas = ao->nextarena; + assert(usable_arenas == NULL || + usable_arenas->address != 0); + } + else { + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = + ao->nextarena; + } + /* Fix the pointer in the nextarena. */ + if (ao->nextarena != NULL) { + assert(ao->nextarena->prevarena == ao); + ao->nextarena->prevarena = + ao->prevarena; + } + /* Record that this arena_object slot is + * available to be reused. + */ + ao->nextarena = unused_arena_objects; + unused_arena_objects = ao; + + /* Free the entire arena. */ + free((void *)ao->address); + ao->address = 0; /* mark unassociated */ + --narenas_currently_allocated; + + UNLOCK(); + return; + } + if (nf == 1) { + /* Case 2. Put ao at the head of + * usable_arenas. Note that because + * ao->nfreepools was 0 before, ao isn't + * currently on the usable_arenas list. + */ + ao->nextarena = usable_arenas; + ao->prevarena = NULL; + if (usable_arenas) + usable_arenas->prevarena = ao; + usable_arenas = ao; + assert(usable_arenas->address != 0); + + UNLOCK(); + return; + } + /* If this arena is now out of order, we need to keep + * the list sorted. The list is kept sorted so that + * the "most full" arenas are used first, which allows + * the nearly empty arenas to be completely freed. In + * a few un-scientific tests, it seems like this + * approach allowed a lot more memory to be freed. + */ + if (ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools) { + /* Case 4. Nothing to do. */ + UNLOCK(); + return; + } + /* Case 3: We have to move the arena towards the end + * of the list, because it has more free pools than + * the arena to its right. + * First unlink ao from usable_arenas. */ - pool->nextpool = freepools; - freepools = pool; + if (ao->prevarena != NULL) { + /* ao isn't at the head of the list */ + assert(ao->prevarena->nextarena == ao); + ao->prevarena->nextarena = ao->nextarena; + } + else { + /* ao is at the head of the list */ + assert(usable_arenas == ao); + usable_arenas = ao->nextarena; + } + ao->nextarena->prevarena = ao->prevarena; + + /* Locate the new insertion point by iterating over + * the list, using our nextarena pointer. + */ + while (ao->nextarena != NULL && + nf > ao->nextarena->nfreepools) { + ao->prevarena = ao->nextarena; + ao->nextarena = ao->nextarena->nextarena; + } + + /* Insert ao at this point. */ + assert(ao->nextarena == NULL || + ao->prevarena == ao->nextarena->prevarena); + assert(ao->prevarena->nextarena == ao->nextarena); + + ao->prevarena->nextarena = ao; + if (ao->nextarena != NULL) + ao->nextarena->prevarena = ao; + + /* Verify that the swaps worked. */ + assert(ao->nextarena == NULL || + nf <= ao->nextarena->nfreepools); + assert(ao->prevarena == NULL || + nf > ao->prevarena->nfreepools); + assert(ao->nextarena == NULL || + ao->nextarena->prevarena == ao); + assert((usable_arenas == ao && + ao->prevarena == NULL) || + ao->prevarena->nextarena == ao); + UNLOCK(); return; } - /* - * Pool was full, so doesn't currently live in any list: + /* Pool was full, so doesn't currently live in any list: * link it to the front of the appropriate usedpools[] list. * This mimics LRU pool usage for new allocations and * targets optimal filling when several pools contain @@ -1302,6 +1610,8 @@ _PyObject_DebugMallocStats(void) * full pools. */ ulong quantization = 0; + /* # of arenas actually allocated. */ + ulong narenas = 0; /* running total -- should equal narenas * ARENA_SIZE */ ulong total; char buf[128]; @@ -1316,36 +1626,38 @@ _PyObject_DebugMallocStats(void) * to march over all the arenas. If we're lucky, most of the memory * will be living in full pools -- would be a shame to miss them. */ - for (i = 0; i < narenas; ++i) { + for (i = 0; i < maxarenas; ++i) { uint poolsinarena; uint j; - uptr base = arenas[i]; + uptr base = arenas[i].address; + + /* Skip arenas which are not allocated. */ + if (arenas[i].address == (uptr)NULL) + continue; + narenas += 1; + + poolsinarena = arenas[i].ntotalpools; + numfreepools += arenas[i].nfreepools; /* round up to pool alignment */ - poolsinarena = ARENA_SIZE / POOL_SIZE; if (base & (uptr)POOL_SIZE_MASK) { - --poolsinarena; arena_alignment += POOL_SIZE; base &= ~(uptr)POOL_SIZE_MASK; base += POOL_SIZE; } - if (i == narenas - 1) { - /* current arena may have raw memory at the end */ - numfreepools += nfreepools; - poolsinarena -= nfreepools; - } - /* visit every pool in the arena */ - for (j = 0; j < poolsinarena; ++j, base += POOL_SIZE) { + assert(base <= (uptr) arenas[i].pool_address); + for (j = 0; + base < (uptr) arenas[i].pool_address; + ++j, base += POOL_SIZE) { poolp p = (poolp)base; const uint sz = p->szidx; uint freeblocks; if (p->ref.count == 0) { /* currently unused */ - ++numfreepools; - assert(pool_is_in_list(p, freepools)); + assert(pool_is_in_list(p, arenas[i].freepools)); continue; } ++numpools[sz]; @@ -1358,6 +1670,7 @@ _PyObject_DebugMallocStats(void) #endif } } + assert(narenas == narenas_currently_allocated); fputc('\n', stderr); fputs("class size num pools blocks in use avail blocks\n" @@ -1383,9 +1696,14 @@ _PyObject_DebugMallocStats(void) fputc('\n', stderr); (void)printone("# times object malloc called", serialno); + (void)printone("# arenas allocated total", ntimes_arena_allocated); + (void)printone("# arenas reclaimed", ntimes_arena_allocated - narenas); + (void)printone("# arenas highwater mark", narenas_highwater); + (void)printone("# arenas allocated current", narenas); + PyOS_snprintf(buf, sizeof(buf), - "%u arenas * %d bytes/arena", narenas, ARENA_SIZE); - (void)printone(buf, (ulong)narenas * ARENA_SIZE); + "%lu arenas * %d bytes/arena", narenas, ARENA_SIZE); + (void)printone(buf, narenas * ARENA_SIZE); fputc('\n', stderr); @@ -1405,12 +1723,14 @@ _PyObject_DebugMallocStats(void) #endif /* PYMALLOC_DEBUG */ #ifdef Py_USING_MEMORY_DEBUGGER -/* Make this function last so gcc won't inline it - since the definition is after the reference. */ +/* Make this function last so gcc won't inline it since the definition is + * after the reference. + */ int Py_ADDRESS_IN_RANGE(void *P, poolp pool) { - return ((pool->arenaindex) < narenas && - (uptr)(P) - arenas[pool->arenaindex] < (uptr)ARENA_SIZE); + return pool->arenaindex < maxarenas && + (uptr)P - arenas[pool->arenaindex].address < (uptr)ARENA_SIZE && + arenas[pool->arenaindex].address != 0; } #endif diff --git a/Python/codecs.c b/Python/codecs.c index 253bc39..0e8c374 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -260,6 +260,56 @@ PyObject *PyCodec_Decoder(const char *encoding) return NULL; } +PyObject *PyCodec_IncrementalEncoder(const char *encoding, + const char *errors) +{ + PyObject *codecs, *ret, *encoder; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + encoder = PyObject_GetAttrString(codecs, "incrementalencoder"); + if (encoder == NULL) { + Py_DECREF(codecs); + return NULL; + } + if (errors) + ret = PyObject_CallFunction(encoder, "O", errors); + else + ret = PyObject_CallFunction(encoder, NULL); + Py_DECREF(encoder); + Py_DECREF(codecs); + return ret; + + onError: + return NULL; +} + +PyObject *PyCodec_IncrementalDecoder(const char *encoding, + const char *errors) +{ + PyObject *codecs, *ret, *decoder; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + decoder = PyObject_GetAttrString(codecs, "incrementaldecoder"); + if (decoder == NULL) { + Py_DECREF(codecs); + return NULL; + } + if (errors) + ret = PyObject_CallFunction(decoder, "O", errors); + else + ret = PyObject_CallFunction(decoder, NULL); + Py_DECREF(decoder); + Py_DECREF(codecs); + return ret; + + onError: + return NULL; +} + PyObject *PyCodec_StreamReader(const char *encoding, PyObject *stream, const char *errors) diff --git a/Tools/buildbot/clean.bat b/Tools/buildbot/clean.bat index d28262e..4b94922 100644 --- a/Tools/buildbot/clean.bat +++ b/Tools/buildbot/clean.bat @@ -1,3 +1,6 @@ @rem Used by the buildbot "clean" step. call "%VS71COMNTOOLS%vsvars32.bat" -devenv.com /clean Debug PCbuild\pcbuild.sln +cd PCbuild +@echo Deleting .pyc/.pyo files ... +python_d.exe rmpyc.py +devenv.com /clean Debug pcbuild.sln diff --git a/Tools/unicode/Makefile b/Tools/unicode/Makefile index 34a9df9..f266d4d 100644 --- a/Tools/unicode/Makefile +++ b/Tools/unicode/Makefile @@ -44,11 +44,11 @@ windows: build/ $(RM) -f build/readme.* iso: build/ - $(PYTHON) gencodec.py MAPPINGS/ISO8859/ build/iso + $(PYTHON) gencodec.py MAPPINGS/ISO8859/ build/ iso $(RM) -f build/isoreadme.* apple: build/ - $(PYTHON) gencodec.py MAPPINGS/VENDORS/APPLE/ build/mac_ + $(PYTHON) gencodec.py MAPPINGS/VENDORS/APPLE/ build/ mac_ $(RM) build/mac_dingbats.* $(RM) build/mac_japanese.* $(RM) build/mac_chin* diff --git a/Tools/unicode/gencodec.py b/Tools/unicode/gencodec.py index 494164a..bb1c9da 100644 --- a/Tools/unicode/gencodec.py +++ b/Tools/unicode/gencodec.py @@ -248,7 +248,7 @@ def python_tabledef_code(varname, map, comments=1, key_precision=2): append(')') return l -def codegen(name, map, comments=1): +def codegen(name, map, encodingname, comments=1): """ Returns Python source for the given map. @@ -272,7 +272,7 @@ def codegen(name, map, comments=1): l = [ '''\ -""" Python Character Mapping Codec generated from '%s' with gencodec.py. +""" Python Character Mapping Codec %s generated from '%s' with gencodec.py. """#" @@ -283,11 +283,9 @@ import codecs class Codec(codecs.Codec): def encode(self,input,errors='strict'): - return codecs.charmap_encode(input,errors,encoding_map) - def decode(self,input,errors='strict'): -''' % name + def decode(self,input,errors='strict'):''' % (encodingname, name) ] if decoding_table_code: l.append('''\ @@ -297,6 +295,20 @@ class Codec(codecs.Codec): return codecs.charmap_decode(input,errors,decoding_map)''') l.append(''' +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input,self.errors,encoding_map)[0] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False):''') + if decoding_table_code: + l.append('''\ + return codecs.charmap_decode(input,self.errors,decoding_table)[0]''') + else: + l.append('''\ + return codecs.charmap_decode(input,self.errors,decoding_map)[0]''') + + l.append(''' class StreamWriter(Codec,codecs.StreamWriter): pass @@ -306,9 +318,16 @@ class StreamReader(Codec,codecs.StreamReader): ### encodings module API def getregentry(): - - return (Codec().encode,Codec().decode,StreamReader,StreamWriter) -''') + return codecs.CodecInfo(( + name=%r, + Codec().encode, + Codec().decode, + streamwriter=StreamWriter, + streamreader=StreamReader, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + )) +''' % encodingname.replace('_', '-')) # Add decoding table or map (with preference to the table) if not decoding_table_code: @@ -331,11 +350,11 @@ def getregentry(): # Final new-line l.append('\n') - return '\n'.join(l) + return '\n'.join(l).expandtabs() -def pymap(name,map,pyfile,comments=1): +def pymap(name,map,pyfile,encodingname,comments=1): - code = codegen(name,map,comments) + code = codegen(name,map,encodingname,comments) f = open(pyfile,'w') f.write(code) f.close() @@ -349,7 +368,7 @@ def marshalmap(name,map,marshalfile): marshal.dump(d,f) f.close() -def convertdir(dir,prefix='',comments=1): +def convertdir(dir, dirprefix='', nameprefix='', comments=1): mapnames = os.listdir(dir) for mapname in mapnames: @@ -360,38 +379,40 @@ def convertdir(dir,prefix='',comments=1): name = name.replace('-','_') name = name.split('.')[0] name = name.lower() + name = nameprefix + name codefile = name + '.py' marshalfile = name + '.mapping' print 'converting %s to %s and %s' % (mapname, - prefix + codefile, - prefix + marshalfile) + dirprefix + codefile, + dirprefix + marshalfile) try: map = readmap(os.path.join(dir,mapname)) if not map: print '* map is empty; skipping' else: - pymap(mappathname, map, prefix + codefile,comments) - marshalmap(mappathname, map, prefix + marshalfile) + pymap(mappathname, map, dirprefix + codefile,name,comments) + marshalmap(mappathname, map, dirprefix + marshalfile) except ValueError, why: print '* conversion failed: %s' % why raise -def rewritepythondir(dir,prefix='',comments=1): +def rewritepythondir(dir, dirprefix='', comments=1): mapnames = os.listdir(dir) for mapname in mapnames: if not mapname.endswith('.mapping'): continue - codefile = mapname[:-len('.mapping')] + '.py' + name = mapname[:-len('.mapping')] + codefile = name + '.py' print 'converting %s to %s' % (mapname, - prefix + codefile) + dirprefix + codefile) try: map = marshal.load(open(os.path.join(dir,mapname), 'rb')) if not map: print '* map is empty; skipping' else: - pymap(mapname, map, prefix + codefile,comments) + pymap(mapname, map, dirprefix + codefile,name,comments) except ValueError, why: print '* conversion failed: %s' % why -- cgit v0.12 From 725ccd181020ba04bfeea41078ddb0c82c284c63 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 09:46:12 +0000 Subject: Merge trunk's 43068, deleting re.py. This leaves the branch in a broken state (unless you happen to have a re.pyc lying around), but it'll be fixed in the next merge. --- Lib/re.py | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Lib/re.py diff --git a/Lib/re.py b/Lib/re.py deleted file mode 100644 index f1cbe2c..0000000 --- a/Lib/re.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Minimal "re" compatibility wrapper. See "sre" for documentation.""" - -engine = "sre" # Some apps might use this undocumented variable - -from sre import * -from sre import __all__ -- cgit v0.12 From 9ada3d6e29d5165dadacbe6be07bcd35cfbef59d Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 09:47:09 +0000 Subject: Merge trunk up to 43069, putting re.py back and hopefully making the branch usable again. --- Lib/re.py | 315 +++++++++++++++++++++++++++++++++++++++++++++++ Lib/sre.py | 315 ----------------------------------------------- Lib/test/regrtest.py | 4 +- Lib/test/test___all__.py | 1 - Lib/test/test_re.py | 2 +- Modules/_sre.c | 6 +- 6 files changed, 322 insertions(+), 321 deletions(-) create mode 100644 Lib/re.py delete mode 100644 Lib/sre.py diff --git a/Lib/re.py b/Lib/re.py new file mode 100644 index 0000000..a33e34e --- /dev/null +++ b/Lib/re.py @@ -0,0 +1,315 @@ +# +# Secret Labs' Regular Expression Engine +# +# re-compatible interface for the sre matching engine +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# + +r"""Support for regular expressions (RE). + +This module provides regular expression matching operations similar to +those found in Perl. It supports both 8-bit and Unicode strings; both +the pattern and the strings being processed can contain null bytes and +characters outside the US ASCII range. + +Regular expressions can contain both special and ordinary characters. +Most ordinary characters, like "A", "a", or "0", are the simplest +regular expressions; they simply match themselves. You can +concatenate ordinary characters, so last matches the string 'last'. + +The special characters are: + "." Matches any character except a newline. + "^" Matches the start of the string. + "$" Matches the end of the string. + "*" Matches 0 or more (greedy) repetitions of the preceding RE. + Greedy means that it will match as many repetitions as possible. + "+" Matches 1 or more (greedy) repetitions of the preceding RE. + "?" Matches 0 or 1 (greedy) of the preceding RE. + *?,+?,?? Non-greedy versions of the previous three special characters. + {m,n} Matches from m to n repetitions of the preceding RE. + {m,n}? Non-greedy version of the above. + "\\" Either escapes special characters or signals a special sequence. + [] Indicates a set of characters. + A "^" as the first character indicates a complementing set. + "|" A|B, creates an RE that will match either A or B. + (...) Matches the RE inside the parentheses. + The contents can be retrieved or matched later in the string. + (?iLmsux) Set the I, L, M, S, U, or X flag for the RE (see below). + (?:...) Non-grouping version of regular parentheses. + (?P...) The substring matched by the group is accessible by name. + (?P=name) Matches the text matched earlier by the group named name. + (?#...) A comment; ignored. + (?=...) Matches if ... matches next, but doesn't consume the string. + (?!...) Matches if ... doesn't match next. + +The special sequences consist of "\\" and a character from the list +below. If the ordinary character is not on the list, then the +resulting RE will match the second character. + \number Matches the contents of the group of the same number. + \A Matches only at the start of the string. + \Z Matches only at the end of the string. + \b Matches the empty string, but only at the start or end of a word. + \B Matches the empty string, but not at the start or end of a word. + \d Matches any decimal digit; equivalent to the set [0-9]. + \D Matches any non-digit character; equivalent to the set [^0-9]. + \s Matches any whitespace character; equivalent to [ \t\n\r\f\v]. + \S Matches any non-whitespace character; equiv. to [^ \t\n\r\f\v]. + \w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]. + With LOCALE, it will match the set [0-9_] plus characters defined + as letters for the current locale. + \W Matches the complement of \w. + \\ Matches a literal backslash. + +This module exports the following functions: + match Match a regular expression pattern to the beginning of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string. + subn Same as sub, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. + findall Find all occurrences of a pattern in a string. + compile Compile a pattern into a RegexObject. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics in a string. + +Some of the functions in this module takes flags as optional parameters: + I IGNORECASE Perform case-insensitive matching. + L LOCALE Make \w, \W, \b, \B, dependent on the current locale. + M MULTILINE "^" matches the beginning of lines as well as the string. + "$" matches the end of lines as well as the string. + S DOTALL "." matches any character at all, including the newline. + X VERBOSE Ignore whitespace and comments for nicer looking RE's. + U UNICODE Make \w, \W, \b, \B, dependent on the Unicode locale. + +This module also defines an exception 'error'. + +""" + +import sys +import sre_compile +import sre_parse + +# public symbols +__all__ = [ "match", "search", "sub", "subn", "split", "findall", + "compile", "purge", "template", "escape", "I", "L", "M", "S", "X", + "U", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE", + "UNICODE", "error" ] + +__version__ = "2.2.1" + +# flags +I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case +L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale +U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode locale +M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline +S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline +X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments + +# sre extensions (experimental, don't rely on these) +T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking +DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation + +# sre exception +error = sre_compile.error + +# -------------------------------------------------------------------- +# public interface + +def match(pattern, string, flags=0): + """Try to apply the pattern at the start of the string, returning + a match object, or None if no match was found.""" + return _compile(pattern, flags).match(string) + +def search(pattern, string, flags=0): + """Scan through string looking for a match to the pattern, returning + a match object, or None if no match was found.""" + return _compile(pattern, flags).search(string) + +def sub(pattern, repl, string, count=0): + """Return the string obtained by replacing the leftmost + non-overlapping occurrences of the pattern in string by the + replacement repl. repl can be either a string or a callable; + if a callable, it's passed the match object and must return + a replacement string to be used.""" + return _compile(pattern, 0).sub(repl, string, count) + +def subn(pattern, repl, string, count=0): + """Return a 2-tuple containing (new_string, number). + new_string is the string obtained by replacing the leftmost + non-overlapping occurrences of the pattern in the source + string by the replacement repl. number is the number of + substitutions that were made. repl can be either a string or a + callable; if a callable, it's passed the match object and must + return a replacement string to be used.""" + return _compile(pattern, 0).subn(repl, string, count) + +def split(pattern, string, maxsplit=0): + """Split the source string by the occurrences of the pattern, + returning a list containing the resulting substrings.""" + return _compile(pattern, 0).split(string, maxsplit) + +def findall(pattern, string, flags=0): + """Return a list of all non-overlapping matches in the string. + + If one or more groups are present in the pattern, return a + list of groups; this will be a list of tuples if the pattern + has more than one group. + + Empty matches are included in the result.""" + return _compile(pattern, flags).findall(string) + +if sys.hexversion >= 0x02020000: + __all__.append("finditer") + def finditer(pattern, string, flags=0): + """Return an iterator over all non-overlapping matches in the + string. For each match, the iterator returns a match object. + + Empty matches are included in the result.""" + return _compile(pattern, flags).finditer(string) + +def compile(pattern, flags=0): + "Compile a regular expression pattern, returning a pattern object." + return _compile(pattern, flags) + +def purge(): + "Clear the regular expression cache" + _cache.clear() + _cache_repl.clear() + +def template(pattern, flags=0): + "Compile a template pattern, returning a pattern object" + return _compile(pattern, flags|T) + +_alphanum = {} +for c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890': + _alphanum[c] = 1 +del c + +def escape(pattern): + "Escape all non-alphanumeric characters in pattern." + s = list(pattern) + alphanum = _alphanum + for i in range(len(pattern)): + c = pattern[i] + if c not in alphanum: + if c == "\000": + s[i] = "\\000" + else: + s[i] = "\\" + c + return pattern[:0].join(s) + +# -------------------------------------------------------------------- +# internals + +_cache = {} +_cache_repl = {} + +_pattern_type = type(sre_compile.compile("", 0)) + +_MAXCACHE = 100 + +def _compile(*key): + # internal: compile pattern + cachekey = (type(key[0]),) + key + p = _cache.get(cachekey) + if p is not None: + return p + pattern, flags = key + if isinstance(pattern, _pattern_type): + return pattern + if not sre_compile.isstring(pattern): + raise TypeError, "first argument must be string or compiled pattern" + try: + p = sre_compile.compile(pattern, flags) + except error, v: + raise error, v # invalid expression + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[cachekey] = p + return p + +def _compile_repl(*key): + # internal: compile replacement pattern + p = _cache_repl.get(key) + if p is not None: + return p + repl, pattern = key + try: + p = sre_parse.parse_template(repl, pattern) + except error, v: + raise error, v # invalid expression + if len(_cache_repl) >= _MAXCACHE: + _cache_repl.clear() + _cache_repl[key] = p + return p + +def _expand(pattern, match, template): + # internal: match.expand implementation hook + template = sre_parse.parse_template(template, pattern) + return sre_parse.expand_template(template, match) + +def _subx(pattern, template): + # internal: pattern.sub/subn implementation helper + template = _compile_repl(template, pattern) + if not template[0] and len(template[1]) == 1: + # literal replacement + return template[1][0] + def filter(match, template=template): + return sre_parse.expand_template(template, match) + return filter + +# register myself for pickling + +import copy_reg + +def _pickle(p): + return _compile, (p.pattern, p.flags) + +copy_reg.pickle(_pattern_type, _pickle, _compile) + +# -------------------------------------------------------------------- +# experimental stuff (see python-dev discussions for details) + +class Scanner: + def __init__(self, lexicon, flags=0): + from sre_constants import BRANCH, SUBPATTERN + self.lexicon = lexicon + # combine phrases into a compound pattern + p = [] + s = sre_parse.Pattern() + s.flags = flags + for phrase, action in lexicon: + p.append(sre_parse.SubPattern(s, [ + (SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))), + ])) + p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) + s.groups = len(p) + self.scanner = sre_compile.compile(p) + def scan(self, string): + result = [] + append = result.append + match = self.scanner.scanner(string).match + i = 0 + while 1: + m = match() + if not m: + break + j = m.end() + if i == j: + break + action = self.lexicon[m.lastindex-1][1] + if callable(action): + self.match = m + action = action(self, m.group()) + if action is not None: + append(action) + i = j + return result, string[i:] diff --git a/Lib/sre.py b/Lib/sre.py deleted file mode 100644 index a33e34e..0000000 --- a/Lib/sre.py +++ /dev/null @@ -1,315 +0,0 @@ -# -# Secret Labs' Regular Expression Engine -# -# re-compatible interface for the sre matching engine -# -# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. -# -# This version of the SRE library can be redistributed under CNRI's -# Python 1.6 license. For any other use, please contact Secret Labs -# AB (info@pythonware.com). -# -# Portions of this engine have been developed in cooperation with -# CNRI. Hewlett-Packard provided funding for 1.6 integration and -# other compatibility work. -# - -r"""Support for regular expressions (RE). - -This module provides regular expression matching operations similar to -those found in Perl. It supports both 8-bit and Unicode strings; both -the pattern and the strings being processed can contain null bytes and -characters outside the US ASCII range. - -Regular expressions can contain both special and ordinary characters. -Most ordinary characters, like "A", "a", or "0", are the simplest -regular expressions; they simply match themselves. You can -concatenate ordinary characters, so last matches the string 'last'. - -The special characters are: - "." Matches any character except a newline. - "^" Matches the start of the string. - "$" Matches the end of the string. - "*" Matches 0 or more (greedy) repetitions of the preceding RE. - Greedy means that it will match as many repetitions as possible. - "+" Matches 1 or more (greedy) repetitions of the preceding RE. - "?" Matches 0 or 1 (greedy) of the preceding RE. - *?,+?,?? Non-greedy versions of the previous three special characters. - {m,n} Matches from m to n repetitions of the preceding RE. - {m,n}? Non-greedy version of the above. - "\\" Either escapes special characters or signals a special sequence. - [] Indicates a set of characters. - A "^" as the first character indicates a complementing set. - "|" A|B, creates an RE that will match either A or B. - (...) Matches the RE inside the parentheses. - The contents can be retrieved or matched later in the string. - (?iLmsux) Set the I, L, M, S, U, or X flag for the RE (see below). - (?:...) Non-grouping version of regular parentheses. - (?P...) The substring matched by the group is accessible by name. - (?P=name) Matches the text matched earlier by the group named name. - (?#...) A comment; ignored. - (?=...) Matches if ... matches next, but doesn't consume the string. - (?!...) Matches if ... doesn't match next. - -The special sequences consist of "\\" and a character from the list -below. If the ordinary character is not on the list, then the -resulting RE will match the second character. - \number Matches the contents of the group of the same number. - \A Matches only at the start of the string. - \Z Matches only at the end of the string. - \b Matches the empty string, but only at the start or end of a word. - \B Matches the empty string, but not at the start or end of a word. - \d Matches any decimal digit; equivalent to the set [0-9]. - \D Matches any non-digit character; equivalent to the set [^0-9]. - \s Matches any whitespace character; equivalent to [ \t\n\r\f\v]. - \S Matches any non-whitespace character; equiv. to [^ \t\n\r\f\v]. - \w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]. - With LOCALE, it will match the set [0-9_] plus characters defined - as letters for the current locale. - \W Matches the complement of \w. - \\ Matches a literal backslash. - -This module exports the following functions: - match Match a regular expression pattern to the beginning of a string. - search Search a string for the presence of a pattern. - sub Substitute occurrences of a pattern found in a string. - subn Same as sub, but also return the number of substitutions made. - split Split a string by the occurrences of a pattern. - findall Find all occurrences of a pattern in a string. - compile Compile a pattern into a RegexObject. - purge Clear the regular expression cache. - escape Backslash all non-alphanumerics in a string. - -Some of the functions in this module takes flags as optional parameters: - I IGNORECASE Perform case-insensitive matching. - L LOCALE Make \w, \W, \b, \B, dependent on the current locale. - M MULTILINE "^" matches the beginning of lines as well as the string. - "$" matches the end of lines as well as the string. - S DOTALL "." matches any character at all, including the newline. - X VERBOSE Ignore whitespace and comments for nicer looking RE's. - U UNICODE Make \w, \W, \b, \B, dependent on the Unicode locale. - -This module also defines an exception 'error'. - -""" - -import sys -import sre_compile -import sre_parse - -# public symbols -__all__ = [ "match", "search", "sub", "subn", "split", "findall", - "compile", "purge", "template", "escape", "I", "L", "M", "S", "X", - "U", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE", - "UNICODE", "error" ] - -__version__ = "2.2.1" - -# flags -I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case -L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale -U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode locale -M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline -S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline -X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments - -# sre extensions (experimental, don't rely on these) -T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking -DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation - -# sre exception -error = sre_compile.error - -# -------------------------------------------------------------------- -# public interface - -def match(pattern, string, flags=0): - """Try to apply the pattern at the start of the string, returning - a match object, or None if no match was found.""" - return _compile(pattern, flags).match(string) - -def search(pattern, string, flags=0): - """Scan through string looking for a match to the pattern, returning - a match object, or None if no match was found.""" - return _compile(pattern, flags).search(string) - -def sub(pattern, repl, string, count=0): - """Return the string obtained by replacing the leftmost - non-overlapping occurrences of the pattern in string by the - replacement repl. repl can be either a string or a callable; - if a callable, it's passed the match object and must return - a replacement string to be used.""" - return _compile(pattern, 0).sub(repl, string, count) - -def subn(pattern, repl, string, count=0): - """Return a 2-tuple containing (new_string, number). - new_string is the string obtained by replacing the leftmost - non-overlapping occurrences of the pattern in the source - string by the replacement repl. number is the number of - substitutions that were made. repl can be either a string or a - callable; if a callable, it's passed the match object and must - return a replacement string to be used.""" - return _compile(pattern, 0).subn(repl, string, count) - -def split(pattern, string, maxsplit=0): - """Split the source string by the occurrences of the pattern, - returning a list containing the resulting substrings.""" - return _compile(pattern, 0).split(string, maxsplit) - -def findall(pattern, string, flags=0): - """Return a list of all non-overlapping matches in the string. - - If one or more groups are present in the pattern, return a - list of groups; this will be a list of tuples if the pattern - has more than one group. - - Empty matches are included in the result.""" - return _compile(pattern, flags).findall(string) - -if sys.hexversion >= 0x02020000: - __all__.append("finditer") - def finditer(pattern, string, flags=0): - """Return an iterator over all non-overlapping matches in the - string. For each match, the iterator returns a match object. - - Empty matches are included in the result.""" - return _compile(pattern, flags).finditer(string) - -def compile(pattern, flags=0): - "Compile a regular expression pattern, returning a pattern object." - return _compile(pattern, flags) - -def purge(): - "Clear the regular expression cache" - _cache.clear() - _cache_repl.clear() - -def template(pattern, flags=0): - "Compile a template pattern, returning a pattern object" - return _compile(pattern, flags|T) - -_alphanum = {} -for c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890': - _alphanum[c] = 1 -del c - -def escape(pattern): - "Escape all non-alphanumeric characters in pattern." - s = list(pattern) - alphanum = _alphanum - for i in range(len(pattern)): - c = pattern[i] - if c not in alphanum: - if c == "\000": - s[i] = "\\000" - else: - s[i] = "\\" + c - return pattern[:0].join(s) - -# -------------------------------------------------------------------- -# internals - -_cache = {} -_cache_repl = {} - -_pattern_type = type(sre_compile.compile("", 0)) - -_MAXCACHE = 100 - -def _compile(*key): - # internal: compile pattern - cachekey = (type(key[0]),) + key - p = _cache.get(cachekey) - if p is not None: - return p - pattern, flags = key - if isinstance(pattern, _pattern_type): - return pattern - if not sre_compile.isstring(pattern): - raise TypeError, "first argument must be string or compiled pattern" - try: - p = sre_compile.compile(pattern, flags) - except error, v: - raise error, v # invalid expression - if len(_cache) >= _MAXCACHE: - _cache.clear() - _cache[cachekey] = p - return p - -def _compile_repl(*key): - # internal: compile replacement pattern - p = _cache_repl.get(key) - if p is not None: - return p - repl, pattern = key - try: - p = sre_parse.parse_template(repl, pattern) - except error, v: - raise error, v # invalid expression - if len(_cache_repl) >= _MAXCACHE: - _cache_repl.clear() - _cache_repl[key] = p - return p - -def _expand(pattern, match, template): - # internal: match.expand implementation hook - template = sre_parse.parse_template(template, pattern) - return sre_parse.expand_template(template, match) - -def _subx(pattern, template): - # internal: pattern.sub/subn implementation helper - template = _compile_repl(template, pattern) - if not template[0] and len(template[1]) == 1: - # literal replacement - return template[1][0] - def filter(match, template=template): - return sre_parse.expand_template(template, match) - return filter - -# register myself for pickling - -import copy_reg - -def _pickle(p): - return _compile, (p.pattern, p.flags) - -copy_reg.pickle(_pattern_type, _pickle, _compile) - -# -------------------------------------------------------------------- -# experimental stuff (see python-dev discussions for details) - -class Scanner: - def __init__(self, lexicon, flags=0): - from sre_constants import BRANCH, SUBPATTERN - self.lexicon = lexicon - # combine phrases into a compound pattern - p = [] - s = sre_parse.Pattern() - s.flags = flags - for phrase, action in lexicon: - p.append(sre_parse.SubPattern(s, [ - (SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))), - ])) - p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) - s.groups = len(p) - self.scanner = sre_compile.compile(p) - def scan(self, string): - result = [] - append = result.append - match = self.scanner.scanner(string).match - i = 0 - while 1: - m = match() - if not m: - break - j = m.end() - if i == j: - break - action = self.lexicon[m.lastindex-1][1] - if callable(action): - self.match = m - action = action(self, m.group()) - if action is not None: - append(action) - i = j - return result, string[i:] diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index b850912..85f57a6 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -110,7 +110,7 @@ import sys import getopt import random import warnings -import sre +import re import cStringIO import traceback @@ -525,7 +525,7 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): _path_created.clear() warnings.filters[:] = fs gc.collect() - sre.purge() + re.purge() _strptime._regex_cache.clear() urlparse.clear_cache() urllib.urlcleanup() diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 74a9cdf..0b2e7da 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -145,7 +145,6 @@ class AllTest(unittest.TestCase): self.check_all("smtplib") self.check_all("sndhdr") self.check_all("socket") - self.check_all("sre") self.check_all("_strptime") self.check_all("symtable") self.check_all("tabnanny") diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 9755005..14a0acf 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -3,7 +3,7 @@ sys.path = ['.'] + sys.path from test.test_support import verbose, run_unittest import re -from sre import Scanner +from re import Scanner import sys, os, traceback from weakref import proxy diff --git a/Modules/_sre.c b/Modules/_sre.c index c167210..81223d7 100644 --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -51,6 +51,8 @@ static char copyright[] = #define SRE_MODULE "sre" #endif +#define SRE_PY_MODULE "re" + /* defining this one enables tracing */ #undef VERBOSE @@ -2455,7 +2457,7 @@ pattern_subx(PatternObject* self, PyObject* template, PyObject* string, } else { /* not a literal; hand it over to the template compiler */ filter = call( - SRE_MODULE, "_subx", + SRE_PY_MODULE, "_subx", PyTuple_Pack(2, self, template) ); if (!filter) @@ -2872,7 +2874,7 @@ match_expand(MatchObject* self, PyObject* args) /* delegate to Python code */ return call( - SRE_MODULE, "_expand", + SRE_PY_MODULE, "_expand", PyTuple_Pack(3, self->pattern, self, template) ); } -- cgit v0.12 From 49fd7fa4431da299196d74087df4a04f99f9c46f Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 10:40:58 +0000 Subject: Merge p3yk branch with the trunk up to revision 45595. This breaks a fair number of tests, all because of the codecs/_multibytecodecs issue described here (it's not a Py3K issue, just something Py3K discovers): http://mail.python.org/pipermail/python-dev/2006-April/064051.html Hye-Shik Chang promised to look for a fix, so no need to fix it here. The tests that are expected to break are: test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecs test_multibytecodec This merge fixes an actual test failure (test_weakref) in this branch, though, so I believe merging is the right thing to do anyway. --- Demo/parser/unparse.py | 249 +- Demo/pdist/makechangelog.py | 10 +- Demo/pdist/rcsbump | 4 +- Demo/pdist/rcslib.py | 4 +- Demo/scripts/eqfix.py | 6 +- Demo/scripts/ftpstats.py | 6 +- Demo/scripts/mboxconvert.py | 4 +- Demo/scripts/update.py | 6 +- Demo/sockets/mcast.py | 1 - Demo/tix/grid.py | 28 + Demo/tkinter/guido/ManPage.py | 8 +- Demo/tkinter/guido/mbox.py | 4 +- Demo/tkinter/guido/tkman.py | 10 +- Doc/Makefile.deps | 1 + Doc/api/concrete.tex | 27 +- Doc/api/intro.tex | 5 +- Doc/api/memory.tex | 4 +- Doc/api/newtypes.tex | 17 - Doc/api/refcounts.dat | 123 + Doc/commontex/boilerplate.tex | 2 +- Doc/commontex/license.tex | 65 +- Doc/dist/dist.tex | 21 +- Doc/howto/regex.tex | 9 +- Doc/lib/compiler.tex | 6 +- Doc/lib/email-dir.py | 131 +- Doc/lib/email-mime.py | 6 +- Doc/lib/email-simple.py | 2 +- Doc/lib/email-unpack.py | 65 +- Doc/lib/email.tex | 72 +- Doc/lib/emailcharsets.tex | 8 +- Doc/lib/emailencoders.tex | 4 +- Doc/lib/emailexc.tex | 6 +- Doc/lib/emailgenerator.tex | 25 +- Doc/lib/emailheaders.tex | 17 +- Doc/lib/emailiter.tex | 4 +- Doc/lib/emailmessage.tex | 73 +- Doc/lib/emailmimebase.tex | 65 +- Doc/lib/emailparser.tex | 26 +- Doc/lib/emailutil.tex | 6 +- Doc/lib/lib.tex | 5 +- Doc/lib/libarray.tex | 8 +- Doc/lib/libast.tex | 4 +- Doc/lib/libaudioop.tex | 26 +- Doc/lib/libbsddb.tex | 5 +- Doc/lib/libcalendar.tex | 192 +- Doc/lib/libcgi.tex | 4 +- Doc/lib/libcodecs.tex | 22 +- Doc/lib/libcollections.tex | 84 +- Doc/lib/libcontextlib.tex | 144 + Doc/lib/libcookielib.tex | 2 +- Doc/lib/libcsv.tex | 91 +- Doc/lib/libdatetime.tex | 11 +- Doc/lib/libdecimal.tex | 28 +- Doc/lib/libfuncs.tex | 24 +- Doc/lib/libgc.tex | 3 +- Doc/lib/libgetpass.tex | 8 +- Doc/lib/libhashlib.tex | 2 +- Doc/lib/libitertools.tex | 27 +- Doc/lib/liblinecache.tex | 8 +- Doc/lib/libnntplib.tex | 30 +- Doc/lib/liboptparse.tex | 22 +- Doc/lib/libos.tex | 34 +- Doc/lib/libossaudiodev.tex | 2 +- Doc/lib/libpdb.tex | 39 + Doc/lib/libprofile.tex | 20 +- Doc/lib/libpycompile.tex | 7 +- Doc/lib/libqueue.tex | 49 + Doc/lib/libre.tex | 7 +- Doc/lib/libreconvert.tex | 80 - Doc/lib/libregex.tex | 370 -- Doc/lib/libregsub.tex | 74 - Doc/lib/librunpy.tex | 74 + Doc/lib/libsets.tex | 5 +- Doc/lib/libsgmllib.tex | 14 +- Doc/lib/libshutil.tex | 10 +- Doc/lib/libsignal.tex | 2 +- Doc/lib/libsocket.tex | 30 +- Doc/lib/libstdtypes.tex | 53 +- Doc/lib/libsubprocess.tex | 4 +- Doc/lib/libthread.tex | 13 + Doc/lib/libthreading.tex | 23 + Doc/lib/libundoc.tex | 138 +- Doc/lib/liburllib2.tex | 2 +- Doc/lib/liburlparse.tex | 176 +- Doc/lib/libwarnings.tex | 8 +- Doc/lib/libxmlrpclib.tex | 6 +- Doc/lib/libzipfile.tex | 15 +- Doc/lib/libzipimport.tex | 12 +- Doc/lib/libzlib.tex | 5 +- Doc/lib/mimelib.tex | 17 +- Doc/lib/xmldomminidom.tex | 2 +- Doc/python-docs.txt | 2 +- Doc/ref/ref3.tex | 87 +- Doc/ref/ref6.tex | 16 +- Doc/ref/ref7.tex | 70 +- Doc/tools/prechm.py | 16 + Doc/tools/toc2bkm.py | 19 +- Doc/tut/tut.tex | 72 +- Doc/whatsnew/whatsnew20.tex | 4 +- Doc/whatsnew/whatsnew21.tex | 17 +- Doc/whatsnew/whatsnew24.tex | 8 +- Doc/whatsnew/whatsnew25.tex | 1451 +++++- Grammar/Grammar | 7 +- Include/Python-ast.h | 46 +- Include/abstract.h | 10 + Include/asdl.h | 8 + Include/code.h | 17 +- Include/compile.h | 2 +- Include/genobject.h | 2 + Include/modsupport.h | 7 +- Include/object.h | 41 +- Include/objimpl.h | 22 +- Include/pymem.h | 17 +- Include/pyport.h | 60 +- Include/setobject.h | 3 + Include/sliceobject.h | 1 + LICENSE | 2 + Lib/Queue.py | 46 + Lib/SimpleXMLRPCServer.py | 8 +- Lib/__future__.py | 4 +- Lib/_threading_local.py | 72 +- Lib/bdb.py | 6 +- Lib/bsddb/__init__.py | 18 +- Lib/bsddb/test/test_all.py | 1 + Lib/bsddb/test/test_pickle.py | 75 + Lib/calendar.py | 709 ++- Lib/codecs.py | 36 +- Lib/contextlib.py | 20 +- Lib/copy_reg.py | 15 +- Lib/ctypes/__init__.py | 31 +- Lib/ctypes/_loader.py | 12 +- Lib/ctypes/test/test_byteswap.py | 70 +- Lib/ctypes/test/test_cfuncs.py | 2 +- Lib/ctypes/test/test_keeprefs.py | 5 + Lib/ctypes/test/test_loading.py | 80 +- Lib/ctypes/test/test_pointers.py | 12 + Lib/ctypes/test/test_posix.py | 6 +- Lib/ctypes/test/test_prototypes.py | 16 +- Lib/ctypes/test/test_random_things.py | 10 +- Lib/ctypes/test/test_sizes.py | 3 + Lib/ctypes/test/test_unaligned_structures.py | 45 + Lib/distutils/command/build_ext.py | 13 +- Lib/distutils/command/install.py | 1 + Lib/distutils/command/install_egg_info.py | 75 + Lib/distutils/command/upload.py | 11 +- Lib/distutils/log.py | 7 +- Lib/distutils/sysconfig.py | 21 +- Lib/doctest.py | 26 +- Lib/dummy_thread.py | 8 + Lib/easy_install.py | 5 + Lib/email/Charset.py | 370 -- Lib/email/Encoders.py | 78 - Lib/email/Errors.py | 53 - Lib/email/FeedParser.py | 477 -- Lib/email/Generator.py | 352 -- Lib/email/Header.py | 495 -- Lib/email/Iterators.py | 67 - Lib/email/MIMEAudio.py | 72 - Lib/email/MIMEBase.py | 24 - Lib/email/MIMEImage.py | 45 - Lib/email/MIMEMessage.py | 32 - Lib/email/MIMEMultipart.py | 39 - Lib/email/MIMENonMultipart.py | 24 - Lib/email/MIMEText.py | 28 - Lib/email/Message.py | 814 --- Lib/email/Parser.py | 88 - Lib/email/Utils.py | 291 -- Lib/email/__init__.py | 77 +- Lib/email/_parseaddr.py | 10 +- Lib/email/base64MIME.py | 172 - Lib/email/base64mime.py | 184 + Lib/email/charset.py | 388 ++ Lib/email/encoders.py | 88 + Lib/email/errors.py | 57 + Lib/email/feedparser.py | 480 ++ Lib/email/generator.py | 348 ++ Lib/email/header.py | 502 ++ Lib/email/iterators.py | 73 + Lib/email/message.py | 773 +++ Lib/email/mime/__init__.py | 0 Lib/email/mime/application.py | 36 + Lib/email/mime/audio.py | 73 + Lib/email/mime/base.py | 26 + Lib/email/mime/image.py | 46 + Lib/email/mime/message.py | 34 + Lib/email/mime/multipart.py | 41 + Lib/email/mime/nonmultipart.py | 26 + Lib/email/mime/text.py | 30 + Lib/email/parser.py | 91 + Lib/email/quopriMIME.py | 318 -- Lib/email/quoprimime.py | 336 ++ Lib/email/test/test_email.py | 129 +- Lib/email/test/test_email_codecs.py | 7 + Lib/email/test/test_email_codecs_renamed.py | 77 + Lib/email/test/test_email_renamed.py | 3078 ++++++++++++ Lib/email/utils.py | 306 ++ Lib/encodings/big5.py | 41 +- Lib/encodings/big5hkscs.py | 41 +- Lib/encodings/cp932.py | 41 +- Lib/encodings/cp949.py | 41 +- Lib/encodings/cp950.py | 41 +- Lib/encodings/euc_jis_2004.py | 41 +- Lib/encodings/euc_jisx0213.py | 41 +- Lib/encodings/euc_jp.py | 41 +- Lib/encodings/euc_kr.py | 41 +- Lib/encodings/gb18030.py | 41 +- Lib/encodings/gb2312.py | 41 +- Lib/encodings/gbk.py | 41 +- Lib/encodings/hz.py | 41 +- Lib/encodings/idna.py | 100 +- Lib/encodings/iso2022_jp.py | 41 +- Lib/encodings/iso2022_jp_1.py | 41 +- Lib/encodings/iso2022_jp_2.py | 41 +- Lib/encodings/iso2022_jp_2004.py | 41 +- Lib/encodings/iso2022_jp_3.py | 41 +- Lib/encodings/iso2022_jp_ext.py | 41 +- Lib/encodings/iso2022_kr.py | 41 +- Lib/encodings/johab.py | 41 +- Lib/encodings/shift_jis.py | 41 +- Lib/encodings/shift_jis_2004.py | 41 +- Lib/encodings/shift_jisx0213.py | 41 +- Lib/getpass.py | 25 +- Lib/glob.py | 2 +- Lib/idlelib/IOBinding.py | 1 + Lib/idlelib/NEWS.txt | 11 +- Lib/idlelib/idlever.py | 2 +- Lib/inspect.py | 6 +- Lib/lib-old/Para.py | 343 -- Lib/lib-old/addpack.py | 67 - Lib/lib-old/cmp.py | 63 - Lib/lib-old/cmpcache.py | 64 - Lib/lib-old/codehack.py | 81 - Lib/lib-old/dircmp.py | 202 - Lib/lib-old/dump.py | 63 - Lib/lib-old/find.py | 26 - Lib/lib-old/fmt.py | 623 --- Lib/lib-old/grep.py | 79 - Lib/lib-old/lockfile.py | 15 - Lib/lib-old/newdir.py | 73 - Lib/lib-old/ni.py | 433 -- Lib/lib-old/packmail.py | 111 - Lib/lib-old/poly.py | 52 - Lib/lib-old/rand.py | 13 - Lib/lib-old/statcache.py | 82 - Lib/lib-old/tb.py | 177 - Lib/lib-old/tzparse.py | 98 - Lib/lib-old/util.py | 25 - Lib/lib-old/whatsound.py | 1 - Lib/lib-old/whrandom.py | 144 - Lib/lib-old/zmod.py | 94 - Lib/lib-tk/Tix.py | 98 +- Lib/lib-tk/Tkinter.py | 24 +- Lib/lib-tk/tkFont.py | 4 +- Lib/linecache.py | 36 +- Lib/logging/__init__.py | 2 +- Lib/mimetools.py | 5 +- Lib/mimetypes.py | 321 +- Lib/pdb.py | 125 +- Lib/pkg_resources.py | 2377 +++++++++ Lib/pkgutil.py | 425 ++ Lib/plat-mac/applesingle.py | 2 +- Lib/platform.py | 3 +- Lib/popen2.py | 37 +- Lib/pstats.py | 144 +- Lib/pydoc.py | 182 +- Lib/random.py | 34 +- Lib/reconvert.py | 192 - Lib/regex_syntax.py | 53 - Lib/regsub.py | 198 - Lib/rexec.py | 2 +- Lib/runpy.py | 377 +- Lib/setuptools.egg-info/PKG-INFO | 89 + Lib/setuptools.egg-info/entry_points.txt | 51 + Lib/setuptools.egg-info/top_level.txt | 3 + Lib/setuptools.egg-info/zip-safe | 0 Lib/setuptools/__init__.py | 64 + Lib/setuptools/archive_util.py | 200 + Lib/setuptools/cli.exe | Bin 0 -> 6144 bytes Lib/setuptools/command/__init__.py | 19 + Lib/setuptools/command/alias.py | 79 + Lib/setuptools/command/bdist_egg.py | 449 ++ Lib/setuptools/command/bdist_rpm.py | 37 + Lib/setuptools/command/build_ext.py | 285 ++ Lib/setuptools/command/build_py.py | 192 + Lib/setuptools/command/develop.py | 116 + Lib/setuptools/command/easy_install.py | 1555 ++++++ Lib/setuptools/command/egg_info.py | 365 ++ Lib/setuptools/command/install.py | 101 + Lib/setuptools/command/install_egg_info.py | 81 + Lib/setuptools/command/install_lib.py | 76 + Lib/setuptools/command/install_scripts.py | 56 + Lib/setuptools/command/rotate.py | 57 + Lib/setuptools/command/saveopts.py | 24 + Lib/setuptools/command/sdist.py | 163 + Lib/setuptools/command/setopt.py | 158 + Lib/setuptools/command/test.py | 119 + Lib/setuptools/command/upload.py | 178 + Lib/setuptools/depends.py | 239 + Lib/setuptools/dist.py | 798 +++ Lib/setuptools/extension.py | 35 + Lib/setuptools/gui.exe | Bin 0 -> 6144 bytes Lib/setuptools/package_index.py | 674 +++ Lib/setuptools/sandbox.py | 203 + Lib/setuptools/site-patch.py | 74 + Lib/setuptools/tests/__init__.py | 364 ++ Lib/setuptools/tests/api_tests.txt | 330 ++ Lib/setuptools/tests/test_resources.py | 483 ++ Lib/sgmllib.py | 34 +- Lib/site.py | 2 + Lib/smtplib.py | 6 +- Lib/socket.py | 12 +- Lib/sqlite3/__init__.py | 24 + Lib/sqlite3/dbapi2.py | 84 + Lib/sqlite3/test/__init__.py | 0 Lib/sqlite3/test/dbapi.py | 732 +++ Lib/sqlite3/test/factory.py | 164 + Lib/sqlite3/test/hooks.py | 115 + Lib/sqlite3/test/regression.py | 48 + Lib/sqlite3/test/transactions.py | 156 + Lib/sqlite3/test/types.py | 339 ++ Lib/sqlite3/test/userfunctions.py | 330 ++ Lib/sre.py | 10 + Lib/subprocess.py | 36 +- Lib/telnetlib.py | 2 +- Lib/test/check_soundcard.vbs | 13 + Lib/test/crashers/README | 5 + Lib/test/crashers/dictresize_attack.py | 32 + Lib/test/crashers/nasty_eq_vs_dict.py | 47 + Lib/test/empty.vbs | 1 + Lib/test/fork_wait.py | 71 + Lib/test/leakers/README.txt | 13 + Lib/test/leakers/test_ctypes.py | 16 + Lib/test/leakers/test_selftype.py | 13 + Lib/test/leakers/test_tee.py | 19 - Lib/test/output/test_augassign | 54 - Lib/test/output/test_coercion | 1054 ---- Lib/test/output/test_compare | 101 - Lib/test/regrtest.py | 67 +- Lib/test/test___all__.py | 4 - Lib/test/test_applesingle.py | 4 +- Lib/test/test_array.py | 2 +- Lib/test/test_ast.py | 11 +- Lib/test/test_audioop.py | 24 +- Lib/test/test_augassign.py | 566 ++- Lib/test/test_bsddb.py | 9 +- Lib/test/test_builtin.py | 4 + Lib/test/test_calendar.py | 265 +- Lib/test/test_capi.py | 87 +- Lib/test/test_cmd_line.py | 3 + Lib/test/test_codecs.py | 93 +- Lib/test/test_coercion.py | 324 +- Lib/test/test_compare.py | 51 +- Lib/test/test_compile.py | 4 + Lib/test/test_compiler.py | 13 +- Lib/test/test_contextlib.py | 69 +- Lib/test/test_copy_reg.py | 29 + Lib/test/test_curses.py | 15 +- Lib/test/test_datetime.py | 11 + Lib/test/test_decimal.py | 50 +- Lib/test/test_descr.py | 12 +- Lib/test/test_difflib.py | 8 +- Lib/test/test_dl.py | 1 + Lib/test/test_doctest.py | 46 +- Lib/test/test_email_renamed.py | 13 + Lib/test/test_file.py | 16 +- Lib/test/test_fileinput.py | 5 +- Lib/test/test_fork1.py | 76 +- Lib/test/test_generators.py | 98 +- Lib/test/test_genexps.py | 2 +- Lib/test/test_getargs2.py | 19 +- Lib/test/test_glob.py | 8 + Lib/test/test_grammar.py | 9 + Lib/test/test_index.py | 137 + Lib/test/test_inspect.py | 6 +- Lib/test/test_mimetypes.py | 1 + Lib/test/test_multibytecodec.py | 135 +- Lib/test/test_multibytecodec_support.py | 210 +- Lib/test/test_optparse.py | 7 + Lib/test/test_parser.py | 4 + Lib/test/test_platform.py | 7 +- Lib/test/test_popen2.py | 4 + Lib/test/test_posix.py | 5 + Lib/test/test_pty.py | 28 +- Lib/test/test_pyclbr.py | 5 +- Lib/test/test_queue.py | 44 + Lib/test/test_quopri.py | 21 +- Lib/test/test_random.py | 21 +- Lib/test/test_regex.py | 113 - Lib/test/test_set.py | 4 +- Lib/test/test_setuptools.py | 16 + Lib/test/test_sgmllib.py | 14 + Lib/test/test_socket.py | 40 +- Lib/test/test_socket_ssl.py | 39 + Lib/test/test_sqlite.py | 16 + Lib/test/test_startfile.py | 37 + Lib/test/test_sundry.py | 54 +- Lib/test/test_sys.py | 5 + Lib/test/test_timeout.py | 5 +- Lib/test/test_tokenize.py | 134 +- Lib/test/test_trace.py | 62 +- Lib/test/test_traceback.py | 10 + Lib/test/test_unicode.py | 16 + Lib/test/test_urllib2.py | 38 +- Lib/test/test_urlparse.py | 101 + Lib/test/test_wait3.py | 32 + Lib/test/test_wait4.py | 29 + Lib/test/test_warnings.py | 4 + Lib/test/test_winsound.py | 149 +- Lib/test/test_with.py | 56 + Lib/test/test_xrange.py | 7 +- Lib/test/test_zipimport.py | 83 +- Lib/tokenize.py | 2 +- Lib/traceback.py | 10 +- Lib/urllib.py | 4 +- Lib/urllib2.py | 205 +- Lib/urlparse.py | 131 +- Lib/uu.py | 4 +- Lib/warnings.py | 10 +- Lib/xmlcore/dom/expatbuilder.py | 14 +- Lib/xmlcore/dom/minicompat.py | 175 +- Lib/xmlcore/dom/minidom.py | 16 +- Lib/xmlcore/dom/xmlbuilder.py | 6 +- Mac/Modules/carbonevt/_CarbonEvtmodule.c | 4 +- Mac/Modules/cf/_CFmodule.c | 4 +- Mac/Modules/gestaltmodule.c | 9 +- Mac/Modules/qd/_Qdmodule.c | 5 +- Makefile.pre.in | 56 +- Misc/ACKS | 3 + Misc/BeOS-setup.py | 2 - Misc/HISTORY | 5287 ++++++++++++++++++++ Misc/README.valgrind | 6 + Misc/RPM/python-2.4.spec | 382 -- Misc/RPM/python-2.5.spec | 385 ++ Misc/SpecialBuilds.txt | 4 +- Misc/build.sh | 24 +- Misc/cheatsheet | 3 - Misc/developers.txt | 6 + Misc/python-config.in | 50 + Modules/Setup.dist | 4 +- Modules/_bsddb.c | 28 +- Modules/_csv.c | 63 +- Modules/_ctypes/_ctypes.c | 255 +- Modules/_ctypes/_ctypes_test.c | 72 +- Modules/_ctypes/callbacks.c | 52 +- Modules/_ctypes/callproc.c | 100 +- Modules/_ctypes/cfield.c | 155 +- Modules/_ctypes/ctypes.h | 39 +- Modules/_ctypes/libffi/configure | 1 + Modules/_ctypes/libffi/configure.ac | 1 + .../_ctypes/libffi/src/powerpc/darwin_closure.S | 2 +- Modules/_ctypes/libffi/src/x86/ffi.c | 4 +- Modules/_ctypes/stgdict.c | 45 +- Modules/_hotshot.c | 8 +- Modules/_lsprof.c | 10 +- Modules/_sqlite/adapters.c | 40 + Modules/_sqlite/adapters.h | 33 + Modules/_sqlite/cache.c | 362 ++ Modules/_sqlite/cache.h | 61 + Modules/_sqlite/connection.c | 1082 ++++ Modules/_sqlite/connection.h | 106 + Modules/_sqlite/converters.c | 40 + Modules/_sqlite/converters.h | 33 + Modules/_sqlite/cursor.c | 1027 ++++ Modules/_sqlite/cursor.h | 71 + Modules/_sqlite/microprotocols.c | 142 + Modules/_sqlite/microprotocols.h | 59 + Modules/_sqlite/module.c | 325 ++ Modules/_sqlite/module.h | 55 + Modules/_sqlite/prepare_protocol.c | 84 + Modules/_sqlite/prepare_protocol.h | 41 + Modules/_sqlite/row.c | 202 + Modules/_sqlite/row.h | 39 + Modules/_sqlite/sqlitecompat.h | 34 + Modules/_sqlite/statement.c | 427 ++ Modules/_sqlite/statement.h | 58 + Modules/_sqlite/util.c | 96 + Modules/_sqlite/util.h | 38 + Modules/_sre.c | 407 +- Modules/_ssl.c | 24 +- Modules/_testcapimodule.c | 114 +- Modules/_tkinter.c | 2 +- Modules/almodule.c | 4 +- Modules/arraymodule.c | 13 +- Modules/audioop.c | 2483 ++++----- Modules/cPickle.c | 92 +- Modules/cStringIO.c | 26 +- Modules/ccpython.cc | 11 - Modules/cjkcodecs/_codecs_cn.c | 5 +- Modules/cjkcodecs/multibytecodec.c | 1358 +++-- Modules/cjkcodecs/multibytecodec.h | 60 +- Modules/collectionsmodule.c | 21 +- Modules/config.c.in | 10 + Modules/datetimemodule.c | 39 +- Modules/functionalmodule.c | 2 +- Modules/gcmodule.c | 75 +- Modules/getpath.c | 13 +- Modules/grpmodule.c | 5 +- Modules/itertoolsmodule.c | 91 +- Modules/main.c | 13 +- Modules/md5.c | 381 ++ Modules/md5.h | 123 +- Modules/md5c.c | 289 -- Modules/md5module.c | 16 +- Modules/operator.c | 6 +- Modules/ossaudiodev.c | 16 +- Modules/parsermodule.c | 41 +- Modules/posixmodule.c | 424 +- Modules/pwdmodule.c | 6 +- Modules/pyexpat.c | 16 +- Modules/regexmodule.c | 690 --- Modules/regexpr.c | 2094 -------- Modules/regexpr.h | 155 - Modules/resource.c | 7 +- Modules/socketmodule.c | 76 +- Modules/spwdmodule.c | 6 +- Modules/stropmodule.c | 66 +- Modules/threadmodule.c | 121 +- Modules/timemodule.c | 9 +- Modules/unicodedata.c | 12 +- Modules/xxsubtype.c | 4 - Modules/zipimport.c | 11 +- Modules/zlibmodule.c | 4 +- Objects/abstract.c | 138 +- Objects/boolobject.c | 72 +- Objects/bufferobject.c | 4 +- Objects/cellobject.c | 25 +- Objects/classobject.c | 236 +- Objects/cobject.c | 41 +- Objects/codeobject.c | 133 + Objects/complexobject.c | 12 +- Objects/descrobject.c | 52 +- Objects/dictobject.c | 43 +- Objects/enumobject.c | 19 +- Objects/fileobject.c | 33 +- Objects/floatobject.c | 34 +- Objects/frameobject.c | 96 +- Objects/funcobject.c | 63 +- Objects/genobject.c | 80 +- Objects/intobject.c | 67 +- Objects/iterobject.c | 14 +- Objects/listobject.c | 201 +- Objects/longobject.c | 88 +- Objects/methodobject.c | 13 +- Objects/moduleobject.c | 3 +- Objects/object.c | 94 +- Objects/obmalloc.c | 2 +- Objects/rangeobject.c | 175 +- Objects/setobject.c | 123 +- Objects/sliceobject.c | 75 +- Objects/stringobject.c | 216 +- Objects/structseq.c | 10 +- Objects/tupleobject.c | 69 +- Objects/typeobject.c | 212 +- Objects/unicodeobject.c | 123 +- Objects/weakrefobject.c | 87 +- PC/VC6/pythoncore.dsp | 8 - PC/_subprocess.c | 2 +- PC/config.c | 2 - PC/dllbase_nt.txt | 1 + PC/os2emx/Makefile | 2 - PC/os2emx/config.c | 2 - PC/os2emx/python24.def | 13 - PC/os2vacpp/config.c | 2 - PC/os2vacpp/makefile | 28 - PC/os2vacpp/makefile.omk | 24 - PC/os2vacpp/python.def | 6 - PC/testpy.py | 10 +- PC/tix.diff | 108 - PCbuild/_ctypes.vcproj | 10 +- PCbuild/_sqlite3.vcproj | 289 ++ PCbuild/_ssl.mak | 6 +- PCbuild/db.build | 10 + PCbuild/make_buildinfo.c | 3 + PCbuild/pcbuild.sln | 14 + PCbuild/python.build | 21 + PCbuild/pythoncore.vcproj | 10 +- PCbuild/readme.txt | 195 +- Parser/Python.asdl | 7 +- Parser/asdl_c.py | 94 +- Parser/bitset.c | 4 +- Parser/firstsets.c | 7 +- Parser/grammar.c | 15 +- Parser/myreadline.c | 6 +- Parser/node.c | 2 +- Parser/parser.c | 6 +- Parser/pgen.c | 27 +- Parser/pgenmain.c | 6 +- Parser/tokenizer.c | 110 +- Python/Python-ast.c | 313 +- Python/asdl.c | 19 +- Python/ast.c | 179 +- Python/bltinmodule.c | 45 +- Python/ceval.c | 150 +- Python/codecs.c | 165 +- Python/compile.c | 135 +- Python/dynload_win.c | 6 + Python/errors.c | 11 + Python/exceptions.c | 9 +- Python/future.c | 10 +- Python/getargs.c | 33 +- Python/getmtime.c | 9 + Python/getopt.c | 9 + Python/graminit.c | 4 +- Python/import.c | 25 +- Python/mactoolboxglue.c | 8 +- Python/modsupport.c | 98 +- Python/pyarena.c | 46 +- Python/pystate.c | 71 +- Python/pystrtod.c | 2 +- Python/pythonrun.c | 147 +- Python/symtable.c | 24 +- Python/sysmodule.c | 20 +- Python/thread_pthread.h | 12 +- Python/traceback.c | 20 +- README | 690 +-- RISCOS/Makefile | 5 - Tools/bgen/bgen/bgenObjectDefinition.py | 5 +- Tools/buildbot/build.bat | 1 + Tools/buildbot/external.bat | 21 + Tools/buildbot/kill_python.bat | 3 + Tools/buildbot/kill_python.c | 56 + Tools/buildbot/kill_python.mak | 2 + Tools/i18n/msgfmt.py | 2 +- Tools/msi/msi.py | 51 +- Tools/msi/uuids.py | 33 + Tools/pybench/Arithmetic.py | 778 +++ Tools/pybench/Calls.py | 410 ++ Tools/pybench/CommandLine.py | 634 +++ Tools/pybench/Constructs.py | 565 +++ Tools/pybench/Dict.py | 503 ++ Tools/pybench/Exceptions.py | 681 +++ Tools/pybench/Imports.py | 139 + Tools/pybench/Instances.py | 68 + Tools/pybench/LICENSE | 25 + Tools/pybench/Lists.py | 292 ++ Tools/pybench/Lookups.py | 946 ++++ Tools/pybench/Numbers.py | 784 +++ Tools/pybench/README | 372 ++ Tools/pybench/Setup.py | 35 + Tools/pybench/Strings.py | 564 +++ Tools/pybench/Tuples.py | 365 ++ Tools/pybench/Unicode.py | 542 ++ Tools/pybench/package/__init__.py | 0 Tools/pybench/package/submodule.py | 0 Tools/pybench/pybench.py | 461 ++ Tools/scripts/byext.py | 16 +- Tools/scripts/classfix.py | 12 +- Tools/scripts/fixcid.py | 12 +- Tools/scripts/ifdef.py | 1 - Tools/scripts/methfix.py | 10 +- Tools/scripts/objgraph.py | 4 +- Tools/scripts/pathfix.py | 4 +- Tools/scripts/pdeps.py | 6 +- Tools/unicode/Makefile | 5 +- Tools/unicode/gencjkcodecs.py | 68 + Tools/unicode/gencodec.py | 2 +- configure | 1231 ++--- configure.in | 148 +- pyconfig.h.in | 6 + setup.py | 367 +- 660 files changed, 56521 insertions(+), 22689 deletions(-) create mode 100644 Demo/tix/grid.py create mode 100644 Doc/lib/libcontextlib.tex delete mode 100644 Doc/lib/libreconvert.tex delete mode 100644 Doc/lib/libregex.tex delete mode 100644 Doc/lib/libregsub.tex create mode 100644 Doc/lib/librunpy.tex create mode 100644 Lib/bsddb/test/test_pickle.py create mode 100644 Lib/ctypes/test/test_unaligned_structures.py create mode 100644 Lib/distutils/command/install_egg_info.py create mode 100644 Lib/easy_install.py delete mode 100644 Lib/email/Charset.py delete mode 100644 Lib/email/Encoders.py delete mode 100644 Lib/email/Errors.py delete mode 100644 Lib/email/FeedParser.py delete mode 100644 Lib/email/Generator.py delete mode 100644 Lib/email/Header.py delete mode 100644 Lib/email/Iterators.py delete mode 100644 Lib/email/MIMEAudio.py delete mode 100644 Lib/email/MIMEBase.py delete mode 100644 Lib/email/MIMEImage.py delete mode 100644 Lib/email/MIMEMessage.py delete mode 100644 Lib/email/MIMEMultipart.py delete mode 100644 Lib/email/MIMENonMultipart.py delete mode 100644 Lib/email/MIMEText.py delete mode 100644 Lib/email/Message.py delete mode 100644 Lib/email/Parser.py delete mode 100644 Lib/email/Utils.py delete mode 100644 Lib/email/base64MIME.py create mode 100644 Lib/email/base64mime.py create mode 100644 Lib/email/charset.py create mode 100644 Lib/email/encoders.py create mode 100644 Lib/email/errors.py create mode 100644 Lib/email/feedparser.py create mode 100644 Lib/email/generator.py create mode 100644 Lib/email/header.py create mode 100644 Lib/email/iterators.py create mode 100644 Lib/email/message.py create mode 100644 Lib/email/mime/__init__.py create mode 100644 Lib/email/mime/application.py create mode 100644 Lib/email/mime/audio.py create mode 100644 Lib/email/mime/base.py create mode 100644 Lib/email/mime/image.py create mode 100644 Lib/email/mime/message.py create mode 100644 Lib/email/mime/multipart.py create mode 100644 Lib/email/mime/nonmultipart.py create mode 100644 Lib/email/mime/text.py create mode 100644 Lib/email/parser.py delete mode 100644 Lib/email/quopriMIME.py create mode 100644 Lib/email/quoprimime.py create mode 100644 Lib/email/test/test_email_codecs_renamed.py create mode 100644 Lib/email/test/test_email_renamed.py create mode 100644 Lib/email/utils.py delete mode 100644 Lib/lib-old/Para.py delete mode 100644 Lib/lib-old/addpack.py delete mode 100644 Lib/lib-old/cmp.py delete mode 100644 Lib/lib-old/cmpcache.py delete mode 100644 Lib/lib-old/codehack.py delete mode 100644 Lib/lib-old/dircmp.py delete mode 100644 Lib/lib-old/dump.py delete mode 100644 Lib/lib-old/find.py delete mode 100644 Lib/lib-old/fmt.py delete mode 100644 Lib/lib-old/grep.py delete mode 100644 Lib/lib-old/lockfile.py delete mode 100644 Lib/lib-old/newdir.py delete mode 100644 Lib/lib-old/ni.py delete mode 100644 Lib/lib-old/packmail.py delete mode 100644 Lib/lib-old/poly.py delete mode 100644 Lib/lib-old/rand.py delete mode 100644 Lib/lib-old/statcache.py delete mode 100644 Lib/lib-old/tb.py delete mode 100644 Lib/lib-old/tzparse.py delete mode 100644 Lib/lib-old/util.py delete mode 100644 Lib/lib-old/whatsound.py delete mode 100644 Lib/lib-old/whrandom.py delete mode 100644 Lib/lib-old/zmod.py create mode 100644 Lib/pkg_resources.py delete mode 100755 Lib/reconvert.py delete mode 100644 Lib/regex_syntax.py delete mode 100644 Lib/regsub.py create mode 100644 Lib/setuptools.egg-info/PKG-INFO create mode 100755 Lib/setuptools.egg-info/entry_points.txt create mode 100644 Lib/setuptools.egg-info/top_level.txt create mode 100644 Lib/setuptools.egg-info/zip-safe create mode 100644 Lib/setuptools/__init__.py create mode 100755 Lib/setuptools/archive_util.py create mode 100755 Lib/setuptools/cli.exe create mode 100644 Lib/setuptools/command/__init__.py create mode 100755 Lib/setuptools/command/alias.py create mode 100644 Lib/setuptools/command/bdist_egg.py create mode 100755 Lib/setuptools/command/bdist_rpm.py create mode 100644 Lib/setuptools/command/build_ext.py create mode 100644 Lib/setuptools/command/build_py.py create mode 100755 Lib/setuptools/command/develop.py create mode 100755 Lib/setuptools/command/easy_install.py create mode 100755 Lib/setuptools/command/egg_info.py create mode 100644 Lib/setuptools/command/install.py create mode 100755 Lib/setuptools/command/install_egg_info.py create mode 100644 Lib/setuptools/command/install_lib.py create mode 100755 Lib/setuptools/command/install_scripts.py create mode 100755 Lib/setuptools/command/rotate.py create mode 100755 Lib/setuptools/command/saveopts.py create mode 100755 Lib/setuptools/command/sdist.py create mode 100755 Lib/setuptools/command/setopt.py create mode 100644 Lib/setuptools/command/test.py create mode 100755 Lib/setuptools/command/upload.py create mode 100644 Lib/setuptools/depends.py create mode 100644 Lib/setuptools/dist.py create mode 100644 Lib/setuptools/extension.py create mode 100755 Lib/setuptools/gui.exe create mode 100755 Lib/setuptools/package_index.py create mode 100755 Lib/setuptools/sandbox.py create mode 100755 Lib/setuptools/site-patch.py create mode 100644 Lib/setuptools/tests/__init__.py create mode 100755 Lib/setuptools/tests/api_tests.txt create mode 100644 Lib/setuptools/tests/test_resources.py create mode 100644 Lib/sqlite3/__init__.py create mode 100644 Lib/sqlite3/dbapi2.py create mode 100644 Lib/sqlite3/test/__init__.py create mode 100644 Lib/sqlite3/test/dbapi.py create mode 100644 Lib/sqlite3/test/factory.py create mode 100644 Lib/sqlite3/test/hooks.py create mode 100644 Lib/sqlite3/test/regression.py create mode 100644 Lib/sqlite3/test/transactions.py create mode 100644 Lib/sqlite3/test/types.py create mode 100644 Lib/sqlite3/test/userfunctions.py create mode 100644 Lib/sre.py create mode 100644 Lib/test/check_soundcard.vbs create mode 100644 Lib/test/crashers/dictresize_attack.py create mode 100644 Lib/test/crashers/nasty_eq_vs_dict.py create mode 100644 Lib/test/empty.vbs create mode 100644 Lib/test/fork_wait.py create mode 100644 Lib/test/leakers/test_ctypes.py create mode 100644 Lib/test/leakers/test_selftype.py delete mode 100644 Lib/test/leakers/test_tee.py delete mode 100644 Lib/test/output/test_augassign delete mode 100644 Lib/test/output/test_coercion delete mode 100644 Lib/test/output/test_compare create mode 100644 Lib/test/test_email_renamed.py delete mode 100644 Lib/test/test_regex.py create mode 100644 Lib/test/test_setuptools.py create mode 100644 Lib/test/test_sqlite.py create mode 100644 Lib/test/test_startfile.py create mode 100644 Lib/test/test_wait3.py create mode 100644 Lib/test/test_wait4.py delete mode 100644 Misc/RPM/python-2.4.spec create mode 100644 Misc/RPM/python-2.5.spec create mode 100644 Misc/python-config.in create mode 100644 Modules/_sqlite/adapters.c create mode 100644 Modules/_sqlite/adapters.h create mode 100644 Modules/_sqlite/cache.c create mode 100644 Modules/_sqlite/cache.h create mode 100644 Modules/_sqlite/connection.c create mode 100644 Modules/_sqlite/connection.h create mode 100644 Modules/_sqlite/converters.c create mode 100644 Modules/_sqlite/converters.h create mode 100644 Modules/_sqlite/cursor.c create mode 100644 Modules/_sqlite/cursor.h create mode 100644 Modules/_sqlite/microprotocols.c create mode 100644 Modules/_sqlite/microprotocols.h create mode 100644 Modules/_sqlite/module.c create mode 100644 Modules/_sqlite/module.h create mode 100644 Modules/_sqlite/prepare_protocol.c create mode 100644 Modules/_sqlite/prepare_protocol.h create mode 100644 Modules/_sqlite/row.c create mode 100644 Modules/_sqlite/row.h create mode 100644 Modules/_sqlite/sqlitecompat.h create mode 100644 Modules/_sqlite/statement.c create mode 100644 Modules/_sqlite/statement.h create mode 100644 Modules/_sqlite/util.c create mode 100644 Modules/_sqlite/util.h delete mode 100644 Modules/ccpython.cc create mode 100644 Modules/md5.c delete mode 100644 Modules/md5c.c delete mode 100644 Modules/regexmodule.c delete mode 100644 Modules/regexpr.c delete mode 100644 Modules/regexpr.h delete mode 100644 PC/tix.diff create mode 100644 PCbuild/_sqlite3.vcproj create mode 100644 PCbuild/db.build create mode 100644 PCbuild/python.build create mode 100644 Tools/buildbot/kill_python.bat create mode 100644 Tools/buildbot/kill_python.c create mode 100644 Tools/buildbot/kill_python.mak create mode 100644 Tools/msi/uuids.py create mode 100644 Tools/pybench/Arithmetic.py create mode 100644 Tools/pybench/Calls.py create mode 100644 Tools/pybench/CommandLine.py create mode 100644 Tools/pybench/Constructs.py create mode 100644 Tools/pybench/Dict.py create mode 100644 Tools/pybench/Exceptions.py create mode 100644 Tools/pybench/Imports.py create mode 100644 Tools/pybench/Instances.py create mode 100644 Tools/pybench/LICENSE create mode 100644 Tools/pybench/Lists.py create mode 100644 Tools/pybench/Lookups.py create mode 100644 Tools/pybench/Numbers.py create mode 100644 Tools/pybench/README create mode 100644 Tools/pybench/Setup.py create mode 100644 Tools/pybench/Strings.py create mode 100644 Tools/pybench/Tuples.py create mode 100644 Tools/pybench/Unicode.py create mode 100644 Tools/pybench/package/__init__.py create mode 100644 Tools/pybench/package/submodule.py create mode 100755 Tools/pybench/pybench.py create mode 100644 Tools/unicode/gencjkcodecs.py diff --git a/Demo/parser/unparse.py b/Demo/parser/unparse.py index dd75c22..510cdb0 100644 --- a/Demo/parser/unparse.py +++ b/Demo/parser/unparse.py @@ -1,5 +1,8 @@ "Usage: unparse.py " import sys +import _ast +import cStringIO +import os class Unparser: """Methods in this class recursively traverse an AST and @@ -70,6 +73,18 @@ class Unparser: if a.asname: self.write(" as "+a.asname) + def _ImportFrom(self, t): + self.fill("from ") + self.write(t.module) + self.write(" import ") + for i, a in enumerate(t.names): + if i == 0: + self.write(", ") + self.write(a.name) + if a.asname: + self.write(" as "+a.asname) + # XXX(jpe) what is level for? + def _Assign(self, t): self.fill() for target in t.targets: @@ -88,6 +103,36 @@ class Unparser: if t.value: self.dispatch(t.value) + def _Pass(self, t): + self.fill("pass") + + def _Break(self, t): + self.fill("break") + + def _Continue(self, t): + self.fill("continue") + + def _Delete(self, t): + self.fill("del ") + self.dispatch(t.targets) + + def _Assert(self, t): + self.fill("assert ") + self.dispatch(t.test) + if t.msg: + self.write(", ") + self.dispatch(t.msg) + + def _Exec(self, t): + self.fill("exec ") + self.dispatch(t.body) + if t.globals: + self.write(" in ") + self.dispatch(t.globals) + if t.locals: + self.write(", ") + self.dispatch(t.locals) + def _Print(self, t): self.fill("print ") do_comma = False @@ -102,6 +147,67 @@ class Unparser: if not t.nl: self.write(",") + def _Global(self, t): + self.fill("global") + for i, n in enumerate(t.names): + if i != 0: + self.write(",") + self.write(" " + n) + + def _Yield(self, t): + self.fill("yield") + if t.value: + self.write(" (") + self.dispatch(t.value) + self.write(")") + + def _Raise(self, t): + self.fill('raise ') + if t.type: + self.dispatch(t.type) + if t.inst: + self.write(", ") + self.dispatch(t.inst) + if t.tback: + self.write(", ") + self.dispatch(t.tback) + + def _TryExcept(self, t): + self.fill("try") + self.enter() + self.dispatch(t.body) + self.leave() + + for ex in t.handlers: + self.dispatch(ex) + if t.orelse: + self.fill("else") + self.enter() + self.dispatch(t.orelse) + self.leave() + + def _TryFinally(self, t): + self.fill("try") + self.enter() + self.dispatch(t.body) + self.leave() + + self.fill("finally") + self.enter() + self.dispatch(t.finalbody) + self.leave() + + def _excepthandler(self, t): + self.fill("except ") + if t.type: + self.dispatch(t.type) + if t.name: + self.write(", ") + self.dispatch(t.name) + self.enter() + self.dispatch(t.body) + self.leave() + def _ClassDef(self, t): self.write("\n") self.fill("class "+t.name) @@ -119,9 +225,24 @@ class Unparser: self.write("\n") self.fill("def "+t.name + "(") self.dispatch(t.args) + self.write(")") + self.enter() + self.dispatch(t.body) + self.leave() + + def _For(self, t): + self.fill("for ") + self.dispatch(t.target) + self.write(" in ") + self.dispatch(t.iter) self.enter() self.dispatch(t.body) self.leave() + if t.orelse: + self.fill("else") + self.enter() + self.dispatch(t.orelse) + self.leave def _If(self, t): self.fill("if ") @@ -136,11 +257,9 @@ class Unparser: self.dispatch(t.orelse) self.leave() - def _For(self, t): - self.fill("for ") - self.dispatch(t.target) - self.write(" in ") - self.dispatch(t.iter) + def _While(self, t): + self.fill("while ") + self.dispatch(t.test) self.enter() self.dispatch(t.body) self.leave() @@ -150,6 +269,16 @@ class Unparser: self.dispatch(t.orelse) self.leave + def _With(self, t): + self.fill("with ") + self.dispatch(t.context_expr) + if t.optional_vars: + self.write(" as ") + self.dispatch(t.optional_vars) + self.enter() + self.dispatch(t.body) + self.leave() + # expr def _Str(self, tree): self.write(repr(tree.s)) @@ -157,6 +286,11 @@ class Unparser: def _Name(self, t): self.write(t.id) + def _Repr(self, t): + self.write("`") + self.dispatch(t.value) + self.write("`") + def _Num(self, t): self.write(repr(t.n)) @@ -167,6 +301,37 @@ class Unparser: self.write(", ") self.write("]") + def _ListComp(self, t): + self.write("[") + self.dispatch(t.elt) + for gen in t.generators: + self.dispatch(gen) + self.write("]") + + def _GeneratorExp(self, t): + self.write("(") + self.dispatch(t.elt) + for gen in t.generators: + self.dispatch(gen) + self.write(")") + + def _comprehension(self, t): + self.write(" for ") + self.dispatch(t.target) + self.write(" in ") + self.dispatch(t.iter) + for if_clause in t.ifs: + self.write(" if ") + self.dispatch(if_clause) + + def _IfExp(self, t): + self.dispatch(t.body) + self.write(" if ") + self.dispatch(t.test) + if t.orelse: + self.write(" else ") + self.dispatch(t.orelse) + def _Dict(self, t): self.write("{") for k,v in zip(t.keys, t.values): @@ -194,8 +359,8 @@ class Unparser: self.write(")") binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", - "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", - "FloorDiv":"//"} + "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&", + "FloorDiv":"//", "Pow": "**"} def _BinOp(self, t): self.write("(") self.dispatch(t.left) @@ -213,6 +378,15 @@ class Unparser: self.dispatch(e) self.write(")") + boolops = {_ast.And: 'and', _ast.Or: 'or'} + def _BoolOp(self, t): + self.write("(") + self.dispatch(t.values[0]) + for v in t.values[1:]: + self.write(" %s " % self.boolops[t.op.__class__]) + self.dispatch(v) + self.write(")") + def _Attribute(self,t): self.dispatch(t.value) self.write(".") @@ -234,12 +408,12 @@ class Unparser: if comma: self.write(", ") else: comma = True self.write("*") - self.dispatch(t.stararg) + self.dispatch(t.starargs) if t.kwargs: if comma: self.write(", ") else: comma = True self.write("**") - self.dispatch(t.stararg) + self.dispatch(t.kwargs) self.write(")") def _Subscript(self, t): @@ -249,6 +423,9 @@ class Unparser: self.write("]") # slice + def _Ellipsis(self, t): + self.write("...") + def _Index(self, t): self.dispatch(t.value) @@ -262,6 +439,12 @@ class Unparser: self.write(":") self.dispatch(t.step) + def _ExtSlice(self, t): + for i, d in enumerate(t.dims): + if i != 0: + self.write(': ') + self.dispatch(d) + # others def _arguments(self, t): first = True @@ -283,13 +466,51 @@ class Unparser: if t.kwarg: if first:first = False else: self.write(", ") - self.write("**"+self.kwarg) - self.write(")") + self.write("**"+t.kwarg) + + def _keyword(self, t): + self.write(t.arg) + self.write("=") + self.dispatch(t.value) + + def _Lambda(self, t): + self.write("lambda ") + self.dispatch(t.args) + self.write(": ") + self.dispatch(t.body) -def roundtrip(filename): +def roundtrip(filename, output=sys.stdout): source = open(filename).read() tree = compile(source, filename, "exec", 0x400) - Unparser(tree) + Unparser(tree, output) + + + +def testdir(a): + try: + names = [n for n in os.listdir(a) if n.endswith('.py')] + except OSError: + print >> sys.stderr, "Directory not readable: %s" % a + else: + for n in names: + fullname = os.path.join(a, n) + if os.path.isfile(fullname): + output = cStringIO.StringIO() + print 'Testing %s' % fullname + try: + roundtrip(fullname, output) + except Exception, e: + print ' Failed to compile, exception is %s' % repr(e) + elif os.path.isdir(fullname): + testdir(fullname) + +def main(args): + if args[0] == '--testdir': + for a in args[1:]: + testdir(a) + else: + for a in args: + roundtrip(a) if __name__=='__main__': - roundtrip(sys.argv[1]) + main(sys.argv[1:]) diff --git a/Demo/pdist/makechangelog.py b/Demo/pdist/makechangelog.py index b26f30b..1ffa588 100755 --- a/Demo/pdist/makechangelog.py +++ b/Demo/pdist/makechangelog.py @@ -6,7 +6,7 @@ import sys import string -import regex +import re import getopt import time @@ -35,9 +35,9 @@ def main(): for rev in allrevs: formatrev(rev, prefix) -parsedateprog = regex.compile( - '^date: \([0-9]+\)/\([0-9]+\)/\([0-9]+\) ' + - '\([0-9]+\):\([0-9]+\):\([0-9]+\); author: \([^ ;]+\)') +parsedateprog = re.compile( + '^date: ([0-9]+)/([0-9]+)/([0-9]+) ' + + '([0-9]+):([0-9]+):([0-9]+); author: ([^ ;]+)') authormap = { 'guido': 'Guido van Rossum ', @@ -70,7 +70,7 @@ def formatrev(rev, prefix): print print -startprog = regex.compile("^Working file: \(.*\)$") +startprog = re.compile("^Working file: (.*)$") def getnextfile(f): while 1: diff --git a/Demo/pdist/rcsbump b/Demo/pdist/rcsbump index e4e9ed5..4fa078e 100755 --- a/Demo/pdist/rcsbump +++ b/Demo/pdist/rcsbump @@ -6,12 +6,12 @@ # Python script for bumping up an RCS major revision number. import sys -import regex +import re import rcslib import string WITHLOCK = 1 -majorrev_re = regex.compile('^[0-9]+') +majorrev_re = re.compile('^[0-9]+') dir = rcslib.RCS() diff --git a/Demo/pdist/rcslib.py b/Demo/pdist/rcslib.py index d5f7b65..3e63869 100755 --- a/Demo/pdist/rcslib.py +++ b/Demo/pdist/rcslib.py @@ -8,7 +8,7 @@ files and (possibly) corresponding work files. import fnmatch import os -import regsub +import re import string import tempfile @@ -150,7 +150,7 @@ class RCS: cmd = 'ci %s%s -t%s %s %s' % \ (lockflag, rev, f.name, otherflags, name) else: - message = regsub.gsub('\([\\"$`]\)', '\\\\\\1', message) + message = re.sub(r'([\"$`])', r'\\\1', message) cmd = 'ci %s%s -m"%s" %s %s' % \ (lockflag, rev, message, otherflags, name) return self._system(cmd) diff --git a/Demo/scripts/eqfix.py b/Demo/scripts/eqfix.py index 165ca49..35c43aa 100755 --- a/Demo/scripts/eqfix.py +++ b/Demo/scripts/eqfix.py @@ -29,7 +29,7 @@ # into a program for a different change to Python programs... import sys -import regex +import re import os from stat import * import string @@ -53,7 +53,7 @@ def main(): if fix(arg): bad = 1 sys.exit(bad) -ispythonprog = regex.compile('^[a-zA-Z0-9_]+\.py$') +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') def ispython(name): return ispythonprog.match(name) >= 0 @@ -104,7 +104,7 @@ def fix(filename): if lineno == 1 and g is None and line[:2] == '#!': # Check for non-Python scripts words = string.split(line[2:]) - if words and regex.search('[pP]ython', words[0]) < 0: + if words and re.search('[pP]ython', words[0]) < 0: msg = filename + ': ' + words[0] msg = msg + ' script; not fixed\n' err(msg) diff --git a/Demo/scripts/ftpstats.py b/Demo/scripts/ftpstats.py index b37a58d..5c1599e 100755 --- a/Demo/scripts/ftpstats.py +++ b/Demo/scripts/ftpstats.py @@ -13,12 +13,12 @@ import os import sys -import regex +import re import string import getopt -pat = '^\([a-zA-Z0-9 :]*\)!\(.*\)!\(.*\)!\([<>].*\)!\([0-9]+\)!\([0-9]+\)$' -prog = regex.compile(pat) +pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$' +prog = re.compile(pat) def main(): maxitems = 25 diff --git a/Demo/scripts/mboxconvert.py b/Demo/scripts/mboxconvert.py index 502d774..8c462f3 100755 --- a/Demo/scripts/mboxconvert.py +++ b/Demo/scripts/mboxconvert.py @@ -10,7 +10,7 @@ import time import os import stat import getopt -import regex +import re def main(): dofile = mmdf @@ -45,7 +45,7 @@ def main(): if sts: sys.exit(sts) -numeric = regex.compile('[1-9][0-9]*') +numeric = re.compile('[1-9][0-9]*') def mh(dir): sts = 0 diff --git a/Demo/scripts/update.py b/Demo/scripts/update.py index 32ad6c8..c936026 100755 --- a/Demo/scripts/update.py +++ b/Demo/scripts/update.py @@ -8,10 +8,10 @@ import os import sys -import regex +import re -pat = '^\([^: \t\n]+\):\([1-9][0-9]*\):' -prog = regex.compile(pat) +pat = '^([^: \t\n]+):([1-9][0-9]*):' +prog = re.compile(pat) class FileObj: def __init__(self, filename): diff --git a/Demo/sockets/mcast.py b/Demo/sockets/mcast.py index 122dad7..1abd305 100755 --- a/Demo/sockets/mcast.py +++ b/Demo/sockets/mcast.py @@ -13,7 +13,6 @@ MYGROUP = '225.0.0.250' import sys import time import struct -import regsub from socket import * diff --git a/Demo/tix/grid.py b/Demo/tix/grid.py new file mode 100644 index 0000000..07ca87f --- /dev/null +++ b/Demo/tix/grid.py @@ -0,0 +1,28 @@ +### +import Tix as tk +from pprint import pprint + +r= tk.Tk() +r.title("test") + +l=tk.Label(r, name="a_label") +l.pack() + +class MyGrid(tk.Grid): + def __init__(self, *args, **kwargs): + kwargs['editnotify']= self.editnotify + tk.Grid.__init__(self, *args, **kwargs) + def editnotify(self, x, y): + return True + +g = MyGrid(r, name="a_grid", +selectunit="cell") +g.pack(fill=tk.BOTH) +for x in xrange(5): + for y in xrange(5): + g.set(x,y,text=str((x,y))) + +c = tk.Button(r, text="Close", command=r.destroy) +c.pack() + +tk.mainloop() diff --git a/Demo/tkinter/guido/ManPage.py b/Demo/tkinter/guido/ManPage.py index 911961e..221af88 100755 --- a/Demo/tkinter/guido/ManPage.py +++ b/Demo/tkinter/guido/ManPage.py @@ -1,6 +1,6 @@ # Widget to display a man page -import regex +import re from Tkinter import * from Tkinter import _tkinter from ScrolledText import ScrolledText @@ -11,10 +11,10 @@ ITALICFONT = '*-Courier-Medium-O-Normal-*-120-*' # XXX Recognizing footers is system dependent # (This one works for IRIX 5.2 and Solaris 2.2) -footerprog = regex.compile( +footerprog = re.compile( '^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n') -emptyprog = regex.compile('^[ \t]*\n') -ulprog = regex.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n') +emptyprog = re.compile('^[ \t]*\n') +ulprog = re.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n') # Basic Man Page class -- does not disable editing class EditableManPage(ScrolledText): diff --git a/Demo/tkinter/guido/mbox.py b/Demo/tkinter/guido/mbox.py index 9b16f6b..3c36d88 100755 --- a/Demo/tkinter/guido/mbox.py +++ b/Demo/tkinter/guido/mbox.py @@ -4,7 +4,7 @@ import os import sys -import regex +import re import getopt import string import mhlib @@ -157,7 +157,7 @@ def scan_unpost(e): scanmenu.unpost() scanmenu.invoke('active') -scanparser = regex.compile('^ *\([0-9]+\)') +scanparser = re.compile('^ *([0-9]+)') def open_folder(e=None): global folder, mhf diff --git a/Demo/tkinter/guido/tkman.py b/Demo/tkinter/guido/tkman.py index 11d9690..6b0b641 100755 --- a/Demo/tkinter/guido/tkman.py +++ b/Demo/tkinter/guido/tkman.py @@ -5,7 +5,7 @@ import sys import os import string -import regex +import re from Tkinter import * from ManPage import ManPage @@ -208,15 +208,15 @@ class SelectionBox: print 'Empty search string' return if not self.casevar.get(): - map = regex.casefold + map = re.IGNORECASE else: map = None try: if map: - prog = regex.compile(search, map) + prog = re.compile(search, map) else: - prog = regex.compile(search) - except regex.error, msg: + prog = re.compile(search) + except re.error, msg: self.frame.bell() print 'Regex error:', msg return diff --git a/Doc/Makefile.deps b/Doc/Makefile.deps index 832402d..20c0688 100644 --- a/Doc/Makefile.deps +++ b/Doc/Makefile.deps @@ -126,6 +126,7 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ lib/libwarnings.tex \ lib/libimp.tex \ lib/libzipimport.tex \ + lib/librunpy.tex \ lib/libpkgutil.tex \ lib/libparser.tex \ lib/libbltin.tex \ diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex index 5521b80..1982bae 100644 --- a/Doc/api/concrete.tex +++ b/Doc/api/concrete.tex @@ -156,7 +156,7 @@ There is no \cfunction{PyNone_Check()} function for the same reason. Create a new integer object with a value of \var{ival}. The current implementation keeps an array of integer objects for all - integers between \code{-1} and \code{100}, when you create an int in + integers between \code{-5} and \code{256}, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of \code{1}. I suspect the behaviour of Python in this case is undefined. :-) @@ -333,7 +333,9 @@ booleans. The following macros are available, however. The pointer value can be retrieved from the resulting value using \cfunction{PyLong_AsVoidPtr()}. \versionadded{1.5.2} -\end{cfuncdesc} + \versionchanged[If the integer is larger than LONG_MAX, + a positive long integer is returned]{2.5} + \end{cfuncdesc} \begin{cfuncdesc}{long}{PyLong_AsLong}{PyObject *pylong} Return a C \ctype{long} representation of the contents of @@ -394,6 +396,8 @@ booleans. The following macros are available, however. produce a usable \ctype{void} pointer for values created with \cfunction{PyLong_FromVoidPtr()}. \versionadded{1.5.2} + \versionchanged[For values outside 0..LONG_MAX, both signed and + unsigned integers are acccepted]{2.5} \end{cfuncdesc} @@ -1803,8 +1807,9 @@ format. \begin{cfuncdesc}{PyObject*}{PyList_GetItem}{PyObject *list, Py_ssize_t index} Return the object at position \var{pos} in the list pointed to by - \var{p}. If \var{pos} is out of bounds, return \NULL{} and set an - \exception{IndexError} exception. + \var{p}. The position must be positive, indexing from the end of the + list is not supported. If \var{pos} is out of bounds, return \NULL{} + and set an \exception{IndexError} exception. \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyList_GET_ITEM}{PyObject *list, Py_ssize_t i} @@ -2264,8 +2269,8 @@ There are a few functions specific to Python functions. \begin{cfuncdesc}{PyObject*}{PyFunction_New}{PyObject *code, PyObject *globals} Return a new function object associated with the code object - \var{code}. \var{globals} must be a dictionary with the the global - varaibles accessible to the function. + \var{code}. \var{globals} must be a dictionary with the global + variables accessible to the function. The function's docstring, name and \var{__module__} are retrieved from the code object, the argument defaults and closure are set to @@ -2811,6 +2816,7 @@ rather than explicitly calling \cfunction{PyGen_New}. \begin{cfuncdesc}{PyObject*}{PyGen_New}{PyFrameObject *frame} Create and return a new generator object based on the \var{frame} object. + A reference to \var{frame} is stolen by this function. The parameter must not be \NULL{}. \end{cfuncdesc} @@ -3025,9 +3031,7 @@ or the abstract number protocol (including \cfunction{PyNumber_Or()}, \cfunction{PyNumber_Xor()}, \cfunction{PyNumber_InPlaceAdd()}, \cfunction{PyNumber_InPlaceSubtract()}, \cfunction{PyNumber_InPlaceOr()}, and \cfunction{PyNumber_InPlaceXor()}). -Note, \cfunction{PyNumber_InPlaceSubtract()} is also useful clearing -clearing a set (\code{s-=s}). - + \begin{ctypedesc}{PySetObject} This subtype of \ctype{PyObject} is used to hold the internal data for both \class{set} and \class{frozenset} objects. It is like a @@ -3111,7 +3115,6 @@ The following functions and macros are available for instances of \class{frozenset}, or an instance of a subtype. \end{cfuncdesc} - The following functions are available for instances of \class{set} or its subtypes but not for instances of \class{frozenset} or its subtypes. @@ -3142,4 +3145,6 @@ its subtypes but not for instances of \class{frozenset} or its subtypes. of \class{set} or its subtype. \end{cfuncdesc} - +\begin{cfuncdesc}{int}{PySet_Clear}{PyObject *set} + Empty an existing set of all elements. +\end{cfuncdesc} diff --git a/Doc/api/intro.tex b/Doc/api/intro.tex index 608d562..96f18ec 100644 --- a/Doc/api/intro.tex +++ b/Doc/api/intro.tex @@ -569,8 +569,11 @@ defined in \file{Modules/getpath.c}). Sometimes, it is desirable to ``uninitialize'' Python. For instance, the application may want to start over (make another call to \cfunction{Py_Initialize()}) or the application is simply done with its -use of Python and wants to free all memory allocated by Python. This +use of Python and wants to free memory allocated by Python. This can be accomplished by calling \cfunction{Py_Finalize()}. The function \cfunction{Py_IsInitialized()}\ttindex{Py_IsInitialized()} returns true if Python is currently in the initialized state. More information about these functions is given in a later chapter. +Notice that \cfunction{Py_Finalize} does \emph{not} free all memory +allocated by the Python interpreter, e.g. memory allocated by extension +modules currently cannot be released. diff --git a/Doc/api/memory.tex b/Doc/api/memory.tex index 3dbe9a5..4bc2c7a 100644 --- a/Doc/api/memory.tex +++ b/Doc/api/memory.tex @@ -195,9 +195,7 @@ free(buf1); /* Fatal -- should be PyMem_Del() */ In addition to the functions aimed at handling raw memory blocks from the Python heap, objects in Python are allocated and released with \cfunction{PyObject_New()}, \cfunction{PyObject_NewVar()} and -\cfunction{PyObject_Del()}, or with their corresponding macros -\cfunction{PyObject_NEW()}, \cfunction{PyObject_NEW_VAR()} and -\cfunction{PyObject_DEL()}. +\cfunction{PyObject_Del()}. These will be explained in the next chapter on defining and implementing new object types in C. diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index b7e25b9..2d758b0 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -62,23 +62,6 @@ defining new object types. after this call as the memory is no longer a valid Python object. \end{cfuncdesc} -\begin{cfuncdesc}{\var{TYPE}*}{PyObject_NEW}{TYPE, PyTypeObject *type} - Macro version of \cfunction{PyObject_New()}, to gain performance at - the expense of safety. This does not check \var{type} for a \NULL{} - value. -\end{cfuncdesc} - -\begin{cfuncdesc}{\var{TYPE}*}{PyObject_NEW_VAR}{TYPE, PyTypeObject *type, - Py_ssize_t size} - Macro version of \cfunction{PyObject_NewVar()}, to gain performance - at the expense of safety. This does not check \var{type} for a - \NULL{} value. -\end{cfuncdesc} - -\begin{cfuncdesc}{void}{PyObject_DEL}{PyObject *op} - Macro version of \cfunction{PyObject_Del()}. -\end{cfuncdesc} - \begin{cfuncdesc}{PyObject*}{Py_InitModule}{char *name, PyMethodDef *methods} Create a new module object based on a name and table of functions, diff --git a/Doc/api/refcounts.dat b/Doc/api/refcounts.dat index f3bd32e..7bba011 100644 --- a/Doc/api/refcounts.dat +++ b/Doc/api/refcounts.dat @@ -31,6 +31,9 @@ # The parameter names are as they appear in the API manual, not the source # code. +PyBool_FromLong:PyObject*::+1: +PyBool_FromLong:long:v:0: + PyBuffer_FromObject:PyObject*::+1: PyBuffer_FromObject:PyObject*:base:+1: PyBuffer_FromObject:int:offset:: @@ -110,6 +113,35 @@ PyComplex_ImagAsDouble:PyObject*:op:0: PyComplex_RealAsDouble:double::: PyComplex_RealAsDouble:PyObject*:op:0: +PyDate_FromDate:PyObject*::+1: +PyDate_FromDate:int:year:: +PyDate_FromDate:int:month:: +PyDate_FromDate:int:day:: + +PyDate_FromTimestamp:PyObject*::+1: +PyDate_FromTimestamp:PyObject*:args:0: + +PyDateTime_FromDateAndTime:PyObject*::+1: +PyDateTime_FromDateAndTime:int:year:: +PyDateTime_FromDateAndTime:int:month:: +PyDateTime_FromDateAndTime:int:day:: +PyDateTime_FromDateAndTime:int:hour:: +PyDateTime_FromDateAndTime:int:minute:: +PyDateTime_FromDateAndTime:int:second:: +PyDateTime_FromDateAndTime:int:usecond:: + +PyDateTime_FromTimestamp:PyObject*::+1: +PyDateTime_FromTimestamp:PyObject*:args:0: + +PyDelta_FromDSU:PyObject*::+1: +PyDelta_FromDSU:int:days:: +PyDelta_FromDSU:int:seconds:: +PyDelta_FromDSU:int:useconds:: + +PyDescr_NewClassMethod:PyObject*::+1: +PyDescr_NewClassMethod:PyTypeObject*:type:: +PyDescr_NewClassMethod:PyMethodDef*:method:: + PyDescr_NewGetSet:PyObject*::+1: PyDescr_NewGetSet:PyTypeObject*:type:: PyDescr_NewGetSet:PyGetSetDef*:getset:: @@ -226,6 +258,15 @@ PyErr_Restore:PyObject*:type:-1: PyErr_Restore:PyObject*:value:-1: PyErr_Restore:PyObject*:traceback:-1: +PyErr_SetExcFromWindowsErr:PyObject*::null: +PyErr_SetExcFromWindowsErr:PyObject*:type:0: +PyErr_SetExcFromWindowsErr:int:ierr:: + +PyErr_SetExcFromWindowsErrWithFilename:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:0: +PyErr_SetExcFromWindowsErrWithFilename:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilename:char*:filename:: + PyErr_SetFromErrno:PyObject*::null: PyErr_SetFromErrno:PyObject*:type:0: @@ -337,6 +378,13 @@ PyFloat_Check:PyObject*:p:0: PyFloat_FromDouble:PyObject*::+1: PyFloat_FromDouble:double:v:: +PyFloat_FromString:PyObject*::+1: +PyFloat_FromString:PyObject*:str:0: +PyFloat_FromString:char**:pend:0:ignored + +PyFrozenSet_New:PyObject*::+1: +PyFrozenSet_New:PyObject*:iterable:0: + PyFunction_GetClosure:PyObject*::0: PyFunction_GetClosure:PyObject*:op:0: @@ -364,6 +412,9 @@ PyFunction_SetDefaults:int::: PyFunction_SetDefaults:PyObject*:op:0: PyFunction_SetDefaults:PyObject*:defaults:+1: +PyGen_New:PyObject*::+1: +PyGen_New:PyFrameObject*:frame:0: + Py_InitModule:PyObject*::0: Py_InitModule:char*:name:: Py_InitModule:PyMethodDef[]:methods:: @@ -432,6 +483,14 @@ PyInt_Check:PyObject*:op:0: PyInt_FromLong:PyObject*::+1: PyInt_FromLong:long:ival:: +PyInt_FromString:PyObject*::+1: +PyInt_FromString:char*:str:0: +PyInt_FromString:char**:pend:0: +PyInt_FromString:int:base:0: + +PyInt_FromSsize_t:PyObject*::+1: +PyInt_FromSsize_t:Py_ssize_t:ival:: + PyInt_GetMax:long::: PyInterpreterState_Clear:void::: @@ -939,6 +998,31 @@ PyRun_File:int:start:: PyRun_File:PyObject*:globals:0: PyRun_File:PyObject*:locals:0: +PyRun_FileEx:PyObject*::+1:??? -- same as eval_code2() +PyRun_FileEx:FILE*:fp:: +PyRun_FileEx:char*:filename:: +PyRun_FileEx:int:start:: +PyRun_FileEx:PyObject*:globals:0: +PyRun_FileEx:PyObject*:locals:0: +PyRun_FileEx:int:closeit:: + +PyRun_FileFlags:PyObject*::+1:??? -- same as eval_code2() +PyRun_FileFlags:FILE*:fp:: +PyRun_FileFlags:char*:filename:: +PyRun_FileFlags:int:start:: +PyRun_FileFlags:PyObject*:globals:0: +PyRun_FileFlags:PyObject*:locals:0: +PyRun_FileFlags:PyCompilerFlags*:flags:: + +PyRun_FileExFlags:PyObject*::+1:??? -- same as eval_code2() +PyRun_FileExFlags:FILE*:fp:: +PyRun_FileExFlags:char*:filename:: +PyRun_FileExFlags:int:start:: +PyRun_FileExFlags:PyObject*:globals:0: +PyRun_FileExFlags:PyObject*:locals:0: +PyRun_FileExFlags:int:closeit:: +PyRun_FileExFlags:PyCompilerFlags*:flags:: + PyRun_InteractiveLoop:int::: PyRun_InteractiveLoop:FILE*:fp:: PyRun_InteractiveLoop:char*:filename:: @@ -960,6 +1044,13 @@ PyRun_String:int:start:: PyRun_String:PyObject*:globals:0: PyRun_String:PyObject*:locals:0: +PyRun_StringFlags:PyObject*::+1:??? -- same as eval_code2() +PyRun_StringFlags:char*:str:: +PyRun_StringFlags:int:start:: +PyRun_StringFlags:PyObject*:globals:0: +PyRun_StringFlags:PyObject*:locals:0: +PyRun_StringFlags:PyCompilerFlags*:flags:: + PySeqIter_New:PyObject*::+1: PySeqIter_New:PyObject*:seq:: @@ -1053,6 +1144,9 @@ PySet_Discard:int::: PySet_Discard:PyObject*:set:0: PySet_Discard:PyObject*:key:-1:no effect if key not found +PySet_New:PyObject*::+1: +PySet_New:PyObject*:iterable:0: + PySet_Pop:PyObject*::0:or returns NULL and raises KeyError if set is empty PySet_Pop:PyObject*:set:0: @@ -1167,6 +1261,12 @@ PyThreadState_New:PyInterpreterState*:interp:: PyThreadState_Swap:PyThreadState*::: PyThreadState_Swap:PyThreadState*:tstate:: +PyTime_FromTime:PyObject*::+1: +PyTime_FromTime:int:hour:: +PyTime_FromTime:int:minute:: +PyTime_FromTime:int:second:: +PyTime_FromTime:int:usecond:: + PyTuple_Check:int::: PyTuple_Check:PyObject*:p:0: @@ -1186,6 +1286,10 @@ PyTuple_GetSlice:int:high:: PyTuple_New:PyObject*::+1: PyTuple_New:int:len:: +PyTuple_Pack:PyObject*::+1: +PyTuple_Pack:int:len:: +PyTuple_Pack:PyObject*:...:0: + PyTuple_SET_ITEM:void::: PyTuple_SET_ITEM:PyTupleObject*:p:0: PyTuple_SET_ITEM:int:pos:: @@ -1298,6 +1402,19 @@ PyUnicode_Decode:int:size:: PyUnicode_Decode:const char*:encoding:: PyUnicode_Decode:const char*:errors:: +PyUnicode_DecodeUTF16Stateful:PyObject*::+1: +PyUnicode_DecodeUTF16Stateful:const char*:s:: +PyUnicode_DecodeUTF16Stateful:int:size:: +PyUnicode_DecodeUTF16Stateful:const char*:errors:: +PyUnicode_DecodeUTF16Stateful:int*:byteorder:: +PyUnicode_DecodeUTF16Stateful:int*:consumed:: + +PyUnicode_DecodeUTF8Stateful:PyObject*::+1: +PyUnicode_DecodeUTF8Stateful:const char*:s:: +PyUnicode_DecodeUTF8Stateful:int:size:: +PyUnicode_DecodeUTF8Stateful:const char*:errors:: +PyUnicode_DecodeUTF8Stateful:int*:consumed:: + PyUnicode_Encode:PyObject*::+1: PyUnicode_Encode:const Py_UNICODE*:s:: PyUnicode_Encode:int:size:: @@ -1513,6 +1630,12 @@ Py_CompileString:char*:str:: Py_CompileString:char*:filename:: Py_CompileString:int:start:: +Py_CompileStringFlags:PyObject*::+1: +Py_CompileStringFlags:char*:str:: +Py_CompileStringFlags:char*:filename:: +Py_CompileStringFlags:int:start:: +Py_CompileStringFlags:PyCompilerFlags*:flags:: + Py_DECREF:void::: Py_DECREF:PyObject*:o:-1: diff --git a/Doc/commontex/boilerplate.tex b/Doc/commontex/boilerplate.tex index b4c9f48..55a4184 100644 --- a/Doc/commontex/boilerplate.tex +++ b/Doc/commontex/boilerplate.tex @@ -5,5 +5,5 @@ Email: \email{docs@python.org} } -\date{\today} % XXX update before final release! +\date{5th April 2006} % XXX update before final release! \input{patchlevel} % include Python version information diff --git a/Doc/commontex/license.tex b/Doc/commontex/license.tex index 525ce8a..d1554c2 100644 --- a/Doc/commontex/license.tex +++ b/Doc/commontex/license.tex @@ -49,6 +49,8 @@ GPL-compatible; the table below summarizes the various releases. \linev{2.4}{2.3}{2004}{PSF}{yes} \linev{2.4.1}{2.4}{2005}{PSF}{yes} \linev{2.4.2}{2.4.1}{2005}{PSF}{yes} + \linev{2.4.3}{2.4.2}{2006}{PSF}{yes} + \linev{2.5}{2.4}{2006}{PSF}{yes} \end{tablev} \note{GPL-compatible doesn't mean that we're distributing @@ -430,26 +432,49 @@ The source for the \module{fpectl} module includes the following notice: The source code for the \module{md5} module contains the following notice: \begin{verbatim} -Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. + 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@aladdin.com + + 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. \end{verbatim} diff --git a/Doc/dist/dist.tex b/Doc/dist/dist.tex index d6ddad8..3ba51d0 100644 --- a/Doc/dist/dist.tex +++ b/Doc/dist/dist.tex @@ -1467,7 +1467,7 @@ script as follows: \lineii{\%description (section)}{\option{long\_description}} \end{tableii} -Additionally, there many options in \file{.spec} files that don't have +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: @@ -1737,6 +1737,8 @@ password: \chapter{Uploading Packages to the Package Index} \label{package-upload} +\versionadded{2.5} + 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. @@ -1754,8 +1756,21 @@ 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 and password stored in -the file \file{\$HOME/.pypirc}, see section~\ref{pypirc}. +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). + +You can use the \programopt{--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 \programopt{--identity=\var{name}} option. + +Other \command{upload} options include +\programopt{--repository=\var{url}} (which lets you override the +repository setting from \file{\$HOME/.pypirc}), and +\programopt{--show-response} (which displays the full response text +from the PyPI server for help in debugging upload problems). \chapter{Examples} \label{examples} diff --git a/Doc/howto/regex.tex b/Doc/howto/regex.tex index 87fdad2..f9867ae 100644 --- a/Doc/howto/regex.tex +++ b/Doc/howto/regex.tex @@ -33,11 +33,8 @@ This document is available from The \module{re} module was added in Python 1.5, and provides Perl-style regular expression patterns. Earlier versions of Python -came with the \module{regex} module, which provides Emacs-style -patterns. Emacs-style patterns are slightly less readable and -don't provide as many features, so there's not much reason to use -the \module{regex} module when writing new code, though you might -encounter old code that uses it. +came with the \module{regex} module, which provided Emacs-style +patterns. \module{regex} module was removed in Python 2.5. Regular expressions (or REs) are essentially a tiny, highly specialized programming language embedded inside Python and made @@ -1458,7 +1455,7 @@ Jeffrey Friedl's \citetitle{Mastering Regular Expressions}, published by O'Reilly. Unfortunately, it exclusively concentrates on Perl and Java's flavours of regular expressions, and doesn't contain any Python material at all, so it won't be useful as a reference for programming -in Python. (The first edition covered Python's now-obsolete +in Python. (The first edition covered Python's now-removed \module{regex} module, which won't help you much.) Consider checking it out from your library. diff --git a/Doc/lib/compiler.tex b/Doc/lib/compiler.tex index e619a9a..f0926e7 100644 --- a/Doc/lib/compiler.tex +++ b/Doc/lib/compiler.tex @@ -40,9 +40,9 @@ modules contained in the package. \begin{funcdesc}{parse}{buf} Returns an abstract syntax tree for the Python source code in \var{buf}. -The function raises SyntaxError if there is an error in the source -code. The return value is a \class{compiler.ast.Module} instance that -contains the tree. +The function raises \exception{SyntaxError} if there is an error in the +source code. The return value is a \class{compiler.ast.Module} instance +that contains the tree. \end{funcdesc} \begin{funcdesc}{parseFile}{path} diff --git a/Doc/lib/email-dir.py b/Doc/lib/email-dir.py index 2d89a2f..c04f57d 100644 --- a/Doc/lib/email-dir.py +++ b/Doc/lib/email-dir.py @@ -1,83 +1,69 @@ #!/usr/bin/env python -"""Send the contents of a directory as a MIME message. +"""Send the contents of a directory as a MIME message.""" -Usage: dirmail [options] from to [to ...]* - -Options: - -h / --help - Print this message and exit. - - -d directory - --directory=directory - Mail the contents of the specified directory, otherwise use the - current directory. Only the regular files in the directory are sent, - and we don't recurse to subdirectories. - -`from' is the email address of the sender of the message. - -`to' is the email address of the recipient of the message, and multiple -recipients may be given. - -The email is sent by forwarding to your local SMTP server, which then does the -normal delivery process. Your local machine must be running an SMTP server. -""" - -import sys import os -import getopt +import sys import smtplib # For guessing MIME type based on file name extension import mimetypes -from email import Encoders -from email.Message import Message -from email.MIMEAudio import MIMEAudio -from email.MIMEBase import MIMEBase -from email.MIMEMultipart import MIMEMultipart -from email.MIMEImage import MIMEImage -from email.MIMEText import MIMEText - -COMMASPACE = ', ' +from optparse import OptionParser +from email import encoders +from email.message import Message +from email.mime.audio import MIMEAudio +from email.mime.base import MIMEBase +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText -def usage(code, msg=''): - print >> sys.stderr, __doc__ - if msg: - print >> sys.stderr, msg - sys.exit(code) +COMMASPACE = ', ' def main(): - try: - opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory=']) - except getopt.error, msg: - usage(1, msg) - - dir = os.curdir - for opt, arg in opts: - if opt in ('-h', '--help'): - usage(0) - elif opt in ('-d', '--directory'): - dir = arg - - if len(args) < 2: - usage(1) - - sender = args[0] - recips = args[1:] - + parser = OptionParser(usage="""\ +Send the contents of a directory as a MIME message. + +Usage: %prog [options] + +Unless the -o option is given, the email is sent by forwarding to your local +SMTP server, which then does the normal delivery process. Your local machine +must be running an SMTP server. +""") + parser.add_option('-d', '--directory', + type='string', action='store', + help="""Mail the contents of the specified directory, + otherwise use the current directory. Only the regular + files in the directory are sent, and we don't recurse to + subdirectories.""") + parser.add_option('-o', '--output', + type='string', action='store', metavar='FILE', + help="""Print the composed message to FILE instead of + sending the message to the SMTP server.""") + parser.add_option('-s', '--sender', + type='string', action='store', metavar='SENDER', + help='The value of the From: header (required)') + parser.add_option('-r', '--recipient', + type='string', action='append', metavar='RECIPIENT', + default=[], dest='recipients', + help='A To: header value (at least one required)') + opts, args = parser.parse_args() + if not opts.sender or not opts.recipients: + parser.print_help() + sys.exit(1) + directory = opts.directory + if not directory: + directory = '.' # Create the enclosing (outer) message outer = MIMEMultipart() - outer['Subject'] = 'Contents of directory %s' % os.path.abspath(dir) - outer['To'] = COMMASPACE.join(recips) - outer['From'] = sender + outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory) + outer['To'] = COMMASPACE.join(opts.recipients) + outer['From'] = opts.sender outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' - # To guarantee the message ends with a newline - outer.epilogue = '' - for filename in os.listdir(dir): - path = os.path.join(dir, filename) + for filename in os.listdir(directory): + path = os.path.join(directory, filename) if not os.path.isfile(path): continue # Guess the content type based on the file's extension. Encoding @@ -108,16 +94,21 @@ def main(): msg.set_payload(fp.read()) fp.close() # Encode the payload using Base64 - Encoders.encode_base64(msg) + encoders.encode_base64(msg) # Set the filename parameter msg.add_header('Content-Disposition', 'attachment', filename=filename) outer.attach(msg) - - # Now send the message - s = smtplib.SMTP() - s.connect() - s.sendmail(sender, recips, outer.as_string()) - s.close() + # Now send or store the message + composed = outer.as_string() + if opts.output: + fp = open(opts.output, 'w') + fp.write(composed) + fp.close() + else: + s = smtplib.SMTP() + s.connect() + s.sendmail(opts.sender, opts.recipients, composed) + s.close() if __name__ == '__main__': diff --git a/Doc/lib/email-mime.py b/Doc/lib/email-mime.py index 048a59f..5097253 100644 --- a/Doc/lib/email-mime.py +++ b/Doc/lib/email-mime.py @@ -2,8 +2,8 @@ import smtplib # Here are the email package modules we'll need -from email.MIMEImage import MIMEImage -from email.MIMEMultipart import MIMEMultipart +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart COMMASPACE = ', ' @@ -15,8 +15,6 @@ msg['Subject'] = 'Our family reunion' msg['From'] = me msg['To'] = COMMASPACE.join(family) msg.preamble = 'Our family reunion' -# Guarantees the message ends in a newline -msg.epilogue = '' # Assume we know that the image files are all in PNG format for file in pngfiles: diff --git a/Doc/lib/email-simple.py b/Doc/lib/email-simple.py index a445f1b..44152a4 100644 --- a/Doc/lib/email-simple.py +++ b/Doc/lib/email-simple.py @@ -2,7 +2,7 @@ import smtplib # Import the email modules we'll need -from email.MIMEText import MIMEText +from email.mime.text import MIMEText # Open a plain text file for reading. For this example, assume that # the text file contains only ASCII characters. diff --git a/Doc/lib/email-unpack.py b/Doc/lib/email-unpack.py index b166fdb..fc05d99 100644 --- a/Doc/lib/email-unpack.py +++ b/Doc/lib/email-unpack.py @@ -1,59 +1,44 @@ #!/usr/bin/env python -"""Unpack a MIME message into a directory of files. +"""Unpack a MIME message into a directory of files.""" -Usage: unpackmail [options] msgfile - -Options: - -h / --help - Print this message and exit. - - -d directory - --directory=directory - Unpack the MIME message into the named directory, which will be - created if it doesn't already exist. - -msgfile is the path to the file containing the MIME message. -""" - -import sys import os -import getopt +import sys +import email import errno import mimetypes -import email - -def usage(code, msg=''): - print >> sys.stderr, __doc__ - if msg: - print >> sys.stderr, msg - sys.exit(code) +from optparse import OptionParser def main(): - try: - opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory=']) - except getopt.error, msg: - usage(1, msg) - - dir = os.curdir - for opt, arg in opts: - if opt in ('-h', '--help'): - usage(0) - elif opt in ('-d', '--directory'): - dir = arg + parser = OptionParser(usage="""\ +Unpack a MIME message into a directory of files. + +Usage: %prog [options] msgfile +""") + parser.add_option('-d', '--directory', + type='string', action='store', + help="""Unpack the MIME message into the named + directory, which will be created if it doesn't already + exist.""") + opts, args = parser.parse_args() + if not opts.directory: + parser.print_help() + sys.exit(1) try: msgfile = args[0] except IndexError: - usage(1) + parser.print_help() + sys.exit(1) try: - os.mkdir(dir) + os.mkdir(opts.directory) except OSError, e: # Ignore directory exists error - if e.errno <> errno.EEXIST: raise + if e.errno <> errno.EEXIST: + raise fp = open(msgfile) msg = email.message_from_file(fp) @@ -74,8 +59,8 @@ def main(): ext = '.bin' filename = 'part-%03d%s' % (counter, ext) counter += 1 - fp = open(os.path.join(dir, filename), 'wb') - fp.write(part.get_payload(decode=1)) + fp = open(os.path.join(opts.directory, filename), 'wb') + fp.write(part.get_payload(decode=True)) fp.close() diff --git a/Doc/lib/email.tex b/Doc/lib/email.tex index 3a90e22..6853325 100644 --- a/Doc/lib/email.tex +++ b/Doc/lib/email.tex @@ -1,4 +1,4 @@ -% Copyright (C) 2001-2004 Python Software Foundation +% Copyright (C) 2001-2006 Python Software Foundation % Author: barry@python.org (Barry Warsaw) \section{\module{email} --- @@ -18,10 +18,10 @@ subsumes most of the functionality in several older standard modules such as \refmodule{rfc822}, \refmodule{mimetools}, \refmodule{multifile}, and other non-standard packages such as \module{mimecntl}. It is specifically \emph{not} designed to do any -sending of email messages to SMTP (\rfc{2821}) servers; that is the -function of the \refmodule{smtplib} module. The \module{email} -package attempts to be as RFC-compliant as possible, supporting in -addition to \rfc{2822}, such MIME-related RFCs as +sending of email messages to SMTP (\rfc{2821}), NNTP, or other servers; those +are functions of modules such as \refmodule{smtplib} and \refmodule{nntplib}. +The \module{email} package attempts to be as RFC-compliant as possible, +supporting in addition to \rfc{2822}, such MIME-related RFCs as \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}. The primary distinguishing feature of the \module{email} package is @@ -41,7 +41,7 @@ The following sections describe the functionality of the should be common in applications: an email message is read as flat text from a file or other source, the text is parsed to produce the object structure of the email message, this structure is manipulated, -and finally rendered back into flat text. +and finally, the object tree is rendered back into flat text. It is perfectly feasible to create the object structure out of whole cloth --- i.e. completely from scratch. From there, a similar @@ -56,6 +56,7 @@ package, a section on differences and porting is provided. \begin{seealso} \seemodule{smtplib}{SMTP protocol client} + \seemodule{nntplib}{NNTP protocol client} \end{seealso} \subsection{Representing an email message} @@ -88,22 +89,51 @@ package, a section on differences and porting is provided. \subsection{Iterators} \input{emailiter} -\subsection{Package History} +\subsection{Package History\label{email-pkg-history}} -Version 1 of the \module{email} package was bundled with Python -releases up to Python 2.2.1. Version 2 was developed for the Python -2.3 release, and backported to Python 2.2.2. It was also available as -a separate distutils-based package, and is compatible back to Python 2.1. +This table describes the release history of the email package, corresponding +to the version of Python that the package was released with. For purposes of +this document, when you see a note about change or added versions, these refer +to the Python version the change was made it, \emph{not} the email package +version. This table also describes the Python compatibility of each version +of the package. -\module{email} version 3.0 was released with Python 2.4 and as a separate -distutils-based package. It is compatible back to Python 2.3. +\begin{tableiii}{l|l|l}{constant}{email version}{distributed with}{compatible with} +\lineiii{1.x}{Python 2.2.0 to Python 2.2.1}{\emph{no longer supported}} +\lineiii{2.5}{Python 2.2.2+ and Python 2.3}{Python 2.1 to 2.5} +\lineiii{3.0}{Python 2.4}{Python 2.3 to 2.5} +\lineiii{4.0}{Python 2.5}{Python 2.3 to 2.5} +\end{tableiii} -Here are the differences between \module{email} version 3 and version 2: +Here are the major differences between \module{email} verson 4 and version 3: + +\begin{itemize} +\item All modules have been renamed according to \pep{8} standards. For + example, the version 3 module \module{email.Message} was renamed to + \module{email.message} in version 4. + +\item A new subpackage \module{email.mime} was added and all the version 3 + \module{email.MIME*} modules were renamed and situated into the + \module{email.mime} subpackage. For example, the version 3 module + \module{email.MIMEText} was renamed to \module{email.mime.text}. + + \emph{Note that the version 3 names will continue to work until Python + 2.6}. + +\item The \module{email.mime.application} module was added, which contains the + \class{MIMEApplication} class. + +\item Methods that were deprecated in version 3 have been removed. These + include \method{Generator.__call__()}, \method{Message.get_type()}, + \method{Message.get_main_type()}, \method{Message.get_subtype()}. +\end{itemize} + +Here are the major differences between \module{email} version 3 and version 2: \begin{itemize} \item The \class{FeedParser} class was introduced, and the \class{Parser} class was implemented in terms of the \class{FeedParser}. All parsing - there for is non-strict, and parsing will make a best effort never to + therefore is non-strict, and parsing will make a best effort never to raise an exception. Problems found while parsing messages are stored in the message's \var{defect} attribute. @@ -117,7 +147,7 @@ Here are the differences between \module{email} version 3 and version 2: \method{Generator.__call__()}, \method{Message.get_type()}, \method{Message.get_main_type()}, \method{Message.get_subtype()}, and the \var{strict} argument to the \class{Parser} class. These are - expected to be removed in email 3.1. + expected to be removed in future versions. \item Support for Pythons earlier than 2.3 has been removed. \end{itemize} @@ -278,12 +308,12 @@ The \class{Message} class has the following differences: \item The method \method{getpayloadastext()} was removed. Similar functionality is supported by the \class{DecodedGenerator} class in the - \refmodule{email.Generator} module. + \refmodule{email.generator} module. \item The method \method{getbodyastext()} was removed. You can get similar functionality by creating an iterator with \function{typed_subpart_iterator()} in the - \refmodule{email.Iterators} module. + \refmodule{email.iterators} module. \end{itemize} The \class{Parser} class has no differences in its public interface. @@ -295,7 +325,7 @@ notification\footnote{Delivery Status Notifications (DSN) are defined in \rfc{1894}.}. The \class{Generator} class has no differences in its public -interface. There is a new class in the \refmodule{email.Generator} +interface. There is a new class in the \refmodule{email.generator} module though, called \class{DecodedGenerator} which provides most of the functionality previously available in the \method{Message.getpayloadastext()} method. @@ -329,11 +359,11 @@ The following modules and classes have been changed: \module{mimelib} provided some utility functions in its \module{address} and \module{date} modules. All of these functions -have been moved to the \refmodule{email.Utils} module. +have been moved to the \refmodule{email.utils} module. The \code{MsgReader} class/module has been removed. Its functionality is most closely supported in the \function{body_line_iterator()} -function in the \refmodule{email.Iterators} module. +function in the \refmodule{email.iterators} module. \subsection{Examples} diff --git a/Doc/lib/emailcharsets.tex b/Doc/lib/emailcharsets.tex index 18f2a01..e0be68a 100644 --- a/Doc/lib/emailcharsets.tex +++ b/Doc/lib/emailcharsets.tex @@ -1,4 +1,4 @@ -\declaremodule{standard}{email.Charset} +\declaremodule{standard}{email.charset} \modulesynopsis{Character Sets} This module provides a class \class{Charset} for representing @@ -7,6 +7,8 @@ well as a character set registry and several convenience methods for manipulating this registry. Instances of \class{Charset} are used in several other modules within the \module{email} package. +Import this class from the \module{email.charset} module. + \versionadded{2.2.2} \begin{classdesc}{Charset}{\optional{input_charset}} @@ -153,7 +155,7 @@ input charset to the output charset automatically. This is not useful for multibyte character sets, which have line length issues (multibyte characters must be split on a character, not a byte boundary); use the higher-level \class{Header} class to deal with these issues (see -\refmodule{email.Header}). \var{convert} defaults to \code{False}. +\refmodule{email.header}). \var{convert} defaults to \code{False}. The type of encoding (base64 or quoted-printable) will be based on the \var{header_encoding} attribute. @@ -188,7 +190,7 @@ This method allows you to compare two \class{Charset} instances for equality. This method allows you to compare two \class{Charset} instances for inequality. \end{methoddesc} -The \module{email.Charset} module also provides the following +The \module{email.charset} module also provides the following functions for adding new entries to the global character set, alias, and codec registries: diff --git a/Doc/lib/emailencoders.tex b/Doc/lib/emailencoders.tex index a49e04d..3d05c2a 100644 --- a/Doc/lib/emailencoders.tex +++ b/Doc/lib/emailencoders.tex @@ -1,4 +1,4 @@ -\declaremodule{standard}{email.Encoders} +\declaremodule{standard}{email.encoders} \modulesynopsis{Encoders for email message payloads.} When creating \class{Message} objects from scratch, you often need to @@ -7,7 +7,7 @@ This is especially true for \mimetype{image/*} and \mimetype{text/*} type messages containing binary data. The \module{email} package provides some convenient encodings in its -\module{Encoders} module. These encoders are actually used by the +\module{encoders} module. These encoders are actually used by the \class{MIMEAudio} and \class{MIMEImage} class constructors to provide default encodings. All encoder functions take exactly one argument, the message object to encode. They usually extract the payload, encode it, and reset the diff --git a/Doc/lib/emailexc.tex b/Doc/lib/emailexc.tex index 6ac0889..3cef1d5 100644 --- a/Doc/lib/emailexc.tex +++ b/Doc/lib/emailexc.tex @@ -1,8 +1,8 @@ -\declaremodule{standard}{email.Errors} +\declaremodule{standard}{email.errors} \modulesynopsis{The exception classes used by the email package.} The following exception classes are defined in the -\module{email.Errors} module: +\module{email.errors} module: \begin{excclassdesc}{MessageError}{} This is the base class for all exceptions that the \module{email} @@ -59,7 +59,7 @@ problem was found, so for example, if a message nested inside a \mimetype{multipart/alternative} had a malformed header, that nested message object would have a defect, but the containing messages would not. -All defect classes are subclassed from \class{email.Errors.MessageDefect}, but +All defect classes are subclassed from \class{email.errors.MessageDefect}, but this class is \emph{not} an exception! \versionadded[All the defect classes were added]{2.4} diff --git a/Doc/lib/emailgenerator.tex b/Doc/lib/emailgenerator.tex index 330abc0..3415442 100644 --- a/Doc/lib/emailgenerator.tex +++ b/Doc/lib/emailgenerator.tex @@ -1,4 +1,4 @@ -\declaremodule{standard}{email.Generator} +\declaremodule{standard}{email.generator} \modulesynopsis{Generate flat text email messages from a message structure.} One of the most common tasks is to generate the flat text of the email @@ -8,7 +8,7 @@ module or the \refmodule{nntplib} module, or print the message on the console. Taking a message object structure and producing a flat text document is the job of the \class{Generator} class. -Again, as with the \refmodule{email.Parser} module, you aren't limited +Again, as with the \refmodule{email.parser} module, you aren't limited to the functionality of the bundled generator; you could write one from scratch yourself. However the bundled generator knows how to generate most email in a standards-compliant way, should handle MIME @@ -17,7 +17,8 @@ transformation from flat text, to a message structure via the \class{Parser} class, and back to flat text, is idempotent (the input is identical to the output). -Here are the public methods of the \class{Generator} class: +Here are the public methods of the \class{Generator} class, imported from the +\module{email.generator} module: \begin{classdesc}{Generator}{outfp\optional{, mangle_from_\optional{, maxheaderlen}}} @@ -40,7 +41,7 @@ mailbox format files. Optional \var{maxheaderlen} specifies the longest length for a non-continued header. When a header line is longer than \var{maxheaderlen} (in characters, with tabs expanded to 8 spaces), -the header will be split as defined in the \module{email.Header} +the header will be split as defined in the \module{email.header.Header} class. Set to zero to disable header wrapping. The default is 78, as recommended (but not required) by \rfc{2822}. \end{classdesc} @@ -81,9 +82,9 @@ be used in extended print statements. As a convenience, see the methods \method{Message.as_string()} and \code{str(aMessage)}, a.k.a. \method{Message.__str__()}, which simplify the generation of a formatted string representation of a -message object. For more detail, see \refmodule{email.Message}. +message object. For more detail, see \refmodule{email.message}. -The \module{email.Generator} module also provides a derived class, +The \module{email.generator} module also provides a derived class, called \class{DecodedGenerator} which is like the \class{Generator} base class, except that non-\mimetype{text} parts are substituted with a format string representing the part. @@ -128,13 +129,5 @@ The default value for \var{fmt} is \code{None}, meaning \versionadded{2.2.2} \end{classdesc} -\subsubsection{Deprecated methods} - -The following methods are deprecated in \module{email} version 2. -They are documented here for completeness. - -\begin{methoddesc}[Generator]{__call__}{msg\optional{, unixfrom}} -This method is identical to the \method{flatten()} method. - -\deprecated{2.2.2}{Use the \method{flatten()} method instead.} -\end{methoddesc} +\versionchanged[The previously deprecated method \method{__call__()} was +removed]{2.5} diff --git a/Doc/lib/emailheaders.tex b/Doc/lib/emailheaders.tex index 2795644b..524d08c 100644 --- a/Doc/lib/emailheaders.tex +++ b/Doc/lib/emailheaders.tex @@ -1,4 +1,4 @@ -\declaremodule{standard}{email.Header} +\declaremodule{standard}{email.header} \modulesynopsis{Representing non-ASCII headers} \rfc{2822} is the base standard that describes the format of email @@ -15,17 +15,18 @@ slew of RFCs have been written describing how to encode email containing non-\ASCII{} characters into \rfc{2822}-compliant format. These RFCs include \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}. The \module{email} package supports these standards in its -\module{email.Header} and \module{email.Charset} modules. +\module{email.header} and \module{email.charset} modules. If you want to include non-\ASCII{} characters in your email headers, say in the \mailheader{Subject} or \mailheader{To} fields, you should use the \class{Header} class and assign the field in the \class{Message} object to an instance of \class{Header} instead of -using a string for the header value. For example: +using a string for the header value. Import the \class{Header} class from the +\module{email.header} module. For example: \begin{verbatim} ->>> from email.Message import Message ->>> from email.Header import Header +>>> from email.message import Message +>>> from email.header import Header >>> msg = Message() >>> h = Header('p\xf6stal', 'iso-8859-1') >>> msg['Subject'] = h @@ -87,7 +88,7 @@ Optional \var{errors} is passed straight through to the Append the string \var{s} to the MIME header. Optional \var{charset}, if given, should be a \class{Charset} instance -(see \refmodule{email.Charset}) or the name of a character set, which +(see \refmodule{email.charset}) or the name of a character set, which will be converted to a \class{Charset} instance. A value of \code{None} (the default) means that the \var{charset} given in the constructor is used. @@ -139,7 +140,7 @@ This method allows you to compare two \class{Header} instances for equality. This method allows you to compare two \class{Header} instances for inequality. \end{methoddesc} -The \module{email.Header} module also provides the following +The \module{email.header} module also provides the following convenient functions. \begin{funcdesc}{decode_header}{header} @@ -155,7 +156,7 @@ encoded string. Here's an example: \begin{verbatim} ->>> from email.Header import decode_header +>>> from email.header import decode_header >>> decode_header('=?iso-8859-1?q?p=F6stal?=') [('p\xf6stal', 'iso-8859-1')] \end{verbatim} diff --git a/Doc/lib/emailiter.tex b/Doc/lib/emailiter.tex index d1a8f98..ef8ef6f 100644 --- a/Doc/lib/emailiter.tex +++ b/Doc/lib/emailiter.tex @@ -1,8 +1,8 @@ -\declaremodule{standard}{email.Iterators} +\declaremodule{standard}{email.iterators} \modulesynopsis{Iterate over a message object tree.} Iterating over a message object tree is fairly easy with the -\method{Message.walk()} method. The \module{email.Iterators} module +\method{Message.walk()} method. The \module{email.iterators} module provides some useful higher level iterations over message object trees. diff --git a/Doc/lib/emailmessage.tex b/Doc/lib/emailmessage.tex index 9b41852..7bd7dd8 100644 --- a/Doc/lib/emailmessage.tex +++ b/Doc/lib/emailmessage.tex @@ -1,10 +1,11 @@ -\declaremodule{standard}{email.Message} +\declaremodule{standard}{email.message} \modulesynopsis{The base class representing email messages.} The central class in the \module{email} package is the -\class{Message} class; it is the base class for the \module{email} -object model. \class{Message} provides the core functionality for -setting and querying header fields, and for accessing message bodies. +\class{Message} class, imported from the \module{email.message} module. It is +the base class for the \module{email} object model. \class{Message} provides +the core functionality for setting and querying header fields, and for +accessing message bodies. Conceptually, a \class{Message} object consists of \emph{headers} and \emph{payloads}. Headers are \rfc{2822} style field names and @@ -45,7 +46,7 @@ begin with \code{From }. For more flexibility, instantiate a \begin{verbatim} from cStringIO import StringIO -from email.Generator import Generator +from email.generator import Generator fp = StringIO() g = Generator(fp, mangle_from_=False, maxheaderlen=60) g.flatten(msg) @@ -119,7 +120,7 @@ client's responsibility to ensure the payload invariants. Optional \begin{methoddesc}[Message]{set_charset}{charset} Set the character set of the payload to \var{charset}, which can -either be a \class{Charset} instance (see \refmodule{email.Charset}), a +either be a \class{Charset} instance (see \refmodule{email.charset}), a string naming a character set, or \code{None}. If it is a string, it will be converted to a \class{Charset} instance. If \var{charset} is \code{None}, the @@ -128,8 +129,8 @@ or \code{None}. If it is a string, it will be converted to a \exception{TypeError}. The message will be assumed to be of type \mimetype{text/*} encoded with -\code{charset.input_charset}. It will be converted to -\code{charset.output_charset} +\var{charset.input_charset}. It will be converted to +\var{charset.output_charset} and encoded properly, if needed, when generating the plain text representation of the message. MIME headers (\mailheader{MIME-Version}, \mailheader{Content-Type}, @@ -513,6 +514,9 @@ message/rfc822 \end{verbatim} \end{methoddesc} +\versionchanged[The previously deprecated methods \method{get_type()}, +\method{get_main_type()}, and \method{get_subtype()} were removed]{2.5} + \class{Message} objects can also optionally contain two instance attributes, which can be used when generating the plain text of a MIME message. @@ -532,7 +536,7 @@ to the message's \var{preamble} attribute. When the \class{Generator} is writing out the plain text representation of a MIME message, and it finds the message has a \var{preamble} attribute, it will write this text in the area between the headers and the first boundary. See -\refmodule{email.Parser} and \refmodule{email.Generator} for details. +\refmodule{email.parser} and \refmodule{email.generator} for details. Note that if the message object has no preamble, the \var{preamble} attribute will be \code{None}. @@ -543,58 +547,15 @@ The \var{epilogue} attribute acts the same way as the \var{preamble} attribute, except that it contains text that appears between the last boundary and the end of the message. -One note: when generating the flat text for a \mimetype{multipart} -message that has no \var{epilogue} (using the standard -\class{Generator} class), no newline is added after the closing -boundary line. If the message object has an \var{epilogue} and its -value does not start with a newline, a newline is printed after the -closing boundary. This seems a little clumsy, but it makes the most -practical sense. The upshot is that if you want to ensure that a -newline get printed after your closing \mimetype{multipart} boundary, -set the \var{epilogue} to the empty string. +\versionchanged[You do not need to set the epilogue to the empty string in +order for the \class{Generator} to print a newline at the end of the +file]{2.5} \end{datadesc} \begin{datadesc}{defects} The \var{defects} attribute contains a list of all the problems found when -parsing this message. See \refmodule{email.Errors} for a detailed description +parsing this message. See \refmodule{email.errors} for a detailed description of the possible parsing defects. \versionadded{2.4} \end{datadesc} - -\subsubsection{Deprecated methods} - -\versionchanged[The \method{add_payload()} method was removed; use the -\method{attach()} method instead]{2.4} - -The following methods are deprecated. They are documented here for -completeness. - -\begin{methoddesc}[Message]{get_type}{\optional{failobj}} -Return the message's content type, as a string of the form -\mimetype{maintype/subtype} as taken from the -\mailheader{Content-Type} header. -The returned string is coerced to lowercase. - -If there is no \mailheader{Content-Type} header in the message, -\var{failobj} is returned (defaults to \code{None}). - -\deprecated{2.2.2}{Use the \method{get_content_type()} method instead.} -\end{methoddesc} - -\begin{methoddesc}[Message]{get_main_type}{\optional{failobj}} -Return the message's \emph{main} content type. This essentially returns the -\var{maintype} part of the string returned by \method{get_type()}, with the -same semantics for \var{failobj}. - -\deprecated{2.2.2}{Use the \method{get_content_maintype()} method instead.} -\end{methoddesc} - -\begin{methoddesc}[Message]{get_subtype}{\optional{failobj}} -Return the message's sub-content type. This essentially returns the -\var{subtype} part of the string returned by \method{get_type()}, with the -same semantics for \var{failobj}. - -\deprecated{2.2.2}{Use the \method{get_content_subtype()} method instead.} -\end{methoddesc} - diff --git a/Doc/lib/emailmimebase.tex b/Doc/lib/emailmimebase.tex index 070c9a2..4735be3 100644 --- a/Doc/lib/emailmimebase.tex +++ b/Doc/lib/emailmimebase.tex @@ -1,3 +1,11 @@ +\declaremodule{standard}{email.mime} +\declaremodule{standard}{email.mime.base} +\declaremodule{standard}{email.mime.nonmultipart} +\declaremodule{standard}{email.mime.multipart} +\declaremodule{standard}{email.mime.audio} +\declaremodule{standard}{email.mime.image} +\declaremodule{standard}{email.mime.message} +\declaremodule{standard}{email.mime.text} Ordinarily, you get a message object structure by passing a file or some text to a parser, which parses the text and returns the root message object. However you can also build a complete message @@ -6,26 +14,16 @@ hand. In fact, you can also take an existing structure and add new \class{Message} objects, move them around, etc. This makes a very convenient interface for slicing-and-dicing MIME messages. -You can create a new object structure by creating \class{Message} -instances, adding attachments and all the appropriate headers manually. -For MIME messages though, the \module{email} package provides some -convenient subclasses to make things easier. Each of these classes -should be imported from a module with the same name as the class, from -within the \module{email} package. E.g.: - -\begin{verbatim} -import email.MIMEImage.MIMEImage -\end{verbatim} - -or - -\begin{verbatim} -from email.MIMEText import MIMEText -\end{verbatim} +You can create a new object structure by creating \class{Message} instances, +adding attachments and all the appropriate headers manually. For MIME +messages though, the \module{email} package provides some convenient +subclasses to make things easier. Here are the classes: \begin{classdesc}{MIMEBase}{_maintype, _subtype, **_params} +Module: \module{email.mime.base} + This is the base class for all the MIME-specific subclasses of \class{Message}. Ordinarily you won't create instances specifically of \class{MIMEBase}, although you could. \class{MIMEBase} is provided @@ -45,6 +43,8 @@ The \class{MIMEBase} class always adds a \mailheader{Content-Type} header \end{classdesc} \begin{classdesc}{MIMENonMultipart}{} +Module: \module{email.mime.nonmultipart} + A subclass of \class{MIMEBase}, this is an intermediate base class for MIME messages that are not \mimetype{multipart}. The primary purpose of this class is to prevent the use of the \method{attach()} method, @@ -57,6 +57,7 @@ exception is raised. \begin{classdesc}{MIMEMultipart}{\optional{subtype\optional{, boundary\optional{, _subparts\optional{, _params}}}}} +Module: \module{email.mime.multipart} A subclass of \class{MIMEBase}, this is an intermediate base class for MIME messages that are \mimetype{multipart}. Optional \var{_subtype} @@ -80,8 +81,31 @@ argument, which is a keyword dictionary. \versionadded{2.2.2} \end{classdesc} +\begin{classdesc}{MIMEApplication}{_data\optional{, _subtype\optional{, + _encoder\optional{, **_params}}}} +Module: \module{email.mime.application} + +A subclass of \class{MIMENonMultipart}, the \class{MIMEApplication} class is +used to represent MIME message objects of major type \mimetype{application}. +\var{_data} is a string containing the raw byte data. Optional \var{_subtype} +specifies the MIME subtype and defaults to \mimetype{octet-stream}. + +Optional \var{_encoder} is a callable (i.e. function) which will +perform the actual encoding of the data for transport. This +callable takes one argument, which is the \class{MIMEApplication} instance. +It should use \method{get_payload()} and \method{set_payload()} to +change the payload to encoded form. It should also add any +\mailheader{Content-Transfer-Encoding} or other headers to the message +object as necessary. The default encoding is base64. See the +\refmodule{email.encoders} module for a list of the built-in encoders. + +\var{_params} are passed straight through to the base class constructor. +\versionadded{2.5} +\end{classdesc} + \begin{classdesc}{MIMEAudio}{_audiodata\optional{, _subtype\optional{, _encoder\optional{, **_params}}}} +Module: \module{email.mime.audio} A subclass of \class{MIMENonMultipart}, the \class{MIMEAudio} class is used to create MIME message objects of major type \mimetype{audio}. @@ -100,13 +124,14 @@ It should use \method{get_payload()} and \method{set_payload()} to change the payload to encoded form. It should also add any \mailheader{Content-Transfer-Encoding} or other headers to the message object as necessary. The default encoding is base64. See the -\refmodule{email.Encoders} module for a list of the built-in encoders. +\refmodule{email.encoders} module for a list of the built-in encoders. \var{_params} are passed straight through to the base class constructor. \end{classdesc} \begin{classdesc}{MIMEImage}{_imagedata\optional{, _subtype\optional{, _encoder\optional{, **_params}}}} +Module: \module{email.mime.image} A subclass of \class{MIMENonMultipart}, the \class{MIMEImage} class is used to create MIME message objects of major type \mimetype{image}. @@ -125,13 +150,15 @@ It should use \method{get_payload()} and \method{set_payload()} to change the payload to encoded form. It should also add any \mailheader{Content-Transfer-Encoding} or other headers to the message object as necessary. The default encoding is base64. See the -\refmodule{email.Encoders} module for a list of the built-in encoders. +\refmodule{email.encoders} module for a list of the built-in encoders. \var{_params} are passed straight through to the \class{MIMEBase} constructor. \end{classdesc} \begin{classdesc}{MIMEMessage}{_msg\optional{, _subtype}} +Module: \module{email.mime.message} + A subclass of \class{MIMENonMultipart}, the \class{MIMEMessage} class is used to create MIME objects of main type \mimetype{message}. \var{_msg} is used as the payload, and must be an instance of class @@ -143,6 +170,8 @@ to \mimetype{rfc822}. \end{classdesc} \begin{classdesc}{MIMEText}{_text\optional{, _subtype\optional{, _charset}}} +Module: \module{email.mime.text} + A subclass of \class{MIMENonMultipart}, the \class{MIMEText} class is used to create MIME objects of major type \mimetype{text}. \var{_text} is the string for the payload. \var{_subtype} is the diff --git a/Doc/lib/emailparser.tex b/Doc/lib/emailparser.tex index 5fac92f..609fa40 100644 --- a/Doc/lib/emailparser.tex +++ b/Doc/lib/emailparser.tex @@ -1,4 +1,4 @@ -\declaremodule{standard}{email.Parser} +\declaremodule{standard}{email.parser} \modulesynopsis{Parse flat text email messages to produce a message object structure.} @@ -41,9 +41,10 @@ message object trees any way it finds necessary. \versionadded{2.4} -The \class{FeedParser} provides an API that is conducive to incremental -parsing of email messages, such as would be necessary when reading the text of -an email message from a source that can block (e.g. a socket). The +The \class{FeedParser}, imported from the \module{email.feedparser} module, +provides an API that is conducive to incremental parsing of email messages, +such as would be necessary when reading the text of an email message from a +source that can block (e.g. a socket). The \class{FeedParser} can of course be used to parse an email message fully contained in a string or a file, but the classic \class{Parser} API may be more convenient for such use cases. The semantics and results of the two @@ -56,14 +57,14 @@ accurate when parsing standards-compliant messages, and it does a very good job of parsing non-compliant messages, providing information about how a message was deemed broken. It will populate a message object's \var{defects} attribute with a list of any problems it found in a message. See the -\refmodule{email.Errors} module for the list of defects that it can find. +\refmodule{email.errors} module for the list of defects that it can find. Here is the API for the \class{FeedParser}: \begin{classdesc}{FeedParser}{\optional{_factory}} Create a \class{FeedParser} instance. Optional \var{_factory} is a no-argument callable that will be called whenever a new message object is -needed. It defaults to the \class{email.Message.Message} class. +needed. It defaults to the \class{email.message.Message} class. \end{classdesc} \begin{methoddesc}[FeedParser]{feed}{data} @@ -82,21 +83,22 @@ more data to a closed \class{FeedParser}. \subsubsection{Parser class API} -The \class{Parser} provides an API that can be used to parse a message when -the complete contents of the message are available in a string or file. The -\module{email.Parser} module also provides a second class, called +The \class{Parser} class, imported from the \module{email.parser} module, +provides an API that can be used to parse a message when the complete contents +of the message are available in a string or file. The +\module{email.parser} module also provides a second class, called \class{HeaderParser} which can be used if you're only interested in the headers of the message. \class{HeaderParser} can be much faster in these situations, since it does not attempt to parse the message body, instead setting the payload to the raw body as a string. \class{HeaderParser} has the same API as the \class{Parser} class. -\begin{classdesc}{Parser}{\optional{_class\optional{, strict}}} +\begin{classdesc}{Parser}{\optional{_class}} The constructor for the \class{Parser} class takes an optional argument \var{_class}. This must be a callable factory (such as a function or a class), and it is used whenever a sub-message object needs to be created. It defaults to \class{Message} (see -\refmodule{email.Message}). The factory will be called without +\refmodule{email.message}). The factory will be called without arguments. The optional \var{strict} flag is ignored. \deprecated{2.4}{Because the @@ -201,6 +203,6 @@ Here are some notes on the parsing semantics: \method{is_multipart()} method may return \code{False}. If such messages were parsed with the \class{FeedParser}, they will have an instance of the \class{MultipartInvariantViolationDefect} class in their - \var{defects} attribute list. See \refmodule{email.Errors} for + \var{defects} attribute list. See \refmodule{email.errors} for details. \end{itemize} diff --git a/Doc/lib/emailutil.tex b/Doc/lib/emailutil.tex index 491a2b9..fe96473 100644 --- a/Doc/lib/emailutil.tex +++ b/Doc/lib/emailutil.tex @@ -1,7 +1,7 @@ -\declaremodule{standard}{email.Utils} +\declaremodule{standard}{email.utils} \modulesynopsis{Miscellaneous email package utilities.} -There are several useful utilities provided in the \module{email.Utils} +There are several useful utilities provided in the \module{email.utils} module: \begin{funcdesc}{quote}{str} @@ -38,7 +38,7 @@ values as might be returned by \method{Message.get_all()}. Here's a simple example that gets all the recipients of a message: \begin{verbatim} -from email.Utils import getaddresses +from email.utils import getaddresses tos = msg.get_all('to', []) ccs = msg.get_all('cc', []) diff --git a/Doc/lib/lib.tex b/Doc/lib/lib.tex index fad8fe7..eac35de 100644 --- a/Doc/lib/lib.tex +++ b/Doc/lib/lib.tex @@ -87,7 +87,6 @@ and how to embed it in other applications. \input{libstrings} % String Services \input{libstring} \input{libre} -\input{libreconvert} \input{libstruct} % XXX also/better in File Formats? \input{libdifflib} \input{libstringio} @@ -372,6 +371,7 @@ and how to embed it in other applications. \input{libbltin} % really __builtin__ \input{libmain} % really __main__ \input{libwarnings} +\input{libcontextlib} \input{libatexit} \input{libtraceback} \input{libfuture} % really __future__ @@ -395,6 +395,7 @@ and how to embed it in other applications. \input{libzipimport} \input{libpkgutil} \input{libmodulefinder} +\input{librunpy} % ============= @@ -454,8 +455,6 @@ and how to embed it in other applications. %\input{libcmpcache} %\input{libcmp} %\input{libni} -%\input{libregex} -%\input{libregsub} \chapter{Reporting Bugs} \input{reportingbugs} diff --git a/Doc/lib/libarray.tex b/Doc/lib/libarray.tex index 897310d..eaf5888 100644 --- a/Doc/lib/libarray.tex +++ b/Doc/lib/libarray.tex @@ -139,8 +139,8 @@ file using the \method{fromfile()} method). \end{methoddesc} \begin{methoddesc}[array]{fromunicode}{s} -Extends this array with data from the given unicode string. -The array must be a type 'u' array; otherwise a ValueError +Extends this array with data from the given unicode string. The array +must be a type \code{'u'} array; otherwise a \exception{ValueError} is raised. Use \samp{array.fromstring(ustr.decode(enc))} to append Unicode data to an array of some other type. \end{methoddesc} @@ -197,8 +197,8 @@ be written to a file by the \method{tofile()} method.) \begin{methoddesc}[array]{tounicode}{} Convert the array to a unicode string. The array must be -a type 'u' array; otherwise a ValueError is raised. Use -array.tostring().decode(enc) to obtain a unicode string +a type \code{'u'} array; otherwise a \exception{ValueError} is raised. +Use \samp{array.tostring().decode(enc)} to obtain a unicode string from an array of some other type. \end{methoddesc} diff --git a/Doc/lib/libast.tex b/Doc/lib/libast.tex index b3c3148..b2956ae 100644 --- a/Doc/lib/libast.tex +++ b/Doc/lib/libast.tex @@ -47,11 +47,11 @@ question mark), the value might be \code{None}. If the attributes can have zero-or-more values (marked with an asterisk), the values are represented as Python lists. -\subsection{Abstract Grammar} +\section{Abstract Grammar} The module defines a string constant \code{__version__} which is the decimal subversion revision number of the file shown below. The abstract grammar is currently defined as follows: -\verbatiminput{../../Parser/Python.asdl} \ No newline at end of file +\verbatiminput{../../Parser/Python.asdl} diff --git a/Doc/lib/libaudioop.tex b/Doc/lib/libaudioop.tex index 76bcdbf..52c6f3d 100644 --- a/Doc/lib/libaudioop.tex +++ b/Doc/lib/libaudioop.tex @@ -12,9 +12,10 @@ is the same format as used by the \refmodule{al} and \refmodule{sunaudiodev} modules. All scalar items are integers, unless specified otherwise. % This para is mostly here to provide an excuse for the index entries... -This module provides support for u-LAW and Intel/DVI ADPCM encodings. +This module provides support for a-LAW, u-LAW and Intel/DVI ADPCM encodings. \index{Intel/DVI ADPCM} \index{ADPCM, Intel/DVI} +\index{a-LAW} \index{u-LAW} A few of the more complicated operations only take 16-bit samples, @@ -42,6 +43,13 @@ Return a tuple \code{(\var{sample}, \var{newstate})} where the sample has the width specified in \var{width}. \end{funcdesc} +\begin{funcdesc}{alaw2lin}{fragment, width} +Convert sound fragments in a-LAW encoding to linearly encoded sound +fragments. a-LAW encoding always uses 8 bits samples, so \var{width} +refers only to the sample width of the output fragment here. +\versionadded{2.5} +\end{funcdesc} + \begin{funcdesc}{avg}{fragment, width} Return the average over all samples in the fragment. \end{funcdesc} @@ -98,10 +106,6 @@ The routine takes time proportional to \code{len(\var{fragment})}. Return the value of sample \var{index} from the fragment. \end{funcdesc} -\begin{funcdesc}{lin2lin}{fragment, width, newwidth} -Convert samples between 1-, 2- and 4-byte formats. -\end{funcdesc} - \begin{funcdesc}{lin2adpcm}{fragment, width, state} Convert samples to 4 bit Intel/DVI ADPCM encoding. ADPCM coding is an adaptive coding scheme, whereby each 4 bit number is the difference @@ -117,6 +121,18 @@ passed as the state. \var{adpcmfrag} is the ADPCM coded fragment packed 2 4-bit values per byte. \end{funcdesc} +\begin{funcdesc}{lin2alaw}{fragment, width} +Convert samples in the audio fragment to a-LAW encoding and return +this as a Python string. a-LAW is an audio encoding format whereby +you get a dynamic range of about 13 bits using only 8 bit samples. It +is used by the Sun audio hardware, among others. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{lin2lin}{fragment, width, newwidth} +Convert samples between 1-, 2- and 4-byte formats. +\end{funcdesc} + \begin{funcdesc}{lin2ulaw}{fragment, width} Convert samples in the audio fragment to u-LAW encoding and return this as a Python string. u-LAW is an audio encoding format whereby diff --git a/Doc/lib/libbsddb.tex b/Doc/lib/libbsddb.tex index fa7bb4b..a5cda6d 100644 --- a/Doc/lib/libbsddb.tex +++ b/Doc/lib/libbsddb.tex @@ -15,9 +15,8 @@ other objects as keys or to store other kinds of objects the user must serialize them somehow, typically using \function{marshal.dumps()} or \function{pickle.dumps}. -Starting with Python 2.3 the \module{bsddb} module requires the -Berkeley DB library version 3.2 or later (it is known to work with 3.2 -through 4.3 at the time of this writing). +The \module{bsddb} module requires a Berkeley DB library version from +3.3 thru 4.4. \begin{seealso} \seeurl{http://pybsddb.sourceforge.net/}{Website with documentation diff --git a/Doc/lib/libcalendar.tex b/Doc/lib/libcalendar.tex index bf3a7d6..acfd2da 100644 --- a/Doc/lib/libcalendar.tex +++ b/Doc/lib/libcalendar.tex @@ -15,12 +15,177 @@ convention). Use \function{setfirstweekday()} to set the first day of the week to Sunday (6) or to any other weekday. Parameters that specify dates are given as integers. -Most of these functions rely on the \module{datetime} module which -uses an idealized calendar, the current Gregorian calendar indefinitely -extended in both directions. This matches the definition of the -"proleptic Gregorian" calendar in Dershowitz and Reingold's book -"Calendrical Calculations", where it's the base calendar for all -computations. +Most of these functions and classses rely on the \module{datetime} +module which uses an idealized calendar, the current Gregorian +calendar indefinitely extended in both directions. This matches +the definition of the "proleptic Gregorian" calendar in Dershowitz +and Reingold's book "Calendrical Calculations", where it's the +base calendar for all computations. + +\begin{classdesc}{Calendar}{\optional{firstweekday}} +Creates a \class{Calendar} object. \var{firstweekday} is an integer +specifying the first day of the week. \code{0} is Monday (the default), +\code{6} is Sunday. + +A \class{Calendar} object provides several methods that can +be used for preparing the calendar data for formatting. This +class doesn't do any formatting itself. This is the job of +subclasses. +\versionadded{2.5} +\end{classdesc} + +\class{Calendar} instances have the following methods: + +\begin{methoddesc}{iterweekdays}{weekday} +Return an iterator for the week day numbers that will be used +for one week. The first number from the iterator will be the +same as the number returned by \method{firstweekday()}. +\end{methoddesc} + +\begin{methoddesc}{itermonthdates}{year, month} +Return an iterator for the month \var{month} (1-12) in the +year \var{year}. This iterator will return all days (as +\class{datetime.date} objects) for the month and all days +before the start of the month or after the end of the month +that are required to get a complete week. +\end{methoddesc} + +\begin{methoddesc}{itermonthdays2}{year, month} +Return an iterator for the month \var{month} in the year +\var{year} similar to \method{itermonthdates()}. Days returned +will be tuples consisting of a day number and a week day +number. +\end{methoddesc} + +\begin{methoddesc}{itermonthdays}{year, month} +Return an iterator for the month \var{month} in the year +\var{year} similar to \method{itermonthdates()}. Days returned +will simply be day numbers. +\end{methoddesc} + +\begin{methoddesc}{monthdatescalendar}{year, month} +Return a list of the weeks in the month \var{month} of +the \var{year} as full weeks. Weeks are lists of seven +\class{datetime.date} objects. +\end{methoddesc} + +\begin{methoddesc}{monthdays2calendar}{year, month} +Return a list of the weeks in the month \var{month} of +the \var{year} as full weeks. Weeks are lists of seven +tuples of day numbers and weekday numbers. +\end{methoddesc} + +\begin{methoddesc}{monthdayscalendar}{year, month} +Return a list of the weeks in the month \var{month} of +the \var{year} as full weeks. Weeks are lists of seven +day numbers. +\end{methoddesc} + +\begin{methoddesc}{yeardatescalendar}{year, month\optional{, width}} +Return the data for the specified year ready for formatting. The return +value is a list of month rows. Each month row contains up to \var{width} +months (defaulting to 3). Each month contains between 4 and 6 weeks and +each week contains 1--7 days. Days are \class{datetime.date} objects. +\end{methoddesc} + +\begin{methoddesc}{yeardays2calendar}{year, month\optional{, width}} +Return the data for the specified year ready for formatting (similar to +\method{yeardatescalendar()}). Entries in the week lists are tuples of +day numbers and weekday numbers. Day numbers outside this month are zero. +\end{methoddesc} + +\begin{methoddesc}{yeardayscalendar}{year, month\optional{, width}} +Return the data for the specified year ready for formatting (similar to +\method{yeardatescalendar()}). Entries in the week lists are day numbers. +Day numbers outside this month are zero. +\end{methoddesc} + + +\begin{classdesc}{TextCalendar}{\optional{firstweekday}} +This class can be used to generate plain text calendars. + +\versionadded{2.5} +\end{classdesc} + +\class{TextCalendar} instances have the following methods: + +\begin{methoddesc}{formatmonth}{theyear, themonth\optional{, w\optional{, l}}} +Return a month's calendar in a multi-line string. If \var{w} is +provided, it specifies the width of the date columns, which are +centered. If \var{l} is given, it specifies the number of lines that +each week will use. Depends on the first weekday as set by +\function{setfirstweekday()}. +\end{methoddesc} + +\begin{methoddesc}{prmonth}{theyear, themonth\optional{, w\optional{, l}}} +Print a month's calendar as returned by \method{formatmonth()}. +\end{methoddesc} + +\begin{methoddesc}{formatyear}{theyear, themonth\optional{, w\optional{, + l\optional{, c\optional{, m}}}}} +Return a \var{m}-column calendar for an entire year as a multi-line string. +Optional parameters \var{w}, \var{l}, and \var{c} are for date column +width, lines per week, and number of spaces between month columns, +respectively. Depends on the first weekday as set by +\method{setfirstweekday()}. The earliest year for which a calendar can +be generated is platform-dependent. +\end{methoddesc} + +\begin{methoddesc}{pryear}{theyear\optional{, w\optional{, l\optional{, + c\optional{, m}}}}} +Print the calendar for an entire year as returned by \method{formatyear()}. +\end{methoddesc} + + +\begin{classdesc}{HTMLCalendar}{\optional{firstweekday}} +This class can be used to generate HTML calendars. + +\versionadded{2.5} +\end{classdesc} + +\class{HTMLCalendar} instances have the following methods: + +\begin{methoddesc}{formatmonth}{theyear, themonth\optional{, withyear}} +Return a month's calendar as an HTML table. If \var{withyear} is +true the year will be included in the header, otherwise just the +month name will be used. +\end{methoddesc} + +\begin{methoddesc}{formatyear}{theyear, themonth\optional{, width}} +Return a year's calendar as an HTML table. \var{width} (defaulting to 3) +specifies the number of months per row. +\end{methoddesc} + +\begin{methoddesc}{formatyearpage}{theyear, themonth\optional{, + width\optional{, css\optional{, encoding}}}} +Return a year's calendar as a complete HTML page. \var{width} +(defaulting to 3) specifies the number of months per row. \var{css} +is the name for the cascading style sheet to be used. \constant{None} +can be passed if no style sheet should be used. \var{encoding} +specifies the encoding to be used for the output (defaulting +to the system default encoding). +\end{methoddesc} + + +\begin{classdesc}{LocaleTextCalendar}{\optional{firstweekday\optional{, locale}}} +This subclass of \class{TextCalendar} can be passed a locale name in the +constructor and will return month and weekday names in the specified locale. +If this locale includes an encoding all strings containing month and weekday +names will be returned as unicode. +\versionadded{2.5} +\end{classdesc} + + +\begin{classdesc}{LocaleHTMLCalendar}{\optional{firstweekday\optional{, locale}}} +This subclass of \class{HTMLCalendar} can be passed a locale name in the +constructor and will return month and weekday names in the specified locale. +If this locale includes an encoding all strings containing month and weekday +names will be returned as unicode. +\versionadded{2.5} +\end{classdesc} + + +For simple text calendars this module provides the following functions. \begin{funcdesc}{setfirstweekday}{weekday} Sets the weekday (\code{0} is Monday, \code{6} is Sunday) to start @@ -80,11 +245,8 @@ Prints a month's calendar as returned by \function{month()}. \end{funcdesc} \begin{funcdesc}{month}{theyear, themonth\optional{, w\optional{, l}}} -Returns a month's calendar in a multi-line string. If \var{w} is -provided, it specifies the width of the date columns, which are -centered. If \var{l} is given, it specifies the number of lines that -each week will use. Depends on the first weekday as set by -\function{setfirstweekday()}. +Returns a month's calendar in a multi-line string using the +\method{formatmonth} of the \class{TextCalendar} class. \versionadded{2.0} \end{funcdesc} @@ -94,12 +256,8 @@ Prints the calendar for an entire year as returned by \end{funcdesc} \begin{funcdesc}{calendar}{year\optional{, w\optional{, l\optional{c}}}} -Returns a 3-column calendar for an entire year as a multi-line string. -Optional parameters \var{w}, \var{l}, and \var{c} are for date column -width, lines per week, and number of spaces between month columns, -respectively. Depends on the first weekday as set by -\function{setfirstweekday()}. The earliest year for which a calendar can -be generated is platform-dependent. +Returns a 3-column calendar for an entire year as a multi-line string +using the \method{formatyear} of the \class{TextCalendar} class. \versionadded{2.0} \end{funcdesc} diff --git a/Doc/lib/libcgi.tex b/Doc/lib/libcgi.tex index cd6f58a..1dd7e03 100644 --- a/Doc/lib/libcgi.tex +++ b/Doc/lib/libcgi.tex @@ -323,7 +323,7 @@ not included. The optional argument \var{strict_parsing} is a flag indicating what to do with parsing errors. If false (the default), errors -are silently ignored. If true, errors raise a ValueError +are silently ignored. If true, errors raise a \exception{ValueError} exception. Use the \function{\refmodule{urllib}.urlencode()} function to convert @@ -347,7 +347,7 @@ not included. The optional argument \var{strict_parsing} is a flag indicating what to do with parsing errors. If false (the default), errors -are silently ignored. If true, errors raise a ValueError +are silently ignored. If true, errors raise a \exception{ValueError} exception. Use the \function{\refmodule{urllib}.urlencode()} function to convert diff --git a/Doc/lib/libcodecs.tex b/Doc/lib/libcodecs.tex index 1806ef0..8a2417e 100644 --- a/Doc/lib/libcodecs.tex +++ b/Doc/lib/libcodecs.tex @@ -112,6 +112,7 @@ class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found or the codec doesn't support an incremental encoder. +\versionadded{2.5} \end{funcdesc} \begin{funcdesc}{getincrementaldecoder}{encoding} @@ -120,6 +121,7 @@ class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found or the codec doesn't support an incremental decoder. +\versionadded{2.5} \end{funcdesc} \begin{funcdesc}{getreader}{encoding} @@ -150,7 +152,7 @@ unencodable part of the input and a position where encoding should continue. The encoder will encode the replacement and continue encoding the original input at the specified position. Negative position values will be treated as being relative to the end of the input string. If the -resulting position is out of bound an IndexError will be raised. +resulting position is out of bound an \exception{IndexError} will be raised. Decoding and translating works similar, except \exception{UnicodeDecodeError} or \exception{UnicodeTranslateError} will be passed to the handler and @@ -229,12 +231,14 @@ an encoding error occurs. Uses an incremental encoder to iteratively encode the input provided by \var{iterable}. This function is a generator. \var{errors} (as well as any other keyword argument) is passed through to the incremental encoder. +\versionadded{2.5} \end{funcdesc} \begin{funcdesc}{iterdecode}{iterable, encoding\optional{, errors}} Uses an incremental decoder to iteratively decode the input provided by \var{iterable}. This function is a generator. \var{errors} (as well as any other keyword argument) is passed through to the incremental encoder. +\versionadded{2.5} \end{funcdesc} The module also provides the following constants which are useful @@ -355,6 +359,8 @@ encoded/decoded with the stateless encoder/decoder. \subsubsection{IncrementalEncoder Objects \label{incremental-encoder-objects}} +\versionadded{2.5} + The \class{IncrementalEncoder} class is used for encoding an input in multiple steps. It defines the following methods which every incremental encoder must define in order to be compatible to the Python codec registry. @@ -437,6 +443,10 @@ define in order to be compatible to the Python codec registry. Decodes \var{object} (taking the current state of the decoder into account) and returns the resulting decoded object. If this is the last call to \method{decode} \var{final} must be true (the default is false). + If \var{final} is true the decoder must decode the input completely and must + flush all buffers. If this isn't possible (e.g. because of incomplete byte + sequences at the end of the input) it must initiate error handling just like + in the stateless case (which might raise an exception). \end{methoddesc} \begin{methoddesc}{reset}{} @@ -690,10 +700,10 @@ transformation can be done (these methods are also called encodings). The simplest method is to map the codepoints 0-255 to the bytes \code{0x0}-\code{0xff}. This means that a unicode object that contains codepoints above \code{U+00FF} can't be encoded with this method (which -is called \code{'latin-1'} or \code{'iso-8859-1'}). unicode.encode() will -raise a UnicodeEncodeError that looks like this: \samp{UnicodeEncodeError: -'latin-1' codec can't encode character u'\e u1234' in position 3: ordinal -not in range(256)}. +is called \code{'latin-1'} or \code{'iso-8859-1'}). +\function{unicode.encode()} will raise a \exception{UnicodeEncodeError} +that looks like this: \samp{UnicodeEncodeError: 'latin-1' codec can't +encode character u'\e u1234' in position 3: ordinal not in range(256)}. There's another group of encodings (the so called charmap encodings) that choose a different subset of all unicode code points and how @@ -1220,7 +1230,7 @@ listed as operand type in the table. \lineiv{rot_13} {rot13} - {byte string} + {Unicode string} {Returns the Caesar-cypher encryption of the operand} \lineiv{string_escape} diff --git a/Doc/lib/libcollections.tex b/Doc/lib/libcollections.tex index 542ef6b..d9bfa39 100644 --- a/Doc/lib/libcollections.tex +++ b/Doc/lib/libcollections.tex @@ -10,9 +10,11 @@ This module implements high-performance container datatypes. Currently, there are two datatypes, deque and defaultdict. -Future additions may include B-trees and Fibonacci heaps. +Future additions may include balanced trees and ordered dictionaries. \versionchanged[Added defaultdict]{2.5} +\subsection{\class{deque} objects \label{deque-objects}} + \begin{funcdesc}{deque}{\optional{iterable}} Returns a new deque objected initialized left-to-right (using \method{append()}) with data from \var{iterable}. If \var{iterable} @@ -137,7 +139,7 @@ IndexError: pop from an empty deque deque(['c', 'b', 'a']) \end{verbatim} -\subsection{Recipes \label{deque-recipes}} +\subsubsection{Recipes \label{deque-recipes}} This section shows various approaches to working with deques. @@ -215,6 +217,8 @@ def maketree(iterable): +\subsection{\class{defaultdict} objects \label{defaultdict-objects}} + \begin{funcdesc}{defaultdict}{\optional{default_factory\optional{, ...}}} Returns a new dictionary-like object. \class{defaultdict} is a subclass of the builtin \class{dict} class. It overrides one method and adds one @@ -255,3 +259,79 @@ the standard \class{dict} operations: from the first argument to the constructor, if present, or to \code{None}, if absent. \end{datadesc} + + +\subsubsection{\class{defaultdict} Examples \label{defaultdict-examples}} + +Using \class{list} as the \member{default_factory}, it is easy to group +a sequence of key-value pairs into a dictionary of lists: + +\begin{verbatim} +>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] +>>> d = defaultdict(list) +>>> for k, v in s: + d[k].append(v) + +>>> d.items() +[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] +\end{verbatim} + +When each key is encountered for the first time, it is not already in the +mapping; so an entry is automatically created using the +\member{default_factory} function which returns an empty \class{list}. The +\method{list.append()} operation then attaches the value to the new list. When +keys are encountered again, the look-up proceeds normally (returning the list +for that key) and the \method{list.append()} operation adds another value to +the list. This technique is simpler and faster than an equivalent technique +using \method{dict.setdefault()}: + +\begin{verbatim} +>>> d = {} +>>> for k, v in s: + d.setdefault(k, []).append(v) + +>>> d.items() +[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] +\end{verbatim} + +Setting the \member{default_factory} to \class{int} makes the +\class{defaultdict} useful for counting (like a bag or multiset in other +languages): + +\begin{verbatim} +>>> s = 'mississippi' +>>> d = defaultdict(int) +>>> for k in s: + d[k] += 1 + +>>> d.items() +[('i', 4), ('p', 2), ('s', 4), ('m', 1)] +\end{verbatim} + +When a letter is first encountered, it is missing from the mapping, so the +\member{default_factory} function calls \function{int()} to supply a default +count of zero. The increment operation then builds up the count for each +letter. This technique makes counting simpler and faster than an equivalent +technique using \method{dict.get()}: + +\begin{verbatim} +>>> d = {} +>>> for k in s: + d[k] = d.get(k, 0) + 1 + +>>> d.items() +[('i', 4), ('p', 2), ('s', 4), ('m', 1)] +\end{verbatim} + +Setting the \member{default_factory} to \class{set} makes the +\class{defaultdict} useful for building a dictionary of sets: + +\begin{verbatim} +>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)] +>>> d = defaultdict(set) +>>> for k, v in s: + d[k].add(v) + +>>> d.items() +[('blue', set([2, 4])), ('red', set([1, 3]))] +\end{verbatim} diff --git a/Doc/lib/libcontextlib.tex b/Doc/lib/libcontextlib.tex new file mode 100644 index 0000000..46f9cdd --- /dev/null +++ b/Doc/lib/libcontextlib.tex @@ -0,0 +1,144 @@ +\section{\module{contextlib} --- + Utilities for \keyword{with}-statement contexts.} + +\declaremodule{standard}{contextlib} +\modulesynopsis{Utilities for \keyword{with}-statement contexts.} + +\versionadded{2.5} + +This module provides utilities for common tasks involving the +\keyword{with} statement. + +Functions provided: + +\begin{funcdesc}{contextmanager}{func} +This function is a decorator that can be used to define context managers +for use with the \keyword{with} statement, without needing to create a +class or separate \method{__enter__()} and \method{__exit__()} methods. + +A simple example: + +\begin{verbatim} +from __future__ import with_statement +from contextlib import contextmanager + +@contextmanager +def tag(name): + print "<%s>" % name + yield + print "" % name + +>>> with tag("h1"): +... print "foo" +... +

+foo +

+\end{verbatim} + +When called, the decorated function must return a generator-iterator. +This iterator must yield exactly one value, which will be bound to the +targets in the \keyword{with} statement's \keyword{as} clause, if any. + +At the point where the generator yields, the block nested in the +\keyword{with} statement is executed. The generator is then resumed +after the block is exited. If an unhandled exception occurs in the +block, it is reraised inside the generator at the point where the yield +occurred. Thus, you can use a +\keyword{try}...\keyword{except}...\keyword{finally} statement to trap +the error (if any), or ensure that some cleanup takes place. + +Note that you can use \code{@contextmanager} to define a context +manager's \method{__context__} method. This is usually more convenient +than creating another class just to serve as a context. For example: + +\begin{verbatim} +from __future__ import with_statement +from contextlib import contextmanager + +class Tag: + def __init__(self, name): + self.name = name + + @contextmanager + def __context__(self): + print "<%s>" % self.name + yield self + print "" % self.name + +h1 = Tag("h1") + +>>> with h1 as me: +... print "hello from", me +

+hello from <__main__.Tag instance at 0x402ce8ec> +

+\end{verbatim} +\end{funcdesc} + +\begin{funcdesc}{nested}{ctx1\optional{, ctx2\optional{, ...}}} +Combine multiple context managers into a single nested context manager. + +Code like this: + +\begin{verbatim} +from contextlib import nested + +with nested(A, B, C) as (X, Y, Z): + do_something() +\end{verbatim} + +is equivalent to this: + +\begin{verbatim} +with A as X: + with B as Y: + with C as Z: + do_something() +\end{verbatim} + +Note that if one of the nested contexts' \method{__exit__()} method +raises an exception, any previous exception state will be lost; the new +exception will be passed to the outer contexts' \method{__exit__()} +method(s), if any. In general, \method{__exit__()} methods should avoid +raising exceptions, and in particular they should not re-raise a +passed-in exception. +\end{funcdesc} + +\label{context-closing} +\begin{funcdesc}{closing}{thing} +Return a context manager that closes \var{thing} upon completion of the +block. This is basically equivalent to: + +\begin{verbatim} +from contextlib import contextmanager + +@contextmanager +def closing(thing): + try: + yield thing + finally: + thing.close() +\end{verbatim} + +And lets you write code like this: +\begin{verbatim} +from __future__ import with_statement +from contextlib import closing +import codecs + +with closing(codecs.open("foo", encoding="utf8")) as f: + for line in f: + print line.encode("latin1") +\end{verbatim} + +without needing to explicitly close \code{f}. Even if an error occurs, +\code{f.close()} will be called when the \keyword{with} block is exited. + +\end{funcdesc} + +\begin{seealso} + \seepep{0343}{The "with" statement} + {The specification, background, and examples for the + Python \keyword{with} statement.} +\end{seealso} diff --git a/Doc/lib/libcookielib.tex b/Doc/lib/libcookielib.tex index a35f97d..ef2d833 100644 --- a/Doc/lib/libcookielib.tex +++ b/Doc/lib/libcookielib.tex @@ -249,7 +249,7 @@ anyway, unless you ask otherwise by passing a true ignore_discard=\constant{False}, ignore_expires=\constant{False}} Save cookies to a file. -This base class raises \class{NotImplementedError}. Subclasses may +This base class raises \exception{NotImplementedError}. Subclasses may leave this method unimplemented. \var{filename} is the name of file in which to save cookies. If diff --git a/Doc/lib/libcsv.tex b/Doc/lib/libcsv.tex index ba0df4f..65053c7 100644 --- a/Doc/lib/libcsv.tex +++ b/Doc/lib/libcsv.tex @@ -33,8 +33,9 @@ form using the \class{DictReader} and \class{DictWriter} classes. \begin{notice} This version of the \module{csv} module doesn't support Unicode input. Also, there are currently some issues regarding \ASCII{} NUL - characters. Accordingly, all input should generally be printable - \ASCII{} to be safe. These restrictions will be removed in the future. + characters. Accordingly, all input should be UTF-8 or printable + \ASCII{} to be safe; see the examples in section~\ref{csv-examples}. + These restrictions will be removed in the future. \end{notice} \begin{seealso} @@ -365,7 +366,7 @@ A read-only description of the dialect in use by the writer. -\subsection{Examples} +\subsection{Examples\label{csv-examples}} The simplest example of reading a CSV file: @@ -426,37 +427,99 @@ for row in csv.reader(['one,two,three']): \end{verbatim} The \module{csv} module doesn't directly support reading and writing -Unicode, but it is 8-bit clean save for some problems with \ASCII{} NUL -characters, so you can write classes that handle the encoding and decoding -for you as long as you avoid encodings like utf-16 that use NULs: +Unicode, but it is 8-bit-clean save for some problems with \ASCII{} NUL +characters. So you can write functions or classes that handle the +encoding and decoding for you as long as you avoid encodings like +UTF-16 that use NULs. UTF-8 is recommended. + +\function{unicode_csv_reader} below is a generator that wraps +\class{csv.reader} to handle Unicode CSV data (a list of Unicode +strings). \function{utf_8_encoder} is a generator that encodes the +Unicode strings as UTF-8, one string (or row) at a time. The encoded +strings are parsed by the CSV reader, and +\function{unicode_csv_reader} decodes the UTF-8-encoded cells back +into Unicode: \begin{verbatim} import csv +def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): + # csv.py doesn't do Unicode; encode temporarily as UTF-8: + csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), + dialect=dialect, **kwargs) + for row in csv_reader: + # decode UTF-8 back to Unicode, cell by cell: + yield [unicode(cell, 'utf-8') for cell in row] + +def utf_8_encoder(unicode_csv_data): + for line in unicode_csv_data: + yield line.encode('utf-8') +\end{verbatim} + +For all other encodings the following \class{UnicodeReader} and +\class{UnicodeWriter} classes can be used. They take an additional +\var{encoding} parameter in their constructor and make sure that the data +passes the real reader or writer encoded as UTF-8: + +\begin{verbatim} +import csv, codecs, cStringIO + +class UTF8Recoder: + """ + Iterator that reads an encoded stream and reencodes the input to UTF-8 + """ + def __init__(self, f, encoding): + self.reader = codecs.getreader(encoding)(f) + + def __iter__(self): + return self + + def next(self): + return self.reader.next().encode("utf-8") + class UnicodeReader: + """ + A CSV reader which will iterate over lines in the CSV file "f", + which is encoded in the given encoding. + """ + def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): + f = UTF8Recoder(f, encoding) self.reader = csv.reader(f, dialect=dialect, **kwds) - self.encoding = encoding def next(self): row = self.reader.next() - return [unicode(s, self.encoding) for s in row] + return [unicode(s, "utf-8") for s in row] def __iter__(self): return self class UnicodeWriter: + """ + A CSV writer which will write rows to CSV file "f", + which is encoded in the given encoding. + """ + def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): - self.writer = csv.writer(f, dialect=dialect, **kwds) - self.encoding = encoding + # Redirect output to a queue + self.queue = cStringIO.StringIO() + self.writer = csv.writer(self.queue, dialect=dialect, **kwds) + self.stream = f + self.encoder = codecs.getincrementalencoder(encoding)() def writerow(self, row): - self.writer.writerow([s.encode(self.encoding) for s in row]) + self.writer.writerow([s.encode("utf-8") for s in row]) + # Fetch UTF-8 output from the queue ... + data = self.queue.getvalue() + data = data.decode("utf-8") + # ... and reencode it into the target encoding + data = self.encoder.encode(data) + # write to the target stream + self.stream.write(data) + # empty queue + self.queue.truncate(0) def writerows(self, rows): for row in rows: self.writerow(row) \end{verbatim} - -They should work just like the \class{csv.reader} and \class{csv.writer} -classes but add an \var{encoding} parameter. diff --git a/Doc/lib/libdatetime.tex b/Doc/lib/libdatetime.tex index 4bba553..cae5d60 100644 --- a/Doc/lib/libdatetime.tex +++ b/Doc/lib/libdatetime.tex @@ -504,7 +504,7 @@ Instance methods: Return a string representing the date, controlled by an explicit format string. Format codes referring to hours, minutes or seconds will see 0 values. - See the section on \method{strftime()} behavior. + See section~\ref{strftime-behavior} -- \method{strftime()} behavior. \end{methoddesc} @@ -970,8 +970,8 @@ Instance methods: \begin{methoddesc}{strftime}{format} Return a string representing the date and time, controlled by an - explicit format string. See the section on \method{strftime()} - behavior. + explicit format string. See section~\ref{strftime-behavior} -- + \method{strftime()} behavior. \end{methoddesc} @@ -1100,7 +1100,8 @@ Instance methods: \begin{methoddesc}{strftime}{format} Return a string representing the time, controlled by an explicit - format string. See the section on \method{strftime()} behavior. + format string. See section~\ref{strftime-behavior} -- + \method{strftime()} behavior. \end{methoddesc} \begin{methoddesc}{utcoffset}{} @@ -1368,7 +1369,7 @@ representing only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). -\subsection{\method{strftime()} Behavior} +\subsection{\method{strftime()} Behavior\label{strftime-behavior}} \class{date}, \class{datetime}, and \class{time} objects all support a \code{strftime(\var{format})} diff --git a/Doc/lib/libdecimal.tex b/Doc/lib/libdecimal.tex index 092f038..ffc3363 100644 --- a/Doc/lib/libdecimal.tex +++ b/Doc/lib/libdecimal.tex @@ -442,9 +442,33 @@ the \function{getcontext()} and \function{setcontext()} functions: Set the current context for the active thread to \var{c}. \end{funcdesc} -New contexts can formed using the \class{Context} constructor described below. -In addition, the module provides three pre-made contexts: +Beginning with Python 2.5, you can also use the \keyword{with} statement +to temporarily change the active context. For example the following code +increases the current decimal precision by 2 places, performs a +calculation, and then automatically restores the previous context: +\begin{verbatim} +from __future__ import with_statement +import decimal + +with decimal.getcontext() as ctx: + ctx.prec += 2 # add 2 more digits of precision + calculate_something() +\end{verbatim} + +The context that's active in the body of the \keyword{with} statement is +a \emph{copy} of the context you provided to the \keyword{with} +statement, so modifying its attributes doesn't affect anything except +that temporary copy. + +You can use any decimal context in a \keyword{with} statement, but if +you just want to make a temporary change to some aspect of the current +context, it's easiest to just use \function{getcontext()} as shown +above. + +New contexts can also be created using the \class{Context} constructor +described below. In addition, the module provides three pre-made +contexts: \begin{classdesc*}{BasicContext} This is a standard context defined by the General Decimal Arithmetic diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex index 0be3aa9..c0352d3 100644 --- a/Doc/lib/libfuncs.tex +++ b/Doc/lib/libfuncs.tex @@ -6,7 +6,7 @@ are always available. They are listed here in alphabetical order. \setindexsubitem{(built-in function)} -\begin{funcdesc}{__import__}{name\optional{, globals\optional{, locals\optional{, fromlist}}}} +\begin{funcdesc}{__import__}{name\optional{, globals\optional{, locals\optional{, fromlist\optional{, level}}}}} This function is invoked by the \keyword{import}\stindex{import} statement. It mainly exists so that you can replace it with another function that has a compatible interface, in order to change the @@ -20,9 +20,9 @@ are always available. They are listed here in alphabetical order. For example, the statement \samp{import spam} results in the following call: \code{__import__('spam',} \code{globals(),} - \code{locals(), [])}; the statement \samp{from spam.ham import eggs} + \code{locals(), [], -1)}; the statement \samp{from spam.ham import eggs} results in \samp{__import__('spam.ham', globals(), locals(), - ['eggs'])}. Note that even though \code{locals()} and + ['eggs'], -1)}. Note that even though \code{locals()} and \code{['eggs']} are passed in as arguments, the \function{__import__()} function does not set the local variable named \code{eggs}; this is done by subsequent code that is generated @@ -52,6 +52,15 @@ def my_import(name): mod = getattr(mod, comp) return mod \end{verbatim} + + \var{level} specifies whether to use absolute or relative imports. + The default is \code{-1} which indicates both absolute and relative + imports will be attempted. \code{0} means only perform absolute imports. + Positive values for \var{level} indicate the number of parent directories + to search relative to the directory of the module calling + \function{__import__}. +\versionchanged[The level parameter was added]{2.5} +\versionchanged[Keyword support for parameters was added]{2.5} \end{funcdesc} \begin{funcdesc}{abs}{x} @@ -683,7 +692,7 @@ class C: \end{funcdesc} \begin{funcdesc}{object}{} - Return a new featureless object. \function{object()} is a base + Return a new featureless object. \class{object} is a base for all new style classes. It has the methods that are common to all instances of new style classes. \versionadded{2.2} @@ -718,8 +727,11 @@ class C: \begin{funcdesc}{pow}{x, y\optional{, z}} Return \var{x} to the power \var{y}; if \var{z} is present, return \var{x} to the power \var{y}, modulo \var{z} (computed more - efficiently than \code{pow(\var{x}, \var{y}) \%\ \var{z}}). The - arguments must have numeric types. With mixed operand types, the + efficiently than \code{pow(\var{x}, \var{y}) \%\ \var{z}}). + The two-argument form \code{pow(\var{x}, \var{y})} is equivalent to using + the power operator: \code{\var{x}**\var{y}}. + + The arguments must have numeric types. With mixed operand types, the coercion rules for binary arithmetic operators apply. For int and long int operands, the result has the same type as the operands (after coercion) unless the second argument is negative; in that diff --git a/Doc/lib/libgc.tex b/Doc/lib/libgc.tex index 54ca26c..0d3408b 100644 --- a/Doc/lib/libgc.tex +++ b/Doc/lib/libgc.tex @@ -35,7 +35,8 @@ Returns true if automatic collection is enabled. \begin{funcdesc}{collect}{\optional{generation}} With no arguments, run a full collection. The optional argument \var{generation} may be an integer specifying which generation to collect -(from 0 to 2). A ValueError is raised if the generation number is invalid. +(from 0 to 2). A \exception{ValueError} is raised if the generation number +is invalid. The number of unreachable objects found is returned. \versionchanged[The optional \var{generation} argument was added]{2.5} diff --git a/Doc/lib/libgetpass.tex b/Doc/lib/libgetpass.tex index 28bfe8f..1d177d3 100644 --- a/Doc/lib/libgetpass.tex +++ b/Doc/lib/libgetpass.tex @@ -11,11 +11,15 @@ The \module{getpass} module provides two functions: -\begin{funcdesc}{getpass}{\optional{prompt}} +\begin{funcdesc}{getpass}{\optional{prompt\optional{, stream}}} Prompt the user for a password without echoing. The user is prompted using the string \var{prompt}, which defaults to - \code{'Password: '}. + \code{'Password: '}. On \UNIX, the prompt is written to the + file-like object \var{stream}, which defaults to + \code{sys.stdout} (this argument is ignored on Windows). + Availability: Macintosh, \UNIX, Windows. + \versionadded[The \var{stream} parameter]{2.5} \end{funcdesc} diff --git a/Doc/lib/libhashlib.tex b/Doc/lib/libhashlib.tex index e9d0b57..62e3fc4 100644 --- a/Doc/lib/libhashlib.tex +++ b/Doc/lib/libhashlib.tex @@ -31,7 +31,7 @@ of the strings fed to it so far using the \method{digest()} or Constructors for hash algorithms that are always present in this module are \function{md5()}, \function{sha1()}, \function{sha224()}, \function{sha256()}, \function{sha384()}, and \function{sha512()}. Additional algorithms may also -be available depending upon the OpenSSL library python uses on your platform. +be available depending upon the OpenSSL library that Python uses on your platform. \index{OpenSSL} For example, to obtain the digest of the string \code{'Nobody inspects diff --git a/Doc/lib/libitertools.tex b/Doc/lib/libitertools.tex index 421d647..20bbc8d 100644 --- a/Doc/lib/libitertools.tex +++ b/Doc/lib/libitertools.tex @@ -276,12 +276,30 @@ by functions or loops that truncate the stream. def izip(*iterables): iterables = map(iter, iterables) while iterables: - result = [i.next() for i in iterables] + result = [it.next() for it in iterables] yield tuple(result) \end{verbatim} \versionchanged[When no iterables are specified, returns a zero length - iterator instead of raising a TypeError exception]{2.4} + iterator instead of raising a \exception{TypeError} + exception]{2.4} + + Note, the left-to-right evaluation order of the iterables is guaranteed. + This makes possible an idiom for clustering a data series into n-length + groups using \samp{izip(*[iter(s)]*n)}. For data that doesn't fit + n-length groups exactly, the last tuple can be pre-padded with fill + values using \samp{izip(*[chain(s, [None]*(n-1))]*n)}. + + Note, when \function{izip()} is used with unequal length inputs, subsequent + iteration over the longer iterables cannot reliably be continued after + \function{izip()} terminates. Potentially, up to one entry will be missing + from each of the left-over iterables. This occurs because a value is fetched + from each iterator in-turn, but the process ends when one of the iterators + terminates. This leaves the last fetched values in limbo (they cannot be + returned in a final, incomplete tuple and they are cannot be pushed back + into the iterator for retrieval with \code{it.next()}). In general, + \function{izip()} should only be used with unequal length inputs when you + don't care about trailing, unmatched values from the longer iterables. \end{funcdesc} \begin{funcdesc}{repeat}{object\optional{, times}} @@ -517,4 +535,9 @@ def pairwise(iterable): pass return izip(a, b) +def grouper(n, iterable, padvalue=None): + "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')" + return izip(*[chain(iterable, repeat(padvalue, n-1))]*n) + + \end{verbatim} diff --git a/Doc/lib/liblinecache.tex b/Doc/lib/liblinecache.tex index c022ba9..1477d3c 100644 --- a/Doc/lib/liblinecache.tex +++ b/Doc/lib/liblinecache.tex @@ -15,7 +15,7 @@ the formatted traceback. The \module{linecache} module defines the following functions: -\begin{funcdesc}{getline}{filename, lineno} +\begin{funcdesc}{getline}{filename, lineno\optional{, module_globals}} Get line \var{lineno} from file named \var{filename}. This function will never throw an exception --- it will return \code{''} on errors (the terminating newline character will be included for lines that are @@ -23,7 +23,11 @@ found). If a file named \var{filename} is not found, the function will look for it in the module\indexiii{module}{search}{path} search path, -\code{sys.path}. +\code{sys.path}, after first checking for a \pep{302} \code{__loader__} +in \var{module_globals}, in case the module was imported from a zipfile +or other non-filesystem import source. + +\versionadded[The \var{module_globals} parameter was added]{2.5} \end{funcdesc} \begin{funcdesc}{clearcache}{} diff --git a/Doc/lib/libnntplib.tex b/Doc/lib/libnntplib.tex index 7f14dee..10330ed 100644 --- a/Doc/lib/libnntplib.tex +++ b/Doc/lib/libnntplib.tex @@ -68,48 +68,48 @@ flag \var{readermode} is true, then a \samp{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 \samp{group}. If -you get unexpected \code{NNTPPermanentError}s, you might need to set +you get unexpected \exception{NNTPPermanentError}s, you might need to set \var{readermode}. \var{readermode} defaults to \code{None}. \var{usenetrc} defaults to \code{True}. \versionchanged[\var{usenetrc} argument added]{2.4} \end{classdesc} -\begin{classdesc}{NNTPError}{} -Derived from the standard exception \code{Exception}, this is the base -class for all exceptions raised by the \code{nntplib} module. -\end{classdesc} +\begin{excdesc}{NNTPError} +Derived from the standard exception \exception{Exception}, this is the +base class for all exceptions raised by the \module{nntplib} module. +\end{excdesc} -\begin{classdesc}{NNTPReplyError}{} +\begin{excdesc}{NNTPReplyError} Exception raised when an unexpected reply is received from the server. For backwards compatibility, the exception \code{error_reply} is equivalent to this class. -\end{classdesc} +\end{excdesc} -\begin{classdesc}{NNTPTemporaryError}{} +\begin{excdesc}{NNTPTemporaryError} Exception raised when an error code in the range 400--499 is received. For backwards compatibility, the exception \code{error_temp} is equivalent to this class. -\end{classdesc} +\end{excdesc} -\begin{classdesc}{NNTPPermanentError}{} +\begin{excdesc}{NNTPPermanentError} Exception raised when an error code in the range 500--599 is received. For backwards compatibility, the exception \code{error_perm} is equivalent to this class. -\end{classdesc} +\end{excdesc} -\begin{classdesc}{NNTPProtocolError}{} +\begin{excdesc}{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 \code{error_proto} is equivalent to this class. -\end{classdesc} +\end{excdesc} -\begin{classdesc}{NNTPDataError}{} +\begin{excdesc}{NNTPDataError} Exception raised when there is some error in the response data. For backwards compatibility, the exception \code{error_data} is equivalent to this class. -\end{classdesc} +\end{excdesc} \subsection{NNTP Objects \label{nntp-objects}} diff --git a/Doc/lib/liboptparse.tex b/Doc/lib/liboptparse.tex index 4ab325b..8aca501 100644 --- a/Doc/lib/liboptparse.tex +++ b/Doc/lib/liboptparse.tex @@ -100,8 +100,8 @@ options; the traditional \UNIX{} syntax is a hyphen (``-'') followed by a single letter, e.g. \code{"-x"} or \code{"-F"}. Also, traditional \UNIX{} syntax allows multiple options to be merged into a single argument, e.g. \code{"-x -F"} is equivalent to \code{"-xF"}. The GNU project -introduced \code{"-{}-"} followed by a series of hyphen-separated words, -e.g. \code{"-{}-file"} or \code{"-{}-dry-run"}. These are the only two option +introduced \code{"{--}"} followed by a series of hyphen-separated words, +e.g. \code{"{--}file"} or \code{"{--}dry-run"}. These are the only two option syntaxes provided by \module{optparse}. Some other option syntaxes that the world has seen include: @@ -170,7 +170,7 @@ For example, consider this hypothetical command-line: prog -v --report /tmp/report.txt foo bar \end{verbatim} -\code{"-v"} and \code{"-{}-report"} are both options. Assuming that +\code{"-v"} and \code{"{--}report"} are both options. Assuming that \longprogramopt{report} takes one argument, \code{"/tmp/report.txt"} is an option argument. \code{"foo"} and \code{"bar"} are positional arguments. @@ -587,7 +587,7 @@ programmer errors and user errors. Programmer errors are usually erroneous calls to \code{parse.add{\_}option()}, e.g. invalid option strings, unknown option attributes, missing option attributes, etc. These are dealt with in the usual way: raise an exception (either -\code{optparse.OptionError} or \code{TypeError}) and let the program crash. +\exception{optparse.OptionError} or \exception{TypeError}) and let the program crash. Handling user errors is much more important, since they are guaranteed to happen no matter how stable your code is. \module{optparse} can automatically @@ -1019,9 +1019,9 @@ callback) as-is. Integer arguments are passed to \code{int()} to convert them to Python integers. If \code{int()} fails, so will \module{optparse}, although with a more -useful error message. (Internally, \module{optparse} raises OptionValueError; -OptionParser catches this exception higher up and terminates your -program with a useful error message.) +useful error message. (Internally, \module{optparse} raises +\exception{OptionValueError}; OptionParser catches this exception higher +up and terminates your program with a useful error message.) Likewise, \code{float} arguments are passed to \code{float()} for conversion, \code{long} arguments to \code{long()}, and \code{complex} arguments to @@ -1032,7 +1032,7 @@ arguments. option attribute (a sequence of strings) defines the set of allowed option arguments. \code{optparse.option.check{\_}choice()} compares user-supplied option arguments against this master list and raises -OptionValueError if an invalid string is given. +\exception{OptionValueError} if an invalid string is given. \subsubsection{Querying and manipulating your option parser\label{optparse-querying-manipulating-option-parser}} @@ -1052,7 +1052,7 @@ that option is removed. If that option provided any other option strings, all of those option strings become invalid. If \code{opt{\_}str} does not occur in any option belonging to this -OptionParser, raises ValueError. +OptionParser, raises \exception{ValueError}. \end{description} @@ -1087,7 +1087,7 @@ The available conflict-handling mechanisms are: \begin{description} \item[\code{error} (default)] assume option conflicts are a programming error and raise -OptionConflictError +\exception{OptionConflictError} \item[\code{resolve}] resolve option conflicts intelligently (see below) \end{description} @@ -1260,7 +1260,7 @@ is a dictionary of arbitrary keyword arguments supplied via \subsubsection{Raising errors in a callback\label{optparse-raising-errors-in-callback}} -The callback function should raise OptionValueError if there are any +The callback function should raise \exception{OptionValueError} if there are any problems with the option or its argument(s). \module{optparse} catches this and terminates the program, printing the error message you supply to stderr. Your message should be clear, concise, accurate, and mention diff --git a/Doc/lib/libos.tex b/Doc/lib/libos.tex index 9af5889..9ded3ae 100644 --- a/Doc/lib/libos.tex +++ b/Doc/lib/libos.tex @@ -343,6 +343,10 @@ Availability: Macintosh, \UNIX, Windows. \versionchanged[When specified, the \var{mode} argument must now start with one of the letters \character{r}, \character{w}, or \character{a}, otherwise a \exception{ValueError} is raised]{2.3} +\versionchanged[On \UNIX, when the \var{mode} argument starts with + \character{a}, the \var{O_APPEND} flag is set on the file descriptor + (which the \cfunction{fdopen()} implementation already does on most + platforms)]{2.5} \end{funcdesc} \begin{funcdesc}{popen}{command\optional{, mode\optional{, bufsize}}} @@ -547,7 +551,8 @@ documentation; flag constants (like \constant{O_RDONLY} and This function is intended for low-level I/O. For normal usage, use the built-in function \function{open()}, which returns a ``file object'' with \method{read()} and \method{write()} methods (and many -more). +more). To wrap a file descriptor in a ``file object'', use +\function{fdopen()}. \end{notice} \end{funcdesc} @@ -1731,6 +1736,27 @@ The \function{spawn()} functions called with \constant{P_NOWAIT} return suitable process handles. \end{funcdesc} +\begin{funcdesc}{wait3}{\optional{options}} +Similar to \function{waitpid()}, except no process id argument is given and +a 3-element tuple containing the child's process id, exit status indication, +and resource usage information is returned. Refer to +\module{resource}.\function{getrusage()} +for details on resource usage information. The option argument is the same +as that provided to \function{waitpid()} and \function{wait4()}. +Availability: \UNIX. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{wait4}{pid, options} +Similar to \function{waitpid()}, except a 3-element tuple, containing the +child's process id, exit status indication, and resource usage information +is returned. Refer to \module{resource}.\function{getrusage()} for details +on resource usage information. The arguments to \function{wait4()} are +the same as those provided to \function{waitpid()}. +Availability: \UNIX. +\versionadded{2.5} +\end{funcdesc} + \begin{datadesc}{WNOHANG} The option for \function{waitpid()} to return immediately if no child process status is available immediately. The function returns @@ -1818,14 +1844,14 @@ Return string-valued system configuration values. string which is the name of a defined system value; these names are specified in a number of standards (\POSIX, \UNIX{} 95, \UNIX{} 98, and others). Some platforms define additional names as well. The names -known to the host operating system are given in the +known to the host operating system are given as the keys of the \code{confstr_names} dictionary. For configuration variables not included in that mapping, passing an integer for \var{name} is also accepted. Availability: Macintosh, \UNIX. -If the configuration value specified by \var{name} isn't defined, the -empty string is returned. +If the configuration value specified by \var{name} isn't defined, +\code{None} is returned. If \var{name} is a string and is not known, \exception{ValueError} is raised. If a specific value for \var{name} is not supported by the diff --git a/Doc/lib/libossaudiodev.tex b/Doc/lib/libossaudiodev.tex index ec79e9e..223cf28 100644 --- a/Doc/lib/libossaudiodev.tex +++ b/Doc/lib/libossaudiodev.tex @@ -311,7 +311,7 @@ The mixer object provides two file-like methods: \begin{methoddesc}[mixer device]{close}{} This method closes the open mixer device file. Any further attempts to -use the mixer after this file is closed will raise an IOError. +use the mixer after this file is closed will raise an \exception{IOError}. \end{methoddesc} \begin{methoddesc}[mixer device]{fileno}{} diff --git a/Doc/lib/libpdb.tex b/Doc/lib/libpdb.tex index 6301175..a5b36a6 100644 --- a/Doc/lib/libpdb.tex +++ b/Doc/lib/libpdb.tex @@ -240,6 +240,45 @@ Condition is an expression which must evaluate to true before the breakpoint is honored. If condition is absent, any existing condition is removed; i.e., the breakpoint is made unconditional. +\item[commands \optional{\var{bpnumber}}] + +Specify a list of commands for breakpoint number \var{bpnumber}. The +commands themselves appear on the following lines. Type a line +containing just 'end' to terminate the commands. An example: + +\begin{verbatim} +(Pdb) commands 1 +(com) print some_variable +(com) end +(Pdb) +\end{verbatim} + +To remove all commands from a breakpoint, type commands and +follow it immediately with end; that is, give no commands. + +With no \var{bpnumber} argument, commands refers to the last +breakpoint set. + +You can use breakpoint commands to start your program up again. +Simply use the continue command, or step, or any other +command that resumes execution. + +Specifying any command resuming execution (currently continue, +step, next, return, jump, quit and their abbreviations) terminates +the command list (as if that command was immediately followed by end). +This is because any time you resume execution +(even with a simple next or step), you may encounter· +another breakpoint--which could have its own command list, leading to +ambiguities about which list to execute. + + If you use the 'silent' command in the command list, the +usual message about stopping at a breakpoint is not printed. This may +be desirable for breakpoints that are to print a specific message and +then continue. If none of the other commands print anything, you +see no sign that the breakpoint was reached. + +\versionadded{2.5} + \item[s(tep)] Execute the current line, stop at the first possible occasion diff --git a/Doc/lib/libprofile.tex b/Doc/lib/libprofile.tex index afc9694..9ff5ba0 100644 --- a/Doc/lib/libprofile.tex +++ b/Doc/lib/libprofile.tex @@ -124,7 +124,7 @@ layer on top of the internal \module{_lsprof} module. The %\end{description} -\section{Instant Users Manual \label{profile-instant}} +\section{Instant User's Manual \label{profile-instant}} This section is provided for users that ``don't want to read the manual.'' It provides a very brief overview, and allows a user to @@ -391,17 +391,17 @@ Analysis of the profiler data is done using this class from the % (This \stmodindex use may be hard to change ;-( ) \stmodindex{pstats} -\begin{classdesc}{Stats}{filename\optional{, \moreargs}} +\begin{classdesc}{Stats}{filename\optional{, \moreargs\optional{, stream=sys.stdout}}} This class constructor creates an instance of a ``statistics object'' from a \var{filename} (or set of filenames). \class{Stats} objects are -manipulated by methods, in order to print useful reports. - -The file selected by the above constructor must have been created by -the corresponding version of \module{profile} or \module{cProfile}. -To be specific, there is -\emph{no} file compatibility guaranteed with future versions of this -profiler, and there is no compatibility with files produced by other -profilers. +manipulated by methods, in order to print useful reports. You may specify +an alternate output stream by giving the keyword argument, \code{stream}. + +The file selected by the above constructor must have been created by the +corresponding version of \module{profile} or \module{cProfile}. To be +specific, there is \emph{no} file compatibility guaranteed with future +versions of this profiler, and there is no compatibility with files produced +by other profilers. %(such as the old system profiler). If several files are provided, all the statistics for identical diff --git a/Doc/lib/libpycompile.tex b/Doc/lib/libpycompile.tex index 0458191..85f0aaa 100644 --- a/Doc/lib/libpycompile.tex +++ b/Doc/lib/libpycompile.tex @@ -30,9 +30,10 @@ Exception raised when an error occurs while attempting to compile the file. \code{+} \code{'c'} (\code{'o'} if optimization is enabled in the current interpreter). If \var{dfile} is specified, it is used as the name of the source file in error messages instead of \var{file}. - If \var{doraise} = True, a PyCompileError is raised when an error is - encountered while compiling \var{file}. If \var{doraise} = False (the default), - an error string is written to sys.stderr, but no exception is raised. + If \var{doraise} is true, a \exception{PyCompileError} is raised when + an error is encountered while compiling \var{file}. If \var{doraise} + is false (the default), an error string is written to \code{sys.stderr}, + but no exception is raised. \end{funcdesc} \begin{funcdesc}{main}{\optional{args}} diff --git a/Doc/lib/libqueue.tex b/Doc/lib/libqueue.tex index f1d892a..95ad47f 100644 --- a/Doc/lib/libqueue.tex +++ b/Doc/lib/libqueue.tex @@ -1,3 +1,4 @@ + \section{\module{Queue} --- A synchronized queue class} @@ -94,3 +95,51 @@ immediately available, else raise the \exception{Empty} exception \begin{methoddesc}{get_nowait}{} Equivalent to \code{get(False)}. \end{methoddesc} + +Two methods are offered to support tracking whether enqueued tasks have +been fully processed by daemon consumer threads. + +\begin{methoddesc}{task_done}{} +Indicate that a formerly enqueued task is complete. Used by queue consumer +threads. For each \method{get()} used to fetch a task, a subsequent call to +\method{task_done()} tells the queue that the processing on the task is complete. + +If a \method{join()} is currently blocking, it will resume when all items +have been processed (meaning that a \method{task_done()} call was received +for every item that had been \method{put()} into the queue). + +Raises a \exception{ValueError} if called more times than there were items +placed in the queue. +\versionadded{2.5} +\end{methoddesc} + +\begin{methoddesc}{join}{} +Blocks until all items in the queue have been gotten and processed. + +The count of unfinished tasks goes up whenever an item is added to the +queue. The count goes down whenever a consumer thread calls \method{task_done()} +to indicate that the item was retrieved and all work on it is complete. +When the count of unfinished tasks drops to zero, join() unblocks. +\versionadded{2.5} +\end{methoddesc} + +Example of how to wait for enqueued tasks to be completed: + +\begin{verbatim} + def worker(): + while True: + item = q.get() + do_work(item) + q.task_done() + + q = Queue() + for i in range(num_worker_threads): + t = Thread(target=worker) + t.setDaemon(True) + t.start() + + for item in source(): + q.put(item) + + q.join() # block until all tasks are done +\end{verbatim} diff --git a/Doc/lib/libre.tex b/Doc/lib/libre.tex index 8e6513a..1404e09 100644 --- a/Doc/lib/libre.tex +++ b/Doc/lib/libre.tex @@ -566,9 +566,6 @@ ignored. >>> re.split('\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] \end{verbatim} - - This function combines and extends the functionality of - the old \function{regsub.split()} and \function{regsub.splitx()}. \end{funcdesc} \begin{funcdesc}{findall}{pattern, string\optional{, flags}} @@ -934,7 +931,7 @@ The equivalent regular expression would be \leftline{\strong{Avoiding recursion}} If you create regular expressions that require the engine to perform a -lot of recursion, you may encounter a RuntimeError exception with +lot of recursion, you may encounter a \exception{RuntimeError} exception with the message \code{maximum recursion limit} exceeded. For example, \begin{verbatim} @@ -943,7 +940,7 @@ the message \code{maximum recursion limit} exceeded. For example, >>> re.match('Begin (\w| )*? end', s).end() Traceback (most recent call last): File "", line 1, in ? - File "/usr/local/lib/python2.3/sre.py", line 132, in match + File "/usr/local/lib/python2.5/re.py", line 132, in match return _compile(pattern, flags).match(string) RuntimeError: maximum recursion limit exceeded \end{verbatim} diff --git a/Doc/lib/libreconvert.tex b/Doc/lib/libreconvert.tex deleted file mode 100644 index 29c6e52..0000000 --- a/Doc/lib/libreconvert.tex +++ /dev/null @@ -1,80 +0,0 @@ -\section{\module{reconvert} --- - Convert regular expressions from regex to re form} -\declaremodule{standard}{reconvert} -\moduleauthor{Andrew M. Kuchling}{amk@amk.ca} -\sectionauthor{Skip Montanaro}{skip@pobox.com} - - -\modulesynopsis{Convert regex-, emacs- or sed-style regular expressions -to re-style syntax.} - - -This module provides a facility to convert regular expressions from the -syntax used by the deprecated \module{regex} module to those used by the -newer \module{re} module. Because of similarity between the regular -expression syntax of \code{sed(1)} and \code{emacs(1)} and the -\module{regex} module, it is also helpful to convert patterns written for -those tools to \module{re} patterns. - -When used as a script, a Python string literal (or any other expression -evaluating to a string) is read from stdin, and the translated expression is -written to stdout as a string literal. Unless stdout is a tty, no trailing -newline is written to stdout. This is done so that it can be used with -Emacs \code{C-U M-|} (shell-command-on-region) which filters the region -through the shell command. - -\begin{seealso} - \seetitle{Mastering Regular Expressions}{Book on regular expressions - by Jeffrey Friedl, published by O'Reilly. The second - edition of the book no longer covers Python at all, - but the first edition covered writing good regular expression - patterns in great detail.} -\end{seealso} - -\subsection{Module Contents} -\nodename{Contents of Module reconvert} - -The module defines two functions and a handful of constants. - -\begin{funcdesc}{convert}{pattern\optional{, syntax=None}} - Convert a \var{pattern} representing a \module{regex}-stype regular - expression into a \module{re}-style regular expression. The optional - \var{syntax} parameter is a bitwise-or'd set of flags that control what - constructs are converted. See below for a description of the various - constants. -\end{funcdesc} - -\begin{funcdesc}{quote}{s\optional{, quote=None}} - Convert a string object to a quoted string literal. - - This is similar to \function{repr} but will return a "raw" string (r'...' - or r"...") when the string contains backslashes, instead of doubling all - backslashes. The resulting string does not always evaluate to the same - string as the original; however it will do just the right thing when passed - into re.compile(). - - The optional second argument forces the string quote; it must be a single - character which is a valid Python string quote. Note that prior to Python - 2.5 this would not accept triple-quoted string delimiters. -\end{funcdesc} - -\begin{datadesc}{RE_NO_BK_PARENS} - Suppress paren conversion. This should be omitted when converting - \code{sed}-style or \code{emacs}-style regular expressions. -\end{datadesc} - -\begin{datadesc}{RE_NO_BK_VBAR} - Suppress vertical bar conversion. This should be omitted when converting - \code{sed}-style or \code{emacs}-style regular expressions. -\end{datadesc} - -\begin{datadesc}{RE_BK_PLUS_QM} - Enable conversion of \code{+} and \code{?} characters. This should be - added to the \var{syntax} arg of \function{convert} when converting - \code{sed}-style regular expressions and omitted when converting - \code{emacs}-style regular expressions. -\end{datadesc} - -\begin{datadesc}{RE_NEWLINE_OR} - When set, newline characters are replaced by \code{|}. -\end{datadesc} diff --git a/Doc/lib/libregex.tex b/Doc/lib/libregex.tex deleted file mode 100644 index 0982f81..0000000 --- a/Doc/lib/libregex.tex +++ /dev/null @@ -1,370 +0,0 @@ -\section{\module{regex} --- - Regular expression operations} -\declaremodule{builtin}{regex} - -\modulesynopsis{Regular expression search and match operations. - \strong{Obsolete!}} - - -This module provides regular expression matching operations similar to -those found in Emacs. - -\strong{Obsolescence note:} -This module is obsolete as of Python version 1.5; it is still being -maintained because much existing code still uses it. All new code in -need of regular expressions should use the new -\code{re}\refstmodindex{re} module, which supports the more powerful -and regular Perl-style regular expressions. Existing code should be -converted. The standard library module -\code{reconvert}\refstmodindex{reconvert} helps in converting -\code{regex} style regular expressions to \code{re}\refstmodindex{re} -style regular expressions. (For more conversion help, see Andrew -Kuchling's\index{Kuchling, Andrew} ``\module{regex-to-re} HOWTO'' at -\url{http://www.python.org/doc/howto/regex-to-re/}.) - -By default the patterns are Emacs-style regular expressions -(with one exception). There is -a way to change the syntax to match that of several well-known -\UNIX{} utilities. The exception is that Emacs' \samp{\e s} -pattern is not supported, since the original implementation references -the Emacs syntax tables. - -This module is 8-bit clean: both patterns and strings may contain null -bytes and characters whose high bit is set. - -\strong{Please note:} There is a little-known fact about Python string -literals which means that you don't usually have to worry about -doubling backslashes, even though they are used to escape special -characters in string literals as well as in regular expressions. This -is because Python doesn't remove backslashes from string literals if -they are followed by an unrecognized escape character. -\emph{However}, if you want to include a literal \dfn{backslash} in a -regular expression represented as a string literal, you have to -\emph{quadruple} it or enclose it in a singleton character class. -E.g.\ to extract \LaTeX\ \samp{\e section\{\textrm{\ldots}\}} headers -from a document, you can use this pattern: -\code{'[\e ]section\{\e (.*\e )\}'}. \emph{Another exception:} -the escape sequence \samp{\e b} is significant in string literals -(where it means the ASCII bell character) as well as in Emacs regular -expressions (where it stands for a word boundary), so in order to -search for a word boundary, you should use the pattern \code{'\e \e b'}. -Similarly, a backslash followed by a digit 0-7 should be doubled to -avoid interpretation as an octal escape. - -\subsection{Regular Expressions} - -A regular expression (or RE) specifies a set of strings that matches -it; the functions in this module let you check if a particular string -matches a given regular expression (or if a given regular expression -matches a particular string, which comes down to the same thing). - -Regular expressions can be concatenated to form new regular -expressions; if \emph{A} and \emph{B} are both regular expressions, -then \emph{AB} is also an regular expression. If a string \emph{p} -matches A and another string \emph{q} matches B, the string \emph{pq} -will match AB. Thus, complex expressions can easily be constructed -from simpler ones like the primitives described here. For details of -the theory and implementation of regular expressions, consult almost -any textbook about compiler construction. - -% XXX The reference could be made more specific, say to -% "Compilers: Principles, Techniques and Tools", by Alfred V. Aho, -% Ravi Sethi, and Jeffrey D. Ullman, or some FA text. - -A brief explanation of the format of regular expressions follows. - -Regular expressions can contain both special and ordinary characters. -Ordinary characters, like '\code{A}', '\code{a}', or '\code{0}', are -the simplest regular expressions; they simply match themselves. You -can concatenate ordinary characters, so '\code{last}' matches the -characters 'last'. (In the rest of this section, we'll write RE's in -\code{this special font}, usually without quotes, and strings to be -matched 'in single quotes'.) - -Special characters either stand for classes of ordinary characters, or -affect how the regular expressions around them are interpreted. - -The special characters are: -\begin{itemize} -\item[\code{.}] (Dot.) Matches any character except a newline. -\item[\code{\^}] (Caret.) Matches the start of the string. -\item[\code{\$}] Matches the end of the string. -\code{foo} matches both 'foo' and 'foobar', while the regular -expression '\code{foo\$}' matches only 'foo'. -\item[\code{*}] Causes the resulting RE to -match 0 or more repetitions of the preceding RE. \code{ab*} will -match 'a', 'ab', or 'a' followed by any number of 'b's. -\item[\code{+}] Causes the -resulting RE to match 1 or more repetitions of the preceding RE. -\code{ab+} will match 'a' followed by any non-zero number of 'b's; it -will not match just 'a'. -\item[\code{?}] Causes the resulting RE to -match 0 or 1 repetitions of the preceding RE. \code{ab?} will -match either 'a' or 'ab'. - -\item[\code{\e}] Either escapes special characters (permitting you to match -characters like '*?+\&\$'), or signals a special sequence; special -sequences are discussed below. Remember that Python also uses the -backslash as an escape sequence in string literals; if the escape -sequence isn't recognized by Python's parser, the backslash and -subsequent character are included in the resulting string. However, -if Python would recognize the resulting sequence, the backslash should -be repeated twice. - -\item[\code{[]}] Used to indicate a set of characters. Characters can -be listed individually, or a range is indicated by giving two -characters and separating them by a '-'. Special characters are -not active inside sets. For example, \code{[akm\$]} -will match any of the characters 'a', 'k', 'm', or '\$'; \code{[a-z]} will -match any lowercase letter. - -If you want to include a \code{]} inside a -set, it must be the first character of the set; to include a \code{-}, -place it as the first or last character. - -Characters \emph{not} within a range can be matched by including a -\code{\^} as the first character of the set; \code{\^} elsewhere will -simply match the '\code{\^}' character. -\end{itemize} - -The special sequences consist of '\code{\e}' and a character -from the list below. If the ordinary character is not on the list, -then the resulting RE will match the second character. For example, -\code{\e\$} matches the character '\$'. Ones where the backslash -should be doubled in string literals are indicated. - -\begin{itemize} -\item[\code{\e|}]\code{A\e|B}, where A and B can be arbitrary REs, -creates a regular expression that will match either A or B. This can -be used inside groups (see below) as well. -% -\item[\code{\e( \e)}] Indicates the start and end of a group; the -contents of a group can be matched later in the string with the -\code{\e [1-9]} special sequence, described next. -\end{itemize} - -\begin{fulllineitems} -\item[\code{\e \e 1, ... \e \e 7, \e 8, \e 9}] -Matches the contents of the group of the same -number. For example, \code{\e (.+\e ) \e \e 1} matches 'the the' or -'55 55', but not 'the end' (note the space after the group). This -special sequence can only be used to match one of the first 9 groups; -groups with higher numbers can be matched using the \code{\e v} -sequence. (\code{\e 8} and \code{\e 9} don't need a double backslash -because they are not octal digits.) -\end{fulllineitems} - -\begin{itemize} -\item[\code{\e \e b}] Matches the empty string, but only at the -beginning or end of a word. A word is defined as a sequence of -alphanumeric characters, so the end of a word is indicated by -whitespace or a non-alphanumeric character. -% -\item[\code{\e B}] Matches the empty string, but when it is \emph{not} at the -beginning or end of a word. -% -\item[\code{\e v}] Must be followed by a two digit decimal number, and -matches the contents of the group of the same number. The group -number must be between 1 and 99, inclusive. -% -\item[\code{\e w}]Matches any alphanumeric character; this is -equivalent to the set \code{[a-zA-Z0-9]}. -% -\item[\code{\e W}] Matches any non-alphanumeric character; this is -equivalent to the set \code{[\^a-zA-Z0-9]}. -\item[\code{\e <}] Matches the empty string, but only at the beginning of a -word. A word is defined as a sequence of alphanumeric characters, so -the end of a word is indicated by whitespace or a non-alphanumeric -character. -\item[\code{\e >}] Matches the empty string, but only at the end of a -word. - -\item[\code{\e \e \e \e}] Matches a literal backslash. - -% In Emacs, the following two are start of buffer/end of buffer. In -% Python they seem to be synonyms for ^$. -\item[\code{\e `}] Like \code{\^}, this only matches at the start of the -string. -\item[\code{\e \e '}] Like \code{\$}, this only matches at the end of -the string. -% end of buffer -\end{itemize} - -\subsection{Module Contents} -\nodename{Contents of Module regex} - -The module defines these functions, and an exception: - - -\begin{funcdesc}{match}{pattern, string} - Return how many characters at the beginning of \var{string} match - the regular expression \var{pattern}. Return \code{-1} if the - string does not match the pattern (this is different from a - zero-length match!). -\end{funcdesc} - -\begin{funcdesc}{search}{pattern, string} - Return the first position in \var{string} that matches the regular - expression \var{pattern}. Return \code{-1} if no position in the string - matches the pattern (this is different from a zero-length match - anywhere!). -\end{funcdesc} - -\begin{funcdesc}{compile}{pattern\optional{, translate}} - Compile a regular expression pattern into a regular expression - object, which can be used for matching using its \code{match()} and - \code{search()} methods, described below. The optional argument - \var{translate}, if present, must be a 256-character string - indicating how characters (both of the pattern and of the strings to - be matched) are translated before comparing them; the \var{i}-th - element of the string gives the translation for the character with - \ASCII{} code \var{i}. This can be used to implement - case-insensitive matching; see the \code{casefold} data item below. - - The sequence - -\begin{verbatim} -prog = regex.compile(pat) -result = prog.match(str) -\end{verbatim} -% -is equivalent to - -\begin{verbatim} -result = regex.match(pat, str) -\end{verbatim} - -but the version using \code{compile()} is more efficient when multiple -regular expressions are used concurrently in a single program. (The -compiled version of the last pattern passed to \code{regex.match()} or -\code{regex.search()} is cached, so programs that use only a single -regular expression at a time needn't worry about compiling regular -expressions.) -\end{funcdesc} - -\begin{funcdesc}{set_syntax}{flags} - Set the syntax to be used by future calls to \code{compile()}, - \code{match()} and \code{search()}. (Already compiled expression - objects are not affected.) The argument is an integer which is the - OR of several flag bits. The return value is the previous value of - the syntax flags. Names for the flags are defined in the standard - module \code{regex_syntax}\refstmodindex{regex_syntax}; read the - file \file{regex_syntax.py} for more information. -\end{funcdesc} - -\begin{funcdesc}{get_syntax}{} - Returns the current value of the syntax flags as an integer. -\end{funcdesc} - -\begin{funcdesc}{symcomp}{pattern\optional{, translate}} -This is like \code{compile()}, but supports symbolic group names: if a -parenthesis-enclosed group begins with a group name in angular -brackets, e.g. \code{'\e([a-z][a-z0-9]*\e)'}, the group can -be referenced by its name in arguments to the \code{group()} method of -the resulting compiled regular expression object, like this: -\code{p.group('id')}. Group names may contain alphanumeric characters -and \code{'_'} only. -\end{funcdesc} - -\begin{excdesc}{error} - Exception raised when a string passed to one of the functions here - is not a valid regular expression (e.g., unmatched parentheses) or - when some other error occurs during compilation or matching. (It is - never an error if a string contains no match for a pattern.) -\end{excdesc} - -\begin{datadesc}{casefold} -A string suitable to pass as the \var{translate} argument to -\code{compile()} to map all upper case characters to their lowercase -equivalents. -\end{datadesc} - -\noindent -Compiled regular expression objects support these methods: - -\setindexsubitem{(regex method)} -\begin{funcdesc}{match}{string\optional{, pos}} - Return how many characters at the beginning of \var{string} match - the compiled regular expression. Return \code{-1} if the string - does not match the pattern (this is different from a zero-length - match!). - - The optional second parameter, \var{pos}, gives an index in the string - where the search is to start; it defaults to \code{0}. This is not - completely equivalent to slicing the string; the \code{'\^'} pattern - character matches at the real beginning of the string and at positions - just after a newline, not necessarily at the index where the search - is to start. -\end{funcdesc} - -\begin{funcdesc}{search}{string\optional{, pos}} - Return the first position in \var{string} that matches the regular - expression \code{pattern}. Return \code{-1} if no position in the - string matches the pattern (this is different from a zero-length - match anywhere!). - - The optional second parameter has the same meaning as for the - \code{match()} method. -\end{funcdesc} - -\begin{funcdesc}{group}{index, index, ...} -This method is only valid when the last call to the \code{match()} -or \code{search()} method found a match. It returns one or more -groups of the match. If there is a single \var{index} argument, -the result is a single string; if there are multiple arguments, the -result is a tuple with one item per argument. If the \var{index} is -zero, the corresponding return value is the entire matching string; if -it is in the inclusive range [1..99], it is the string matching the -corresponding parenthesized group (using the default syntax, -groups are parenthesized using \code{{\e}(} and \code{{\e})}). If no -such group exists, the corresponding result is \code{None}. - -If the regular expression was compiled by \code{symcomp()} instead of -\code{compile()}, the \var{index} arguments may also be strings -identifying groups by their group name. -\end{funcdesc} - -\noindent -Compiled regular expressions support these data attributes: - -\setindexsubitem{(regex attribute)} - -\begin{datadesc}{regs} -When the last call to the \code{match()} or \code{search()} method found a -match, this is a tuple of pairs of indexes corresponding to the -beginning and end of all parenthesized groups in the pattern. Indices -are relative to the string argument passed to \code{match()} or -\code{search()}. The 0-th tuple gives the beginning and end or the -whole pattern. When the last match or search failed, this is -\code{None}. -\end{datadesc} - -\begin{datadesc}{last} -When the last call to the \code{match()} or \code{search()} method found a -match, this is the string argument passed to that method. When the -last match or search failed, this is \code{None}. -\end{datadesc} - -\begin{datadesc}{translate} -This is the value of the \var{translate} argument to -\code{regex.compile()} that created this regular expression object. If -the \var{translate} argument was omitted in the \code{regex.compile()} -call, this is \code{None}. -\end{datadesc} - -\begin{datadesc}{givenpat} -The regular expression pattern as passed to \code{compile()} or -\code{symcomp()}. -\end{datadesc} - -\begin{datadesc}{realpat} -The regular expression after stripping the group names for regular -expressions compiled with \code{symcomp()}. Same as \code{givenpat} -otherwise. -\end{datadesc} - -\begin{datadesc}{groupindex} -A dictionary giving the mapping from symbolic group names to numerical -group indexes for regular expressions compiled with \code{symcomp()}. -\code{None} otherwise. -\end{datadesc} diff --git a/Doc/lib/libregsub.tex b/Doc/lib/libregsub.tex deleted file mode 100644 index b41b700..0000000 --- a/Doc/lib/libregsub.tex +++ /dev/null @@ -1,74 +0,0 @@ -\section{\module{regsub} --- - String operations using regular expressions} - -\declaremodule{standard}{regsub} -\modulesynopsis{Substitution and splitting operations that use - regular expressions. \strong{Obsolete!}} - - -This module defines a number of functions useful for working with -regular expressions (see built-in module \refmodule{regex}). - -Warning: these functions are not thread-safe. - -\strong{Obsolescence note:} -This module is obsolete as of Python version 1.5; it is still being -maintained because much existing code still uses it. All new code in -need of regular expressions should use the new \refmodule{re} module, which -supports the more powerful and regular Perl-style regular expressions. -Existing code should be converted. The standard library module -\module{reconvert} helps in converting \refmodule{regex} style regular -expressions to \refmodule{re} style regular expressions. (For more -conversion help, see Andrew Kuchling's\index{Kuchling, Andrew} -``regex-to-re HOWTO'' at -\url{http://www.python.org/doc/howto/regex-to-re/}.) - - -\begin{funcdesc}{sub}{pat, repl, str} -Replace the first occurrence of pattern \var{pat} in string -\var{str} by replacement \var{repl}. If the pattern isn't found, -the string is returned unchanged. The pattern may be a string or an -already compiled pattern. The replacement may contain references -\samp{\e \var{digit}} to subpatterns and escaped backslashes. -\end{funcdesc} - -\begin{funcdesc}{gsub}{pat, repl, str} -Replace all (non-overlapping) occurrences of pattern \var{pat} in -string \var{str} by replacement \var{repl}. The same rules as for -\code{sub()} apply. Empty matches for the pattern are replaced only -when not adjacent to a previous match, so e.g. -\code{gsub('', '-', 'abc')} returns \code{'-a-b-c-'}. -\end{funcdesc} - -\begin{funcdesc}{split}{str, pat\optional{, maxsplit}} -Split the string \var{str} in fields separated by delimiters matching -the pattern \var{pat}, and return a list containing the fields. Only -non-empty matches for the pattern are considered, so e.g. -\code{split('a:b', ':*')} returns \code{['a', 'b']} and -\code{split('abc', '')} returns \code{['abc']}. The \var{maxsplit} -defaults to 0. If it is nonzero, only \var{maxsplit} number of splits -occur, and the remainder of the string is returned as the final -element of the list. -\end{funcdesc} - -\begin{funcdesc}{splitx}{str, pat\optional{, maxsplit}} -Split the string \var{str} in fields separated by delimiters matching -the pattern \var{pat}, and return a list containing the fields as well -as the separators. For example, \code{splitx('a:::b', ':*')} returns -\code{['a', ':::', 'b']}. Otherwise, this function behaves the same -as \code{split}. -\end{funcdesc} - -\begin{funcdesc}{capwords}{s\optional{, pat}} -Capitalize words separated by optional pattern \var{pat}. The default -pattern uses any characters except letters, digits and underscores as -word delimiters. Capitalization is done by changing the first -character of each word to upper case. -\end{funcdesc} - -\begin{funcdesc}{clear_cache}{} -The regsub module maintains a cache of compiled regular expressions, -keyed on the regular expression string and the syntax of the regex -module at the time the expression was compiled. This function clears -that cache. -\end{funcdesc} diff --git a/Doc/lib/librunpy.tex b/Doc/lib/librunpy.tex new file mode 100644 index 0000000..4be9901 --- /dev/null +++ b/Doc/lib/librunpy.tex @@ -0,0 +1,74 @@ +\section{\module{runpy} --- + Locating and executing Python modules.} + +\declaremodule{standard}{runpy} % standard library, in Python + +\moduleauthor{Nick Coghlan}{ncoghlan@gmail.com} + +\modulesynopsis{Locate and execute Python modules as scripts} + +\versionadded{2.5} + +The \module{runpy} module is used to locate and run Python modules +without importing them first. It's main use is to implement the +\programopt{-m} command line switch that allows scripts to be located +using the Python module namespace rather than the filesystem. + +When executed as a script, the module effectively operates as follows: +\begin{verbatim} + del sys.argv[0] # Remove the runpy module from the arguments + run_module(sys.argv[0], run_name="__main__", alter_sys=True) +\end{verbatim} + +The \module{runpy} module provides a single function: + +\begin{funcdesc}{run_module}{mod_name\optional{, init_globals} +\optional{, run_name}\optional{, alter_sys}} +Execute the code of the specified module and return the resulting +module globals dictionary. The module's code is first located using +the standard import mechanism (refer to PEP 302 for details) and +then executed in a fresh module namespace. + +The optional dictionary argument \var{init_globals} may be used to +pre-populate the globals dictionary before the code is executed. +The supplied dictionary will not be modified. If any of the special +global variables below are defined in the supplied dictionary, those +definitions are overridden by the \code{run_module} function. + +The special global variables \code{__name__}, \code{__file__}, +\code{__loader__} and \code{__builtins__} are set in the globals +dictionary before the module code is executed. + +\code{__name__} is set to \var{run_name} if this optional argument is +supplied, and the \var{mod_name} argument otherwise. + +\code{__loader__} is set to the PEP 302 module loader used to retrieve +the code for the module (This loader may be a wrapper around the +standard import mechanism). + +\code{__file__} is set to the name provided by the module loader. If +the loader does not make filename information available, this +variable is set to \code{None}. + +\code{__builtins__} is automatically initialised with a reference to +the top level namespace of the \module{__builtin__} module. + +If the argument \var{alter_sys} is supplied and evaluates to +\code{True}, then \code{sys.argv[0]} is updated with the value of +\code{__file__} and \code{sys.modules[__name__]} is updated with a +temporary module object for the module being executed. Both +\code{sys.argv[0]} and \code{sys.modules[__name__]} are restored to +their original values before the function returns. + +Note that this manipulation of \module{sys} is not thread-safe. Other +threads may see the partially initialised module, as well as the +altered list of arguments. It is recommended that the \module{sys} +module be left alone when invoking this function from threaded code. +\end{funcdesc} + +\begin{seealso} + +\seepep{338}{Executing modules as scripts}{PEP written and +implemented by Nick Coghlan.} + +\end{seealso} diff --git a/Doc/lib/libsets.tex b/Doc/lib/libsets.tex index dd85ec7..22bf34b 100644 --- a/Doc/lib/libsets.tex +++ b/Doc/lib/libsets.tex @@ -151,12 +151,13 @@ but not found in \class{ImmutableSet}: \lineiii{\var{s}.add(\var{x})}{} {add element \var{x} to set \var{s}} \lineiii{\var{s}.remove(\var{x})}{} - {remove \var{x} from set \var{s}; raises KeyError if not present} + {remove \var{x} from set \var{s}; raises \exception{KeyError} + if not present} \lineiii{\var{s}.discard(\var{x})}{} {removes \var{x} from set \var{s} if present} \lineiii{\var{s}.pop()}{} {remove and return an arbitrary element from \var{s}; raises - KeyError if empty} + \exception{KeyError} if empty} \lineiii{\var{s}.clear()}{} {remove all elements from set \var{s}} \end{tableiii} diff --git a/Doc/lib/libsgmllib.tex b/Doc/lib/libsgmllib.tex index 27bf0b0..1578313 100644 --- a/Doc/lib/libsgmllib.tex +++ b/Doc/lib/libsgmllib.tex @@ -95,12 +95,22 @@ lower case, and the \var{method} argument is the bound method which should be used to support semantic interpretation of the start tag. The \var{attributes} argument is a list of \code{(\var{name}, \var{value})} pairs containing the attributes found inside the tag's -\code{<>} brackets. The \var{name} has been translated to lower case -and double quotes and backslashes in the \var{value} have been interpreted. +\code{<>} brackets. + +The \var{name} has been translated to lower case. +Double quotes and backslashes in the \var{value} have been interpreted, +as well as known character references and known entity references +terminated by a semicolon (normally, entity references can be terminated +by any non-alphanumerical character, but this would break the very +common case of \code{} when \code{eggs} +is a valid entity name). + For instance, for the tag \code{}, this method would be called as \samp{unknown_starttag('a', [('href', 'http://www.cwi.nl/')])}. The base implementation simply calls \var{method} with \var{attributes} as the only argument. +\versionadded[Handling of entity and character references within + attribute values]{2.5} \end{methoddesc} \begin{methoddesc}{handle_endtag}{tag, method} diff --git a/Doc/lib/libshutil.tex b/Doc/lib/libshutil.tex index a217150..449d741 100644 --- a/Doc/lib/libshutil.tex +++ b/Doc/lib/libshutil.tex @@ -73,18 +73,18 @@ file type and creator codes will not be correct. If \var{symlinks} is true, symbolic links in the source tree are represented as symbolic links in the new tree; if false or omitted, the contents of the linked files are copied to - the new tree. If exception(s) occur, an Error is raised + the new tree. If exception(s) occur, an \exception{Error} is raised with a list of reasons. The source code for this should be considered an example rather than a tool. - \versionchanged[Error is raised if any exceptions occur during copying, - rather than printing a message]{2.3} + \versionchanged[\exception{Error} is raised if any exceptions occur during + copying, rather than printing a message]{2.3} \versionchanged[Create intermediate directories needed to create \var{dst}, - rather than raising an error. Copy permissions and times of directories using - \function{copystat()}]{2.5} + rather than raising an error. Copy permissions and times of + directories using \function{copystat()}]{2.5} \end{funcdesc} diff --git a/Doc/lib/libsignal.tex b/Doc/lib/libsignal.tex index f168b6d..cfdb4dd 100644 --- a/Doc/lib/libsignal.tex +++ b/Doc/lib/libsignal.tex @@ -100,7 +100,7 @@ The \module{signal} module defines the following functions: Any previously scheduled alarm is canceled (only one alarm can be scheduled at any time). The returned value is then the number of seconds before any previously set alarm was to have been delivered. - If \var{time} is zero, no alarm id scheduled, and any scheduled + If \var{time} is zero, no alarm is scheduled, and any scheduled alarm is canceled. The return value is the number of seconds remaining before a previously scheduled alarm. If the return value is zero, no alarm is currently scheduled. (See the \UNIX{} man page diff --git a/Doc/lib/libsocket.tex b/Doc/lib/libsocket.tex index cc7bd75..c7b656d 100644 --- a/Doc/lib/libsocket.tex +++ b/Doc/lib/libsocket.tex @@ -317,10 +317,11 @@ Availability: \UNIX. \versionadded{2.4} \end{funcdesc} \begin{funcdesc}{fromfd}{fd, family, type\optional{, proto}} -Build a socket object from an existing file descriptor (an integer as -returned by a file object's \method{fileno()} method). Address family, -socket type and protocol number are as for the \function{socket()} function -above. The file descriptor should refer to a socket, but this is not +Duplicate the file descriptor \var{fd} (an integer as returned by a file +object's \method{fileno()} method) and build a socket object from the +result. Address family, socket type and protocol number are as for the +\function{socket()} function above. +The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on a socket passed to a program as @@ -626,7 +627,7 @@ timeouts on socket operations. \end{methoddesc} \begin{methoddesc}[socket]{gettimeout}{} -Returns the timeout in floating seconds associated with socket +Return the timeout in floating seconds associated with socket operations, or \code{None} if no timeout is set. This reflects the last call to \method{setblocking()} or \method{settimeout()}. \versionadded{2.3} @@ -677,6 +678,25 @@ use \method{recv()} and \method{send()} without \var{flags} argument instead. +Socket objects also have these (read-only) attributes that correspond +to the values given to the \class{socket} constructor. + +\begin{memberdesc}[socket]{family} +The socket family. +\versionadded{2.5} +\end{memberdesc} + +\begin{memberdesc}[socket]{type} +The socket type. +\versionadded{2.5} +\end{memberdesc} + +\begin{memberdesc}[socket]{proto} +The socket protocol. +\versionadded{2.5} +\end{memberdesc} + + \subsection{SSL Objects \label{ssl-objects}} SSL objects have the following methods. diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index d2a0425..8d011fd 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -185,10 +185,12 @@ There are four distinct numeric types: \dfn{plain integers}, In addition, Booleans are a subtype of plain integers. Plain integers (also just called \dfn{integers}) are implemented using \ctype{long} in C, which gives them at least 32 -bits of precision. Long integers have unlimited precision. Floating -point numbers are implemented using \ctype{double} in C. All bets on -their precision are off unless you happen to know the machine you are -working with. +bits of precision (\code{sys.maxint} is always set to the maximum +plain integer value for the current platform, the minimum value is +\code{-sys.maxint - 1}). Long integers have unlimited precision. +Floating point numbers are implemented using \ctype{double} in C. +All bets on their precision are off unless you happen to know the +machine you are working with. \obindex{numeric} \obindex{Boolean} \obindex{integer} @@ -249,6 +251,7 @@ comparison operations): \hline \lineiii{\var{x} * \var{y}}{product of \var{x} and \var{y}}{} \lineiii{\var{x} / \var{y}}{quotient of \var{x} and \var{y}}{(1)} + \lineiii{\var{x} // \var{y}}{(floored) quotient of \var{x} and \var{y}}{(5)} \lineiii{\var{x} \%{} \var{y}}{remainder of \code{\var{x} / \var{y}}}{(4)} \hline \lineiii{-\var{x}}{\var{x} negated}{} @@ -299,6 +302,9 @@ Complex floor division operator, modulo operator, and \function{divmod()}. \deprecated{2.3}{Instead convert to float using \function{abs()} if appropriate.} +\item[(5)] +Also referred to as integer division. The resultant value is a whole integer, +though the result's type is not necessarily int. \end{description} % XXXJH exceptions: overflow (when? what operations?) zerodivision @@ -1278,7 +1284,8 @@ that do not apply to immutable instances of \class{frozenset}: \lineiii{\var{s}.add(\var{x})}{} {add element \var{x} to set \var{s}} \lineiii{\var{s}.remove(\var{x})}{} - {remove \var{x} from set \var{s}; raises KeyError if not present} + {remove \var{x} from set \var{s}; raises \exception{KeyError} + if not present} \lineiii{\var{s}.discard(\var{x})}{} {removes \var{x} from set \var{s} if present} \lineiii{\var{s}.pop()}{} @@ -1495,6 +1502,38 @@ Files have the following methods: Any operation which requires that the file be open will raise a \exception{ValueError} after the file has been closed. Calling \method{close()} more than once is allowed. + + As of Python 2.5, you can avoid having to call this method explicitly + if you use the \keyword{with} statement. For example, the following + code will automatically close \code{f} when the \keyword{with} block + is exited: + +\begin{verbatim} +from __future__ import with_statement + +with open("hello.txt") as f: + for line in f: + print line +\end{verbatim} + + In older versions of Python, you would have needed to do this to get + the same effect: + +\begin{verbatim} +f = open("hello.txt") +try: + for line in f: + print line +finally: + f.close() +\end{verbatim} + + \note{Not all ``file-like'' types in Python support use as a context + manager for the \keyword{with} statement. If your code is intended to + work with any file-like object, you can use the \function{closing()} + function in the \module{contextlib} module instead of using the object + directly. See section~\ref{context-closing} for details.} + \end{methoddesc} \begin{methoddesc}[file]{flush}{} @@ -1783,14 +1822,14 @@ class, respectively. When a method is unbound, its \code{im_self} attribute will be \code{None} and if called, an explicit \code{self} object must be passed as the first argument. In this case, \code{self} must be an instance of the unbound method's class (or a -subclass of that class), otherwise a \code{TypeError} is raised. +subclass of that class), otherwise a \exception{TypeError} is raised. Like function objects, methods objects support getting arbitrary attributes. However, since method attributes are actually stored on the underlying function object (\code{meth.im_func}), setting method attributes on either bound or unbound methods is disallowed. Attempting to set a method attribute results in a -\code{TypeError} being raised. In order to set a method attribute, +\exception{TypeError} being raised. In order to set a method attribute, you need to explicitly set it on the underlying function object: \begin{verbatim} diff --git a/Doc/lib/libsubprocess.tex b/Doc/lib/libsubprocess.tex index f48b29b..4417797 100644 --- a/Doc/lib/libsubprocess.tex +++ b/Doc/lib/libsubprocess.tex @@ -135,8 +135,8 @@ The arguments are the same as for the Popen constructor. Example: \begin{funcdesc}{check_call}{*popenargs, **kwargs} Run command with arguments. Wait for command to complete. If the exit -code was zero then return, otherwise raise CalledProcessError. The -CalledProcessError object will have the return code in the +code was zero then return, otherwise raise \exception{CalledProcessError.} +The \exception{CalledProcessError} object will have the return code in the \member{errno} attribute. The arguments are the same as for the Popen constructor. Example: diff --git a/Doc/lib/libthread.tex b/Doc/lib/libthread.tex index 4914948d..9e0c202 100644 --- a/Doc/lib/libthread.tex +++ b/Doc/lib/libthread.tex @@ -100,6 +100,19 @@ Return the status of the lock:\ \code{True} if it has been acquired by some thread, \code{False} if not. \end{methoddesc} +In addition to these methods, lock objects can also be used via the +\keyword{with} statement, e.g.: + +\begin{verbatim} +from __future__ import with_statement +import thread + +a_lock = thread.allocate_lock() + +with a_lock: + print "a_lock is locked while this executes" +\end{verbatim} + \strong{Caveats:} \begin{itemize} diff --git a/Doc/lib/libthreading.tex b/Doc/lib/libthreading.tex index 33839a4..8fb3137 100644 --- a/Doc/lib/libthreading.tex +++ b/Doc/lib/libthreading.tex @@ -675,3 +675,26 @@ keyword arguments \var{kwargs}, after \var{interval} seconds have passed. Stop the timer, and cancel the execution of the timer's action. This will only work if the timer is still in its waiting stage. \end{methoddesc} + +\subsection{Using locks, conditions, and semaphores in the \keyword{with} +statement \label{with-locks}} + +All of the objects provided by this module that have \method{acquire()} and +\method{release()} methods can be used as context managers for a \keyword{with} +statement. The \method{acquire()} method will be called when the block is +entered, and \method{release()} will be called when the block is exited. + +Currently, \class{Lock}, \class{RLock}, \class{Condition}, \class{Semaphore}, +and \class{BoundedSemaphore} objects may be used as \keyword{with} +statement context managers. For example: + +\begin{verbatim} +from __future__ import with_statement +import threading + +some_rlock = threading.RLock() + +with some_rlock: + print "some_rlock is locked while this executes" +\end{verbatim} + diff --git a/Doc/lib/libundoc.tex b/Doc/lib/libundoc.tex index 6cef183..df78152 100644 --- a/Doc/lib/libundoc.tex +++ b/Doc/lib/libundoc.tex @@ -42,15 +42,15 @@ document these. \begin{description} \item[\module{ntpath}] --- Implementation of \module{os.path} on Win32, Win64, WinCE, and -OS/2 platforms. + OS/2 platforms. \item[\module{posixpath}] --- Implementation of \module{os.path} on \POSIX. \item[\module{bsddb185}] --- Backwards compatibility module for systems which still use the Berkeley -DB 1.85 module. It is normally only available on certain BSD Unix-based -systems. It should never be used directly. + DB 1.85 module. It is normally only available on certain BSD Unix-based + systems. It should never be used directly. \end{description} @@ -62,14 +62,14 @@ systems. It should never be used directly. \item[\module{linuxaudiodev}] --- Play audio data on the Linux audio device. Replaced in Python 2.3 -by the \module{ossaudiodev} module. + by the \module{ossaudiodev} module. \item[\module{sunaudio}] --- Interpret Sun audio headers (may become obsolete or a tool/demo). \item[\module{toaiff}] --- Convert "arbitrary" sound files to AIFF files; should probably -become a tool or demo. Requires the external program \program{sox}. + become a tool or demo. Requires the external program \program{sox}. \end{description} @@ -78,12 +78,13 @@ become a tool or demo. Requires the external program \program{sox}. These modules are not normally available for import; additional work must be done to make them available. -Those which are written in Python will be installed into the directory -\file{lib-old/} installed as part of the standard library. To use -these, the directory must be added to \code{sys.path}, possibly using -\envvar{PYTHONPATH}. +%%% lib-old is empty as of Python 2.5 +% Those which are written in Python will be installed into the directory +% \file{lib-old/} installed as part of the standard library. To use +% these, the directory must be added to \code{sys.path}, possibly using +% \envvar{PYTHONPATH}. -Obsolete extension modules written in C are not built by default. +These extension modules written in C are not built by default. Under \UNIX, these must be enabled by uncommenting the appropriate lines in \file{Modules/Setup} in the build tree and either rebuilding Python if the modules are statically linked, or building and @@ -92,122 +93,11 @@ installing the shared object if using dynamically-loaded extensions. % XXX need Windows instructions! \begin{description} -\item[\module{addpack}] ---- Alternate approach to packages. Use the built-in package support -instead. - -\item[\module{cmp}] ---- File comparison function. Use the newer \refmodule{filecmp} instead. - -\item[\module{cmpcache}] ---- Caching version of the obsolete \module{cmp} module. Use the -newer \refmodule{filecmp} instead. - -\item[\module{codehack}] ---- Extract function name or line number from a function -code object (these are now accessible as attributes: -\member{co.co_name}, \member{func.func_name}, -\member{co.co_firstlineno}). - -\item[\module{dircmp}] ---- Class to build directory diff tools on (may become a demo or tool). -\deprecated{2.0}{The \refmodule{filecmp} module replaces -\module{dircmp}.} - -\item[\module{dump}] ---- Print python code that reconstructs a variable. - -\item[\module{fmt}] ---- Text formatting abstractions (too slow). - -\item[\module{lockfile}] ---- Wrapper around FCNTL file locking (use -\function{fcntl.lockf()}/\function{flock()} instead; see \refmodule{fcntl}). - -\item[\module{newdir}] ---- New \function{dir()} function (the standard \function{dir()} is -now just as good). - -\item[\module{Para}] ---- Helper for \module{fmt}. - -\item[\module{poly}] ---- Polynomials. - -\item[\module{rand}] ---- Old interface to the random number generator. - -\item[\module{regex}] ---- Emacs-style regular expression support; may still be used in some -old code (extension module). Refer to the -\citetitle[http://www.python.org/doc/1.6/lib/module-regex.html]{Python -1.6 Documentation} for documentation. - -\item[\module{regsub}] ---- Regular expression based string replacement utilities, for use -with \module{regex} (extension module). Refer to the -\citetitle[http://www.python.org/doc/1.6/lib/module-regsub.html]{Python -1.6 Documentation} for documentation. - -\item[\module{statcache}] ---- Caches the results of os.stat(). Using the cache can be fragile -and error-prone, just use \code{os.stat()} directly. - -\item[\module{tb}] ---- Print tracebacks, with a dump of local variables (use -\function{pdb.pm()} or \refmodule{traceback} instead). - \item[\module{timing}] ---- Measure time intervals to high resolution (use -\function{time.clock()} instead). (This is an extension module.) - -\item[\module{tzparse}] ---- Parse a timezone specification (unfinished; may disappear in the -future, and does not work when the \envvar{TZ} environment variable is -not set). - -\item[\module{util}] ---- Useful functions that don't fit elsewhere. - -\item[\module{whatsound}] ---- Recognize sound files; use \refmodule{sndhdr} instead. - -\item[\module{whrandom}] ---- Old random number generator. Use \module{random} instead. - -\item[\module{zmod}] ---- Compute properties of mathematical ``fields.'' +--- Measure time intervals to high resolution (use \function{time.clock()} + instead). \end{description} - -The following modules are obsolete, but are likely to re-surface as -tools or scripts: - -\begin{description} -\item[\module{find}] ---- Find files matching pattern in directory tree. - -\item[\module{grep}] ---- \program{grep} implementation in Python. - -\item[\module{packmail}] ---- Create a self-unpacking \UNIX{} shell archive. -\end{description} - - -The following modules were documented in previous versions of this -manual, but are now considered obsolete. The source for the -documentation is still available as part of the documentation source -archive. - -\begin{description} -\item[\module{ni}] ---- Import modules in ``packages.'' Basic package support is now -built in. The built-in support is very similar to what is provided in -this module. -\end{description} - - \section{SGI-specific Extension modules} The following are SGI specific, and may be out of touch with the @@ -219,5 +109,5 @@ current version of reality. \item[\module{sv}] --- Interface to the ``simple video'' board on SGI Indigo -(obsolete hardware). + (obsolete hardware). \end{description} diff --git a/Doc/lib/liburllib2.tex b/Doc/lib/liburllib2.tex index 706c54b..e0c4568 100644 --- a/Doc/lib/liburllib2.tex +++ b/Doc/lib/liburllib2.tex @@ -384,7 +384,7 @@ determined by sorting the handler instances. \method{\var{protocol}_open()} are called to handle the request. This stage ends when a handler either returns a non-\constant{None} value (ie. a response), or raises an exception - (usually URLError). Exceptions are allowed to propagate. + (usually \exception{URLError}). Exceptions are allowed to propagate. In fact, the above algorithm is first tried for methods named \method{default_open}. If all such methods return diff --git a/Doc/lib/liburlparse.tex b/Doc/lib/liburlparse.tex index 8f80d6b..f18efe9 100644 --- a/Doc/lib/liburlparse.tex +++ b/Doc/lib/liburlparse.tex @@ -23,50 +23,76 @@ draft!). It supports the following URL schemes: \code{file}, \code{ftp}, \code{gopher}, \code{hdl}, \code{http}, \code{https}, \code{imap}, \code{mailto}, \code{mms}, \code{news}, \code{nntp}, \code{prospero}, \code{rsync}, \code{rtsp}, \code{rtspu}, -\code{sftp}, \code{shttp}, \code{sip}, \code{snews}, \code{svn}, +\code{sftp}, \code{shttp}, \code{sip}, \code{sips}, \code{snews}, \code{svn}, \code{svn+ssh}, \code{telnet}, \code{wais}. -\versionadded[Support for the \code{sftp} scheme]{2.5} + +\versionadded[Support for the \code{sftp} and \code{sips} schemes]{2.5} The \module{urlparse} module defines the following functions: -\begin{funcdesc}{urlparse}{urlstring\optional{, default_scheme\optional{, allow_fragments}}} -Parse a URL into 6 components, returning a 6-tuple: (addressing -scheme, network location, path, parameters, query, fragment -identifier). This corresponds to the general structure of a URL: +\begin{funcdesc}{urlparse}{urlstring\optional{, + default_scheme\optional{, allow_fragments}}} +Parse a URL into six components, returning a 6-tuple. This +corresponds to the general structure of a URL: \code{\var{scheme}://\var{netloc}/\var{path};\var{parameters}?\var{query}\#\var{fragment}}. Each tuple item is a string, possibly empty. -The components are not broken up in smaller parts (e.g. the network +The components are not broken up in smaller parts (for example, the network location is a single string), and \% escapes are not expanded. -The delimiters as shown above are not part of the tuple items, +The delimiters as shown above are not part of the result, except for a leading slash in the \var{path} component, which is -retained if present. - -Example: - -\begin{verbatim} -urlparse('http://www.cwi.nl:80/%7Eguido/Python.html') -\end{verbatim} - -yields the tuple +retained if present. For example: \begin{verbatim} +>>> from urlparse import urlparse +>>> o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html') +>>> o ('http', 'www.cwi.nl:80', '/%7Eguido/Python.html', '', '', '') +>>> o.scheme +'http' +>>> o.port +80 +>>> o.geturl() +'http://www.cwi.nl:80/%7Eguido/Python.html' \end{verbatim} If the \var{default_scheme} argument is specified, it gives the -default addressing scheme, to be used only if the URL string does not +default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. -If the \var{allow_fragments} argument is zero, fragment identifiers +If the \var{allow_fragments} argument is false, fragment identifiers are not allowed, even if the URL's addressing scheme normally does -support them. The default value for this argument is \code{1}. +support them. The default value for this argument is \constant{True}. + +The return value is actually an instance of a subclass of +\pytype{tuple}. This class has the following additional read-only +convenience attributes: + +\begin{tableiv}{l|c|l|c}{member}{Attribute}{Index}{Value}{Value if not present} + \lineiv{scheme} {0} {URL scheme specifier} {empty string} + \lineiv{netloc} {1} {Network location part} {empty string} + \lineiv{path} {2} {Hierarchical path} {empty string} + \lineiv{params} {3} {Parameters for last path element} {empty string} + \lineiv{query} {4} {Query component} {empty string} + \lineiv{fragment}{5} {Fragment identifier} {empty string} + \lineiv{username}{ } {User name} {\constant{None}} + \lineiv{password}{ } {Password} {\constant{None}} + \lineiv{hostname}{ } {Host name (lower case)} {\constant{None}} + \lineiv{port} { } {Port number as integer, if present} {\constant{None}} +\end{tableiv} + +See section~\ref{urlparse-result-object}, ``Results of +\function{urlparse()} and \function{urlsplit()},'' for more +information on the result object. + +\versionchanged[Added attributes to return value]{2.5} \end{funcdesc} -\begin{funcdesc}{urlunparse}{tuple} -Construct a URL string from a tuple as returned by \code{urlparse()}. +\begin{funcdesc}{urlunparse}{parts} +Construct a URL from a tuple as returned by \code{urlparse()}. +The \var{parts} argument be any six-item iterable. This may result in a slightly different, but equivalent URL, if the -URL that was parsed originally had redundant delimiters, e.g. a ? with -an empty query (the draft states that these are equivalent). +URL that was parsed originally had unnecessary delimiters (for example, +a ? with an empty query; the RFC states that these are equivalent). \end{funcdesc} \begin{funcdesc}{urlsplit}{urlstring\optional{, @@ -79,12 +105,38 @@ the URL (see \rfc{2396}) is wanted. A separate function is needed to separate the path segments and parameters. This function returns a 5-tuple: (addressing scheme, network location, path, query, fragment identifier). + +The return value is actually an instance of a subclass of +\pytype{tuple}. This class has the following additional read-only +convenience attributes: + +\begin{tableiv}{l|c|l|c}{member}{Attribute}{Index}{Value}{Value if not present} + \lineiv{scheme} {0} {URL scheme specifier} {empty string} + \lineiv{netloc} {1} {Network location part} {empty string} + \lineiv{path} {2} {Hierarchical path} {empty string} + \lineiv{query} {3} {Query component} {empty string} + \lineiv{fragment} {4} {Fragment identifier} {empty string} + \lineiv{username} { } {User name} {\constant{None}} + \lineiv{password} { } {Password} {\constant{None}} + \lineiv{hostname} { } {Host name (lower case)} {\constant{None}} + \lineiv{port} { } {Port number as integer, if present} {\constant{None}} +\end{tableiv} + +See section~\ref{urlparse-result-object}, ``Results of +\function{urlparse()} and \function{urlsplit()},'' for more +information on the result object. + \versionadded{2.2} +\versionchanged[Added attributes to return value]{2.5} \end{funcdesc} -\begin{funcdesc}{urlunsplit}{tuple} +\begin{funcdesc}{urlunsplit}{parts} Combine the elements of a tuple as returned by \function{urlsplit()} into a complete URL as a string. +The \var{parts} argument be any five-item iterable. +This may result in a slightly different, but equivalent URL, if the +URL that was parsed originally had unnecessary delimiters (for example, +a ? with an empty query; the RFC states that these are equivalent). \versionadded{2.2} \end{funcdesc} @@ -93,22 +145,16 @@ Construct a full (``absolute'') URL by combining a ``base URL'' (\var{base}) with a ``relative URL'' (\var{url}). Informally, this uses components of the base URL, in particular the addressing scheme, the network location and (part of) the path, to provide missing -components in the relative URL. - -Example: - -\begin{verbatim} -urljoin('http://www.cwi.nl/%7Eguido/Python.html', 'FAQ.html') -\end{verbatim} - -yields the string +components in the relative URL. For example: \begin{verbatim} +>>> from urlparse import urljoin +>>> urljoin('http://www.cwi.nl/%7Eguido/Python.html', 'FAQ.html') 'http://www.cwi.nl/%7Eguido/FAQ.html' \end{verbatim} -The \var{allow_fragments} argument has the same meaning as for -\code{urlparse()}. +The \var{allow_fragments} argument has the same meaning and default as +for \function{urlparse()}. \end{funcdesc} \begin{funcdesc}{urldefrag}{url} @@ -133,3 +179,61 @@ in \var{url}, returns \var{url} unmodified and an empty string. both Uniform Resource Names (URNs) and Uniform Resource Locators (URLs).} \end{seealso} + + +\subsection{Results of \function{urlparse()} and \function{urlsplit()} + \label{urlparse-result-object}} + +The result objects from the \function{urlparse()} and +\function{urlsplit()} functions are subclasses of the \pytype{tuple} +type. These subclasses add the attributes described in those +functions, as well as provide an additional method: + +\begin{methoddesc}[ParseResult]{geturl}{} + Return the re-combined version of the original URL as a string. + This may differ from the original URL in that the scheme will always + be normalized to lower case and empty components may be dropped. + Specifically, empty parameters, queries, and fragment identifiers + will be removed. + + The result of this method is a fixpoint if passed back through the + original parsing function: + +\begin{verbatim} +>>> import urlparse +>>> url = 'HTTP://www.Python.org/doc/#' + +>>> r1 = urlparse.urlsplit(url) +>>> r1.geturl() +'http://www.Python.org/doc/' + +>>> r2 = urlparse.urlsplit(r1.geturl()) +>>> r2.geturl() +'http://www.Python.org/doc/' +\end{verbatim} + +\versionadded{2.5} +\end{methoddesc} + +The following classes provide the implementations of the parse results:: + +\begin{classdesc*}{BaseResult} + Base class for the concrete result classes. This provides most of + the attribute definitions. It does not provide a \method{geturl()} + method. It is derived from \class{tuple}, but does not override the + \method{__init__()} or \method{__new__()} methods. +\end{classdesc*} + + +\begin{classdesc}{ParseResult}{scheme, netloc, path, params, query, fragment} + Concrete class for \function{urlparse()} results. The + \method{__new__()} method is overridden to support checking that the + right number of arguments are passed. +\end{classdesc} + + +\begin{classdesc}{SplitResult}{scheme, netloc, path, query, fragment} + Concrete class for \function{urlsplit()} results. The + \method{__new__()} method is overridden to support checking that the + right number of arguments are passed. +\end{classdesc} diff --git a/Doc/lib/libwarnings.tex b/Doc/lib/libwarnings.tex index 8655451..7b829a0 100644 --- a/Doc/lib/libwarnings.tex +++ b/Doc/lib/libwarnings.tex @@ -169,7 +169,8 @@ the latter would defeat the purpose of the warning message). \end{funcdesc} \begin{funcdesc}{warn_explicit}{message, category, filename, - lineno\optional{, module\optional{, registry}}} + lineno\optional{, module\optional{, registry\optional{, + module_globals}}}} This is a low-level interface to the functionality of \function{warn()}, passing in explicitly the message, category, filename and line number, and optionally the module name and the @@ -179,6 +180,11 @@ stripped; if no registry is passed, the warning is never suppressed. \var{message} must be a string and \var{category} a subclass of \exception{Warning} or \var{message} may be a \exception{Warning} instance, in which case \var{category} will be ignored. + +\var{module_globals}, if supplied, should be the global namespace in use +by the code for which the warning is issued. (This argument is used to +support displaying source for modules found in zipfiles or other +non-filesystem import sources, and was added in Python 2.5.) \end{funcdesc} \begin{funcdesc}{showwarning}{message, category, filename, diff --git a/Doc/lib/libxmlrpclib.tex b/Doc/lib/libxmlrpclib.tex index 0fb88c5..1c36f99 100644 --- a/Doc/lib/libxmlrpclib.tex +++ b/Doc/lib/libxmlrpclib.tex @@ -203,7 +203,7 @@ It also supports certain of Python's built-in operators through \subsection{Binary Objects \label{binary-objects}} -This class may initialized from string data (which may include NULs). +This class may be initialized from string data (which may include NULs). The primary access to the content of a \class{Binary} object is provided by an attribute: @@ -303,10 +303,6 @@ Convert any Python value to one of the XML-RPC Boolean constants, \code{True} or \code{False}. \end{funcdesc} -\begin{funcdesc}{binary}{data} -Trivially convert any Python string to a \class{Binary} object. -\end{funcdesc} - \begin{funcdesc}{dumps}{params\optional{, methodname\optional{, methodresponse\optional{, encoding\optional{, allow_none}}}}} diff --git a/Doc/lib/libzipfile.tex b/Doc/lib/libzipfile.tex index 32ca3e0..4e06ef6 100644 --- a/Doc/lib/libzipfile.tex +++ b/Doc/lib/libzipfile.tex @@ -141,10 +141,17 @@ cat myzip.zip >> python.exe Write the file named \var{filename} to the archive, giving it the archive name \var{arcname} (by default, this will be the same as \var{filename}, but without a drive letter and with leading path - separators removed). If given, \var{compress_type} overrides the value - given for the \var{compression} parameter to the constructor for - the new entry. The archive must be open with mode \code{'w'} or - \code{'a'}. + separators removed). If given, \var{compress_type} overrides the + value given for the \var{compression} parameter to the constructor + for the new entry. The archive must be open with mode \code{'w'} + or \code{'a'}. + + \note{There is no official file name encoding for ZIP files. + If you have unicode file names, please convert them to byte strings + in your desired encoding before passing them to \method{write()}. + WinZip interprets all file names as encoded in CP437, also known + as DOS Latin.} + \note{Archive names should be relative to the archive root, that is, they should not start with a path separator.} \end{methoddesc} diff --git a/Doc/lib/libzipimport.tex b/Doc/lib/libzipimport.tex index 0a60b29..770ea21 100644 --- a/Doc/lib/libzipimport.tex +++ b/Doc/lib/libzipimport.tex @@ -69,8 +69,8 @@ The available attributes of this module are: \begin{classdesc}{zipimporter}{archivepath} Create a new zipimporter instance. \var{archivepath} must be a path to - a zipfile. \class{ZipImportError} is raised if \var{archivepath} doesn't - point to a valid ZIP archive. + a zipfile. \exception{ZipImportError} is raised if \var{archivepath} + doesn't point to a valid ZIP archive. \end{classdesc} \begin{methoddesc}{find_module}{fullname\optional{, path}} @@ -83,7 +83,7 @@ The available attributes of this module are: \begin{methoddesc}{get_code}{fullname} Return the code object for the specified module. Raise - \class{ZipImportError} if the module couldn't be found. + \exception{ZipImportError} if the module couldn't be found. \end{methoddesc} \begin{methoddesc}{get_data}{pathname} @@ -93,20 +93,20 @@ The available attributes of this module are: \begin{methoddesc}{get_source}{fullname} Return the source code for the specified module. Raise - \class{ZipImportError} if the module couldn't be found, return + \exception{ZipImportError} if the module couldn't be found, return \constant{None} if the archive does contain the module, but has no source for it. \end{methoddesc} \begin{methoddesc}{is_package}{fullname} Return True if the module specified by \var{fullname} is a package. - Raise \class{ZipImportError} if the module couldn't be found. + Raise \exception{ZipImportError} if the module couldn't be found. \end{methoddesc} \begin{methoddesc}{load_module}{fullname} Load the module specified by \var{fullname}. \var{fullname} must be the fully qualified (dotted) module name. It returns the imported - module, or raises \class{ZipImportError} if it wasn't found. + module, or raises \exception{ZipImportError} if it wasn't found. \end{methoddesc} \subsection{Examples} diff --git a/Doc/lib/libzlib.tex b/Doc/lib/libzlib.tex index 2df8b85..dfbb43d 100644 --- a/Doc/lib/libzlib.tex +++ b/Doc/lib/libzlib.tex @@ -166,11 +166,14 @@ continue. If \var{max_length} is not supplied then the whole input is decompressed, and \member{unconsumed_tail} is an empty string. \end{methoddesc} -\begin{methoddesc}[Decompress]{flush}{} +\begin{methoddesc}[Decompress]{flush}{\optional{length}} All pending input is processed, and a string containing the remaining uncompressed output is returned. After calling \method{flush()}, the \method{decompress()} method cannot be called again; the only realistic action is to delete the object. + +The optional parameter \var{length} sets the initial size of the +output buffer. \end{methoddesc} \begin{seealso} diff --git a/Doc/lib/mimelib.tex b/Doc/lib/mimelib.tex index 67de597..491d844 100644 --- a/Doc/lib/mimelib.tex +++ b/Doc/lib/mimelib.tex @@ -12,9 +12,9 @@ \authoraddress{\email{barry@python.org}} \date{\today} -\release{3.0} % software release, not documentation +\release{4.0} % software release, not documentation \setreleaseinfo{} % empty for final release -\setshortversion{3.0} % major.minor only for software +\setshortversion{4.0} % major.minor only for software \begin{document} @@ -38,11 +38,11 @@ The \module{email} package provides classes and utilities to create, parse, generate, and modify email messages, conforming to all the relevant email and MIME related RFCs. -This document describes version 3.0 of the \module{email} package, which is -distributed with Python 2.4 and is available as a standalone distutils-based -package for use with Python 2.3. \module{email} 3.0 is not compatible with -Python versions earlier than 2.3. For more information about the -\module{email} package, including download links and mailing lists, see +This document describes version 4.0 of the \module{email} package, which is +distributed with Python 2.5 and is available as a standalone distutils-based +package for use with earlier Python versions. \module{email} 4.0 is not +compatible with Python versions earlier than 2.3. For more information about +the \module{email} package, including download links and mailing lists, see \ulink{Python's email SIG}{http://www.python.org/sigs/email-sig}. The documentation that follows was written for the Python project, so @@ -51,7 +51,8 @@ package documentation, there are a few notes to be aware of: \begin{itemize} \item Deprecation and ``version added'' notes are relative to the - Python version a feature was added or deprecated. + Python version a feature was added or deprecated. See + the package history in section \ref{email-pkg-history} for details. \item If you're reading this documentation as part of the standalone \module{email} package, some of the internal links to diff --git a/Doc/lib/xmldomminidom.tex b/Doc/lib/xmldomminidom.tex index f7657eb..093915f 100644 --- a/Doc/lib/xmldomminidom.tex +++ b/Doc/lib/xmldomminidom.tex @@ -165,7 +165,7 @@ XML. With an explicit \var{encoding} argument, the result is a byte string in the specified encoding. It is recommended that this argument is -always specified. To avoid UnicodeError exceptions in case of +always specified. To avoid \exception{UnicodeError} exceptions in case of unrepresentable text data, the encoding argument should be specified as "utf-8". diff --git a/Doc/python-docs.txt b/Doc/python-docs.txt index 017fece..bf475b6 100644 --- a/Doc/python-docs.txt +++ b/Doc/python-docs.txt @@ -180,4 +180,4 @@ Answers to the Questions whether it's safe to remove, see the "Why is Python Installed on my Computer?" FAQ, found at: - http://www.python.org/doc/faq/installed.html + http://www.python.org/doc/faq/installed/ diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index 737b861..964013f 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -1035,7 +1035,7 @@ by the built-in \function{classmethod()} constructor. %========================================================================= \section{New-style and classic classes} -Classes and instances come in two flavours: old-style or classic, and new-style. +Classes and instances come in two flavors: old-style or classic, and new-style. Up to Python 2.1, old-style classes were the only flavour available to the user. The concept of (old-style) class is unrelated to the concept of type: if @@ -1065,10 +1065,14 @@ the way special methods are invoked. Others are "fixes" that could not be implemented before for compatibility concerns, like the method resolution order in case of multiple inheritance. -This manuel is not up-to-date with respect to new-style classes. For now, +This manual is not up-to-date with respect to new-style classes. For now, please see \url{http://www.python.org/doc/newstyle.html} for more information. -The plan is to eventually drop old-style classes, leaving only the semantics of new-style classes. This change will probably only be feasible in Python 3.0. +The plan is to eventually drop old-style classes, leaving only the semantics of +new-style classes. This change will probably only be feasible in Python 3.0. +\index{class}{new-style} +\index{class}{classic} +\index{class}{old-style} %========================================================================= \section{Special method names\label{specialnames}} @@ -2053,14 +2057,15 @@ exception is raised. But see the following exception: \item Exception to the previous item: if the left operand is an instance of -a built-in type or a new-style class, and the right operand is an -instance of a proper subclass of that type or class, the right -operand's \method{__rop__()} method is tried \emph{before} the left -operand's \method{__op__()} method. This is done so that a subclass can -completely override binary operators. Otherwise, the left operand's -__op__ method would always accept the right operand: when an instance -of a given class is expected, an instance of a subclass of that class -is always acceptable. +a built-in type or a new-style class, and the right operand is an instance +of a proper subclass of that type or class and overrides the base's +\method{__rop__()} method, the right operand's \method{__rop__()} method +is tried \emph{before} the left operand's \method{__op__()} method. + +This is done so that a subclass can completely override binary operators. +Otherwise, the left operand's \method{__op__()} method would always +accept the right operand: when an instance of a given class is expected, +an instance of a subclass of that class is always acceptable. \item @@ -2106,3 +2111,63 @@ implement a \method{__coerce__()} method, for use by the built-in \function{coerce()} function. \end{itemize} + +\subsection{Context Managers and Contexts\label{context-managers}} + +\versionadded{2.5} + +A \dfn{context manager} is an object that manages the entry to, and exit +from, a \dfn{context} surrounding a block of code. Context managers are +normally invoked using the \keyword{with} statement (described in +section~\ref{with}), but can also be used by directly invoking their +methods. +\stindex{with} +\index{context manager} +\index{context} + +Typical uses of context managers include saving and restoring various +kinds of global state, locking and unlocking resources, closing opened +files, etc. + +\begin{methoddesc}[context manager]{__context__}{self} +Invoked when the object is used as the context expression of a +\keyword{with} statement. The return value must implement +\method{__enter__()} and \method{__exit__()} methods. Simple context +managers that wish to directly +implement \method{__enter__()} and \method{__exit__()} should just +return \var{self}. + +Context managers written in Python can also implement this method using +a generator function decorated with the +\function{contextlib.contextmanager} decorator, as this can be simpler +than writing individual \method{__enter__()} and \method{__exit__()} +methods when the state to be managed is complex. +\end{methoddesc} + +\begin{methoddesc}[context]{__enter__}{self} +Enter the context defined by this object. The \keyword{with} statement +will bind this method's return value to the target(s) specified in the +\keyword{as} clause of the statement, if any. +\end{methoddesc} + +\begin{methoddesc}[context]{__exit__}{exc_type, exc_value, traceback} +Exit the context defined by this object. The parameters describe the +exception that caused the context to be exited. If the context was +exited without an exception, all three arguments will be +\constant{None}. + +If an exception is supplied, and the method wishes to suppress the +exception (i.e., prevent it from being propagated), it should return a +true value. Otherwise, the exception will be processed normally upon +exit from this method. + +Note that \method{__exit__} methods should not reraise the passed-in +exception; this is the caller's responsibility. +\end{methoddesc} + +\begin{seealso} + \seepep{0343}{The "with" statement} + {The specification, background, and examples for the + Python \keyword{with} statement.} +\end{seealso} + diff --git a/Doc/ref/ref6.tex b/Doc/ref/ref6.tex index d1d23ac..1eb1258 100644 --- a/Doc/ref/ref6.tex +++ b/Doc/ref/ref6.tex @@ -488,11 +488,12 @@ enough information is saved so that the next time \method{next()} is invoked, the function can proceed exactly as if the \keyword{yield} statement were just another external call. -The \keyword{yield} statement is not allowed in the \keyword{try} -clause of a \keyword{try} ...\ \keyword{finally} construct. The -difficulty is that there's no guarantee the generator will ever be -resumed, hence no guarantee that the \keyword{finally} block will ever -get executed. +As of Python version 2.5, the \keyword{yield} statement is now +allowed in the \keyword{try} clause of a \keyword{try} ...\ +\keyword{finally} construct. If the generator is not resumed before +it is finalized (by reaching a zero reference count or by being garbage +collected), the generator-iterator's \method{close()} method will be +called, allowing any pending \keyword{finally} clauses to execute. \begin{notice} In Python 2.2, the \keyword{yield} statement is only allowed @@ -510,6 +511,11 @@ from __future__ import generators \seepep{0255}{Simple Generators} {The proposal for adding generators and the \keyword{yield} statement to Python.} + + \seepep{0342}{Coroutines via Enhanced Generators} + {The proposal that, among other generator enhancements, + proposed allowing \keyword{yield} to appear inside a + \keyword{try} ... \keyword{finally} block.} \end{seealso} diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index 90627a4..a2d46a8 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -46,6 +46,7 @@ Summarizing: \productioncont{| \token{while_stmt}} \productioncont{| \token{for_stmt}} \productioncont{| \token{try_stmt}} + \productioncont{| \token{with_stmt}} \productioncont{| \token{funcdef}} \productioncont{| \token{classdef}} \production{suite} @@ -305,8 +306,75 @@ statement to generate exceptions may be found in section~\ref{raise}. \section{The \keyword{with} statement\label{with}} \stindex{with} -The \keyword{with} statement specifies +\versionadded{2.5} +The \keyword{with} statement is used to wrap the execution of a block +with methods defined by a context manager (see +section~\ref{context-managers}). This allows common +\keyword{try}...\keyword{except}...\keyword{finally} usage patterns to +be encapsulated as context managers for convenient reuse. + +\begin{productionlist} + \production{with_stmt} + {"with" \token{expression} ["as" target_list] ":" \token{suite}} +\end{productionlist} + +The execution of the \keyword{with} statement proceeds as follows: + +\begin{enumerate} + +\item The expression is evaluated, to obtain a context manager +object. + +\item The context manager's \method{__context__()} method is invoked to +obtain a context object. + +\item The context object's \method{__enter__()} method is invoked. + +\item If a target list was included in the \keyword{with} +statement, the return value from \method{__enter__()} is assigned to it. + +\note{The \keyword{with} statement guarantees that if the +\method{__enter__()} method returns without an error, then +\method{__exit__()} will always be called. Thus, if an error occurs +during the assignment to the target list, it will be treated the same as +an error occurring within the suite would be. See step 6 below.} + +\item The suite is executed. + +\item The context object's \method{__exit__()} method is invoked. If an +exception caused the suite to be exited, its type, value, and +traceback are passed as arguments to \method{__exit__()}. Otherwise, +three \constant{None} arguments are supplied. + +If the suite was exited due to an exception, and the return +value from the \method{__exit__()} method was false, the exception is +reraised. If the return value was true, the exception is suppressed, and +execution continues with the statement following the \keyword{with} +statement. + +If the suite was exited for any reason other than an exception, the +return value from \method{__exit__()} is ignored, and execution proceeds +at the normal location for the kind of exit that was taken. + +\end{enumerate} + +\begin{notice} +In Python 2.5, the \keyword{with} statement is only allowed +when the \code{with_statement} feature has been enabled. It will always +be enabled in Python 2.6. This \code{__future__} import statement can +be used to enable the feature: + +\begin{verbatim} +from __future__ import with_statement +\end{verbatim} +\end{notice} + +\begin{seealso} + \seepep{0343}{The "with" statement} + {The specification, background, and examples for the + Python \keyword{with} statement.} +\end{seealso} \section{Function definitions\label{function}} \indexii{function}{definition} diff --git a/Doc/tools/prechm.py b/Doc/tools/prechm.py index 7b2f393..57a43fd 100644 --- a/Doc/tools/prechm.py +++ b/Doc/tools/prechm.py @@ -150,6 +150,22 @@ class Book: # Library Doc list of books: # each 'book' : (Dir, Title, First page, Content page, Index page) supported_libraries = { + '2.5': + [ + Book('.', 'Main page', 'index'), + Book('.', 'Global Module Index', 'modindex'), + Book('whatsnew', "What's New", 'index', 'contents'), + Book('tut','Tutorial','tut','node2'), + Book('lib','Library Reference','lib','contents','genindex'), + Book('ref','Language Reference','ref','contents','genindex'), + Book('mac','Macintosh Reference','mac','contents','genindex'), + Book('ext','Extending and Embedding','ext','contents'), + Book('api','Python/C API','api','contents','genindex'), + Book('doc','Documenting Python','doc','contents'), + Book('inst','Installing Python Modules', 'inst', 'index'), + Book('dist','Distributing Python Modules', 'dist', 'index', 'genindex'), + ], + '2.4': [ Book('.', 'Main page', 'index'), diff --git a/Doc/tools/toc2bkm.py b/Doc/tools/toc2bkm.py index 636459a..ab669ba 100755 --- a/Doc/tools/toc2bkm.py +++ b/Doc/tools/toc2bkm.py @@ -44,6 +44,20 @@ _transition_map = { INCLUDED_LEVELS = ("chapter", "section", "subsection", "subsubsection") +class BadSectionNesting(Exception): + """Raised for unsupported section level transitions.""" + + def __init__(self, level, newsection, path, lineno): + self.level = level + self.newsection = newsection + self.path = path + self.lineno = lineno + + def __str__(self): + return ("illegal transition from %s to %s at %s (line %s)" + % (self.level, self.newsection, self.path, self.lineno)) + + def parse_toc(fp, bigpart=None): toc = top = [] stack = [toc] @@ -65,7 +79,10 @@ def parse_toc(fp, bigpart=None): if stype not in INCLUDED_LEVELS: # we don't want paragraphs & subparagraphs continue - direction = _transition_map[(level, stype)] + try: + direction = _transition_map[(level, stype)] + except KeyError: + raise BadSectionNesting(level, stype, fp.name, lineno) if direction == OUTER_TO_INNER: toc = toc[-1][-1] stack.insert(0, toc) diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex index 054985b..7f9a7ee 100644 --- a/Doc/tut/tut.tex +++ b/Doc/tut/tut.tex @@ -1012,7 +1012,7 @@ individual elements of a list: \end{verbatim} Assignment to slices is also possible, and this can even change the size -of the list: +of the list or clear it entirely: \begin{verbatim} >>> # Replace some items: @@ -1027,9 +1027,14 @@ of the list: ... a[1:1] = ['bletch', 'xyzzy'] >>> a [123, 'bletch', 'xyzzy', 1234] ->>> a[:0] = a # Insert (a copy of) itself at the beginning +>>> # Insert (a copy of) itself at the beginning +>>> a[:0] = a >>> a [123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234] +>>> # Clear the list: replace all items with an empty list +>>> a[:] = [] +>>> a +[] \end{verbatim} The built-in function \function{len()} also applies to lists: @@ -2023,9 +2028,9 @@ applied to complex expressions and nested functions: There is a way to remove an item from a list given its index instead of its value: the \keyword{del} statement. This differs from the \method{pop()}) method which returns a value. The \keyword{del} -statement can also be used to -remove slices from a list (which we did earlier by assignment of an -empty list to the slice). For example: +statement can also be used to remove slices from a list or clear the +entire list (which we did earlier by assignment of an empty list to +the slice). For example: \begin{verbatim} >>> a = [-1, 1, 66.25, 333, 333, 1234.5] @@ -2035,6 +2040,9 @@ empty list to the slice). For example: >>> del a[2:4] >>> a [1, 66.25, 1234.5] +>>> del a[:] +>>> a +[] \end{verbatim} \keyword{del} can also be used to delete entire variables: @@ -3710,19 +3718,49 @@ Traceback (most recent call last): KeyboardInterrupt \end{verbatim} -A \emph{finally clause} is executed whether or not an exception has -occurred in the try clause. When an exception has occurred, it is -re-raised after the finally clause is executed. The finally clause is -also executed ``on the way out'' when the \keyword{try} statement is -left via a \keyword{break} or \keyword{return} statement. +A \emph{finally clause} is always executed before leaving the +\keyword{try} statement, whether an exception has occurred or not. +When an exception has occurred in the \keyword{try} clause and has not +been handled by an \keyword{except} clause (or it has occurred in a +\keyword{except} or \keyword{else} clause), it is re-raised after the +\keyword{finally} clause has been executed. The \keyword{finally} clause +is also executed ``on the way out'' when any other clause of the +\keyword{try} statement is left via a \keyword{break}, \keyword{continue} +or \keyword{return} statement. A more complicated example: + +\begin{verbatim} +>>> def divide(x, y): +... try: +... result = x / y +... except ZeroDivisionError: +... print "division by zero!" +... else: +... print "result is", result +... finally: +... print "executing finally clause" +... +>>> divide(2, 1) +result is 2 +executing finally clause +>>> divide(2, 0) +division by zero! +executing finally clause +>>> divide("2", "1") +executing finally clause +Traceback (most recent call last): + File "", line 1, in ? + File "", line 3, in divide +TypeError: unsupported operand type(s) for /: 'str' and 'str' +\end{verbatim} -The code in the finally clause is useful for releasing external -resources (such as files or network connections), regardless of -whether the use of the resource was successful. +As you can see, the \keyword{finally} clause is executed in any +event. The \exception{TypeError} raised by dividing two strings +is not handled by the \keyword{except} clause and therefore +re-raised after the \keyword{finally} clauses has been executed. -A \keyword{try} statement must either have one or more except clauses -or one finally clause, but not both (because it would be unclear which -clause should be executed first). +In real world applications, the \keyword{finally} clause is useful +for releasing external resources (such as files or network connections), +regardless of whether the use of the resource was successful. \chapter{Classes \label{classes}} @@ -5340,7 +5378,7 @@ users. \item \citetitle[../ref/ref.html]{Language Reference}: A detailed explanation of Python's syntax and semantics. It's heavy reading, -but is useful as a +but is useful as a complete guide to the language itself. \end{itemize} diff --git a/Doc/whatsnew/whatsnew20.tex b/Doc/whatsnew/whatsnew20.tex index b89ac19..bf458fa 100644 --- a/Doc/whatsnew/whatsnew20.tex +++ b/Doc/whatsnew/whatsnew20.tex @@ -1214,8 +1214,8 @@ the function to be called on exit. \item{\module{gettext}:} This module provides internationalization (I18N) and localization (L10N) support for Python programs by providing an interface to the GNU gettext message catalog library. -(Integrated by Barry Warsaw, from separate contributions by Martin von -Loewis, Peter Funk, and James Henstridge.) +(Integrated by Barry Warsaw, from separate contributions by Martin +von~L\"owis, Peter Funk, and James Henstridge.) \item{\module{linuxaudiodev}:} Support for the \file{/dev/audio} device on Linux, a twin to the existing \module{sunaudiodev} module. diff --git a/Doc/whatsnew/whatsnew21.tex b/Doc/whatsnew/whatsnew21.tex index b7ea3f2..f3d0245 100644 --- a/Doc/whatsnew/whatsnew21.tex +++ b/Doc/whatsnew/whatsnew21.tex @@ -5,7 +5,7 @@ % $Id$ \title{What's New in Python 2.1} -\release{1.00} +\release{1.01} \author{A.M. Kuchling} \authoraddress{ \strong{Python Software Foundation}\\ @@ -16,14 +16,7 @@ \section{Introduction} -It's that time again... time for a new Python release, Python 2.1. -One recent goal of the Python development team has been to accelerate -the pace of new releases, with a new release coming every 6 to 9 -months. 2.1 is the first release to come out at this faster pace, with -the first alpha appearing in January, 3 months after the final version -of 2.0 was released. - -This article explains the new features in 2.1. While there aren't as +This article explains the new features in Python 2.1. While there aren't as many changes in 2.1 as there were in Python 2.0, there are still some pleasant surprises in store. 2.1 is the first release to be steered through the use of Python Enhancement Proposals, or PEPs, so most of @@ -34,6 +27,12 @@ provides an overview of the new features for Python programmers. Refer to the Python 2.1 documentation, or to the specific PEP, for more details about any new feature that particularly interests you. +One recent goal of the Python development team has been to accelerate +the pace of new releases, with a new release coming every 6 to 9 +months. 2.1 is the first release to come out at this faster pace, with +the first alpha appearing in January, 3 months after the final version +of 2.0 was released. + The final release of Python 2.1 was made on April 17, 2001. %====================================================================== diff --git a/Doc/whatsnew/whatsnew24.tex b/Doc/whatsnew/whatsnew24.tex index e9ff4d9..51baece 100644 --- a/Doc/whatsnew/whatsnew24.tex +++ b/Doc/whatsnew/whatsnew24.tex @@ -803,8 +803,8 @@ from SimpleXMLRPCServer import (SimpleXMLRPCServer, The PEP also proposes that all \keyword{import} statements be absolute imports, with a leading \samp{.} character to indicate a relative -import. This part of the PEP is not yet implemented, and will have to -wait for Python 2.5 or some other future version. +import. This part of the PEP was not implemented for Python 2.4, +but was completed for Python 2.5. \begin{seealso} \seepep{328}{Imports: Multi-Line and Absolute/Relative} @@ -1336,7 +1336,7 @@ be used to implement other rotating handlers. \item The \module{marshal} module now shares interned strings on unpacking a data structure. This may shrink the size of certain pickle strings, but the primary effect is to make \file{.pyc} files significantly smaller. -(Contributed by Martin von Loewis.) +(Contributed by Martin von~L\"owis.) \item The \module{nntplib} module's \class{NNTP} class gained \method{description()} and \method{descriptions()} methods to retrieve @@ -1688,7 +1688,7 @@ Some of the changes to Python's build process and to the C API are: \begin{itemize} \item The Windows port now builds under MSVC++ 7.1 as well as version 6. - (Contributed by Martin von Loewis.) + (Contributed by Martin von~L\"owis.) \end{itemize} diff --git a/Doc/whatsnew/whatsnew25.tex b/Doc/whatsnew/whatsnew25.tex index 5743285..db6c25a 100644 --- a/Doc/whatsnew/whatsnew25.tex +++ b/Doc/whatsnew/whatsnew25.tex @@ -2,9 +2,14 @@ \usepackage{distutils} % $Id$ +% The easy_install stuff +% Describe the pkgutil module +% Stateful codec changes +% Fix XXX comments +% Count up the patches and bugs \title{What's New in Python 2.5} -\release{0.0} +\release{0.1} \author{A.M. Kuchling} \authoraddress{\email{amk@amk.ca}} @@ -14,7 +19,10 @@ This article explains the new features in Python 2.5. No release date for Python 2.5 has been set; it will probably be released in the -autumn of 2006. +autumn of 2006. \pep{356} describes the planned release schedule. + +Comments, suggestions, and error reports are welcome; please e-mail them +to the author or open a bug in the Python bug tracker. % XXX Compare with previous release in 2 - 3 sentences here. @@ -27,6 +35,32 @@ rationale, refer to the PEP for a particular new feature. %====================================================================== +\section{PEP 243: Uploading Modules to PyPI} + +PEP 243 describes an HTTP-based protocol for submitting software +packages to a central archive. The Python package index at +\url{http://cheeseshop.python.org} now supports package uploads, and +the new \command{upload} Distutils command will upload a package to the +repository. + +Before a package can be uploaded, you must be able to build a +distribution using the \command{sdist} Distutils command. Once that +works, you can run \code{python setup.py upload} to add your package +to the PyPI archive. Optionally you can GPG-sign the package by +supplying the \longprogramopt{sign} and +\longprogramopt{identity} options. + +\begin{seealso} + +\seepep{243}{Module Repository Upload Mechanism}{PEP written by +Sean Reifschneider; implemented by Martin von~L\"owis +and Richard Jones. Note that the PEP doesn't exactly +describe what's implemented in PyPI.} + +\end{seealso} + + +%====================================================================== \section{PEP 308: Conditional Expressions} For a long time, people have been requesting a way to write @@ -43,10 +77,10 @@ else: \end{verbatim} There have been endless tedious discussions of syntax on both -python-dev and comp.lang.python, and even a vote that found the -majority of voters wanted some way to write conditional expressions, -but there was no syntax that was clearly preferred by a majority. -Candidates include C's \code{cond ? true_v : false_v}, +python-dev and comp.lang.python. A vote was even held that found the +majority of voters wanted conditional expressions in some form, +but there was no syntax that was preferred by a clear majority. +Candidates included C's \code{cond ? true_v : false_v}, \code{if cond then true_v else false_v}, and 16 other variations. GvR eventually chose a surprising syntax: @@ -55,11 +89,12 @@ GvR eventually chose a surprising syntax: x = true_value if condition else false_value \end{verbatim} -Evaluation is still lazy as in existing Boolean expression, so the -evaluation jumps around a bit. The \var{condition} expression is -evaluated first, and the \var{true_value} expression is evaluated only -if the condition was true. Similarly, the \var{false_value} -expression is only evaluated when the condition is false. +Evaluation is still lazy as in existing Boolean expressions, so the +order of evaluation jumps around a bit. The \var{condition} +expression in the middle is evaluated first, and the \var{true_value} +expression is evaluated only if the condition was true. Similarly, +the \var{false_value} expression is only evaluated when the condition +is false. This syntax may seem strange and backwards; why does the condition go in the \emph{middle} of the expression, and not in the front as in C's @@ -110,7 +145,7 @@ around your conditional expressions, you won't run into this case. \begin{seealso} \seepep{308}{Conditional Expressions}{PEP written by -Guido van Rossum and Raymond D. Hettinger; implemented by Thomas +Guido van~Rossum and Raymond D. Hettinger; implemented by Thomas Wouters.} \end{seealso} @@ -192,7 +227,16 @@ set to a URL for the package's source code. This means it's now possible to look up an entry in the package index, determine the dependencies for a package, and download the required packages. -% XXX put example here +\begin{verbatim} +VERSION = '1.0' +setup(name='PyPackage', + version=VERSION, + requires=['numarray', 'zlib (>=1.1.4)'], + obsoletes=['OldPackage'] + download_url=('http://www.example.com/pypackage/dist/pkg-%s.tar.gz' + % VERSION), + ) +\end{verbatim} \begin{seealso} @@ -206,31 +250,178 @@ implemented by Richard Jones and Fred Drake.} %====================================================================== \section{PEP 328: Absolute and Relative Imports} -% XXX write this +The simpler part of PEP 328 was implemented in Python 2.4: parentheses +could now be used to enclose the names imported from a module using +the \code{from ... import ...} statement, making it easier to import +many different names. + +The more complicated part has been implemented in Python 2.5: +importing a module can be specified to use absolute or +package-relative imports. The plan is to move toward making absolute +imports the default in future versions of Python. + +Let's say you have a package directory like this: +\begin{verbatim} +pkg/ +pkg/__init__.py +pkg/main.py +pkg/string.py +\end{verbatim} + +This defines a package named \module{pkg} containing the +\module{pkg.main} and \module{pkg.string} submodules. + +Consider the code in the \file{main.py} module. What happens if it +executes the statement \code{import string}? In Python 2.4 and +earlier, it will first look in the package's directory to perform a +relative import, finds \file{pkg/string.py}, imports the contents of +that file as the \module{pkg.string} module, and that module is bound +to the name \samp{string} in the \module{pkg.main} module's namespace. + +That's fine if \module{pkg.string} was what you wanted. But what if +you wanted Python's standard \module{string} module? There's no clean +way to ignore \module{pkg.string} and look for the standard module; +generally you had to look at the contents of \code{sys.modules}, which +is slightly unclean. +Holger Krekel's \module{py.std} package provides a tidier way to perform +imports from the standard library, \code{import py ; py.std.string.join()}, +but that package isn't available on all Python installations. + +Reading code which relies on relative imports is also less clear, +because a reader may be confused about which module, \module{string} +or \module{pkg.string}, is intended to be used. Python users soon +learned not to duplicate the names of standard library modules in the +names of their packages' submodules, but you can't protect against +having your submodule's name being used for a new module added in a +future version of Python. + +In Python 2.5, you can switch \keyword{import}'s behaviour to +absolute imports using a \code{from __future__ import absolute_import} +directive. This absolute-import behaviour will become the default in +a future version (probably Python 2.7). Once absolute imports +are the default, \code{import string} will +always find the standard library's version. +It's suggested that users should begin using absolute imports as much +as possible, so it's preferable to begin writing \code{from pkg import +string} in your code. + +Relative imports are still possible by adding a leading period +to the module name when using the \code{from ... import} form: + +\begin{verbatim} +# Import names from pkg.string +from .string import name1, name2 +# Import pkg.string +from . import string +\end{verbatim} + +This imports the \module{string} module relative to the current +package, so in \module{pkg.main} this will import \var{name1} and +\var{name2} from \module{pkg.string}. Additional leading periods +perform the relative import starting from the parent of the current +package. For example, code in the \module{A.B.C} module can do: + +\begin{verbatim} +from . import D # Imports A.B.D +from .. import E # Imports A.E +from ..F import G # Imports A.F.G +\end{verbatim} + +Leading periods cannot be used with the \code{import \var{modname}} +form of the import statement, only the \code{from ... import} form. + +\begin{seealso} + +\seepep{328}{Imports: Multi-Line and Absolute/Relative} +{PEP written by Aahz; implemented by Thomas Wouters.} + +\seeurl{http://codespeak.net/py/current/doc/index.html} +{The py library by Holger Krekel, which contains the \module{py.std} package.} + +\end{seealso} %====================================================================== \section{PEP 338: Executing Modules as Scripts} -% XXX write this +The \programopt{-m} switch added in Python 2.4 to execute a module as +a script gained a few more abilities. Instead of being implemented in +C code inside the Python interpreter, the switch now uses an +implementation in a new module, \module{runpy}. + +The \module{runpy} module implements a more sophisticated import +mechanism so that it's now possible to run modules in a package such +as \module{pychecker.checker}. The module also supports alternative +import mechanisms such as the \module{zipimport} module. This means +you can add a .zip archive's path to \code{sys.path} and then use the +\programopt{-m} switch to execute code from the archive. + + +\begin{seealso} + +\seepep{338}{Executing modules as scripts}{PEP written and +implemented by Nick Coghlan.} + +\end{seealso} %====================================================================== \section{PEP 341: Unified try/except/finally} -% XXX write this +Until Python 2.5, the \keyword{try} statement came in two +flavours. You could use a \keyword{finally} block to ensure that code +is always executed, or one or more \keyword{except} blocks to catch +specific exceptions. You couldn't combine both \keyword{except} blocks and a +\keyword{finally} block, because generating the right bytecode for the +combined version was complicated and it wasn't clear what the +semantics of the combined should be. + +GvR spent some time working with Java, which does support the +equivalent of combining \keyword{except} blocks and a +\keyword{finally} block, and this clarified what the statement should +mean. In Python 2.5, you can now write: + +\begin{verbatim} +try: + block-1 ... +except Exception1: + handler-1 ... +except Exception2: + handler-2 ... +else: + else-block +finally: + final-block +\end{verbatim} + +The code in \var{block-1} is executed. If the code raises an +exception, the handlers are tried in order: \var{handler-1}, +\var{handler-2}, ... If no exception is raised, the \var{else-block} +is executed. No matter what happened previously, the +\var{final-block} is executed once the code block is complete and any +raised exceptions handled. Even if there's an error in an exception +handler or the \var{else-block} and a new exception is raised, the +\var{final-block} is still executed. + +\begin{seealso} + +\seepep{341}{Unifying try-except and try-finally}{PEP written by Georg Brandl; +implementation by Thomas Lee.} + +\end{seealso} %====================================================================== -\section{PEP 342: New Generator Features} +\section{PEP 342: New Generator Features\label{section-generators}} +Python 2.5 adds a simple way to pass values \emph{into} a generator. As introduced in Python 2.3, generators only produce output; once a generator's code is invoked to create an iterator, there's no way to pass any new information into the function when its execution is -resumed. Hackish solutions to this include making the generator's -code look at a global variable and then changing the global variable's +resumed. Sometimes the ability to pass in some information would be +useful. Hackish solutions to this include making the generator's code +look at a global variable and then changing the global variable's value, or passing in some mutable object that callers then modify. -Python 2.5 adds the ability to pass values \emph{into} a generator. To refresh your memory of basic generators, here's a simple example: @@ -239,7 +430,7 @@ def counter (maximum): i = 0 while i < maximum: yield i - i += 1 + i += 1 \end{verbatim} When you call \code{counter(10)}, the result is an iterator that @@ -261,11 +452,14 @@ I recommend that you always put parentheses around a \keyword{yield} expression when you're doing something with the returned value, as in the above example. The parentheses aren't always necessary, but it's easier to always add them instead of having to remember when they're -needed.\footnote{The exact rules are that a \keyword{yield}-expression must -always be parenthesized except when it occurs at the top-level -expression on the right-hand side of an assignment, meaning you can -write \code{val = yield i} but have to use parentheses when there's an -operation, as in \code{val = (yield i) + 12}.} +needed. + +(\pep{342} explains the exact rules, which are that a +\keyword{yield}-expression must always be parenthesized except when it +occurs at the top-level expression on the right-hand side of an +assignment. This means you can write \code{val = yield i} but have to +use parentheses when there's an operation, as in \code{val = (yield i) ++ 12}.) Values are sent into a generator by calling its \method{send(\var{value})} method. The generator's code is then @@ -281,11 +475,11 @@ def counter (maximum): i = 0 while i < maximum: val = (yield i) - # If value provided, change counter + # If value provided, change counter if val is not None: i = val - else: - i += 1 + else: + i += 1 \end{verbatim} And here's an example of changing the counter: @@ -344,13 +538,32 @@ Generators also become \emph{coroutines}, a more generalized form of subroutines. Subroutines are entered at one point and exited at another point (the top of the function, and a \keyword{return statement}), but coroutines can be entered, exited, and resumed at -many different points (the \keyword{yield} statements). - +many different points (the \keyword{yield} statements). We'll have to +figure out patterns for using coroutines effectively in Python. + +The addition of the \method{close()} method has one side effect that +isn't obvious. \method{close()} is called when a generator is +garbage-collected, so this means the generator's code gets one last +chance to run before the generator is destroyed. This last chance +means that \code{try...finally} statements in generators can now be +guaranteed to work; the \keyword{finally} clause will now always get a +chance to run. The syntactic restriction that you couldn't mix +\keyword{yield} statements with a \code{try...finally} suite has +therefore been removed. This seems like a minor bit of language +trivia, but using generators and \code{try...finally} is actually +necessary in order to implement the \keyword{with} statement +described by PEP 343. I'll look at this new statement in the following +section. + +Another even more esoteric effect of this change: previously, the +\member{gi_frame} attribute of a generator was always a frame object. +It's now possible for \member{gi_frame} to be \code{None} +once the generator has been exhausted. \begin{seealso} \seepep{342}{Coroutines via Enhanced Generators}{PEP written by -Guido van Rossum and Phillip J. Eby; +Guido van~Rossum and Phillip J. Eby; implemented by Phillip J. Eby. Includes examples of some fancier uses of generators as coroutines.} @@ -367,14 +580,323 @@ Sugalski.} %====================================================================== \section{PEP 343: The 'with' statement} -% XXX write this +The \keyword{with} statement allows a clearer version of code that +uses \code{try...finally} blocks to ensure that clean-up code is +executed. + +In this section, I'll discuss the statement as it will commonly be +used. In the next section, I'll examine the implementation details +and show how to write objects called ``context managers'' and +``contexts'' for use with this statement. + +The \keyword{with} statement is a new control-flow structure whose +basic structure is: + +\begin{verbatim} +with expression [as variable]: + with-block +\end{verbatim} + +The expression is evaluated, and it should result in a type of object +that's called a context manager. The context manager can return a +value that can optionally be bound to the name \var{variable}. (Note +carefully: \var{variable} is \emph{not} assigned the result of +\var{expression}.) One method of the context manager is run before +\var{with-block} is executed, and another method is run after the +block is done, even if the block raised an exception. + +To enable the statement in Python 2.5, you need +to add the following directive to your module: + +\begin{verbatim} +from __future__ import with_statement +\end{verbatim} + +The statement will always be enabled in Python 2.6. + +Some standard Python objects can now behave as context managers. File +objects are one example: + +\begin{verbatim} +with open('/etc/passwd', 'r') as f: + for line in f: + print line + ... more processing code ... +\end{verbatim} + +After this statement has executed, the file object in \var{f} will +have been automatically closed at this point, even if the 'for' loop +raised an exception part-way through the block. + +The \module{threading} module's locks and condition variables +also support the \keyword{with} statement: + +\begin{verbatim} +lock = threading.Lock() +with lock: + # Critical section of code + ... +\end{verbatim} + +The lock is acquired before the block is executed, and always released once +the block is complete. + +The \module{decimal} module's contexts, which encapsulate the desired +precision and rounding characteristics for computations, can also be +used as context managers. + +\begin{verbatim} +import decimal + +# Displays with default precision of 28 digits +v1 = decimal.Decimal('578') +print v1.sqrt() + +with decimal.Context(prec=16): + # All code in this block uses a precision of 16 digits. + # The original context is restored on exiting the block. + print v1.sqrt() +\end{verbatim} + +\subsection{Writing Context Managers} + +Under the hood, the \keyword{with} statement is fairly complicated. +Most people will only use \keyword{with} in company with +existing objects that are documented to work as context managers, and +don't need to know these details, so you can skip the following section if +you like. Authors of new context managers will need to understand the +details of the underlying implementation. + +A high-level explanation of the context management protocol is: + +\begin{itemize} +\item The expression is evaluated and should result in an object +that's a context manager, meaning that it has a +\method{__context__()} method. + +\item This object's \method{__context__()} method is called, and must +return a context object. + +\item The context's \method{__enter__()} method is called. +The value returned is assigned to \var{VAR}. If no \code{as \var{VAR}} +clause is present, the value is simply discarded. + +\item The code in \var{BLOCK} is executed. + +\item If \var{BLOCK} raises an exception, the context object's +\method{__exit__(\var{type}, \var{value}, \var{traceback})} is called +with the exception's information, the same values returned by +\function{sys.exc_info()}. The method's return value +controls whether the exception is re-raised: any false value +re-raises the exception, and \code{True} will result in suppressing it. +You'll only rarely want to suppress the exception; the +author of the code containing the \keyword{with} statement will +never realize anything went wrong. + +\item If \var{BLOCK} didn't raise an exception, +the context object's \method{__exit__()} is still called, +but \var{type}, \var{value}, and \var{traceback} are all \code{None}. + +\end{itemize} + +Let's think through an example. I won't present detailed code but +will only sketch the necessary code. The example will be writing a +context manager for a database that supports transactions. + +(For people unfamiliar with database terminology: a set of changes to +the database are grouped into a transaction. Transactions can be +either committed, meaning that all the changes are written into the +database, or rolled back, meaning that the changes are all discarded +and the database is unchanged. See any database textbook for more +information.) +% XXX find a shorter reference? + +Let's assume there's an object representing a database connection. +Our goal will be to let the user write code like this: + +\begin{verbatim} +db_connection = DatabaseConnection() +with db_connection as cursor: + cursor.execute('insert into ...') + cursor.execute('delete from ...') + # ... more operations ... +\end{verbatim} + +The transaction should either be committed if the code in the block +runs flawlessly, or rolled back if there's an exception. + +First, the \class{DatabaseConnection} needs a \method{__context__()} +method. Sometimes an object can be its own context manager and can +simply return \code{self}; the \module{threading} module's lock objects +can do this. For our database example, though, we need to +create a new object; I'll call this class \class{DatabaseContext}. +Our \method{__context__()} must therefore look like this: + +\begin{verbatim} +class DatabaseConnection: + ... + def __context__ (self): + return DatabaseContext(self) + + # Database interface + def cursor (self): + "Returns a cursor object and starts a new transaction" + def commit (self): + "Commits current transaction" + def rollback (self): + "Rolls back current transaction" +\end{verbatim} + +The context needs the connection object so that the connection +object's \method{commit()} or \method{rollback()} methods can be +called: + +\begin{verbatim} +class DatabaseContext: + def __init__ (self, connection): + self.connection = connection +\end{verbatim} + +The \method {__enter__()} method is pretty easy, having only +to start a new transaction. In this example, +the resulting cursor object would be a useful result, +so the method will return it. The user can +then add \code{as cursor} to their \keyword{with} statement +to bind the cursor to a variable name. + +\begin{verbatim} +class DatabaseContext: + ... + def __enter__ (self): + # Code to start a new transaction + cursor = self.connection.cursor() + return cursor +\end{verbatim} + +The \method{__exit__()} method is the most complicated because it's +where most of the work has to be done. The method has to check if an +exception occurred. If there was no exception, the transaction is +committed. The transaction is rolled back if there was an exception. +Here the code will just fall off the end of the function, returning +the default value of \code{None}. \code{None} is false, so the exception +will be re-raised automatically. If you wished, you could be more explicit +and add a \keyword{return} at the marked location. + +\begin{verbatim} +class DatabaseContext: + ... + def __exit__ (self, type, value, tb): + if tb is None: + # No exception, so commit + self.connection.commit() + else: + # Exception occurred, so rollback. + self.connection.rollback() + # return False +\end{verbatim} + + +\subsection{The contextlib module\label{module-contextlib}} + +The new \module{contextlib} module provides some functions and a +decorator that are useful for writing context managers. + +The decorator is called \function{contextmanager}, and lets you write +a simple context manager as a generator. The generator should yield +exactly one value. The code up to the \keyword{yield} will be +executed as the \method{__enter__()} method, and the value yielded +will be the method's return value that will get bound to the variable +in the \keyword{with} statement's \keyword{as} clause, if any. The +code after the \keyword{yield} will be executed in the +\method{__exit__()} method. Any exception raised in the block +will be raised by the \keyword{yield} statement. + +Our database example from the previous section could be written +using this decorator as: + +\begin{verbatim} +from contextlib import contextmanager + +@contextmanager +def db_transaction (connection): + cursor = connection.cursor() + try: + yield cursor + except: + connection.rollback() + raise + else: + connection.commit() + +db = DatabaseConnection() +with db_transaction(db) as cursor: + ... +\end{verbatim} + +You can also use this decorator to write the \method{__context__()} method +for a class without creating a new class for the context: + +\begin{verbatim} +class DatabaseConnection: + + @contextmanager + def __context__ (self): + cursor = self.cursor() + try: + yield cursor + except: + self.rollback() + raise + else: + self.commit() +\end{verbatim} + + +There's a \function{nested(\var{mgr1}, \var{mgr2}, ...)} manager that +combines a number of context managers so you don't need to write +nested \keyword{with} statements. This example statement does two +things, starting a database transaction and acquiring a thread lock: + +\begin{verbatim} +lock = threading.Lock() +with nested (db_transaction(db), lock) as (cursor, locked): + ... +\end{verbatim} + +Finally, the \function{closing(\var{object})} context manager +returns \var{object} so that it can be bound to a variable, +and calls \code{\var{object}.close()} at the end of the block. + +\begin{verbatim} +import urllib, sys +from contextlib import closing + +with closing(urllib.urlopen('http://www.yahoo.com')) as f: + for line in f: + sys.stdout.write(line) +\end{verbatim} + +\begin{seealso} + +\seepep{343}{The ``with'' statement}{PEP written by Guido van~Rossum +and Nick Coghlan; implemented by Mike Bland, Guido van~Rossum, and +Neal Norwitz. The PEP shows the code generated for a \keyword{with} +statement, which can be helpful in learning how context managers +work.} + +\seeurl{../lib/module-contextlib.html}{The documentation +for the \module{contextlib} module.} + +\end{seealso} %====================================================================== \section{PEP 352: Exceptions as New-Style Classes} -Exception classes can now be new-style classes, not just classic classes, -and the built-in \exception{Exception} class and all +Exception classes can now be new-style classes, not just classic +classes, and the built-in \exception{Exception} class and all the +standard built-in exceptions (\exception{NameError}, +\exception{ValueError}, etc.) are now new-style classes. The inheritance hierarchy for exceptions has been rearranged a bit. In 2.5, the inheritance relationships are: @@ -428,8 +950,70 @@ in a few releases. \begin{seealso} -\seepep{352}{}{PEP written by -Brett Cannon and Guido van Rossum; implemented by Brett Cannon.} +\seepep{352}{Required Superclass for Exceptions}{PEP written by +Brett Cannon and Guido van~Rossum; implemented by Brett Cannon.} + +\end{seealso} + + +%====================================================================== +\section{PEP 353: Using ssize_t as the index type\label{section-353}} + +A wide-ranging change to Python's C API, using a new +\ctype{Py_ssize_t} type definition instead of \ctype{int}, +will permit the interpreter to handle more data on 64-bit platforms. +This change doesn't affect Python's capacity on 32-bit platforms. + +Various pieces of the Python interpreter used C's \ctype{int} type to +store sizes or counts; for example, the number of items in a list or +tuple were stored in an \ctype{int}. The C compilers for most 64-bit +platforms still define \ctype{int} as a 32-bit type, so that meant +that lists could only hold up to \code{2**31 - 1} = 2147483647 items. +(There are actually a few different programming models that 64-bit C +compilers can use -- see +\url{http://www.unix.org/version2/whatsnew/lp64_wp.html} for a +discussion -- but the most commonly available model leaves \ctype{int} +as 32 bits.) + +A limit of 2147483647 items doesn't really matter on a 32-bit platform +because you'll run out of memory before hitting the length limit. +Each list item requires space for a pointer, which is 4 bytes, plus +space for a \ctype{PyObject} representing the item. 2147483647*4 is +already more bytes than a 32-bit address space can contain. + +It's possible to address that much memory on a 64-bit platform, +however. The pointers for a list that size would only require 16GiB +of space, so it's not unreasonable that Python programmers might +construct lists that large. Therefore, the Python interpreter had to +be changed to use some type other than \ctype{int}, and this will be a +64-bit type on 64-bit platforms. The change will cause +incompatibilities on 64-bit machines, so it was deemed worth making +the transition now, while the number of 64-bit users is still +relatively small. (In 5 or 10 years, we may \emph{all} be on 64-bit +machines, and the transition would be more painful then.) + +This change most strongly affects authors of C extension modules. +Python strings and container types such as lists and tuples +now use \ctype{Py_ssize_t} to store their size. +Functions such as \cfunction{PyList_Size()} +now return \ctype{Py_ssize_t}. Code in extension modules +may therefore need to have some variables changed to +\ctype{Py_ssize_t}. + +The \cfunction{PyArg_ParseTuple()} and \cfunction{Py_BuildValue()} functions +have a new conversion code, \samp{n}, for \ctype{Py_ssize_t}. +\cfunction{PyArg_ParseTuple()}'s \samp{s\#} and \samp{t\#} still output +\ctype{int} by default, but you can define the macro +\csimplemacro{PY_SSIZE_T_CLEAN} before including \file{Python.h} +to make them return \ctype{Py_ssize_t}. + +\pep{353} has a section on conversion guidelines that +extension authors should read to learn about supporting 64-bit +platforms. + +\begin{seealso} + +\seepep{353}{Using ssize_t as the index type}{PEP written and implemented by Martin von~L\"owis.} \end{seealso} @@ -437,7 +1021,47 @@ Brett Cannon and Guido van Rossum; implemented by Brett Cannon.} %====================================================================== \section{PEP 357: The '__index__' method} -% XXX write this +The NumPy developers had a problem that could only be solved by adding +a new special method, \method{__index__}. When using slice notation, +as in \code{[\var{start}:\var{stop}:\var{step}]}, the values of the +\var{start}, \var{stop}, and \var{step} indexes must all be either +integers or long integers. NumPy defines a variety of specialized +integer types corresponding to unsigned and signed integers of 8, 16, +32, and 64 bits, but there was no way to signal that these types could +be used as slice indexes. + +Slicing can't just use the existing \method{__int__} method because +that method is also used to implement coercion to integers. If +slicing used \method{__int__}, floating-point numbers would also +become legal slice indexes and that's clearly an undesirable +behaviour. + +Instead, a new special method called \method{__index__} was added. It +takes no arguments and returns an integer giving the slice index to +use. For example: + +\begin{verbatim} +class C: + def __index__ (self): + return self.value +\end{verbatim} + +The return value must be either a Python integer or long integer. +The interpreter will check that the type returned is correct, and +raises a \exception{TypeError} if this requirement isn't met. + +A corresponding \member{nb_index} slot was added to the C-level +\ctype{PyNumberMethods} structure to let C extensions implement this +protocol. \cfunction{PyNumber_Index(\var{obj})} can be used in +extension code to call the \method{__index__} function and retrieve +its result. + +\begin{seealso} + +\seepep{357}{Allowing Any Object to be Used for Slicing}{PEP written +and implemented by Travis Oliphant.} + +\end{seealso} %====================================================================== @@ -448,10 +1072,29 @@ language. \begin{itemize} +\item The \class{dict} type has a new hook for letting subclasses +provide a default value when a key isn't contained in the dictionary. +When a key isn't found, the dictionary's +\method{__missing__(\var{key})} +method will be called. This hook is used to implement +the new \class{defaultdict} class in the \module{collections} +module. The following example defines a dictionary +that returns zero for any missing key: + +\begin{verbatim} +class zerodict (dict): + def __missing__ (self, key): + return 0 + +d = zerodict({1:1, 2:2}) +print d[1], d[2] # Prints 1, 2 +print d[3], d[4] # Prints 0, 0 +\end{verbatim} + \item The \function{min()} and \function{max()} built-in functions gained a \code{key} keyword argument analogous to the \code{key} -argument for \method{sort()}. This argument supplies a function -that takes a single argument and is called for every value in the list; +argument for \method{sort()}. This argument supplies a function that +takes a single argument and is called for every value in the list; \function{min()}/\function{max()} will return the element with the smallest/largest return value from this function. For example, to find the longest string in a list, you can do: @@ -459,7 +1102,7 @@ For example, to find the longest string in a list, you can do: \begin{verbatim} L = ['medium', 'longest', 'short'] # Prints 'longest' -print max(L, key=len) +print max(L, key=len) # Prints 'short', because lexicographically 'short' has the largest value print max(L) \end{verbatim} @@ -472,9 +1115,18 @@ false values. \function{any()} returns \constant{True} if any value returned by the iterator is true; otherwise it will return \constant{False}. \function{all()} returns \constant{True} only if all of the values returned by the iterator evaluate as being true. +(Suggested by GvR, and implemented by Raymond Hettinger.) -% XXX who added? +\item ASCII is now the default encoding for modules. It's now +a syntax error if a module contains string literals with 8-bit +characters but doesn't have an encoding declaration. In Python 2.4 +this triggered a warning, not a syntax error. See \pep{263} +for how to declare a module's encoding; for example, you might add +a line like this near the top of the source file: +\begin{verbatim} +# -*- coding: latin1 -*- +\end{verbatim} \item The list of base classes in a class definition can now be empty. As an example, this is now legal: @@ -489,6 +1141,24 @@ class C(): %====================================================================== +\subsection{Interactive Interpreter Changes} + +In the interactive interpreter, \code{quit} and \code{exit} +have long been strings so that new users get a somewhat helpful message +when they try to quit: + +\begin{verbatim} +>>> quit +'Use Ctrl-D (i.e. EOF) to exit.' +\end{verbatim} + +In Python 2.5, \code{quit} and \code{exit} are now objects that still +produce string representations of themselves, but are also callable. +Newbies who try \code{quit()} or \code{exit()} will now exit the +interpreter as they expect. (Implemented by Georg Brandl.) + + +%====================================================================== \subsection{Optimizations} \begin{itemize} @@ -500,43 +1170,135 @@ In 2.5 the internal data structure has been customized for implementing sets, and as a result sets will use a third less memory and are somewhat faster. (Implemented by Raymond Hettinger.) +\item The performance of some Unicode operations, such as +character map decoding, has been improved. +% Patch 1313939 + +\item The code generator's peephole optimizer now performs +simple constant folding in expressions. If you write something like +\code{a = 2+3}, the code generator will do the arithmetic and produce +code corresponding to \code{a = 5}. + \end{itemize} The net result of the 2.5 optimizations is that Python 2.5 runs the -pystone benchmark around XX\% faster than Python 2.4. +pystone benchmark around XXX\% faster than Python 2.4. %====================================================================== \section{New, Improved, and Deprecated Modules} -As usual, Python's standard library received a number of enhancements and -bug fixes. Here's a partial list of the most notable changes, sorted -alphabetically by module name. Consult the -\file{Misc/NEWS} file in the source tree for a more -complete list of changes, or look through the SVN logs for all the -details. +The standard library received many enhancements and bug fixes in +Python 2.5. Here's a partial list of the most notable changes, sorted +alphabetically by module name. Consult the \file{Misc/NEWS} file in +the source tree for a more complete list of changes, or look through +the SVN logs for all the details. \begin{itemize} -% ctypes added - -% collections.deque now has .remove() - % the cPickle module no longer accepts the deprecated None option in the % args tuple returned by __reduce__(). -% csv module improvements +\item The \module{audioop} module now supports the a-LAW encoding, +and the code for u-LAW encoding has been improved. (Contributed by +Lars Immisch.) -% datetime.datetime() now has a strptime class method which can be used to -% create datetime object using a string and format. +\item The \module{collections} module gained a new type, +\class{defaultdict}, that subclasses the standard \class{dict} +type. The new type mostly behaves like a dictionary but constructs a +default value when a key isn't present, automatically adding it to the +dictionary for the requested key value. -\item A new \module{hashlib} module has been added to replace the -\module{md5} and \module{sha} modules. \module{hashlib} adds support -for additional secure hashes (SHA-224, SHA-256, SHA-384, and SHA-512). -When available, the module uses OpenSSL for fast platform optimized -implementations of algorithms. The old \module{md5} and \module{sha} -modules still exist as wrappers around hashlib to preserve backwards -compatibility. (Contributed by Gregory P. Smith.) +The first argument to \class{defaultdict}'s constructor is a factory +function that gets called whenever a key is requested but not found. +This factory function receives no arguments, so you can use built-in +type constructors such as \function{list()} or \function{int()}. For +example, +you can make an index of words based on their initial letter like this: + +\begin{verbatim} +words = """Nel mezzo del cammin di nostra vita +mi ritrovai per una selva oscura +che la diritta via era smarrita""".lower().split() + +index = defaultdict(list) + +for w in words: + init_letter = w[0] + index[init_letter].append(w) +\end{verbatim} + +Printing \code{index} results in the following output: + +\begin{verbatim} +defaultdict(, {'c': ['cammin', 'che'], 'e': ['era'], + 'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'], + 'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'], + 'p': ['per'], 's': ['selva', 'smarrita'], + 'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']} +\end{verbatim} + +The \class{deque} double-ended queue type supplied by the +\module{collections} module now has a \method{remove(\var{value})} +method that removes the first occurrence of \var{value} in the queue, +raising \exception{ValueError} if the value isn't found. + +\item New module: The \module{contextlib} module contains helper functions for use +with the new \keyword{with} statement. See +section~\ref{module-contextlib} for more about this module. +(Contributed by Phillip J. Eby.) + +\item New module: The \module{cProfile} module is a C implementation of +the existing \module{profile} module that has much lower overhead. +The module's interface is the same as \module{profile}: you run +\code{cProfile.run('main()')} to profile a function, can save profile +data to a file, etc. It's not yet known if the Hotshot profiler, +which is also written in C but doesn't match the \module{profile} +module's interface, will continue to be maintained in future versions +of Python. (Contributed by Armin Rigo.) + +\item The \module{csv} module, which parses files in +comma-separated value format, received several enhancements and a +number of bugfixes. You can now set the maximum size in bytes of a +field by calling the \method{csv.field_size_limit(\var{new_limit})} +function; omitting the \var{new_limit} argument will return the +currently-set limit. The \class{reader} class now has a +\member{line_num} attribute that counts the number of physical lines +read from the source; records can span multiple physical lines, so +\member{line_num} is not the same as the number of records read. +(Contributed by Skip Montanaro and Andrew McNamara.) + +\item The \class{datetime} class in the \module{datetime} +module now has a \method{strptime(\var{string}, \var{format})} +method for parsing date strings, contributed by Josh Spoerri. +It uses the same format characters as \function{time.strptime()} and +\function{time.strftime()}: + +\begin{verbatim} +from datetime import datetime + +ts = datetime.strptime('10:13:15 2006-03-07', + '%H:%M:%S %Y-%m-%d') +\end{verbatim} + +\item The \module{fileinput} module was made more flexible. +Unicode filenames are now supported, and a \var{mode} parameter that +defaults to \code{"r"} was added to the +\function{input()} function to allow opening files in binary or +universal-newline mode. Another new parameter, \var{openhook}, +lets you use a function other than \function{open()} +to open the input files. Once you're iterating over +the set of files, the \class{FileInput} object's new +\method{fileno()} returns the file descriptor for the currently opened file. +(Contributed by Georg Brandl.) + +\item In the \module{gc} module, the new \function{get_count()} function +returns a 3-tuple containing the current collection counts for the +three GC generations. This is accounting information for the garbage +collector; when these counts reach a specified threshold, a garbage +collection sweep will be made. The existing \function{gc.collect()} +function now takes an optional \var{generation} argument of 0, 1, or 2 +to specify which generation to collect. \item The \function{nsmallest()} and \function{nlargest()} functions in the \module{heapq} module @@ -568,6 +1330,11 @@ itertools.islice(iterable, s.start, s.stop, s.step) (Contributed by Raymond Hettinger.) +\item The \module{nis} module now supports accessing domains other +than the system default domain by supplying a \var{domain} argument to +the \function{nis.match()} and \function{nis.maps()} functions. +(Contributed by Ben Bell.) + \item The \module{operator} module's \function{itemgetter()} and \function{attrgetter()} functions now support multiple fields. A call such as \code{operator.attrgetter('a', 'b')} @@ -575,11 +1342,10 @@ will return a function that retrieves the \member{a} and \member{b} attributes. Combining this new feature with the \method{sort()} method's \code{key} parameter lets you easily sort lists using multiple fields. - -% XXX who added? +(Contributed by Raymond Hettinger.) -\item The \module{os} module underwent a number of changes. The +\item The \module{os} module underwent several changes. The \member{stat_float_times} variable now defaults to true, meaning that \function{os.stat()} will now return time values as floats. (This doesn't necessarily mean that \function{os.stat()} will return times @@ -591,11 +1357,34 @@ Constants named \member{os.SEEK_SET}, \member{os.SEEK_CUR}, and \function{os.lseek()} function. Two new constants for locking are \member{os.O_SHLOCK} and \member{os.O_EXLOCK}. +Two new functions, \function{wait3()} and \function{wait4()}, were +added. They're similar the \function{waitpid()} function which waits +for a child process to exit and returns a tuple of the process ID and +its exit status, but \function{wait3()} and \function{wait4()} return +additional information. \function{wait3()} doesn't take a process ID +as input, so it waits for any child process to exit and returns a +3-tuple of \var{process-id}, \var{exit-status}, \var{resource-usage} +as returned from the \function{resource.getrusage()} function. +\function{wait4(\var{pid})} does take a process ID. +(Contributed by Chad J. Schroeder.) + On FreeBSD, the \function{os.stat()} function now returns times with nanosecond resolution, and the returned object now has \member{st_gen} and \member{st_birthtime}. The \member{st_flags} member is also available, if the platform supports it. -% XXX patch 1180695, 1212117 +(Contributed by Antti Louko and Diego Petten\`o.) +% (Patch 1180695, 1212117) + +\item The old \module{regex} and \module{regsub} modules, which have been +deprecated ever since Python 2.0, have finally been deleted. +Other deleted modules: \module{statcache}, \module{tzparse}, +\module{whrandom}. + +\item The \file{lib-old} directory, +which includes ancient modules such as \module{dircmp} and +\module{ni}, was also deleted. \file{lib-old} wasn't on the default +\code{sys.path}, so unless your programs explicitly added the directory to +\code{sys.path}, this removal shouldn't affect your code. \item The \module{socket} module now supports \constant{AF_NETLINK} sockets on Linux, thanks to a patch from Philippe Biondi. @@ -605,9 +1394,26 @@ article about them is at \url{http://www.linuxjournal.com/article/7356}. In Python code, netlink addresses are represented as a tuple of 2 integers, \code{(\var{pid}, \var{group_mask})}. -\item New module: \module{spwd} provides functions for accessing the -shadow password database on systems that support it. -% XXX give example +Socket objects also gained accessor methods \method{getfamily()}, +\method{gettype()}, and \method{getproto()} methods to retrieve the +family, type, and protocol values for the socket. + +\item New module: the \module{spwd} module provides functions for +accessing the shadow password database on systems that support +shadow passwords. + +\item The Python developers switched from CVS to Subversion during the 2.5 +development process. Information about the exact build version is +available as the \code{sys.subversion} variable, a 3-tuple +of \code{(\var{interpreter-name}, \var{branch-name}, \var{revision-range})}. +For example, at the time of writing +my copy of 2.5 was reporting \code{('CPython', 'trunk', '45313:45315')}. + +This information is also available to C extensions via the +\cfunction{Py_GetBuildInfo()} function that returns a +string of build information like this: +\code{"trunk:45355:45356M, Apr 13 2006, 07:42:19"}. +(Contributed by Barry Warsaw.) \item The \class{TarFile} class in the \module{tarfile} module now has an \method{extractall()} method that extracts all members from the @@ -625,17 +1431,28 @@ of the Unicode character database. Version 3.2.0 is required by some specifications, so it's still available as \member{unicodedata.db_3_2_0}. -\item A new package \module{xml.etree} has been added, which contains -a subset of the ElementTree XML library. Available modules are -\module{ElementTree}, \module{ElementPath}, and -\module{ElementInclude}, from ElementTree 1.2.6. (Contributed by -Fredrik Lundh.) +\item The \module{webbrowser} module received a number of +enhancements. +It's now usable as a script with \code{python -m webbrowser}, taking a +URL as the argument; there are a number of switches +to control the behaviour (\programopt{-n} for a new browser window, +\programopt{-t} for a new tab). New module-level functions, +\function{open_new()} and \function{open_new_tab()}, were added +to support this. The module's \function{open()} function supports an +additional feature, an \var{autoraise} parameter that signals whether +to raise the open window when possible. A number of additional +browsers were added to the supported list such as Firefox, Opera, +Konqueror, and elinks. (Contributed by Oleg Broytmann and George +Brandl.) +% Patch #754022 + \item The \module{xmlrpclib} module now supports returning \class{datetime} objects for the XML-RPC date type. Supply \code{use_datetime=True} to the \function{loads()} function or the \class{Unmarshaller} class to enable this feature. -% XXX patch 1120353 + (Contributed by Skip Montanaro.) +% Patch 1120353 \end{itemize} @@ -643,13 +1460,358 @@ Fredrik Lundh.) %====================================================================== -% whole new modules get described in \subsections here +\subsection{The ctypes package} + +The \module{ctypes} package, written by Thomas Heller, has been added +to the standard library. \module{ctypes} lets you call arbitrary functions +in shared libraries or DLLs. Long-time users may remember the \module{dl} module, which +provides functions for loading shared libraries and calling functions in them. The \module{ctypes} package is much fancier. + +To load a shared library or DLL, you must create an instance of the +\class{CDLL} class and provide the name or path of the shared library +or DLL. Once that's done, you can call arbitrary functions +by accessing them as attributes of the \class{CDLL} object. + +\begin{verbatim} +import ctypes + +libc = ctypes.CDLL('libc.so.6') +result = libc.printf("Line of output\n") +\end{verbatim} + +Type constructors for the various C types are provided: \function{c_int}, +\function{c_float}, \function{c_double}, \function{c_char_p} (equivalent to \ctype{char *}), and so forth. Unlike Python's types, the C versions are all mutable; you can assign to their \member{value} attribute +to change the wrapped value. Python integers and strings will be automatically +converted to the corresponding C types, but for other types you +must call the correct type constructor. (And I mean \emph{must}; +getting it wrong will often result in the interpreter crashing +with a segmentation fault.) + +You shouldn't use \function{c_char_p} with a Python string when the C function will be modifying the memory area, because Python strings are +supposed to be immutable; breaking this rule will cause puzzling bugs. When you need a modifiable memory area, +use \function{create_string_buffer()}: + +\begin{verbatim} +s = "this is a string" +buf = ctypes.create_string_buffer(s) +libc.strfry(buf) +\end{verbatim} + +C functions are assumed to return integers, but you can set +the \member{restype} attribute of the function object to +change this: + +\begin{verbatim} +>>> libc.atof('2.71828') +-1783957616 +>>> libc.atof.restype = ctypes.c_double +>>> libc.atof('2.71828') +2.71828 +\end{verbatim} + +\module{ctypes} also provides a wrapper for Python's C API +as the \code{ctypes.pythonapi} object. This object does \emph{not} +release the global interpreter lock before calling a function, because the lock must be held when calling into the interpreter's code. +There's a \class{py_object()} type constructor that will create a +\ctype{PyObject *} pointer. A simple usage: + +\begin{verbatim} +import ctypes + +d = {} +ctypes.pythonapi.PyObject_SetItem(ctypes.py_object(d), + ctypes.py_object("abc"), ctypes.py_object(1)) +# d is now {'abc', 1}. +\end{verbatim} + +Don't forget to use \class{py_object()}; if it's omitted you end +up with a segmentation fault. + +\module{ctypes} has been around for a while, but people still write +and distribution hand-coded extension modules because you can't rely on \module{ctypes} being present. +Perhaps developers will begin to write +Python wrappers atop a library accessed through \module{ctypes} instead +of extension modules, now that \module{ctypes} is included with core Python. + +\begin{seealso} + +\seeurl{http://starship.python.net/crew/theller/ctypes/} +{The ctypes web page, with a tutorial, reference, and FAQ.} + +\end{seealso} + + +%====================================================================== +\subsection{The ElementTree package} + +A subset of Fredrik Lundh's ElementTree library for processing XML has +been added to the standard library as \module{xmlcore.etree}. The +available modules are +\module{ElementTree}, \module{ElementPath}, and +\module{ElementInclude} from ElementTree 1.2.6. +The \module{cElementTree} accelerator module is also included. + +The rest of this section will provide a brief overview of using +ElementTree. Full documentation for ElementTree is available at +\url{http://effbot.org/zone/element-index.htm}. -% XXX new distutils features: upload +ElementTree represents an XML document as a tree of element nodes. +The text content of the document is stored as the \member{.text} +and \member{.tail} attributes of +(This is one of the major differences between ElementTree and +the Document Object Model; in the DOM there are many different +types of node, including \class{TextNode}.) -% XXX should hashlib perhaps be described here instead? -% XXX should xml.etree perhaps be described here instead? +The most commonly used parsing function is \function{parse()}, that +takes either a string (assumed to contain a filename) or a file-like +object and returns an \class{ElementTree} instance: + +\begin{verbatim} +from xmlcore.etree import ElementTree as ET + +tree = ET.parse('ex-1.xml') + +feed = urllib.urlopen( + 'http://planet.python.org/rss10.xml') +tree = ET.parse(feed) +\end{verbatim} +Once you have an \class{ElementTree} instance, you +can call its \method{getroot()} method to get the root \class{Element} node. + +There's also an \function{XML()} function that takes a string literal +and returns an \class{Element} node (not an \class{ElementTree}). +This function provides a tidy way to incorporate XML fragments, +approaching the convenience of an XML literal: + +\begin{verbatim} +svg = et.XML(""" + """) +svg.set('height', '320px') +svg.append(elem1) +\end{verbatim} + +Each XML element supports some dictionary-like and some list-like +access methods. Dictionary-like operations are used to access attribute +values, and list-like operations are used to access child nodes. + +\begin{tableii}{c|l}{code}{Operation}{Result} + \lineii{elem[n]}{Returns n'th child element.} + \lineii{elem[m:n]}{Returns list of m'th through n'th child elements.} + \lineii{len(elem)}{Returns number of child elements.} + \lineii{elem.getchildren()}{Returns list of child elements.} + \lineii{elem.append(elem2)}{Adds \var{elem2} as a child.} + \lineii{elem.insert(index, elem2)}{Inserts \var{elem2} at the specified location.} + \lineii{del elem[n]}{Deletes n'th child element.} + \lineii{elem.keys()}{Returns list of attribute names.} + \lineii{elem.get(name)}{Returns value of attribute \var{name}.} + \lineii{elem.set(name, value)}{Sets new value for attribute \var{name}.} + \lineii{elem.attrib}{Retrieves the dictionary containing attributes.} + \lineii{del elem.attrib[name]}{Deletes attribute \var{name}.} +\end{tableii} + +Comments and processing instructions are also represented as +\class{Element} nodes. To check if a node is a comment or processing +instructions: + +\begin{verbatim} +if elem.tag is ET.Comment: + ... +elif elem.tag is ET.ProcessingInstruction: + ... +\end{verbatim} + +To generate XML output, you should call the +\method{ElementTree.write()} method. Like \function{parse()}, +it can take either a string or a file-like object: + +\begin{verbatim} +# Encoding is US-ASCII +tree.write('output.xml') + +# Encoding is UTF-8 +f = open('output.xml', 'w') +tree.write(f, 'utf-8') +\end{verbatim} + +(Caution: the default encoding used for output is ASCII, which isn't +very useful for general XML work, raising an exception if there are +any characters with values greater than 127. You should always +specify a different encoding such as UTF-8 that can handle any Unicode +character.) + +This section is only a partial description of the ElementTree interfaces. +Please read the package's official documentation for more details. + +\begin{seealso} + +\seeurl{http://effbot.org/zone/element-index.htm} +{Official documentation for ElementTree.} + + +\end{seealso} + + +%====================================================================== +\subsection{The hashlib package} + +A new \module{hashlib} module, written by Gregory P. Smith, +has been added to replace the +\module{md5} and \module{sha} modules. \module{hashlib} adds support +for additional secure hashes (SHA-224, SHA-256, SHA-384, and SHA-512). +When available, the module uses OpenSSL for fast platform optimized +implementations of algorithms. + +The old \module{md5} and \module{sha} modules still exist as wrappers +around hashlib to preserve backwards compatibility. The new module's +interface is very close to that of the old modules, but not identical. +The most significant difference is that the constructor functions +for creating new hashing objects are named differently. + +\begin{verbatim} +# Old versions +h = md5.md5() +h = md5.new() + +# New version +h = hashlib.md5() + +# Old versions +h = sha.sha() +h = sha.new() + +# New version +h = hashlib.sha1() + +# Hash that weren't previously available +h = hashlib.sha224() +h = hashlib.sha256() +h = hashlib.sha384() +h = hashlib.sha512() + +# Alternative form +h = hashlib.new('md5') # Provide algorithm as a string +\end{verbatim} + +Once a hash object has been created, its methods are the same as before: +\method{update(\var{string})} hashes the specified string into the +current digest state, \method{digest()} and \method{hexdigest()} +return the digest value as a binary string or a string of hex digits, +and \method{copy()} returns a new hashing object with the same digest state. + + +%====================================================================== +\subsection{The sqlite3 package} + +The pysqlite module (\url{http://www.pysqlite.org}), a wrapper for the +SQLite embedded database, has been added to the standard library under +the package name \module{sqlite3}. + +SQLite is a C library that provides a SQL-language database that +stores data in disk files without requiring a separate server process. +pysqlite was written by Gerhard H\"aring and provides a SQL interface +compliant with the DB-API 2.0 specification described by +\pep{249}. This means that it should be possible to write the first +version of your applications using SQLite for data storage. If +switching to a larger database such as PostgreSQL or Oracle is +later necessary, the switch should be relatively easy. + +If you're compiling the Python source yourself, note that the source +tree doesn't include the SQLite code, only the wrapper module. +You'll need to have the SQLite libraries and headers installed before +compiling Python, and the build process will compile the module when +the necessary headers are available. + +To use the module, you must first create a \class{Connection} object +that represents the database. Here the data will be stored in the +\file{/tmp/example} file: + +\begin{verbatim} +conn = sqlite3.connect('/tmp/example') +\end{verbatim} + +You can also supply the special name \samp{:memory:} to create +a database in RAM. + +Once you have a \class{Connection}, you can create a \class{Cursor} +object and call its \method{execute()} method to perform SQL commands: + +\begin{verbatim} +c = conn.cursor() + +# Create table +c.execute('''create table stocks +(date timestamp, trans varchar, symbol varchar, + qty decimal, price decimal)''') + +# Insert a row of data +c.execute("""insert into stocks + values ('2006-01-05','BUY','RHAT',100,35.14)""") +\end{verbatim} + +Usually your SQL operations will need to use values from Python +variables. You shouldn't assemble your query using Python's string +operations because doing so is insecure; it makes your program +vulnerable to an SQL injection attack. + +Instead, use SQLite's parameter substitution. Put \samp{?} as a +placeholder wherever you want to use a value, and then provide a tuple +of values as the second argument to the cursor's \method{execute()} +method. For example: + +\begin{verbatim} +# Never do this -- insecure! +symbol = 'IBM' +c.execute("... where symbol = '%s'" % symbol) + +# Do this instead +t = (symbol,) +c.execute('select * from stocks where symbol=?', ('IBM',)) + +# Larger example +for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00), + ('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00), + ('2006-04-06', 'SELL', 'IBM', 500, 53.00), + ): + c.execute('insert into stocks values (?,?,?,?,?)', t) +\end{verbatim} + +To retrieve data after executing a SELECT statement, you can either +treat the cursor as an iterator, call the cursor's \method{fetchone()} +method to retrieve a single matching row, +or call \method{fetchall()} to get a list of the matching rows. + +This example uses the iterator form: + +\begin{verbatim} +>>> c = conn.cursor() +>>> c.execute('select * from stocks order by price') +>>> for row in c: +... print row +... +(u'2006-01-05', u'BUY', u'RHAT', 100, 35.140000000000001) +(u'2006-03-28', u'BUY', u'IBM', 1000, 45.0) +(u'2006-04-06', u'SELL', u'IBM', 500, 53.0) +(u'2006-04-05', u'BUY', u'MSOFT', 1000, 72.0) +>>> +\end{verbatim} + +For more information about the SQL dialect supported by SQLite, see +\url{http://www.sqlite.org}. + +\begin{seealso} + +\seeurl{http://www.pysqlite.org} +{The pysqlite web page.} + +\seeurl{http://www.sqlite.org} +{The SQLite web page; the documentation describes the syntax and the +available data types for the supported SQL dialect.} + +\seepep{249}{Database API Specification 2.0}{PEP written by +Marc-Andr\'e Lemburg.} + +\end{seealso} % ====================================================================== @@ -659,11 +1821,32 @@ Changes to Python's build process and to the C API include: \begin{itemize} -\item The design of the bytecode compiler has changed a great deal, no -longer generating bytecode by traversing the parse tree. Instead +\item The largest change to the C API came from \pep{353}, +which modifies the interpreter to use a \ctype{Py_ssize_t} type +definition instead of \ctype{int}. See the earlier +section~\ref{section-353} for a discussion of this change. + +\item The design of the bytecode compiler has changed a great deal, to +no longer generate bytecode by traversing the parse tree. Instead the parse tree is converted to an abstract syntax tree (or AST), and it is the abstract syntax tree that's traversed to produce the bytecode. +It's possible for Python code to obtain AST objects by using the +\function{compile()} built-in and specifying \code{_ast.PyCF_ONLY_AST} +as the value of the +\var{flags} parameter: + +\begin{verbatim} +from _ast import PyCF_ONLY_AST +ast = compile("""a=0 +for i in range(10): + a += i +""", "", 'exec', PyCF_ONLY_AST) + +assignment = ast.body[0] +for_loop = ast.body[1] +\end{verbatim} + No documentation has been written for the AST code yet. To start learning about it, read the definition of the various AST nodes in \file{Parser/Python.asdl}. A Python script reads this file and @@ -689,6 +1872,18 @@ AST sprints at conferences such as PyCon. new set, \cfunction{PySet_Add()} and \cfunction{PySet_Discard()} to add and remove elements, and \cfunction{PySet_Contains} and \cfunction{PySet_Size} to examine the set's state. +(Contributed by Raymond Hettinger.) + +\item C code can now obtain information about the exact revision +of the Python interpreter by calling the +\cfunction{Py_GetBuildInfo()} function that returns a +string of build information like this: +\code{"trunk:45355:45356M, Apr 13 2006, 07:42:19"}. +(Contributed by Barry Warsaw.) + +\item The CPython interpreter is still written in C, but +the code can now be compiled with a {\Cpp} compiler without errors. +(Implemented by Anthony Baxter, Martin von~L\"owis, Skip Montanaro.) \item The \cfunction{PyRange_New()} function was removed. It was never documented, never used in the core code, and had dangerously lax @@ -700,7 +1895,17 @@ error checking. %====================================================================== \subsection{Port-Specific Changes} -Platform-specific changes go here. +\begin{itemize} + +\item MacOS X (10.3 and higher): dynamic loading of modules +now uses the \cfunction{dlopen()} function instead of MacOS-specific +functions. + +\item Windows: \file{.dll} is no longer supported as a filename extension for +extension modules. \file{.pyd} is now the only filename extension that will +be searched for. + +\end{itemize} %====================================================================== @@ -715,7 +1920,38 @@ Some of the more notable changes are: \begin{itemize} -\item Details go here. +\item Evan Jones's patch to obmalloc, first described in a talk +at PyCon DC 2005, was applied. Python 2.4 allocated small objects in +256K-sized arenas, but never freed arenas. With this patch, Python +will free arenas when they're empty. The net effect is that on some +platforms, when you allocate many objects, Python's memory usage may +actually drop when you delete them, and the memory may be returned to +the operating system. (Implemented by Evan Jones, and reworked by Tim +Peters.) + +Note that this change means extension modules need to be more careful +with how they allocate memory. Python's API has many different +functions for allocating memory that are grouped into families. For +example, \cfunction{PyMem_Malloc()}, \cfunction{PyMem_Realloc()}, and +\cfunction{PyMem_Free()} are one family that allocates raw memory, +while \cfunction{PyObject_Malloc()}, \cfunction{PyObject_Realloc()}, +and \cfunction{PyObject_Free()} are another family that's supposed to +be used for creating Python objects. + +Previously these different families all reduced to the platform's +\cfunction{malloc()} and \cfunction{free()} functions. This meant +it didn't matter if you got things wrong and allocated memory with the +\cfunction{PyMem} function but freed it with the \cfunction{PyObject} +function. With the obmalloc change, these families now do different +things, and mismatches will probably result in a segfault. You should +carefully test your C extension modules with Python 2.5. + +\item Coverity, a company that markets a source code analysis tool + called Prevent, provided the results of their examination of the Python + source code. The analysis found about 60 bugs that + were quickly fixed. Many of the bugs were refcounting problems, often + occurring in error-handling code. See + \url{http://scan.coverity.com} for the statistics. \end{itemize} @@ -728,24 +1964,32 @@ changes to your code: \begin{itemize} -\item Some old deprecated modules (\module{statcache}, \module{tzparse}, - \module{whrandom}) have been moved to \file{Lib/lib-old}. -You can get access to these modules again by adding the directory -to your \code{sys.path}: - -\begin{verbatim} -import os -from distutils import sysconfig - -lib_dir = sysconfig.get_python_lib(standard_lib=True) -old_dir = os.path.join(lib_dir, 'lib-old') -sys.path.append(old_dir) -\end{verbatim} - -Doing so is discouraged, however; it's better to update any code that -still uses these modules. - -% the pickle module no longer uses the deprecated bin parameter. +\item ASCII is now the default encoding for modules. It's now +a syntax error if a module contains string literals with 8-bit +characters but doesn't have an encoding declaration. In Python 2.4 +this triggered a warning, not a syntax error. + +\item The \module{pickle} module no longer uses the deprecated \var{bin} parameter. + +\item Previously, the \member{gi_frame} attribute of a generator +was always a frame object. Because of the \pep{342} changes +described in section~\ref{section-generators}, it's now possible +for \member{gi_frame} to be \code{None}. + +\item C API: Many functions now use \ctype{Py_ssize_t} +instead of \ctype{int} to allow processing more data +on 64-bit machines. Extension code may need to make +the same change to avoid warnings and to support 64-bit machines. +See the earlier +section~\ref{section-353} for a discussion of this change. + +\item C API: +The obmalloc changes mean that +you must be careful to not mix usage +of the \cfunction{PyMem_*()} and \cfunction{PyObject_*()} +families of functions. Memory allocated with +one family's \cfunction{*_Malloc()} must be +freed with the corresponding family's \cfunction{*_Free()} function. \end{itemize} @@ -755,6 +1999,7 @@ still uses these modules. The author would like to thank the following people for offering suggestions, corrections and assistance with various drafts of this -article: . +article: Phillip J. Eby, Kent Johnson, Martin von~L\"owis, Gustavo +Niemeyer, Mike Rovner, Thomas Wouters. \end{document} diff --git a/Grammar/Grammar b/Grammar/Grammar index 33c37d2..a613de6 100644 --- a/Grammar/Grammar +++ b/Grammar/Grammar @@ -7,6 +7,9 @@ # with someone who can; ask around on python-dev for help. Fred # Drake will probably be listening there. +# NOTE WELL: You should also follow all the steps listed in PEP 306, +# "How to Change Python's Grammar" + # Start symbols for the grammar: # single_input is a single interactive statement; # file_input is a module or sequence of commands read from an input file; @@ -119,11 +122,11 @@ argument: test [gen_for] | test '=' test # Really [keyword '='] test list_iter: list_for | list_if list_for: 'for' exprlist 'in' testlist_safe [list_iter] -list_if: 'if' test [list_iter] +list_if: 'if' old_test [list_iter] gen_iter: gen_for | gen_if gen_for: 'for' exprlist 'in' or_test [gen_iter] -gen_if: 'if' test [gen_iter] +gen_if: 'if' old_test [gen_iter] testlist1: test (',' test)* diff --git a/Include/Python-ast.h b/Include/Python-ast.h index b3bc063..3e21030 100644 --- a/Include/Python-ast.h +++ b/Include/Python-ast.h @@ -35,9 +35,10 @@ typedef struct _keyword *keyword_ty; typedef struct _alias *alias_ty; +enum _mod_kind {Module_kind=1, Interactive_kind=2, Expression_kind=3, + Suite_kind=4}; struct _mod { - enum { Module_kind=1, Interactive_kind=2, Expression_kind=3, - Suite_kind=4 } kind; + enum _mod_kind kind; union { struct { asdl_seq *body; @@ -58,14 +59,15 @@ struct _mod { } v; }; +enum _stmt_kind {FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3, + Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7, + For_kind=8, While_kind=9, If_kind=10, With_kind=11, + Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14, + Assert_kind=15, Import_kind=16, ImportFrom_kind=17, + Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21, + Break_kind=22, Continue_kind=23}; struct _stmt { - enum { FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3, - Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7, - For_kind=8, While_kind=9, If_kind=10, With_kind=11, - Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14, - Assert_kind=15, Import_kind=16, ImportFrom_kind=17, - Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21, - Break_kind=22, Continue_kind=23 } kind; + enum _stmt_kind kind; union { struct { identifier name; @@ -181,12 +183,14 @@ struct _stmt { int col_offset; }; +enum _expr_kind {BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4, + IfExp_kind=5, Dict_kind=6, ListComp_kind=7, + GeneratorExp_kind=8, Yield_kind=9, Compare_kind=10, + Call_kind=11, Repr_kind=12, Num_kind=13, Str_kind=14, + Attribute_kind=15, Subscript_kind=16, Name_kind=17, + List_kind=18, Tuple_kind=19}; struct _expr { - enum { BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4, - IfExp_kind=5, Dict_kind=6, ListComp_kind=7, GeneratorExp_kind=8, - Yield_kind=9, Compare_kind=10, Call_kind=11, Repr_kind=12, - Num_kind=13, Str_kind=14, Attribute_kind=15, Subscript_kind=16, - Name_kind=17, List_kind=18, Tuple_kind=19 } kind; + enum _expr_kind kind; union { struct { boolop_ty op; @@ -236,7 +240,7 @@ struct _expr { struct { expr_ty left; - asdl_seq *ops; + asdl_int_seq *ops; asdl_seq *comparators; } Compare; @@ -292,9 +296,9 @@ struct _expr { int col_offset; }; +enum _slice_kind {Ellipsis_kind=1, Slice_kind=2, ExtSlice_kind=3, Index_kind=4}; struct _slice { - enum { Ellipsis_kind=1, Slice_kind=2, ExtSlice_kind=3, Index_kind=4 } - kind; + enum _slice_kind kind; union { struct { expr_ty lower; @@ -323,6 +327,8 @@ struct _excepthandler { expr_ty type; expr_ty name; asdl_seq *body; + int lineno; + int col_offset; }; struct _arguments { @@ -403,7 +409,7 @@ expr_ty ListComp(expr_ty elt, asdl_seq * generators, int lineno, int expr_ty GeneratorExp(expr_ty elt, asdl_seq * generators, int lineno, int col_offset, PyArena *arena); expr_ty Yield(expr_ty value, int lineno, int col_offset, PyArena *arena); -expr_ty Compare(expr_ty left, asdl_seq * ops, asdl_seq * comparators, int +expr_ty Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators, int lineno, int col_offset, PyArena *arena); expr_ty Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, expr_ty starargs, expr_ty kwargs, int lineno, int col_offset, PyArena @@ -427,8 +433,8 @@ slice_ty ExtSlice(asdl_seq * dims, PyArena *arena); slice_ty Index(expr_ty value, PyArena *arena); comprehension_ty comprehension(expr_ty target, expr_ty iter, asdl_seq * ifs, PyArena *arena); -excepthandler_ty excepthandler(expr_ty type, expr_ty name, asdl_seq * body, - PyArena *arena); +excepthandler_ty excepthandler(expr_ty type, expr_ty name, asdl_seq * body, int + lineno, int col_offset, PyArena *arena); arguments_ty arguments(asdl_seq * args, identifier vararg, identifier kwarg, asdl_seq * defaults, PyArena *arena); keyword_ty keyword(identifier arg, expr_ty value, PyArena *arena); diff --git a/Include/abstract.h b/Include/abstract.h index b76f257..d4bd588 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -4,6 +4,11 @@ extern "C" { #endif +#ifdef PY_SSIZE_T_CLEAN +#define PyObject_CallFunction _PyObject_CallFunction_SizeT +#define PyObject_CallMethod _PyObject_CallMethod_SizeT +#endif + /* Abstract Object Interface (many thanks to Jim Fulton) */ /* @@ -337,6 +342,11 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ Python expression: o.method(args). */ + PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable, + char *format, ...); + PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *o, + char *name, + char *format, ...); PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, ...); diff --git a/Include/asdl.h b/Include/asdl.h index c1c5603..84e837e 100644 --- a/Include/asdl.h +++ b/Include/asdl.h @@ -5,7 +5,9 @@ typedef PyObject * identifier; typedef PyObject * string; typedef PyObject * object; +#ifndef __cplusplus typedef enum {false, true} bool; +#endif /* It would be nice if the code generated by asdl_c.py was completely independent of Python, but it is a goal the requires too much work @@ -20,7 +22,13 @@ typedef struct { void *elements[1]; } asdl_seq; +typedef struct { + int size; + int elements[1]; +} asdl_int_seq; + asdl_seq *asdl_seq_new(int size, PyArena *arena); +asdl_int_seq *asdl_int_seq_new(int size, PyArena *arena); #define asdl_seq_GET(S, I) (S)->elements[(I)] #define asdl_seq_LEN(S) ((S) == NULL ? 0 : (S)->size) diff --git a/Include/code.h b/Include/code.h index a4509e3..23d9e17 100644 --- a/Include/code.h +++ b/Include/code.h @@ -44,7 +44,7 @@ typedef struct { /* These are no longer used. */ #define CO_GENERATOR_ALLOWED 0x1000 #define CO_FUTURE_DIVISION 0x2000 -#define CO_FUTURE_ABSIMPORT 0x4000 /* absolute import by default */ +#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */ #define CO_FUTURE_WITH_STATEMENT 0x8000 #endif @@ -72,6 +72,21 @@ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); ((*(co)->co_code->ob_type->tp_as_buffer->bf_getreadbuffer) \ ((co)->co_code, 0, (void **)(pp))) +typedef struct _addr_pair { + int ap_lower; + int ap_upper; +} PyAddrPair; + +/* Check whether lasti (an instruction offset) falls outside bounds + and whether it is a line number that should be traced. Returns + a line number if it should be traced or -1 if the line should not. + + If lasti is not within bounds, updates bounds. +*/ + +PyAPI_FUNC(int) PyCode_CheckLineNumber(PyCodeObject* co, + int lasti, PyAddrPair *bounds); + #ifdef __cplusplus } #endif diff --git a/Include/compile.h b/Include/compile.h index 4ac6982..2bde6fb 100644 --- a/Include/compile.h +++ b/Include/compile.h @@ -22,7 +22,7 @@ typedef struct { #define FUTURE_NESTED_SCOPES "nested_scopes" #define FUTURE_GENERATORS "generators" #define FUTURE_DIVISION "division" -#define FUTURE_ABSIMPORT "absolute_import" +#define FUTURE_ABSOLUTE_IMPORT "absolute_import" #define FUTURE_WITH_STATEMENT "with_statement" struct _mod; /* Declare the existence of this type */ diff --git a/Include/genobject.h b/Include/genobject.h index f4226ed..ca84432 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -13,6 +13,7 @@ typedef struct { PyObject_HEAD /* The gi_ prefix is intended to remind of generator-iterator. */ + /* Note: gi_frame can be NULL if the generator is "finished" */ struct _frame *gi_frame; /* True if generator is being executed. */ @@ -28,6 +29,7 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_CheckExact(op) ((op)->ob_type == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); #ifdef __cplusplus } diff --git a/Include/modsupport.h b/Include/modsupport.h index c356f03..23d5d3a 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -17,8 +17,10 @@ extern "C" { #define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT #define PyArg_VaParse _PyArg_VaParse_SizeT #define PyArg_VaParseTupleAndKeywords _PyArg_VaParseTupleAndKeywords_SizeT -#define PyArg_BuildValue _PyArg_BuildValue_SizeT -#define PyArg_VaBuildValue _PyArg_VaBuildValue_SizeT +#define Py_BuildValue _Py_BuildValue_SizeT +#define Py_VaBuildValue _Py_VaBuildValue_SizeT +#else +PyAPI_FUNC(PyObject *) _Py_VaBuildValue_SizeT(const char *, va_list); #endif PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); @@ -27,6 +29,7 @@ PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, const char *, char **, ...); PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...); PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...); +PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw); PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); diff --git a/Include/object.h b/Include/object.h index 9198007..cdbddfe 100644 --- a/Include/object.h +++ b/Include/object.h @@ -327,6 +327,7 @@ typedef struct _typeobject { Py_ssize_t tp_allocs; Py_ssize_t tp_frees; Py_ssize_t tp_maxalloc; + struct _typeobject *tp_prev; struct _typeobject *tp_next; #endif } PyTypeObject; @@ -566,6 +567,9 @@ environment the global variable trick is not safe.) PyAPI_DATA(Py_ssize_t) _Py_RefTotal; PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname, int lineno, PyObject *op); +PyAPI_FUNC(PyObject *) _PyDict_Dummy(void); +PyAPI_FUNC(PyObject *) _PySet_Dummy(void); +PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void); #define _Py_INC_REFTOTAL _Py_RefTotal++ #define _Py_DEC_REFTOTAL _Py_RefTotal-- #define _Py_REF_DEBUG_COMMA , @@ -583,8 +587,9 @@ PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname, #ifdef COUNT_ALLOCS PyAPI_FUNC(void) inc_count(PyTypeObject *); +PyAPI_FUNC(void) dec_count(PyTypeObject *); #define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type) -#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++ +#define _Py_INC_TPFREES(OP) dec_count((OP)->ob_type) #define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees-- #define _Py_COUNT_ALLOCS_COMMA , #else @@ -630,6 +635,40 @@ PyAPI_FUNC(void) _Py_AddToAllObjects(PyObject *, int force); else \ _Py_Dealloc((PyObject *)(op)) +/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear + * and tp_dealloc implementatons. + * + * Note that "the obvious" code can be deadly: + * + * Py_XDECREF(op); + * op = NULL; + * + * Typically, `op` is something like self->containee, and `self` is done + * using its `containee` member. In the code sequence above, suppose + * `containee` is non-NULL with a refcount of 1. Its refcount falls to + * 0 on the first line, which can trigger an arbitrary amount of code, + * possibly including finalizers (like __del__ methods or weakref callbacks) + * coded in Python, which in turn can release the GIL and allow other threads + * to run, etc. Such code may even invoke methods of `self` again, or cause + * cyclic gc to trigger, but-- oops! --self->containee still points to the + * object being torn down, and it may be in an insane state while being torn + * down. This has in fact been a rich historic source of miserable (rare & + * hard-to-diagnose) segfaulting (and other) bugs. + * + * The safe way is: + * + * Py_CLEAR(op); + * + * That arranges to set `op` to NULL _before_ decref'ing, so that any code + * triggered as a side-effect of `op` getting torn down no longer believes + * `op` points to a valid object. + * + * There are cases where it's safe to use the naive code, but they're brittle. + * For example, if `op` points to a Python integer, you know that destroying + * one of those can't cause problems -- but in part that relies on that + * Python integers aren't currently weakly referencable. Best practice is + * to use Py_CLEAR() even if you can't think of a reason for why you need to. + */ #define Py_CLEAR(op) \ do { \ if (op) { \ diff --git a/Include/objimpl.h b/Include/objimpl.h index 7c68194..03b6a8d 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -101,7 +101,7 @@ PyAPI_FUNC(void) PyObject_Free(void *); /* Macros */ #ifdef WITH_PYMALLOC -#ifdef PYMALLOC_DEBUG +#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); PyAPI_FUNC(void) _PyObject_DebugFree(void *p); @@ -124,11 +124,7 @@ PyAPI_FUNC(void) _PyObject_DebugMallocStats(void); #else /* ! WITH_PYMALLOC */ #define PyObject_MALLOC PyMem_MALLOC #define PyObject_REALLOC PyMem_REALLOC -/* This is an odd one! For backward compatibility with old extensions, the - PyMem "release memory" functions have to invoke the object allocator's - free() function. When pymalloc isn't enabled, that leaves us using - the platform free(). */ -#define PyObject_FREE free +#define PyObject_FREE PyMem_FREE #endif /* WITH_PYMALLOC */ @@ -307,13 +303,13 @@ PyAPI_FUNC(void) PyObject_GC_Del(void *); * "visit" and "arg". This is intended to keep tp_traverse functions * looking as much alike as possible. */ -#define Py_VISIT(op) \ - do { \ - if (op) { \ - int vret = visit((op), arg); \ - if (vret) \ - return vret; \ - } \ +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((PyObject *)(op), arg); \ + if (vret) \ + return vret; \ + } \ } while (0) /* This is here for the sake of backwards compatibility. Extensions that diff --git a/Include/pymem.h b/Include/pymem.h index f8aef29..671f967 100644 --- a/Include/pymem.h +++ b/Include/pymem.h @@ -59,6 +59,7 @@ PyAPI_FUNC(void) PyMem_Free(void *); /* Redirect all memory operations to Python's debugging allocator. */ #define PyMem_MALLOC PyObject_MALLOC #define PyMem_REALLOC PyObject_REALLOC +#define PyMem_FREE PyObject_FREE #else /* ! PYMALLOC_DEBUG */ @@ -68,14 +69,10 @@ PyAPI_FUNC(void) PyMem_Free(void *); pymalloc. To solve these problems, allocate an extra byte. */ #define PyMem_MALLOC(n) malloc((n) ? (n) : 1) #define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1) +#define PyMem_FREE free #endif /* PYMALLOC_DEBUG */ -/* In order to avoid breaking old code mixing PyObject_{New, NEW} with - PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" - functions have to be redirected to the object deallocator. */ -#define PyMem_FREE PyObject_FREE - /* * Type-oriented memory interface * ============================== @@ -95,11 +92,11 @@ PyAPI_FUNC(void) PyMem_Free(void *); #define PyMem_RESIZE(p, type, n) \ ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) -/* In order to avoid breaking old code mixing PyObject_{New, NEW} with - PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" - functions have to be redirected to the object deallocator. */ -#define PyMem_Del PyObject_Free -#define PyMem_DEL PyObject_FREE +/* PyMem{Del,DEL} are left over from ancient days, and shouldn't be used + * anymore. They're just confusing aliases for PyMem_{Free,FREE} now. + */ +#define PyMem_Del PyMem_Free +#define PyMem_DEL PyMem_FREE #ifdef __cplusplus } diff --git a/Include/pyport.h b/Include/pyport.h index 9111d86..2bce415 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -85,6 +85,10 @@ typedef PY_LONG_LONG Py_intptr_t; # error "Python needs a typedef for Py_uintptr_t in pyport.h." #endif /* HAVE_UINTPTR_T */ +/* Py_ssize_t is a signed integral type such that sizeof(Py_ssize_t) == + * sizeof(size_t). C99 doesn't define such a thing directly (size_t is an + * unsigned integral type). See PEP 353 for details. + */ #ifdef HAVE_SSIZE_T typedef ssize_t Py_ssize_t; #elif SIZEOF_VOID_P == SIZEOF_SIZE_T @@ -92,7 +96,46 @@ typedef Py_intptr_t Py_ssize_t; #else # error "Python needs a typedef for Py_ssize_t in pyport.h." #endif + +/* Largest positive value of type Py_ssize_t. */ #define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1)) +/* Smallest negative value of type Py_ssize_t. */ +#define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) + +/* PY_FORMAT_SIZE_T is a platform-specific modifier for use in a printf + * format to convert an argument with the width of a size_t or Py_ssize_t. + * C99 introduced "z" for this purpose, but not all platforms support that; + * e.g., MS compilers use "I" instead. + * + * These "high level" Python format functions interpret "z" correctly on + * all platforms (Python interprets the format string itself, and does whatever + * the platform C requires to convert a size_t/Py_ssize_t argument): + * + * PyString_FromFormat + * PyErr_Format + * PyString_FromFormatV + * + * Lower-level uses require that you interpolate the correct format modifier + * yourself (e.g., calling printf, fprintf, sprintf, PyOS_snprintf); for + * example, + * + * Py_ssize_t index; + * fprintf(stderr, "index %" PY_FORMAT_SIZE_T "d sucks\n", index); + * + * That will expand to %ld, or %Id, or to something else correct for a + * Py_ssize_t on the platform. + */ +#ifndef PY_FORMAT_SIZE_T +# if SIZEOF_SIZE_T == SIZEOF_INT +# define PY_FORMAT_SIZE_T "" +# elif SIZEOF_SIZE_T == SIZEOF_LONG +# define PY_FORMAT_SIZE_T "l" +# elif defined(MS_WINDOWS) +# define PY_FORMAT_SIZE_T "I" +# else +# error "This platform's pyconfig.h needs to define PY_FORMAT_SIZE_T" +# endif +#endif #include @@ -367,7 +410,8 @@ extern "C" { * typedef int T1 Py_DEPRECATED(2.4); * extern int x() Py_DEPRECATED(2.5); */ -#if defined(__GNUC__) && (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) +#if defined(__GNUC__) && ((__GNUC__ >= 4) || \ + (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) #define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__)) #else #define Py_DEPRECATED(VERSION_UNUSED) @@ -436,14 +480,12 @@ extern double hypot(double, double); #endif -/******************************************************************* -On 4.4BSD-descendants, ctype functions serves the whole range of -wchar_t character set rather than single byte code points only. -This characteristic can break some operations of string object -including str.upper() and str.split() on UTF-8 locales. This -workaround was provided by Tim Robbins of FreeBSD project. He said -the incompatibility will be fixed in FreeBSD 6. -********************************************************************/ +/* On 4.4BSD-descendants, ctype functions serves the whole range of + * wchar_t character set rather than single byte code points only. + * This characteristic can break some operations of string object + * including str.upper() and str.split() on UTF-8 locales. This + * workaround was provided by Tim Robbins of FreeBSD project. + */ #ifdef __FreeBSD__ #include diff --git a/Include/setobject.h b/Include/setobject.h index cea95cc..cc93968 100644 --- a/Include/setobject.h +++ b/Include/setobject.h @@ -78,10 +78,13 @@ PyAPI_FUNC(PyObject *) PySet_New(PyObject *); PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *); PyAPI_FUNC(Py_ssize_t) PySet_Size(PyObject *anyset); #define PySet_GET_SIZE(so) (((PySetObject *)(so))->used) +PyAPI_FUNC(int) PySet_Clear(PyObject *set); PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); PyAPI_FUNC(int) PySet_Discard(PyObject *set, PyObject *key); PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); +PyAPI_FUNC(int) _PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry); PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); +PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); #ifdef __cplusplus } diff --git a/Include/sliceobject.h b/Include/sliceobject.h index 17f36dc..dbc34b2 100644 --- a/Include/sliceobject.h +++ b/Include/sliceobject.h @@ -30,6 +30,7 @@ PyAPI_DATA(PyTypeObject) PySlice_Type; PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop, PyObject* step); +PyAPI_FUNC(PyObject *) _PySlice_FromIndices(Py_ssize_t start, Py_ssize_t stop); PyAPI_FUNC(int) PySlice_GetIndices(PySliceObject *r, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); PyAPI_FUNC(int) PySlice_GetIndicesEx(PySliceObject *r, Py_ssize_t length, diff --git a/LICENSE b/LICENSE index f0fc62a..5affefc 100644 --- a/LICENSE +++ b/LICENSE @@ -51,6 +51,8 @@ the various releases. 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.5 2.4 2006 PSF yes Footnotes: diff --git a/Lib/Queue.py b/Lib/Queue.py index c6c608b..51ad354 100644 --- a/Lib/Queue.py +++ b/Lib/Queue.py @@ -35,6 +35,51 @@ class Queue: # Notify not_full whenever an item is removed from the queue; # a thread waiting to put is notified then. self.not_full = threading.Condition(self.mutex) + # Notify all_tasks_done whenever the number of unfinished tasks + # drops to zero; thread waiting to join() is notified to resume + self.all_tasks_done = threading.Condition(self.mutex) + self.unfinished_tasks = 0 + + def task_done(self): + """Indicate that a formerly enqueued task is complete. + + Used by Queue consumer threads. For each get() used to fetch a task, + a subsequent call to task_done() tells the queue that the processing + on the task is complete. + + If a join() is currently blocking, it will resume when all items + have been processed (meaning that a task_done() call was received + for every item that had been put() into the queue). + + Raises a ValueError if called more times than there were items + placed in the queue. + """ + self.all_tasks_done.acquire() + try: + unfinished = self.unfinished_tasks - 1 + if unfinished <= 0: + if unfinished < 0: + raise ValueError('task_done() called too many times') + self.all_tasks_done.notifyAll() + self.unfinished_tasks = unfinished + finally: + self.all_tasks_done.release() + + def join(self): + """Blocks until all items in the Queue have been gotten and processed. + + The count of unfinished tasks goes up whenever an item is added to the + queue. The count goes down whenever a consumer thread calls task_done() + to indicate the item was retrieved and all work on it is complete. + + When the count of unfinished tasks drops to zero, join() unblocks. + """ + self.all_tasks_done.acquire() + try: + while self.unfinished_tasks: + self.all_tasks_done.wait() + finally: + self.all_tasks_done.release() def qsize(self): """Return the approximate size of the queue (not reliable!).""" @@ -86,6 +131,7 @@ class Queue: raise Full self.not_full.wait(remaining) self._put(item) + self.unfinished_tasks += 1 self.not_empty.notify() finally: self.not_full.release() diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/SimpleXMLRPCServer.py index 156c2ba..a0b44e1 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/SimpleXMLRPCServer.py @@ -104,7 +104,11 @@ from xmlrpclib import Fault import SocketServer import BaseHTTPServer import sys -import os, fcntl +import os +try: + import fcntl +except ImportError: + fcntl = None def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d @@ -493,7 +497,7 @@ class SimpleXMLRPCServer(SocketServer.TCPServer, # [Bug #1222790] If possible, set close-on-exec flag; if a # method spawns a subprocess, the subprocess shouldn't have # the listening socket open. - if hasattr(fcntl, 'FD_CLOEXEC'): + if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) flags |= fcntl.FD_CLOEXEC fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) diff --git a/Lib/__future__.py b/Lib/__future__.py index d95ce5f..d8e14d1 100644 --- a/Lib/__future__.py +++ b/Lib/__future__.py @@ -64,7 +64,7 @@ __all__ = ["all_feature_names"] + all_feature_names CO_NESTED = 0x0010 # nested_scopes CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000) CO_FUTURE_DIVISION = 0x2000 # division -CO_FUTURE_ABSIMPORT = 0x4000 # absolute_import +CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement class _Feature: @@ -109,7 +109,7 @@ division = _Feature((2, 2, 0, "alpha", 2), absolute_import = _Feature((2, 5, 0, "alpha", 1), (2, 7, 0, "alpha", 0), - CO_FUTURE_ABSIMPORT) + CO_FUTURE_ABSOLUTE_IMPORT) with_statement = _Feature((2, 5, 0, "alpha", 1), (2, 6, 0, "alpha", 0), diff --git a/Lib/_threading_local.py b/Lib/_threading_local.py index 90717a8..f0ce857 100644 --- a/Lib/_threading_local.py +++ b/Lib/_threading_local.py @@ -1,9 +1,9 @@ -"""Thread-local objects +"""Thread-local objects. -(Note that this module provides a Python version of thread - threading.local class. Depending on the version of Python you're - using, there may be a faster one available. You should always import - the local class from threading.) +(Note that this module provides a Python version of the threading.local + class. Depending on the version of Python you're using, there may be a + faster one available. You should always import the `local` class from + `threading`.) Thread-local objects support the management of thread-local data. If you have data that you want to be local to a thread, simply create @@ -133,7 +133,17 @@ affects what we see: >>> del mydata """ -# Threading import is at end +__all__ = ["local"] + +# We need to use objects from the threading module, but the threading +# module may also want to use our `local` class, if support for locals +# 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 +# then, so problems introduced by fiddling the order of imports here won't +# manifest on most boxes. class _localbase(object): __slots__ = '_local__key', '_local__args', '_local__lock' @@ -202,36 +212,30 @@ class local(_localbase): finally: lock.release() + def __del__(self): + import threading - def __del__(): - threading_enumerate = enumerate - __getattribute__ = object.__getattribute__ - - def __del__(self): - key = __getattribute__(self, '_local__key') + key = object.__getattribute__(self, '_local__key') + try: + threads = list(threading.enumerate()) + except: + # If enumerate 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: - threads = list(threading_enumerate()) - except: - # if enumerate 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 + __dict__ = thread.__dict__ + except AttributeError: + # Thread is dying, rest in peace. + continue - return __del__ - __del__ = __del__() + if key in __dict__: + try: + del __dict__[key] + except KeyError: + pass # didn't have anything in this thread -from threading import currentThread, enumerate, RLock +from threading import currentThread, RLock diff --git a/Lib/bdb.py b/Lib/bdb.py index 8f808cc..08b48c3 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -479,10 +479,10 @@ class Breakpoint: else: disp = 'keep ' if self.enabled: - disp = disp + 'yes' + disp = disp + 'yes ' else: - disp = disp + 'no ' - print '%-4dbreakpoint %s at %s:%d' % (self.number, disp, + disp = disp + 'no ' + print '%-4dbreakpoint %s at %s:%d' % (self.number, disp, self.file, self.line) if self.cond: print '\tstop only if %s' % (self.cond,) diff --git a/Lib/bsddb/__init__.py b/Lib/bsddb/__init__.py index d3ee773..c004c08 100644 --- a/Lib/bsddb/__init__.py +++ b/Lib/bsddb/__init__.py @@ -287,10 +287,9 @@ def hashopen(file, flag='c', mode=0666, pgsize=None, ffactor=None, nelem=None, cachesize=None, lorder=None, hflags=0): flags = _checkflag(flag, file) - e = _openDBEnv() + e = _openDBEnv(cachesize) d = db.DB(e) d.set_flags(hflags) - if cachesize is not None: d.set_cachesize(0, cachesize) if pgsize is not None: d.set_pagesize(pgsize) if lorder is not None: d.set_lorder(lorder) if ffactor is not None: d.set_h_ffactor(ffactor) @@ -305,9 +304,8 @@ def btopen(file, flag='c', mode=0666, pgsize=None, lorder=None): flags = _checkflag(flag, file) - e = _openDBEnv() + e = _openDBEnv(cachesize) d = db.DB(e) - if cachesize is not None: d.set_cachesize(0, cachesize) if pgsize is not None: d.set_pagesize(pgsize) if lorder is not None: d.set_lorder(lorder) d.set_flags(btflags) @@ -324,9 +322,8 @@ def rnopen(file, flag='c', mode=0666, rlen=None, delim=None, source=None, pad=None): flags = _checkflag(flag, file) - e = _openDBEnv() + e = _openDBEnv(cachesize) d = db.DB(e) - if cachesize is not None: d.set_cachesize(0, cachesize) if pgsize is not None: d.set_pagesize(pgsize) if lorder is not None: d.set_lorder(lorder) d.set_flags(rnflags) @@ -339,8 +336,13 @@ def rnopen(file, flag='c', mode=0666, #---------------------------------------------------------------------- -def _openDBEnv(): +def _openDBEnv(cachesize): e = db.DBEnv() + if cachesize is not None: + if cachesize >= 20480: + e.set_cachesize(0, cachesize) + else: + raise error, "cachesize must be >= 20480" e.open('.', db.DB_PRIVATE | db.DB_CREATE | db.DB_THREAD | db.DB_INIT_LOCK | db.DB_INIT_MPOOL) return e @@ -358,7 +360,7 @@ def _checkflag(flag, file): #flags = db.DB_CREATE | db.DB_TRUNCATE # we used db.DB_TRUNCATE flag for this before but BerkeleyDB # 4.2.52 changed to disallowed truncate with txn environments. - if os.path.isfile(file): + if file is not None and os.path.isfile(file): os.unlink(file) else: raise error, "flags should be one of 'r', 'w', 'c' or 'n'" diff --git a/Lib/bsddb/test/test_all.py b/Lib/bsddb/test/test_all.py index 972cd06..abfaf47 100644 --- a/Lib/bsddb/test/test_all.py +++ b/Lib/bsddb/test/test_all.py @@ -65,6 +65,7 @@ def suite(): 'test_join', 'test_lock', 'test_misc', + 'test_pickle', 'test_queue', 'test_recno', 'test_thread', diff --git a/Lib/bsddb/test/test_pickle.py b/Lib/bsddb/test/test_pickle.py new file mode 100644 index 0000000..3916e5c --- /dev/null +++ b/Lib/bsddb/test/test_pickle.py @@ -0,0 +1,75 @@ + +import sys, os, string +import pickle +try: + import cPickle +except ImportError: + cPickle = None +import unittest +import glob + +try: + # For Pythons w/distutils pybsddb + from bsddb3 import db +except ImportError, e: + # For Python 2.3 + from bsddb import db + + +#---------------------------------------------------------------------- + +class pickleTestCase(unittest.TestCase): + """Verify that DBError can be pickled and unpickled""" + db_home = 'db_home' + db_name = 'test-dbobj.db' + + def setUp(self): + homeDir = os.path.join(os.path.dirname(sys.argv[0]), 'db_home') + self.homeDir = homeDir + try: os.mkdir(homeDir) + except os.error: pass + + def tearDown(self): + if hasattr(self, 'db'): + del self.db + if hasattr(self, 'env'): + del self.env + files = glob.glob(os.path.join(self.homeDir, '*')) + for file in files: + os.remove(file) + + def _base_test_pickle_DBError(self, pickle): + self.env = db.DBEnv() + self.env.open(self.homeDir, db.DB_CREATE | db.DB_INIT_MPOOL) + self.db = db.DB(self.env) + self.db.open(self.db_name, db.DB_HASH, db.DB_CREATE) + self.db.put('spam', 'eggs') + assert self.db['spam'] == 'eggs' + try: + self.db.put('spam', 'ham', flags=db.DB_NOOVERWRITE) + except db.DBError, egg: + pickledEgg = pickle.dumps(egg) + #print repr(pickledEgg) + rottenEgg = pickle.loads(pickledEgg) + if rottenEgg.args != egg.args or type(rottenEgg) != type(egg): + raise Exception, (rottenEgg, '!=', egg) + else: + raise Exception, "where's my DBError exception?!?" + + self.db.close() + self.env.close() + + def test01_pickle_DBError(self): + self._base_test_pickle_DBError(pickle=pickle) + + if cPickle: + def test02_cPickle_DBError(self): + self._base_test_pickle_DBError(pickle=cPickle) + +#---------------------------------------------------------------------- + +def test_suite(): + return unittest.makeSuite(pickleTestCase) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/Lib/calendar.py b/Lib/calendar.py index 3ffcff5..7800aae 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -5,17 +5,32 @@ default, these calendars have Monday as the first day of the week, and Sunday as the last (the European convention). Use setfirstweekday() to set the first day of the week (0=Monday, 6=Sunday).""" -import datetime +from __future__ import with_statement +import sys, datetime, locale -__all__ = ["error","setfirstweekday","firstweekday","isleap", - "leapdays","weekday","monthrange","monthcalendar", - "prmonth","month","prcal","calendar","timegm", - "month_name", "month_abbr", "day_name", "day_abbr", - "weekheader"] +__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", + "firstweekday", "isleap", "leapdays", "weekday", "monthrange", + "monthcalendar", "prmonth", "month", "prcal", "calendar", + "timegm", "month_name", "month_abbr", "day_name", "day_abbr"] # Exception raised for bad input (with string parameter for details) error = ValueError +# Exceptions raised for bad input +class IllegalMonthError(ValueError): + def __init__(self, month): + self.month = month + def __str__(self): + return "bad month number %r; must be 1-12" % self.month + + +class IllegalWeekdayError(ValueError): + def __init__(self, weekday): + self.weekday = weekday + def __str__(self): + return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday + + # Constants for months referenced later January = 1 February = 2 @@ -30,7 +45,7 @@ mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] class _localized_month: - _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)] + _months = [datetime.date(2001, i+1, 1).strftime for i in xrange(12)] _months.insert(0, lambda x: "") def __init__(self, format): @@ -46,10 +61,11 @@ class _localized_month: def __len__(self): return 13 + class _localized_day: # January 1, 2001, was a Monday. - _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)] + _days = [datetime.date(2001, 1, i+1).strftime for i in xrange(7)] def __init__(self, format): self.format = format @@ -64,6 +80,7 @@ class _localized_day: def __len__(self): return 7 + # Full and abbreviated names of weekdays day_name = _localized_day('%A') day_abbr = _localized_day('%a') @@ -75,23 +92,12 @@ month_abbr = _localized_month('%b') # Constants for weekdays (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) -_firstweekday = 0 # 0 = Monday, 6 = Sunday - -def firstweekday(): - return _firstweekday - -def setfirstweekday(weekday): - """Set weekday (Monday=0, Sunday=6) to start each week.""" - global _firstweekday - if not MONDAY <= weekday <= SUNDAY: - raise ValueError, \ - 'bad weekday number; must be 0 (Monday) to 6 (Sunday)' - _firstweekday = weekday def isleap(year): """Return 1 for leap years, 0 for non-leap years.""" return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) + def leapdays(y1, y2): """Return number of leap years in range [y1, y2). Assume y1 <= y2.""" @@ -99,128 +105,501 @@ def leapdays(y1, y2): y2 -= 1 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400) + def weekday(year, month, day): """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31).""" return datetime.date(year, month, day).weekday() + def monthrange(year, month): """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month.""" if not 1 <= month <= 12: - raise ValueError, 'bad month number' + raise IllegalMonthError(month) day1 = weekday(year, month, 1) ndays = mdays[month] + (month == February and isleap(year)) return day1, ndays -def monthcalendar(year, month): - """Return a matrix representing a month's calendar. - Each row represents a week; days outside this month are zero.""" - day1, ndays = monthrange(year, month) - rows = [] - r7 = range(7) - day = (_firstweekday - day1 + 6) % 7 - 5 # for leading 0's in first week - while day <= ndays: - row = [0, 0, 0, 0, 0, 0, 0] - for i in r7: - if 1 <= day <= ndays: row[i] = day - day = day + 1 - rows.append(row) - return rows - -def prweek(theweek, width): - """Print a single week (no newline).""" - print week(theweek, width), - -def week(theweek, width): - """Returns a single week in a string (no newline).""" - days = [] - for day in theweek: + +class Calendar(object): + """ + Base calendar class. This class doesn't do any formatting. It simply + provides data to subclasses. + """ + + def __init__(self, firstweekday=0): + self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday + + def getfirstweekday(self): + return self._firstweekday % 7 + + def setfirstweekday(self, firstweekday): + self._firstweekday = firstweekday + + firstweekday = property(getfirstweekday, setfirstweekday) + + def iterweekdays(self): + """ + Return a iterator for one week of weekday numbers starting with the + configured first one. + """ + for i in xrange(self.firstweekday, self.firstweekday + 7): + yield i%7 + + def itermonthdates(self, year, month): + """ + Return an iterator for one month. The iterator will yield datetime.date + values and will always iterate through complete weeks, so it will yield + dates outside the specified month. + """ + date = datetime.date(year, month, 1) + # Go back to the beginning of the week + days = (date.weekday() - self.firstweekday) % 7 + date -= datetime.timedelta(days=days) + oneday = datetime.timedelta(days=1) + while True: + yield date + date += oneday + if date.month != month and date.weekday() == self.firstweekday: + break + + def itermonthdays2(self, year, month): + """ + Like itermonthdates(), but will yield (day number, weekday number) + tuples. For days outside the specified month the day number is 0. + """ + for date in self.itermonthdates(year, month): + if date.month != month: + yield (0, date.weekday()) + else: + yield (date.day, date.weekday()) + + def itermonthdays(self, year, month): + """ + Like itermonthdates(), but will yield day numbers tuples. For days + outside the specified month the day number is 0. + """ + for date in self.itermonthdates(year, month): + if date.month != month: + yield 0 + else: + yield date.day + + def monthdatescalendar(self, year, month): + """ + Return a matrix (list of lists) representing a month's calendar. + Each row represents a week; week entries are datetime.date values. + """ + dates = list(self.itermonthdates(year, month)) + return [ dates[i:i+7] for i in xrange(0, len(dates), 7) ] + + def monthdays2calendar(self, year, month): + """ + Return a matrix representing a month's calendar. + Each row represents a week; week entries are + (day number, weekday number) tuples. Day numbers outside this month + are zero. + """ + days = list(self.itermonthdays2(year, month)) + return [ days[i:i+7] for i in xrange(0, len(days), 7) ] + + def monthdayscalendar(self, year, month): + """ + Return a matrix representing a month's calendar. + Each row represents a week; days outside this month are zero. + """ + days = list(self.itermonthdays(year, month)) + return [ days[i:i+7] for i in xrange(0, len(days), 7) ] + + def yeardatescalendar(self, year, width=3): + """ + Return the data for the specified year ready for formatting. The return + value is a list of month rows. Each month row contains upto width months. + Each month contains between 4 and 6 weeks and each week contains 1-7 + days. Days are datetime.date objects. + """ + months = [ + self.monthdatescalendar(year, i) + for i in xrange(January, January+12) + ] + return [months[i:i+width] for i in xrange(0, len(months), width) ] + + def yeardays2calendar(self, year, width=3): + """ + Return the data for the specified year ready for formatting (similar to + yeardatescalendar()). Entries in the week lists are + (day number, weekday number) tuples. Day numbers outside this month are + zero. + """ + months = [ + self.monthdays2calendar(year, i) + for i in xrange(January, January+12) + ] + return [months[i:i+width] for i in xrange(0, len(months), width) ] + + def yeardayscalendar(self, year, width=3): + """ + Return the data for the specified year ready for formatting (similar to + yeardatescalendar()). Entries in the week lists are day numbers. + Day numbers outside this month are zero. + """ + months = [ + self.monthdayscalendar(year, i) + for i in xrange(January, January+12) + ] + return [months[i:i+width] for i in xrange(0, len(months), width) ] + + +class TextCalendar(Calendar): + """ + Subclass of Calendar that outputs a calendar as a simple plain text + similar to the UNIX program cal. + """ + + def prweek(self, theweek, width): + """ + Print a single week (no newline). + """ + print self.week(theweek, width), + + def formatday(self, day, weekday, width): + """ + Returns a formatted day. + """ if day == 0: s = '' else: s = '%2i' % day # right-align single-digit days - days.append(s.center(width)) - return ' '.join(days) - -def weekheader(width): - """Return a header for a week.""" - if width >= 9: - names = day_name - else: - names = day_abbr - days = [] - for i in range(_firstweekday, _firstweekday + 7): - days.append(names[i%7][:width].center(width)) - return ' '.join(days) - -def prmonth(theyear, themonth, w=0, l=0): - """Print a month's calendar.""" - print month(theyear, themonth, w, l), - -def month(theyear, themonth, w=0, l=0): - """Return a month's calendar string (multi-line).""" - w = max(2, w) - l = max(1, l) - s = ("%s %r" % (month_name[themonth], theyear)).center( - 7 * (w + 1) - 1).rstrip() + \ - '\n' * l + weekheader(w).rstrip() + '\n' * l - for aweek in monthcalendar(theyear, themonth): - s = s + week(aweek, w).rstrip() + '\n' * l - return s[:-l] + '\n' - -# Spacing of month columns for 3-column year calendar + return s.center(width) + + def formatweek(self, theweek, width): + """ + Returns a single week in a string (no newline). + """ + return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) + + def formatweekday(self, day, width): + """ + Returns a formatted week day name. + """ + if width >= 9: + names = day_name + else: + names = day_abbr + return names[day][:width].center(width) + + def formatweekheader(self, width): + """ + Return a header for a week. + """ + return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays()) + + def formatmonthname(self, theyear, themonth, width, withyear=True): + """ + Return a formatted month name. + """ + s = month_name[themonth] + if withyear: + s = "%s %r" % (s, theyear) + return s.center(width) + + def prmonth(self, theyear, themonth, w=0, l=0): + """ + Print a month's calendar. + """ + print self.formatmonth(theyear, themonth, w, l), + + def formatmonth(self, theyear, themonth, w=0, l=0): + """ + Return a month's calendar string (multi-line). + """ + w = max(2, w) + l = max(1, l) + s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) + s = s.rstrip() + s += '\n' * l + s += self.formatweekheader(w).rstrip() + s += '\n' * l + for week in self.monthdays2calendar(theyear, themonth): + s += self.formatweek(week, w).rstrip() + s += '\n' * l + return s + + def formatyear(self, theyear, w=2, l=1, c=6, m=3): + """ + Returns a year's calendar as a multi-line string. + """ + w = max(2, w) + l = max(1, l) + c = max(2, c) + colwidth = (w + 1) * 7 - 1 + v = [] + a = v.append + a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) + a('\n'*l) + header = self.formatweekheader(w) + for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): + # months in this row + months = xrange(m*i+1, min(m*(i+1)+1, 13)) + a('\n'*l) + names = (self.formatmonthname(theyear, k, colwidth, False) + for k in months) + a(formatstring(names, colwidth, c).rstrip()) + a('\n'*l) + headers = (header for k in months) + a(formatstring(headers, colwidth, c).rstrip()) + a('\n'*l) + # max number of weeks for this row + height = max(len(cal) for cal in row) + for j in xrange(height): + weeks = [] + for cal in row: + if j >= len(cal): + weeks.append('') + else: + weeks.append(self.formatweek(cal[j], w)) + a(formatstring(weeks, colwidth, c).rstrip()) + a('\n' * l) + return ''.join(v) + + def pryear(self, theyear, w=0, l=0, c=6, m=3): + """Print a year's calendar.""" + print self.formatyear(theyear, w, l, c, m) + + +class HTMLCalendar(Calendar): + """ + This calendar returns complete HTML pages. + """ + + # CSS classes for the day s + cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] + + def formatday(self, day, weekday): + """ + Return a day as a table cell. + """ + if day == 0: + return ' ' # day outside month + else: + return '%d' % (self.cssclasses[weekday], day) + + def formatweek(self, theweek): + """ + Return a complete week as a table row. + """ + s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) + return '%s' % s + + def formatweekday(self, day): + """ + Return a weekday name as a table header. + """ + return '%s' % (self.cssclasses[day], day_abbr[day]) + + def formatweekheader(self): + """ + Return a header for a week as a table row. + """ + s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) + return '%s' % s + + def formatmonthname(self, theyear, themonth, withyear=True): + """ + Return a month name as a table row. + """ + if withyear: + s = '%s %s' % (month_name[themonth], theyear) + else: + s = '%s' % month_name[themonth] + return '%s' % s + + def formatmonth(self, theyear, themonth, withyear=True): + """ + Return a formatted month as a table. + """ + v = [] + a = v.append + a('') + a('\n') + a(self.formatmonthname(theyear, themonth, withyear=withyear)) + a('\n') + a(self.formatweekheader()) + a('\n') + for week in self.monthdays2calendar(theyear, themonth): + a(self.formatweek(week)) + a('\n') + a('
') + a('\n') + return ''.join(v) + + def formatyear(self, theyear, width=3): + """ + Return a formatted year as a table of tables. + """ + v = [] + a = v.append + width = max(width, 1) + a('') + a('\n') + a('' % (width, theyear)) + for i in xrange(January, January+12, width): + # months in this row + months = xrange(i, min(i+width, 13)) + a('') + for m in months: + a('') + a('') + a('
%s
') + a(self.formatmonth(theyear, m, withyear=False)) + a('
') + return ''.join(v) + + def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + """ + Return a formatted year as a complete HTML page. + """ + if encoding is None: + encoding = sys.getdefaultencoding() + v = [] + a = v.append + a('\n' % encoding) + a('\n') + a('\n') + a('\n') + a('\n' % encoding) + if css is not None: + a('\n' % css) + a('Calendar for %d</title\n' % theyear) + a('</head>\n') + a('<body>\n') + a(self.formatyear(theyear, width)) + a('</body>\n') + a('</html>\n') + return ''.join(v).encode(encoding, "xmlcharrefreplace") + + +class TimeEncoding: + def __init__(self, locale): + self.locale = locale + + def __context__(self): + return self + + def __enter__(self): + self.oldlocale = locale.setlocale(locale.LC_TIME, self.locale) + return locale.getlocale(locale.LC_TIME)[1] + + def __exit__(self, *args): + locale.setlocale(locale.LC_TIME, self.oldlocale) + + +class LocaleTextCalendar(TextCalendar): + """ + This class can be passed a locale name in the constructor and will return + month and weekday names in the specified locale. If this locale includes + an encoding all strings containing month and weekday names will be returned + as unicode. + """ + + def __init__(self, firstweekday=0, locale=None): + TextCalendar.__init__(self, firstweekday) + if locale is None: + locale = locale.getdefaultlocale() + self.locale = locale + + def formatweekday(self, day, width): + with TimeEncoding(self.locale) as encoding: + if width >= 9: + names = day_name + else: + names = day_abbr + name = names[day] + if encoding is not None: + name = name.decode(encoding) + return name[:width].center(width) + + def formatmonthname(self, theyear, themonth, width, withyear=True): + with TimeEncoding(self.locale) as encoding: + s = month_name[themonth] + if encoding is not None: + s = s.decode(encoding) + if withyear: + s = "%s %r" % (s, theyear) + return s.center(width) + + +class LocaleHTMLCalendar(HTMLCalendar): + """ + This class can be passed a locale name in the constructor and will return + month and weekday names in the specified locale. If this locale includes + an encoding all strings containing month and weekday names will be returned + as unicode. + """ + def __init__(self, firstweekday=0, locale=None): + HTMLCalendar.__init__(self, firstweekday) + if locale is None: + locale = locale.getdefaultlocale() + self.locale = locale + + def formatweekday(self, day): + with TimeEncoding(self.locale) as encoding: + s = day_abbr[day] + if encoding is not None: + s = s.decode(encoding) + return '<th class="%s">%s</th>' % (self.cssclasses[day], s) + + def formatmonthname(self, theyear, themonth, withyear=True): + with TimeEncoding(self.locale) as encoding: + s = month_name[themonth] + if encoding is not None: + s = s.decode(encoding) + if withyear: + s = '%s %s' % (s, theyear) + return '<tr><th colspan="7" class="month">%s</th></tr>' % s + + +# Support for old module level interface +c = TextCalendar() + +firstweekday = c.getfirstweekday + +def setfirstweekday(firstweekday): + if not MONDAY <= firstweekday <= SUNDAY: + raise IllegalWeekdayError(firstweekday) + c.firstweekday = firstweekday + +monthcalendar = c.monthdayscalendar +prweek = c.prweek +week = c.formatweek +weekheader = c.formatweekheader +prmonth = c.prmonth +month = c.formatmonth +calendar = c.formatyear +prcal = c.pryear + + +# Spacing of month columns for multi-column year calendar _colwidth = 7*3 - 1 # Amount printed by prweek() _spacing = 6 # Number of spaces between columns -def format3c(a, b, c, colwidth=_colwidth, spacing=_spacing): - """Prints 3-column formatting for year calendars""" - print format3cstring(a, b, c, colwidth, spacing) - -def format3cstring(a, b, c, colwidth=_colwidth, spacing=_spacing): - """Returns a string formatted from 3 strings, centered within 3 columns.""" - return (a.center(colwidth) + ' ' * spacing + b.center(colwidth) + - ' ' * spacing + c.center(colwidth)) - -def prcal(year, w=0, l=0, c=_spacing): - """Print a year's calendar.""" - print calendar(year, w, l, c), - -def calendar(year, w=0, l=0, c=_spacing): - """Returns a year's calendar as a multi-line string.""" - w = max(2, w) - l = max(1, l) - c = max(2, c) - colwidth = (w + 1) * 7 - 1 - s = repr(year).center(colwidth * 3 + c * 2).rstrip() + '\n' * l - header = weekheader(w) - header = format3cstring(header, header, header, colwidth, c).rstrip() - for q in range(January, January+12, 3): - s = (s + '\n' * l + - format3cstring(month_name[q], month_name[q+1], month_name[q+2], - colwidth, c).rstrip() + - '\n' * l + header + '\n' * l) - data = [] - height = 0 - for amonth in range(q, q + 3): - cal = monthcalendar(year, amonth) - if len(cal) > height: - height = len(cal) - data.append(cal) - for i in range(height): - weeks = [] - for cal in data: - if i >= len(cal): - weeks.append('') - else: - weeks.append(week(cal[i], w)) - s = s + format3cstring(weeks[0], weeks[1], weeks[2], - colwidth, c).rstrip() + '\n' * l - return s[:-l] + '\n' + +def format(cols, colwidth=_colwidth, spacing=_spacing): + """Prints multi-column formatting for year calendars""" + print formatstring(cols, colwidth, spacing) + + +def formatstring(cols, colwidth=_colwidth, spacing=_spacing): + """Returns a string formatted from n strings, centered within n columns.""" + spacing *= ' ' + return spacing.join(c.center(colwidth) for c in cols) + EPOCH = 1970 _EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal() + def timegm(tuple): """Unrelated but handy function to calculate Unix timestamp from GMT.""" year, month, day, hour, minute, second = tuple[:6] @@ -229,3 +608,97 @@ def timegm(tuple): minutes = hours*60 + minute seconds = minutes*60 + second return seconds + + +def main(args): + import optparse + parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]") + parser.add_option( + "-w", "--width", + dest="width", type="int", default=2, + help="width of date column (default 2, text only)" + ) + parser.add_option( + "-l", "--lines", + dest="lines", type="int", default=1, + help="number of lines for each week (default 1, text only)" + ) + parser.add_option( + "-s", "--spacing", + dest="spacing", type="int", default=6, + help="spacing between months (default 6, text only)" + ) + parser.add_option( + "-m", "--months", + dest="months", type="int", default=3, + help="months per row (default 3, text only)" + ) + parser.add_option( + "-c", "--css", + dest="css", default="calendar.css", + help="CSS to use for page (html only)" + ) + parser.add_option( + "-L", "--locale", + dest="locale", default=None, + help="locale to be used from month and weekday names" + ) + parser.add_option( + "-e", "--encoding", + dest="encoding", default=None, + help="Encoding to use for output" + ) + parser.add_option( + "-t", "--type", + dest="type", default="text", + choices=("text", "html"), + help="output type (text or html)" + ) + + (options, args) = parser.parse_args(args) + + if options.locale and not options.encoding: + parser.error("if --locale is specified --encoding is required") + sys.exit(1) + + if options.type == "html": + if options.locale: + cal = LocaleHTMLCalendar(locale=options.locale) + else: + cal = HTMLCalendar() + encoding = options.encoding + if encoding is None: + encoding = sys.getdefaultencoding() + optdict = dict(encoding=encoding, css=options.css) + if len(args) == 1: + print cal.formatyearpage(datetime.date.today().year, **optdict) + elif len(args) == 2: + print cal.formatyearpage(int(args[1]), **optdict) + else: + parser.error("incorrect number of arguments") + sys.exit(1) + else: + if options.locale: + cal = LocaleTextCalendar(locale=options.locale) + else: + cal = TextCalendar() + optdict = dict(w=options.width, l=options.lines) + if len(args) != 3: + optdict["c"] = options.spacing + optdict["m"] = options.months + if len(args) == 1: + result = cal.formatyear(datetime.date.today().year, **optdict) + elif len(args) == 2: + result = cal.formatyear(int(args[1]), **optdict) + elif len(args) == 3: + result = cal.formatmonth(int(args[1]), int(args[2]), **optdict) + else: + parser.error("incorrect number of arguments") + sys.exit(1) + if options.encoding: + result = result.encode(options.encoding) + print result + + +if __name__ == "__main__": + main(sys.argv) diff --git a/Lib/codecs.py b/Lib/codecs.py index 28856c7..1518d75 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -14,8 +14,7 @@ import __builtin__, sys try: from _codecs import * except ImportError, why: - raise SystemError,\ - 'Failed to load the builtin codecs: %s' % why + raise SystemError('Failed to load the builtin codecs: %s' % why) __all__ = ["register", "lookup", "open", "EncodedFile", "BOM", "BOM_BE", "BOM_LE", "BOM32_BE", "BOM32_LE", "BOM64_BE", "BOM64_LE", @@ -156,13 +155,13 @@ class Codec: class IncrementalEncoder(object): """ - A IncrementalEncoder encodes an input in multiple steps. The input can be + An IncrementalEncoder encodes an input in multiple steps. The input can be passed piece by piece to the encode() method. The IncrementalEncoder remembers the state of the Encoding process between calls to encode(). """ def __init__(self, errors='strict'): """ - Creates a IncrementalEncoder instance. + Creates an IncrementalEncoder instance. The IncrementalEncoder may use different error handling schemes by providing the errors keyword argument. See the module docstring @@ -182,6 +181,33 @@ class IncrementalEncoder(object): Resets the encoder to the initial state. """ +class BufferedIncrementalEncoder(IncrementalEncoder): + """ + This subclass of IncrementalEncoder can be used as the baseclass for an + incremental encoder if the encoder must keep some of the output in a + buffer between calls to encode(). + """ + def __init__(self, errors='strict'): + IncrementalEncoder.__init__(self, errors) + self.buffer = "" # unencoded input that is kept between calls to encode() + + def _buffer_encode(self, input, errors, final): + # Overwrite this method in subclasses: It must encode input + # and return an (output, length consumed) tuple + raise NotImplementedError + + def encode(self, input, final=False): + # encode input (taking the buffer into account) + data = self.buffer + input + (result, consumed) = self._buffer_encode(data, self.errors, final) + # keep unencoded input until the next call + self.buffer = data[consumed:] + return result + + def reset(self): + IncrementalEncoder.reset(self) + self.buffer = "" + class IncrementalDecoder(object): """ An IncrementalDecoder decodes an input in multiple steps. The input can be @@ -234,7 +260,7 @@ class BufferedIncrementalDecoder(IncrementalDecoder): def reset(self): IncrementalDecoder.reset(self) - self.bytebuffer = "" + self.buffer = "" # # The StreamWriter and StreamReader class provide generic working diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 0a5d608..aa5335d 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -30,9 +30,22 @@ class GeneratorContextManager(object): else: try: self.gen.throw(type, value, traceback) - return True - except StopIteration: - return True + raise RuntimeError("generator didn't stop after throw()") + except StopIteration, exc: + # Suppress the exception *unless* it's the same exception that + # was passed to throw(). This prevents a StopIteration + # raised inside the "with" statement from being suppressed + return exc is not value + except: + # only re-raise if it's *not* the exception that was + # passed to throw(), because __exit__() must not raise + # an exception unless __exit__() itself failed. But throw() + # has to raise the exception to signal propagation, so this + # fixes the impedance mismatch between the throw() protocol + # and the __exit__() protocol. + # + if sys.exc_info()[1] is not value: + raise def contextmanager(func): @@ -68,6 +81,7 @@ def contextmanager(func): try: helper.__name__ = func.__name__ helper.__doc__ = func.__doc__ + helper.__dict__ = func.__dict__ except: pass return helper diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py index 169520d..f4661ed 100644 --- a/Lib/copy_reg.py +++ b/Lib/copy_reg.py @@ -111,8 +111,19 @@ def _slotnames(cls): # Slots found -- gather slot names from all base classes for c in cls.__mro__: if "__slots__" in c.__dict__: - names += [name for name in c.__dict__["__slots__"] - if name not in ("__dict__", "__weakref__")] + slots = c.__dict__['__slots__'] + # if class has a single slot, it can be given as a string + if isinstance(slots, basestring): + slots = (slots,) + for name in slots: + # special descriptors + if name in ("__dict__", "__weakref__"): + continue + # mangled names + elif name.startswith('__') and not name.endswith('__'): + names.append('_%s%s' % (c.__name__, name)) + else: + names.append(name) # Cache the outcome in the class if at all possible try: diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index dd0f640..28ac180 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -9,11 +9,7 @@ from _ctypes import Union, Structure, Array from _ctypes import _Pointer from _ctypes import CFuncPtr as _CFuncPtr from _ctypes import __version__ as _ctypes_version -try: - from _ctypes import RTLD_LOCAL, RTLD_GLOBAL -except (ImportError, AttributeError): - RTLD_GLOBAL = RTLD_LOCAL = None - +from _ctypes import RTLD_LOCAL, RTLD_GLOBAL from _ctypes import ArgumentError from struct import calcsize as _calcsize @@ -304,10 +300,11 @@ class CDLL(object): raise AttributeError, name return self.__getitem__(name) - def __getitem__(self, name): - func = self._FuncPtr(name, self) - func.__name__ = name - setattr(self, name, func) + def __getitem__(self, name_or_ordinal): + func = self._FuncPtr((name_or_ordinal, self)) + if not isinstance(name_or_ordinal, (int, long)): + func.__name__ = name_or_ordinal + setattr(self, name_or_ordinal, func) return func class PyDLL(CDLL): @@ -384,21 +381,29 @@ if _os.name in ("nt", "ce"): _pointer_type_cache[None] = c_void_p -# functions - -from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, cast - if sizeof(c_uint) == sizeof(c_void_p): c_size_t = c_uint elif sizeof(c_ulong) == sizeof(c_void_p): c_size_t = c_ulong +# functions + +from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr + ## void *memmove(void *, const void *, size_t); memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) ## void *memset(void *, int, size_t) memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) +def PYFUNCTYPE(restype, *argtypes): + class CFunctionType(_CFuncPtr): + _argtypes_ = argtypes + _restype_ = restype + _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI + return CFunctionType +cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr) + _string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=0): """string_at(addr[, size]) -> string diff --git a/Lib/ctypes/_loader.py b/Lib/ctypes/_loader.py index 7bde6c6..7a48c1c 100644 --- a/Lib/ctypes/_loader.py +++ b/Lib/ctypes/_loader.py @@ -1,14 +1,11 @@ -# WORK IN PROGRESS! DO NOT (yet) USE! import sys, os import ctypes -__all__ = ["LibraryLoader", "RTLD_LOCAL", "RTLD_GLOBAL"] - if os.name in ("nt", "ce"): from _ctypes import LoadLibrary as dlopen - RTLD_LOCAL = RTLD_GLOBAL = None else: - from _ctypes import dlopen, RTLD_LOCAL, RTLD_GLOBAL + from _ctypes import dlopen +from _ctypes import RTLD_LOCAL, RTLD_GLOBAL # _findLib(name) returns an iterable of possible names for a library. if os.name in ("nt", "ce"): @@ -56,7 +53,10 @@ elif os.name == "posix": expr = '/[^\(\)\s]*lib%s\.[^\(\)\s]*' % name res = re.search(expr, os.popen('/sbin/ldconfig -p 2>/dev/null').read()) if not res: - return None + cmd = 'ldd %s 2>/dev/null' % sys.executable + res = re.search(expr, os.popen(cmd).read()) + if not res: + return None return res.group(0) def _get_soname(f): diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py index 1b31f90..d0ada40 100644 --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -149,7 +149,7 @@ class Test(unittest.TestCase): self.failUnless(c_char.__ctype_le__ is c_char) self.failUnless(c_char.__ctype_be__ is c_char) - def test_struct_fields(self): + def test_struct_fields_1(self): if sys.byteorder == "little": base = BigEndianStructure else: @@ -198,17 +198,20 @@ class Test(unittest.TestCase): pass self.assertRaises(TypeError, setattr, S, "_fields_", [("s", T)]) - # crashes on solaris with a core dump. - def X_test_struct_fields(self): + def test_struct_fields_2(self): + # standard packing in struct uses no alignment. + # So, we have to align using pad bytes. + # + # Unaligned accesses will crash Python (on those platforms that + # don't allow it, like sparc solaris). if sys.byteorder == "little": base = BigEndianStructure - fmt = ">bhid" + fmt = ">bxhid" else: base = LittleEndianStructure - fmt = "<bhid" + fmt = "<bxhid" class S(base): - _pack_ = 1 # struct with '<' or '>' uses standard alignment. _fields_ = [("b", c_byte), ("h", c_short), ("i", c_int), @@ -218,5 +221,60 @@ class Test(unittest.TestCase): s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14) self.failUnlessEqual(bin(s1), bin(s2)) + def test_unaligned_nonnative_struct_fields(self): + if sys.byteorder == "little": + base = BigEndianStructure + fmt = ">b h xi xd" + else: + base = LittleEndianStructure + fmt = "<b h xi xd" + + class S(base): + _pack_ = 1 + _fields_ = [("b", c_byte), + + ("h", c_short), + + ("_1", c_byte), + ("i", c_int), + + ("_2", c_byte), + ("d", c_double)] + + s1 = S() + s1.b = 0x12 + s1.h = 0x1234 + s1.i = 0x12345678 + s1.d = 3.14 + s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14) + self.failUnlessEqual(bin(s1), bin(s2)) + + def test_unaligned_native_struct_fields(self): + if sys.byteorder == "little": + fmt = "<b h xi xd" + else: + base = LittleEndianStructure + fmt = ">b h xi xd" + + class S(Structure): + _pack_ = 1 + _fields_ = [("b", c_byte), + + ("h", c_short), + + ("_1", c_byte), + ("i", c_int), + + ("_2", c_byte), + ("d", c_double)] + + s1 = S() + s1.b = 0x12 + s1.h = 0x1234 + s1.i = 0x12345678 + s1.d = 3.14 + s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14) + self.failUnlessEqual(bin(s1), bin(s2)) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py index 7c2b28b..6e0798d 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -179,7 +179,7 @@ else: def __getattr__(self, name): if name[:2] == '__' and name[-2:] == '__': raise AttributeError, name - func = self._FuncPtr("s_" + name, self) + func = self._FuncPtr(("s_" + name, self)) setattr(self, name, func) return func diff --git a/Lib/ctypes/test/test_keeprefs.py b/Lib/ctypes/test/test_keeprefs.py index 39e70e3..7318f29 100644 --- a/Lib/ctypes/test/test_keeprefs.py +++ b/Lib/ctypes/test/test_keeprefs.py @@ -140,5 +140,10 @@ class PointerToStructure(unittest.TestCase): r.a[0].x = 42 r.a[0].y = 99 + # to avoid leaking when tests are run several times + # clean up the types left in the cache. + from ctypes import _pointer_type_cache + del _pointer_type_cache[POINT] + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index 80564b8..4558417 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -2,40 +2,72 @@ from ctypes import * import sys, unittest import os, StringIO +libc_name = None +if os.name == "nt": + libc_name = "msvcrt" +elif os.name == "ce": + libc_name = "coredll" +elif sys.platform == "darwin": + libc_name = "libc.dylib" +elif sys.platform == "cygwin": + libc_name = "cygwin1.dll" +else: + for line in os.popen("ldd %s" % sys.executable): + if "libc.so" in line: + if sys.platform == "openbsd3": + libc_name = line.split()[4] + else: + libc_name = line.split()[2] +## print "libc_name is", libc_name + break + class LoaderTest(unittest.TestCase): unknowndll = "xxrandomnamexx" - def test_load(self): - if os.name == "nt": - name = "msvcrt" - elif os.name == "ce": - name = "coredll" - elif sys.platform == "darwin": - name = "libc.dylib" - elif sys.platform.startswith("freebsd"): - name = "libc.so" - elif sys.platform == "sunos5": - name = "libc.so" - else: - name = "libc.so.6" - cdll.load(name) - self.assertRaises(OSError, cdll.load, self.unknowndll) - - def test_load_version(self): - version = "6" - name = "c" - if sys.platform == "linux2": - cdll.load_version(name, version) + if libc_name is not None: + def test_load(self): + cdll.load(libc_name) + cdll.load(os.path.basename(libc_name)) + self.assertRaises(OSError, cdll.load, self.unknowndll) + + if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": + def test_load_version(self): + cdll.load_version("c", "6") # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.load_version, name, "9") - self.assertRaises(OSError, cdll.load_version, self.unknowndll, "") + self.assertRaises(OSError, cdll.load_version, "c", "9") + self.assertRaises(OSError, cdll.load_version, self.unknowndll, "") - if os.name == "posix" and sys.platform != "sunos5": def test_find(self): name = "c" cdll.find(name) self.assertRaises(OSError, cdll.find, self.unknowndll) + if os.name in ("nt", "ce"): + def test_load_library(self): + if os.name == "nt": + windll.load_library("kernel32").GetModuleHandleW + windll.LoadLibrary("kernel32").GetModuleHandleW + WinDLL("kernel32").GetModuleHandleW + elif os.name == "ce": + windll.load_library("coredll").GetModuleHandleW + windll.LoadLibrary("coredll").GetModuleHandleW + WinDLL("coredll").GetModuleHandleW + + def test_load_ordinal_functions(self): + import _ctypes_test + dll = WinDLL(_ctypes_test.__file__) + # We load the same function both via ordinal and name + func_ord = dll[2] + func_name = dll.GetString + # addressof gets the address where the function pointer is stored + a_ord = addressof(func_ord) + a_name = addressof(func_name) + f_ord_addr = c_void_p.from_address(a_ord).value + f_name_addr = c_void_p.from_address(a_name).value + self.failUnlessEqual(hex(f_ord_addr), hex(f_name_addr)) + + self.failUnlessRaises(AttributeError, dll.__getitem__, 1234) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index 6172abb..3a324a6 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -166,6 +166,18 @@ class PointersTestCase(unittest.TestCase): result = func( byref(argc), argv ) assert result == 'world', result + def test_bug_1467852(self): + # http://sourceforge.net/tracker/?func=detail&atid=532154&aid=1467852&group_id=71702 + x = c_int(5) + dummy = [] + for i in range(32000): + dummy.append(c_int(i)) + y = c_int(6) + p = pointer(x) + pp = pointer(p) + q = pointer(y) + pp[0] = q # <== + self.failUnlessEqual(p[0], 6) if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_posix.py b/Lib/ctypes/test/test_posix.py index 2b4fdff..fe0a40a 100644 --- a/Lib/ctypes/test/test_posix.py +++ b/Lib/ctypes/test/test_posix.py @@ -8,8 +8,10 @@ if os.name == "posix" and sys.platform == "linux2": class TestRTLD_GLOBAL(unittest.TestCase): def test_GL(self): - cdll.load('libGL.so', mode=RTLD_GLOBAL) - cdll.load('libGLU.so') + if os.path.exists('/usr/lib/libGL.so'): + cdll.load('libGL.so', mode=RTLD_GLOBAL) + if os.path.exists('/usr/lib/libGLU.so'): + cdll.load('libGLU.so') ##if os.name == "posix" and sys.platform != "darwin": diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py index 2c3d75b..47f5da1 100644 --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -24,6 +24,19 @@ import unittest import _ctypes_test testdll = cdll.load(_ctypes_test.__file__) +# Return machine address `a` as a (possibly long) non-negative integer. +# Starting with Python 2.5, id(anything) is always non-negative, and +# the ctypes addressof() inherits that via PyLong_FromVoidPtr(). +def positive_address(a): + if a >= 0: + return a + # View the bits in `a` as unsigned instead. + import struct + num_bits = struct.calcsize("P") * 8 # num bits in native machine address + a += 1L << num_bits + assert a >= 0 + return a + def c_wbuffer(init): n = len(init) + 1 return (c_wchar * n)(*init) @@ -43,7 +56,8 @@ class CharPointersTestCase(unittest.TestCase): ci = c_int(0) func.argtypes = POINTER(c_int), - self.failUnlessEqual(addressof(ci), func(byref(ci))) + self.failUnlessEqual(positive_address(addressof(ci)), + positive_address(func(byref(ci)))) func.argtypes = c_char_p, self.assertRaises(ArgumentError, func, byref(ci)) diff --git a/Lib/ctypes/test/test_random_things.py b/Lib/ctypes/test/test_random_things.py index cd50ca8..78a665b 100644 --- a/Lib/ctypes/test/test_random_things.py +++ b/Lib/ctypes/test/test_random_things.py @@ -51,16 +51,14 @@ class CallbackTracbackTestCase(unittest.TestCase): def test_IntegerDivisionError(self): cb = CFUNCTYPE(c_int, c_int)(callback_func) out = self.capture_stderr(cb, 0) - self.failUnlessEqual(out.splitlines()[-1], - "ZeroDivisionError: " - "integer division or modulo by zero") + self.failUnlessEqual(out.splitlines()[-1][:19], + "ZeroDivisionError: ") def test_FloatDivisionError(self): cb = CFUNCTYPE(c_int, c_double)(callback_func) out = self.capture_stderr(cb, 0.0) - self.failUnlessEqual(out.splitlines()[-1], - "ZeroDivisionError: " - "float division") + self.failUnlessEqual(out.splitlines()[-1][:19], + "ZeroDivisionError: ") def test_TypeErrorDivisionError(self): cb = CFUNCTYPE(c_int, c_char_p)(callback_func) diff --git a/Lib/ctypes/test/test_sizes.py b/Lib/ctypes/test/test_sizes.py index 6fb9ca0..208c00e 100644 --- a/Lib/ctypes/test/test_sizes.py +++ b/Lib/ctypes/test/test_sizes.py @@ -20,5 +20,8 @@ class SizesTestCase(unittest.TestCase): self.failUnlessEqual(8, sizeof(c_int64)) self.failUnlessEqual(8, sizeof(c_uint64)) + def test_size_t(self): + self.failUnlessEqual(sizeof(c_void_p), sizeof(c_size_t)) + if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_unaligned_structures.py b/Lib/ctypes/test/test_unaligned_structures.py new file mode 100644 index 0000000..89343ba --- /dev/null +++ b/Lib/ctypes/test/test_unaligned_structures.py @@ -0,0 +1,45 @@ +import sys, unittest +from ctypes import * + +structures = [] +byteswapped_structures = [] + + +if sys.byteorder == "little": + SwappedStructure = BigEndianStructure +else: + SwappedStructure = LittleEndianStructure + +for typ in [c_short, c_int, c_long, c_longlong, + c_float, c_double, + c_ushort, c_uint, c_ulong, c_ulonglong]: + class X(Structure): + _pack_ = 1 + _fields_ = [("pad", c_byte), + ("value", typ)] + class Y(SwappedStructure): + _pack_ = 1 + _fields_ = [("pad", c_byte), + ("value", typ)] + structures.append(X) + byteswapped_structures.append(Y) + +class TestStructures(unittest.TestCase): + def test_native(self): + for typ in structures: +## print typ.value + self.failUnlessEqual(typ.value.offset, 1) + o = typ() + o.value = 4 + self.failUnlessEqual(o.value, 4) + + def test_swapped(self): + for typ in byteswapped_structures: +## print >> sys.stderr, typ.value + self.failUnlessEqual(typ.value.offset, 1) + o = typ() + o.value = 4 + self.failUnlessEqual(o.value, 4) + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index 6ea5d57..5771252 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py @@ -185,7 +185,9 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ + (sys.platform.startswith('linux') and + sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -688,6 +690,13 @@ class build_ext (Command): # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra else: - return ext.libraries + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + return ext.libraries + [pythonlib] + else: + return ext.libraries # class build_ext diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py index 7723761..453151d 100644 --- a/Lib/distutils/command/install.py +++ b/Lib/distutils/command/install.py @@ -601,6 +601,7 @@ class install (Command): ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), + ('install_egg_info', lambda self:True), ] # class install diff --git a/Lib/distutils/command/install_egg_info.py b/Lib/distutils/command/install_egg_info.py new file mode 100644 index 0000000..c31ac29 --- /dev/null +++ b/Lib/distutils/command/install_egg_info.py @@ -0,0 +1,75 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%s.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + sys.version[:3] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + log.info("Writing %s", target) + if not self.dry_run: + f = open(target, 'w') + self.distribution.metadata.write_pkg_file(f) + f.close() + + def get_outputs(self): + 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. + +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 to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py index 62767a3..6f4ce81 100644 --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -29,6 +29,7 @@ class upload(Command): 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), ] boolean_options = ['show-response', 'sign'] @@ -38,8 +39,13 @@ class upload(Command): self.repository = '' self.show_response = 0 self.sign = False + self.identity = None def finalize_options(self): + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) if os.environ.has_key('HOME'): rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): @@ -67,7 +73,10 @@ class upload(Command): def upload_file(self, command, pyversion, filename): # Sign if requested if self.sign: - spawn(("gpg", "--detach-sign", "-a", filename), + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the meta-data in case we need to diff --git a/Lib/distutils/log.py b/Lib/distutils/log.py index cf3ee13..95d4c1c 100644 --- a/Lib/distutils/log.py +++ b/Lib/distutils/log.py @@ -20,7 +20,12 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: - print msg % args + if not args: + # msg may contain a '%'. If args is empty, + # don't even try to string-format + print msg + else: + print msg % args sys.stdout.flush() def log(self, level, msg, *args): diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index dc603be..49536f0 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -31,7 +31,7 @@ landmark = os.path.join(argv0_path, "Modules", "Setup") python_build = os.path.isfile(landmark) -del argv0_path, landmark +del landmark def get_python_version(): @@ -185,7 +185,7 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: - inc_dir = os.curdir + inc_dir = argv0_path else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': @@ -213,8 +213,8 @@ def parse_config_h(fp, g=None): """ if g is None: g = {} - define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n") + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") # while 1: line = fp.readline() @@ -351,6 +351,17 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(file(filename), g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + # On MacOSX we need to check the setting of the environment variable # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. @@ -361,7 +372,7 @@ def _init_posix(): if cur_target == '': cur_target = cfg_target os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - if cfg_target != cur_target: + elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) raise DistutilsPlatformError(my_msg) diff --git a/Lib/doctest.py b/Lib/doctest.py index 6244fae..70c355a 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -236,6 +236,15 @@ def _normalize_module(module, depth=2): else: raise TypeError("Expected a module, string, or None") +def _load_testfile(filename, package, module_relative): + if module_relative: + package = _normalize_module(package, 3) + filename = _module_relative_path(package, filename) + if hasattr(package, '__loader__'): + if hasattr(package.__loader__, 'get_data'): + return package.__loader__.get_data(filename), filename + return open(filename).read(), filename + def _indent(s, indent=4): """ Add the given number of space characters to the beginning every @@ -1319,13 +1328,13 @@ class DocTestRunner: __LINECACHE_FILENAME_RE = re.compile(r'<doctest ' r'(?P<name>[\w\.]+)' r'\[(?P<examplenum>\d+)\]>$') - def __patched_linecache_getlines(self, filename): + def __patched_linecache_getlines(self, filename, module_globals=None): m = self.__LINECACHE_FILENAME_RE.match(filename) if m and m.group('name') == self.test.name: example = self.test.examples[int(m.group('examplenum'))] return example.source.splitlines(True) else: - return self.save_linecache_getlines(filename) + return self.save_linecache_getlines(filename, module_globals) def run(self, test, compileflags=None, out=None, clear_globs=True): """ @@ -1933,9 +1942,7 @@ def testfile(filename, module_relative=True, name=None, package=None, "relative paths.") # Relativize the path - if module_relative: - package = _normalize_module(package) - filename = _module_relative_path(package, filename) + text, filename = _load_testfile(filename, package, module_relative) # If no name was given, then use the file's name. if name is None: @@ -1955,8 +1962,7 @@ def testfile(filename, module_relative=True, name=None, package=None, runner = DocTestRunner(verbose=verbose, optionflags=optionflags) # Read the file, convert it to a test, and run it. - s = open(filename).read() - test = parser.get_doctest(s, globs, name, filename, 0) + test = parser.get_doctest(text, globs, name, filename, 0) runner.run(test) if report: @@ -2336,15 +2342,13 @@ def DocFileTest(path, module_relative=True, package=None, "relative paths.") # Relativize the path. - if module_relative: - package = _normalize_module(package) - path = _module_relative_path(package, path) + doc, path = _load_testfile(path, package, module_relative) + if "__file__" not in globs: globs["__file__"] = path # Find the file and read it. name = os.path.basename(path) - doc = open(path).read() # Convert it to a test, and wrap it in a DocFileCase. test = parser.get_doctest(doc, globs, name, path, 0) diff --git a/Lib/dummy_thread.py b/Lib/dummy_thread.py index fb3abbf..d69d840 100644 --- a/Lib/dummy_thread.py +++ b/Lib/dummy_thread.py @@ -113,6 +113,14 @@ class LockType(object): self.locked_status = True return True + __enter__ = acquire + + def __exit__(self, typ, val, tb): + self.release() + + def __context__(self): + return self + def release(self): """Release the dummy lock.""" # XXX Perhaps shouldn't actually bother to test? Could lead diff --git a/Lib/easy_install.py b/Lib/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/Lib/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/Lib/email/Charset.py b/Lib/email/Charset.py deleted file mode 100644 index fd4043b..0000000 --- a/Lib/email/Charset.py +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -import email.base64MIME -import email.quopriMIME -from email.Encoders import encode_7or8bit - - - -# Flags for types of header encodings -QP = 1 # Quoted-Printable -BASE64 = 2 # Base64 -SHORTEST = 3 # the shorter of QP and base64, but only for headers - -# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 -MISC_LEN = 7 - -DEFAULT_CHARSET = 'us-ascii' - - - -# Defaults -CHARSETS = { - # input header enc body enc output conv - 'iso-8859-1': (QP, QP, None), - 'iso-8859-2': (QP, QP, None), - 'iso-8859-3': (QP, QP, None), - 'iso-8859-4': (QP, QP, None), - # iso-8859-5 is Cyrillic, and not especially used - # iso-8859-6 is Arabic, also not particularly used - # iso-8859-7 is Greek, QP will not make it readable - # iso-8859-8 is Hebrew, QP will not make it readable - 'iso-8859-9': (QP, QP, None), - 'iso-8859-10': (QP, QP, None), - # iso-8859-11 is Thai, QP will not make it readable - 'iso-8859-13': (QP, QP, None), - 'iso-8859-14': (QP, QP, None), - 'iso-8859-15': (QP, QP, None), - 'windows-1252':(QP, QP, None), - 'viscii': (QP, QP, None), - 'us-ascii': (None, None, None), - 'big5': (BASE64, BASE64, None), - 'gb2312': (BASE64, BASE64, None), - 'euc-jp': (BASE64, None, 'iso-2022-jp'), - 'shift_jis': (BASE64, None, 'iso-2022-jp'), - 'iso-2022-jp': (BASE64, None, None), - 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (SHORTEST, BASE64, 'utf-8'), - # We're making this one up to represent raw unencoded 8-bit - '8bit': (None, BASE64, 'utf-8'), - } - -# Aliases for other commonly-used names for character sets. Map -# them to the real ones used in email. -ALIASES = { - 'latin_1': 'iso-8859-1', - 'latin-1': 'iso-8859-1', - 'latin_2': 'iso-8859-2', - 'latin-2': 'iso-8859-2', - 'latin_3': 'iso-8859-3', - 'latin-3': 'iso-8859-3', - 'latin_4': 'iso-8859-4', - 'latin-4': 'iso-8859-4', - 'latin_5': 'iso-8859-9', - 'latin-5': 'iso-8859-9', - 'latin_6': 'iso-8859-10', - 'latin-6': 'iso-8859-10', - 'latin_7': 'iso-8859-13', - 'latin-7': 'iso-8859-13', - 'latin_8': 'iso-8859-14', - 'latin-8': 'iso-8859-14', - 'latin_9': 'iso-8859-15', - 'latin-9': 'iso-8859-15', - 'cp949': 'ks_c_5601-1987', - 'euc_jp': 'euc-jp', - 'euc_kr': 'euc-kr', - 'ascii': 'us-ascii', - } - - -# Map charsets to their Unicode codec strings. -CODEC_MAP = { - 'gb2312': 'eucgb2312_cn', - 'big5': 'big5_tw', - # Hack: We don't want *any* conversion for stuff marked us-ascii, as all - # sorts of garbage might be sent to us in the guise of 7-bit us-ascii. - # Let that stuff pass through without conversion to/from Unicode. - 'us-ascii': None, - } - - - -# Convenience functions for extending the above mappings -def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): - """Add character set properties to the global registry. - - charset is the input character set, and must be the canonical name of a - character set. - - Optional header_enc and body_enc is either Charset.QP for - quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for - the shortest of qp or base64 encoding, or None for no encoding. SHORTEST - is only valid for header_enc. It describes how message headers and - message bodies in the input charset are to be encoded. Default is no - encoding. - - Optional output_charset is the character set that the output should be - in. Conversions will proceed from input charset, to Unicode, to the - output charset when the method Charset.convert() is called. The default - is to output in the same character set as the input. - - Both input_charset and output_charset must have Unicode codec entries in - the module's charset-to-codec mapping; use add_codec(charset, codecname) - to add codecs the module does not know about. See the codecs module's - documentation for more information. - """ - if body_enc == SHORTEST: - raise ValueError('SHORTEST not allowed for body_enc') - CHARSETS[charset] = (header_enc, body_enc, output_charset) - - -def add_alias(alias, canonical): - """Add a character set alias. - - alias is the alias name, e.g. latin-1 - canonical is the character set's canonical name, e.g. iso-8859-1 - """ - ALIASES[alias] = canonical - - -def add_codec(charset, codecname): - """Add a codec that map characters in the given charset to/from Unicode. - - charset is the canonical name of a character set. codecname is the name - of a Python codec, as appropriate for the second argument to the unicode() - built-in, or to the encode() method of a Unicode string. - """ - CODEC_MAP[charset] = codecname - - - -class Charset: - """Map character sets to their email properties. - - This class provides information about the requirements imposed on email - for a specific character set. It also provides convenience routines for - converting between character sets, given the availability of the - applicable codecs. Given a character set, it will do its best to provide - information on how to use that character set in an email in an - RFC-compliant way. - - Certain character sets must be encoded with quoted-printable or base64 - when used in email headers or bodies. Certain character sets must be - converted outright, and are not allowed in email. Instances of this - module expose the following information about a character set: - - input_charset: The initial character set specified. Common aliases - are converted to their `official' email names (e.g. latin_1 - is converted to iso-8859-1). Defaults to 7-bit us-ascii. - - header_encoding: If the character set must be encoded before it can be - used in an email header, this attribute will be set to - Charset.QP (for quoted-printable), Charset.BASE64 (for - base64 encoding), or Charset.SHORTEST for the shortest of - QP or BASE64 encoding. Otherwise, it will be None. - - body_encoding: Same as header_encoding, but describes the encoding for the - mail message's body, which indeed may be different than the - header encoding. Charset.SHORTEST is not allowed for - body_encoding. - - output_charset: Some character sets must be converted before the can be - used in email headers or bodies. If the input_charset is - one of them, this attribute will contain the name of the - charset output will be converted to. Otherwise, it will - be None. - - input_codec: The name of the Python codec used to convert the - input_charset to Unicode. If no conversion codec is - necessary, this attribute will be None. - - output_codec: The name of the Python codec used to convert Unicode - to the output_charset. If no conversion codec is necessary, - this attribute will have the same value as the input_codec. - """ - def __init__(self, input_charset=DEFAULT_CHARSET): - # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to - # unicode because its .lower() is locale insensitive. - input_charset = unicode(input_charset, 'ascii').lower() - # Set the input charset after filtering through the aliases - self.input_charset = ALIASES.get(input_charset, input_charset) - # We can try to guess which encoding and conversion to use by the - # charset_map dictionary. Try that first, but let the user override - # it. - henc, benc, conv = CHARSETS.get(self.input_charset, - (SHORTEST, BASE64, None)) - if not conv: - conv = self.input_charset - # Set the attributes, allowing the arguments to override the default. - self.header_encoding = henc - self.body_encoding = benc - self.output_charset = ALIASES.get(conv, conv) - # Now set the codecs. If one isn't defined for input_charset, - # guess and try a Unicode codec with the same name as input_codec. - self.input_codec = CODEC_MAP.get(self.input_charset, - self.input_charset) - self.output_codec = CODEC_MAP.get(self.output_charset, - self.output_charset) - - def __str__(self): - return self.input_charset.lower() - - __repr__ = __str__ - - def __eq__(self, other): - return str(self) == str(other).lower() - - def __ne__(self, other): - return not self.__eq__(other) - - def get_body_encoding(self): - """Return the content-transfer-encoding used for body encoding. - - This is either the string `quoted-printable' or `base64' depending on - the encoding used, or it is a function in which case you should call - the function with a single argument, the Message object being - encoded. The function should then set the Content-Transfer-Encoding - header itself to whatever is appropriate. - - Returns "quoted-printable" if self.body_encoding is QP. - Returns "base64" if self.body_encoding is BASE64. - Returns "7bit" otherwise. - """ - assert self.body_encoding <> SHORTEST - if self.body_encoding == QP: - return 'quoted-printable' - elif self.body_encoding == BASE64: - return 'base64' - else: - return encode_7or8bit - - def convert(self, s): - """Convert a string from the input_codec to the output_codec.""" - if self.input_codec <> self.output_codec: - return unicode(s, self.input_codec).encode(self.output_codec) - else: - return s - - def to_splittable(self, s): - """Convert a possibly multibyte string to a safely splittable format. - - Uses the input_codec to try and convert the string to Unicode, so it - can be safely split on character boundaries (even for multibyte - characters). - - Returns the string as-is if it isn't known how to convert it to - Unicode with the input_charset. - - Characters that could not be converted to Unicode will be replaced - with the Unicode replacement character U+FFFD. - """ - if isinstance(s, unicode) or self.input_codec is None: - return s - try: - return unicode(s, self.input_codec, 'replace') - except LookupError: - # Input codec not installed on system, so return the original - # string unchanged. - return s - - def from_splittable(self, ustr, to_output=True): - """Convert a splittable string back into an encoded string. - - Uses the proper codec to try and convert the string from Unicode back - into an encoded format. Return the string as-is if it is not Unicode, - or if it could not be converted from Unicode. - - Characters that could not be converted from Unicode will be replaced - with an appropriate character (usually '?'). - - If to_output is True (the default), uses output_codec to convert to an - encoded format. If to_output is False, uses input_codec. - """ - if to_output: - codec = self.output_codec - else: - codec = self.input_codec - if not isinstance(ustr, unicode) or codec is None: - return ustr - try: - return ustr.encode(codec, 'replace') - except LookupError: - # Output codec not installed - return ustr - - def get_output_charset(self): - """Return the output character set. - - This is self.output_charset if that is not None, otherwise it is - self.input_charset. - """ - return self.output_charset or self.input_charset - - def encoded_header_len(self, s): - """Return the length of the encoded header string.""" - cset = self.get_output_charset() - # The len(s) of a 7bit encoding is len(s) - if self.header_encoding == BASE64: - return email.base64MIME.base64_len(s) + len(cset) + MISC_LEN - elif self.header_encoding == QP: - return email.quopriMIME.header_quopri_len(s) + len(cset) + MISC_LEN - elif self.header_encoding == SHORTEST: - lenb64 = email.base64MIME.base64_len(s) - lenqp = email.quopriMIME.header_quopri_len(s) - return min(lenb64, lenqp) + len(cset) + MISC_LEN - else: - return len(s) - - def header_encode(self, s, convert=False): - """Header-encode a string, optionally converting it to output_charset. - - If convert is True, the string will be converted from the input - charset to the output charset automatically. This is not useful for - multibyte character sets, which have line length issues (multibyte - characters must be split on a character, not a byte boundary); use the - high-level Header class to deal with these issues. convert defaults - to False. - - The type of encoding (base64 or quoted-printable) will be based on - self.header_encoding. - """ - cset = self.get_output_charset() - if convert: - s = self.convert(s) - # 7bit/8bit encodings return the string unchanged (modulo conversions) - if self.header_encoding == BASE64: - return email.base64MIME.header_encode(s, cset) - elif self.header_encoding == QP: - return email.quopriMIME.header_encode(s, cset, maxlinelen=None) - elif self.header_encoding == SHORTEST: - lenb64 = email.base64MIME.base64_len(s) - lenqp = email.quopriMIME.header_quopri_len(s) - if lenb64 < lenqp: - return email.base64MIME.header_encode(s, cset) - else: - return email.quopriMIME.header_encode(s, cset, maxlinelen=None) - else: - return s - - def body_encode(self, s, convert=True): - """Body-encode a string and convert it to output_charset. - - If convert is True (the default), the string will be converted from - the input charset to output charset automatically. Unlike - header_encode(), there are no issues with byte boundaries and - multibyte charsets in email bodies, so this is usually pretty safe. - - The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. - """ - if convert: - s = self.convert(s) - # 7bit/8bit encodings return the string unchanged (module conversions) - if self.body_encoding is BASE64: - return email.base64MIME.body_encode(s) - elif self.body_encoding is QP: - return email.quopriMIME.body_encode(s) - else: - return s diff --git a/Lib/email/Encoders.py b/Lib/email/Encoders.py deleted file mode 100644 index baac2a3..0000000 --- a/Lib/email/Encoders.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Encodings and related functions.""" - -import base64 -from quopri import encodestring as _encodestring - -def _qencode(s): - enc = _encodestring(s, quotetabs=True) - # Must encode spaces, which quopri.encodestring() doesn't do - return enc.replace(' ', '=20') - - -def _bencode(s): - # We can't quite use base64.encodestring() since it tacks on a "courtesy - # newline". Blech! - if not s: - return s - hasnewline = (s[-1] == '\n') - value = base64.encodestring(s) - if not hasnewline and value[-1] == '\n': - return value[:-1] - return value - - - -def encode_base64(msg): - """Encode the message's payload in Base64. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = _bencode(orig) - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'base64' - - - -def encode_quopri(msg): - """Encode the message's payload in quoted-printable. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = _qencode(orig) - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'quoted-printable' - - - -def encode_7or8bit(msg): - """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" - orig = msg.get_payload() - if orig is None: - # There's no payload. For backwards compatibility we use 7bit - msg['Content-Transfer-Encoding'] = '7bit' - return - # We play a trick to make this go fast. If encoding to ASCII succeeds, we - # know the data must be 7bit, otherwise treat it as 8bit. - try: - orig.encode('ascii') - except UnicodeError: - # iso-2022-* is non-ASCII but still 7-bit - charset = msg.get_charset() - output_cset = charset and charset.output_charset - if output_cset and output_cset.lower().startswith('iso-2202-'): - msg['Content-Transfer-Encoding'] = '7bit' - else: - msg['Content-Transfer-Encoding'] = '8bit' - else: - msg['Content-Transfer-Encoding'] = '7bit' - - - -def encode_noop(msg): - """Do nothing.""" diff --git a/Lib/email/Errors.py b/Lib/email/Errors.py deleted file mode 100644 index e13a2c7..0000000 --- a/Lib/email/Errors.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""email package exception classes.""" - - - -class MessageError(Exception): - """Base class for errors in the email package.""" - - -class MessageParseError(MessageError): - """Base class for message parsing errors.""" - - -class HeaderParseError(MessageParseError): - """Error while parsing headers.""" - - -class BoundaryError(MessageParseError): - """Couldn't find terminating boundary.""" - - -class MultipartConversionError(MessageError, TypeError): - """Conversion to a multipart is prohibited.""" - - - -# These are parsing defects which the parser was able to work around. -class MessageDefect: - """Base class for a message defect.""" - - def __init__(self, line=None): - self.line = line - -class NoBoundaryInMultipartDefect(MessageDefect): - """A message claimed to be a multipart but had no boundary parameter.""" - -class StartBoundaryNotFoundDefect(MessageDefect): - """The claimed start boundary was never found.""" - -class FirstHeaderLineIsContinuationDefect(MessageDefect): - """A message had a continuation line as its first header line.""" - -class MisplacedEnvelopeHeaderDefect(MessageDefect): - """A 'Unix-from' header was found in the middle of a header block.""" - -class MalformedHeaderDefect(MessageDefect): - """Found a header that was missing a colon, or was otherwise malformed.""" - -class MultipartInvariantViolationDefect(MessageDefect): - """A message claimed to be a multipart but no subparts were found.""" diff --git a/Lib/email/FeedParser.py b/Lib/email/FeedParser.py deleted file mode 100644 index a2130e2..0000000 --- a/Lib/email/FeedParser.py +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright (C) 2004-2006 Python Software Foundation -# Authors: Baxter, Wouters and Warsaw -# Contact: email-sig@python.org - -"""FeedParser - An email feed parser. - -The feed parser implements an interface for incrementally parsing an email -message, line by line. This has advantages for certain applications, such as -those reading email messages off a socket. - -FeedParser.feed() is the primary interface for pushing new data into the -parser. It returns when there's nothing more it can do with the available -data. When you have no more data to push into the parser, call .close(). -This completes the parsing and returns the root message object. - -The other advantage of this parser is that it will never throw a parsing -exception. Instead, when it finds something unexpected, it adds a 'defect' to -the current message. Defects are just instances that live on the message -object's .defects attribute. -""" - -import re -from email import Errors -from email import Message - -NLCRE = re.compile('\r\n|\r|\n') -NLCRE_bol = re.compile('(\r\n|\r|\n)') -NLCRE_eol = re.compile('(\r\n|\r|\n)$') -NLCRE_crack = re.compile('(\r\n|\r|\n)') -# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character -# except controls, SP, and ":". -headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])') -EMPTYSTRING = '' -NL = '\n' - -NeedMoreData = object() - - - -class BufferedSubFile(object): - """A file-ish object that can have new data loaded into it. - - You can also push and pop line-matching predicates onto a stack. When the - current predicate matches the current line, a false EOF response - (i.e. empty string) is returned instead. This lets the parser adhere to a - simple abstraction -- it parses until EOF closes the current message. - """ - def __init__(self): - # The last partial line pushed into this object. - self._partial = '' - # The list of full, pushed lines, in reverse order - self._lines = [] - # The stack of false-EOF checking predicates. - self._eofstack = [] - # A flag indicating whether the file has been closed or not. - self._closed = False - - def push_eof_matcher(self, pred): - self._eofstack.append(pred) - - def pop_eof_matcher(self): - return self._eofstack.pop() - - def close(self): - # Don't forget any trailing partial line. - self._lines.append(self._partial) - self._partial = '' - self._closed = True - - def readline(self): - if not self._lines: - if self._closed: - return '' - return NeedMoreData - # Pop the line off the stack and see if it matches the current - # false-EOF predicate. - line = self._lines.pop() - # RFC 2046, section 5.1.2 requires us to recognize outer level - # boundaries at any level of inner nesting. Do this, but be sure it's - # in the order of most to least nested. - for ateof in self._eofstack[::-1]: - if ateof(line): - # We're at the false EOF. But push the last line back first. - self._lines.append(line) - return '' - return line - - def unreadline(self, line): - # Let the consumer push a line back into the buffer. - assert line is not NeedMoreData - self._lines.append(line) - - def push(self, data): - """Push some new data into this object.""" - # Handle any previous leftovers - data, self._partial = self._partial + data, '' - # Crack into lines, but preserve the newlines on the end of each - parts = NLCRE_crack.split(data) - # The *ahem* interesting behaviour of re.split when supplied grouping - # parentheses is that the last element of the resulting list is the - # data after the final RE. In the case of a NL/CR terminated string, - # this is the empty string. - self._partial = parts.pop() - # parts is a list of strings, alternating between the line contents - # and the eol character(s). Gather up a list of lines after - # re-attaching the newlines. - lines = [] - for i in range(len(parts) // 2): - lines.append(parts[i*2] + parts[i*2+1]) - self.pushlines(lines) - - def pushlines(self, lines): - # Reverse and insert at the front of the lines. - self._lines[:0] = lines[::-1] - - def is_closed(self): - return self._closed - - def __iter__(self): - return self - - def next(self): - line = self.readline() - if line == '': - raise StopIteration - return line - - - -class FeedParser: - """A feed-style parser of email.""" - - def __init__(self, _factory=Message.Message): - """_factory is called with no arguments to create a new message obj""" - self._factory = _factory - self._input = BufferedSubFile() - self._msgstack = [] - self._parse = self._parsegen().next - self._cur = None - self._last = None - self._headersonly = False - - # Non-public interface for supporting Parser's headersonly flag - def _set_headersonly(self): - self._headersonly = True - - def feed(self, data): - """Push more data into the parser.""" - self._input.push(data) - self._call_parse() - - def _call_parse(self): - try: - self._parse() - except StopIteration: - pass - - def close(self): - """Parse all remaining data and return the root message object.""" - self._input.close() - self._call_parse() - root = self._pop_message() - assert not self._msgstack - # Look for final set of defects - if root.get_content_maintype() == 'multipart' \ - and not root.is_multipart(): - root.defects.append(Errors.MultipartInvariantViolationDefect()) - return root - - def _new_message(self): - msg = self._factory() - if self._cur and self._cur.get_content_type() == 'multipart/digest': - msg.set_default_type('message/rfc822') - if self._msgstack: - self._msgstack[-1].attach(msg) - self._msgstack.append(msg) - self._cur = msg - self._last = msg - - def _pop_message(self): - retval = self._msgstack.pop() - if self._msgstack: - self._cur = self._msgstack[-1] - else: - self._cur = None - return retval - - def _parsegen(self): - # Create a new message and start by parsing headers. - self._new_message() - headers = [] - # Collect the headers, searching for a line that doesn't match the RFC - # 2822 header or continuation pattern (including an empty line). - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - if not headerRE.match(line): - # If we saw the RFC defined header/body separator - # (i.e. newline), just throw it away. Otherwise the line is - # part of the body so push it back. - if not NLCRE.match(line): - self._input.unreadline(line) - break - headers.append(line) - # Done with the headers, so parse them and figure out what we're - # supposed to see in the body of the message. - self._parse_headers(headers) - # Headers-only parsing is a backwards compatibility hack, which was - # necessary in the older parser, which could throw errors. All - # remaining lines in the input are thrown into the message body. - if self._headersonly: - lines = [] - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - if self._cur.get_content_type() == 'message/delivery-status': - # message/delivery-status contains blocks of headers separated by - # a blank line. We'll represent each header block as a separate - # nested message object, but the processing is a bit different - # than standard message/* types because there is no body for the - # nested messages. A blank line separates the subparts. - while True: - self._input.push_eof_matcher(NLCRE.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - msg = self._pop_message() - # We need to pop the EOF matcher in order to tell if we're at - # the end of the current file, not the end of the last block - # of message headers. - self._input.pop_eof_matcher() - # The input stream must be sitting at the newline or at the - # EOF. We want to see if we're at the end of this subpart, so - # first consume the blank line, then test the next line to see - # if we're at this subpart's EOF. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - if line == '': - break - # Not at EOF so this is a line we're going to need. - self._input.unreadline(line) - return - if self._cur.get_content_maintype() == 'message': - # The message claims to be a message/* type, then what follows is - # another RFC 2822 message. - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - self._pop_message() - return - if self._cur.get_content_maintype() == 'multipart': - boundary = self._cur.get_boundary() - if boundary is None: - # The message /claims/ to be a multipart but it has not - # defined a boundary. That's a problem which we'll handle by - # reading everything until the EOF and marking the message as - # defective. - self._cur.defects.append(Errors.NoBoundaryInMultipartDefect()) - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - # Create a line match predicate which matches the inter-part - # boundary as well as the end-of-multipart boundary. Don't push - # this onto the input stream until we've scanned past the - # preamble. - separator = '--' + boundary - boundaryre = re.compile( - '(?P<sep>' + re.escape(separator) + - r')(?P<end>--)?(?P<ws>[ \t]*)(?P<linesep>\r\n|\r|\n)?$') - capturing_preamble = True - preamble = [] - linesep = False - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - mo = boundaryre.match(line) - if mo: - # If we're looking at the end boundary, we're done with - # this multipart. If there was a newline at the end of - # the closing boundary, then we need to initialize the - # epilogue with the empty string (see below). - if mo.group('end'): - linesep = mo.group('linesep') - break - # We saw an inter-part boundary. Were we in the preamble? - if capturing_preamble: - if preamble: - # According to RFC 2046, the last newline belongs - # to the boundary. - lastline = preamble[-1] - eolmo = NLCRE_eol.search(lastline) - if eolmo: - preamble[-1] = lastline[:-len(eolmo.group(0))] - self._cur.preamble = EMPTYSTRING.join(preamble) - capturing_preamble = False - self._input.unreadline(line) - continue - # We saw a boundary separating two parts. Consume any - # multiple boundary lines that may be following. Our - # interpretation of RFC 2046 BNF grammar does not produce - # body parts within such double boundaries. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - mo = boundaryre.match(line) - if not mo: - self._input.unreadline(line) - break - # Recurse to parse this subpart; the input stream points - # at the subpart's first line. - self._input.push_eof_matcher(boundaryre.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - # Because of RFC 2046, the newline preceding the boundary - # separator actually belongs to the boundary, not the - # previous subpart's payload (or epilogue if the previous - # part is a multipart). - if self._last.get_content_maintype() == 'multipart': - epilogue = self._last.epilogue - if epilogue == '': - self._last.epilogue = None - elif epilogue is not None: - mo = NLCRE_eol.search(epilogue) - if mo: - end = len(mo.group(0)) - self._last.epilogue = epilogue[:-end] - else: - payload = self._last.get_payload() - if isinstance(payload, basestring): - mo = NLCRE_eol.search(payload) - if mo: - payload = payload[:-len(mo.group(0))] - self._last.set_payload(payload) - self._input.pop_eof_matcher() - self._pop_message() - # Set the multipart up for newline cleansing, which will - # happen if we're in a nested multipart. - self._last = self._cur - else: - # I think we must be in the preamble - assert capturing_preamble - preamble.append(line) - # We've seen either the EOF or the end boundary. If we're still - # capturing the preamble, we never saw the start boundary. Note - # that as a defect and store the captured text as the payload. - # Everything from here to the EOF is epilogue. - if capturing_preamble: - self._cur.defects.append(Errors.StartBoundaryNotFoundDefect()) - self._cur.set_payload(EMPTYSTRING.join(preamble)) - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # If the end boundary ended in a newline, we'll need to make sure - # the epilogue isn't None - if linesep: - epilogue = [''] - else: - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - epilogue.append(line) - # Any CRLF at the front of the epilogue is not technically part of - # the epilogue. Also, watch out for an empty string epilogue, - # which means a single newline. - if epilogue: - firstline = epilogue[0] - bolmo = NLCRE_bol.match(firstline) - if bolmo: - epilogue[0] = firstline[len(bolmo.group(0)):] - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # Otherwise, it's some non-multipart type, so the entire rest of the - # file contents becomes the payload. - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - - def _parse_headers(self, lines): - # Passed a list of lines that make up the headers for the current msg - lastheader = '' - lastvalue = [] - for lineno, line in enumerate(lines): - # Check for continuation - if line[0] in ' \t': - if not lastheader: - # The first line of the headers was a continuation. This - # is illegal, so let's note the defect, store the illegal - # line, and ignore it for purposes of headers. - defect = Errors.FirstHeaderLineIsContinuationDefect(line) - self._cur.defects.append(defect) - continue - lastvalue.append(line) - continue - if lastheader: - # XXX reconsider the joining of folded lines - lhdr = EMPTYSTRING.join(lastvalue)[:-1].rstrip('\r\n') - self._cur[lastheader] = lhdr - lastheader, lastvalue = '', [] - # Check for envelope header, i.e. unix-from - if line.startswith('From '): - if lineno == 0: - # Strip off the trailing newline - mo = NLCRE_eol.search(line) - if mo: - line = line[:-len(mo.group(0))] - self._cur.set_unixfrom(line) - continue - elif lineno == len(lines) - 1: - # Something looking like a unix-from at the end - it's - # probably the first line of the body, so push back the - # line and stop. - self._input.unreadline(line) - return - else: - # Weirdly placed unix-from line. Note this as a defect - # and ignore it. - defect = Errors.MisplacedEnvelopeHeaderDefect(line) - self._cur.defects.append(defect) - continue - # Split the line on the colon separating field name from value. - i = line.find(':') - if i < 0: - defect = Errors.MalformedHeaderDefect(line) - self._cur.defects.append(defect) - continue - lastheader = line[:i] - lastvalue = [line[i+1:].lstrip()] - # Done with all the lines, so handle the last header. - if lastheader: - # XXX reconsider the joining of folded lines - self._cur[lastheader] = EMPTYSTRING.join(lastvalue).rstrip('\r\n') diff --git a/Lib/email/Generator.py b/Lib/email/Generator.py deleted file mode 100644 index 7969916..0000000 --- a/Lib/email/Generator.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Classes to generate plain text from a message object tree.""" - -import re -import sys -import time -import random -import warnings -from cStringIO import StringIO - -from email.Header import Header - -UNDERSCORE = '_' -NL = '\n' - -fcre = re.compile(r'^From ', re.MULTILINE) - -def _is8bitstring(s): - if isinstance(s, str): - try: - unicode(s, 'us-ascii') - except UnicodeError: - return True - return False - - - -class Generator: - """Generates output from a Message object tree. - - This basic generator writes the message to the given file object as plain - text. - """ - # - # Public interface - # - - def __init__(self, outfp, mangle_from_=True, maxheaderlen=78): - """Create the generator for message flattening. - - outfp is the output file-like object for writing the message to. It - must have a write() method. - - Optional mangle_from_ is a flag that, when True (the default), escapes - From_ lines in the body of the message by putting a `>' in front of - them. - - Optional maxheaderlen specifies the longest length for a non-continued - header. When a header line is longer (in characters, with tabs - expanded to 8 spaces) than maxheaderlen, the header will split as - defined in the Header class. Set maxheaderlen to zero to disable - header wrapping. The default is 78, as recommended (but not required) - by RFC 2822. - """ - self._fp = outfp - self._mangle_from_ = mangle_from_ - self._maxheaderlen = maxheaderlen - - def write(self, s): - # Just delegate to the file object - self._fp.write(s) - - def flatten(self, msg, unixfrom=False): - """Print the message object tree rooted at msg to the output file - specified when the Generator instance was created. - - unixfrom is a flag that forces the printing of a Unix From_ delimiter - before the first object in the message tree. If the original message - has no From_ delimiter, a `standard' one is crafted. By default, this - is False to inhibit the printing of any From_ delimiter. - - Note that for subobjects, no From_ line is printed. - """ - if unixfrom: - ufrom = msg.get_unixfrom() - if not ufrom: - ufrom = 'From nobody ' + time.ctime(time.time()) - print >> self._fp, ufrom - self._write(msg) - - # For backwards compatibility, but this is slower - def __call__(self, msg, unixfrom=False): - warnings.warn('__call__() deprecated; use flatten()', - DeprecationWarning, 2) - self.flatten(msg, unixfrom) - - def clone(self, fp): - """Clone this generator with the exact same options.""" - return self.__class__(fp, self._mangle_from_, self._maxheaderlen) - - # - # Protected interface - undocumented ;/ - # - - def _write(self, msg): - # We can't write the headers yet because of the following scenario: - # say a multipart message includes the boundary string somewhere in - # its body. We'd have to calculate the new boundary /before/ we write - # the headers so that we can write the correct Content-Type: - # parameter. - # - # The way we do this, so as to make the _handle_*() methods simpler, - # is to cache any subpart writes into a StringIO. The we write the - # headers and the StringIO contents. That way, subpart handlers can - # Do The Right Thing, and can still modify the Content-Type: header if - # necessary. - oldfp = self._fp - try: - self._fp = sfp = StringIO() - self._dispatch(msg) - finally: - self._fp = oldfp - # Write the headers. First we see if the message object wants to - # handle that itself. If not, we'll do it generically. - meth = getattr(msg, '_write_headers', None) - if meth is None: - self._write_headers(msg) - else: - meth(self) - self._fp.write(sfp.getvalue()) - - def _dispatch(self, msg): - # Get the Content-Type: for the message, then try to dispatch to - # self._handle_<maintype>_<subtype>(). If there's no handler for the - # full MIME type, then dispatch to self._handle_<maintype>(). If - # that's missing too, then dispatch to self._writeBody(). - main = msg.get_content_maintype() - sub = msg.get_content_subtype() - specific = UNDERSCORE.join((main, sub)).replace('-', '_') - meth = getattr(self, '_handle_' + specific, None) - if meth is None: - generic = main.replace('-', '_') - meth = getattr(self, '_handle_' + generic, None) - if meth is None: - meth = self._writeBody - meth(msg) - - # - # Default handlers - # - - def _write_headers(self, msg): - for h, v in msg.items(): - print >> self._fp, '%s:' % h, - if self._maxheaderlen == 0: - # Explicit no-wrapping - print >> self._fp, v - elif isinstance(v, Header): - # Header instances know what to do - print >> self._fp, v.encode() - elif _is8bitstring(v): - # If we have raw 8bit data in a byte string, we have no idea - # what the encoding is. There is no safe way to split this - # string. If it's ascii-subset, then we could do a normal - # ascii split, but if it's multibyte then we could break the - # string. There's no way to know so the least harm seems to - # be to not split the string and risk it being too long. - print >> self._fp, v - else: - # Header's got lots of smarts, so use it. - print >> self._fp, Header( - v, maxlinelen=self._maxheaderlen, - header_name=h, continuation_ws='\t').encode() - # A blank line always separates headers from body - print >> self._fp - - # - # Handlers for writing types and subtypes - # - - def _handle_text(self, msg): - payload = msg.get_payload() - if payload is None: - return - if not isinstance(payload, basestring): - raise TypeError('string payload expected: %s' % type(payload)) - if self._mangle_from_: - payload = fcre.sub('>From ', payload) - self._fp.write(payload) - - # Default body handler - _writeBody = _handle_text - - def _handle_multipart(self, msg): - # The trick here is to write out each part separately, merge them all - # together, and then make sure that the boundary we've chosen isn't - # present in the payload. - msgtexts = [] - subparts = msg.get_payload() - if subparts is None: - subparts = [] - elif isinstance(subparts, basestring): - # e.g. a non-strict parse of a message with no starting boundary. - self._fp.write(subparts) - return - elif not isinstance(subparts, list): - # Scalar payload - subparts = [subparts] - for part in subparts: - s = StringIO() - g = self.clone(s) - g.flatten(part, unixfrom=False) - msgtexts.append(s.getvalue()) - # Now make sure the boundary we've selected doesn't appear in any of - # the message texts. - alltext = NL.join(msgtexts) - # BAW: What about boundaries that are wrapped in double-quotes? - boundary = msg.get_boundary(failobj=_make_boundary(alltext)) - # If we had to calculate a new boundary because the body text - # contained that string, set the new boundary. We don't do it - # unconditionally because, while set_boundary() preserves order, it - # doesn't preserve newlines/continuations in headers. This is no big - # deal in practice, but turns out to be inconvenient for the unittest - # suite. - if msg.get_boundary() <> boundary: - msg.set_boundary(boundary) - # If there's a preamble, write it out, with a trailing CRLF - if msg.preamble is not None: - print >> self._fp, msg.preamble - # dash-boundary transport-padding CRLF - print >> self._fp, '--' + boundary - # body-part - if msgtexts: - self._fp.write(msgtexts.pop(0)) - # *encapsulation - # --> delimiter transport-padding - # --> CRLF body-part - for body_part in msgtexts: - # delimiter transport-padding CRLF - print >> self._fp, '\n--' + boundary - # body-part - self._fp.write(body_part) - # close-delimiter transport-padding - self._fp.write('\n--' + boundary + '--') - if msg.epilogue is not None: - print >> self._fp - self._fp.write(msg.epilogue) - - def _handle_message_delivery_status(self, msg): - # We can't just write the headers directly to self's file object - # because this will leave an extra newline between the last header - # block and the boundary. Sigh. - blocks = [] - for part in msg.get_payload(): - s = StringIO() - g = self.clone(s) - g.flatten(part, unixfrom=False) - text = s.getvalue() - lines = text.split('\n') - # Strip off the unnecessary trailing empty line - if lines and lines[-1] == '': - blocks.append(NL.join(lines[:-1])) - else: - blocks.append(text) - # Now join all the blocks with an empty line. This has the lovely - # effect of separating each block with an empty line, but not adding - # an extra one after the last one. - self._fp.write(NL.join(blocks)) - - def _handle_message(self, msg): - s = StringIO() - g = self.clone(s) - # The payload of a message/rfc822 part should be a multipart sequence - # of length 1. The zeroth element of the list should be the Message - # object for the subpart. Extract that object, stringify it, and - # write it out. - g.flatten(msg.get_payload(0), unixfrom=False) - self._fp.write(s.getvalue()) - - - -_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' - -class DecodedGenerator(Generator): - """Generator a text representation of a message. - - Like the Generator base class, except that non-text parts are substituted - with a format string representing the part. - """ - def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): - """Like Generator.__init__() except that an additional optional - argument is allowed. - - Walks through all subparts of a message. If the subpart is of main - type `text', then it prints the decoded payload of the subpart. - - Otherwise, fmt is a format string that is used instead of the message - payload. fmt is expanded with the following keywords (in - %(keyword)s format): - - type : Full MIME type of the non-text part - maintype : Main MIME type of the non-text part - subtype : Sub-MIME type of the non-text part - filename : Filename of the non-text part - description: Description associated with the non-text part - encoding : Content transfer encoding of the non-text part - - The default value for fmt is None, meaning - - [Non-text (%(type)s) part of message omitted, filename %(filename)s] - """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) - if fmt is None: - self._fmt = _FMT - else: - self._fmt = fmt - - def _dispatch(self, msg): - for part in msg.walk(): - maintype = part.get_content_maintype() - if maintype == 'text': - print >> self, part.get_payload(decode=True) - elif maintype == 'multipart': - # Just skip this - pass - else: - print >> self, self._fmt % { - 'type' : part.get_content_type(), - 'maintype' : part.get_content_maintype(), - 'subtype' : part.get_content_subtype(), - 'filename' : part.get_filename('[no filename]'), - 'description': part.get('Content-Description', - '[no description]'), - 'encoding' : part.get('Content-Transfer-Encoding', - '[no encoding]'), - } - - - -# Helper -_width = len(repr(sys.maxint-1)) -_fmt = '%%0%dd' % _width - -def _make_boundary(text=None): - # Craft a random boundary. If text is given, ensure that the chosen - # boundary doesn't appear in the text. - token = random.randrange(sys.maxint) - boundary = ('=' * 15) + (_fmt % token) + '==' - if text is None: - return boundary - b = boundary - counter = 0 - while True: - cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE) - if not cre.search(text): - break - b = boundary + '.' + str(counter) - counter += 1 - return b diff --git a/Lib/email/Header.py b/Lib/email/Header.py deleted file mode 100644 index 5e24afe..0000000 --- a/Lib/email/Header.py +++ /dev/null @@ -1,495 +0,0 @@ -# Copyright (C) 2002-2004 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -"""Header encoding and decoding functionality.""" - -import re -import binascii - -import email.quopriMIME -import email.base64MIME -from email.Errors import HeaderParseError -from email.Charset import Charset - -NL = '\n' -SPACE = ' ' -USPACE = u' ' -SPACE8 = ' ' * 8 -UEMPTYSTRING = u'' - -MAXLINELEN = 76 - -USASCII = Charset('us-ascii') -UTF8 = Charset('utf-8') - -# Match encoded-word strings in the form =?charset?q?Hello_World?= -ecre = re.compile(r''' - =\? # literal =? - (?P<charset>[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P<encoding>[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P<encoded>.*?) # non-greedy up to the next ?= is the encoded string - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) - -# Field name regexp, including trailing colon, but not separating whitespace, -# according to RFC 2822. Character range is from tilde to exclamation mark. -# For use with .match() -fcre = re.compile(r'[\041-\176]+:$') - - - -# Helpers -_max_append = email.quopriMIME._max_append - - - -def decode_header(header): - """Decode a message header value without converting charset. - - Returns a list of (decoded_string, charset) pairs containing each of the - decoded parts of the header. Charset is None for non-encoded parts of the - header, otherwise a lower-case string containing the name of the character - set specified in the encoded string. - - An email.Errors.HeaderParseError may be raised when certain decoding error - occurs (e.g. a base64 decoding exception). - """ - # If no encoding, just return the header - header = str(header) - if not ecre.search(header): - return [(header, None)] - decoded = [] - dec = '' - for line in header.splitlines(): - # This line might not have an encoding in it - if not ecre.search(line): - decoded.append((line, None)) - continue - parts = ecre.split(line) - while parts: - unenc = parts.pop(0).strip() - if unenc: - # Should we continue a long line? - if decoded and decoded[-1][1] is None: - decoded[-1] = (decoded[-1][0] + SPACE + unenc, None) - else: - decoded.append((unenc, None)) - if parts: - charset, encoding = [s.lower() for s in parts[0:2]] - encoded = parts[2] - dec = None - if encoding == 'q': - dec = email.quopriMIME.header_decode(encoded) - elif encoding == 'b': - try: - dec = email.base64MIME.decode(encoded) - except binascii.Error: - # Turn this into a higher level exception. BAW: Right - # now we throw the lower level exception away but - # when/if we get exception chaining, we'll preserve it. - raise HeaderParseError - if dec is None: - dec = encoded - - if decoded and decoded[-1][1] == charset: - decoded[-1] = (decoded[-1][0] + dec, decoded[-1][1]) - else: - decoded.append((dec, charset)) - del parts[0:3] - return decoded - - - -def make_header(decoded_seq, maxlinelen=None, header_name=None, - continuation_ws=' '): - """Create a Header from a sequence of pairs as returned by decode_header() - - decode_header() takes a header value string and returns a sequence of - pairs of the format (decoded_string, charset) where charset is the string - name of the character set. - - This function takes one of those sequence of pairs and returns a Header - instance. Optional maxlinelen, header_name, and continuation_ws are as in - the Header constructor. - """ - h = Header(maxlinelen=maxlinelen, header_name=header_name, - continuation_ws=continuation_ws) - for s, charset in decoded_seq: - # None means us-ascii but we can simply pass it on to h.append() - if charset is not None and not isinstance(charset, Charset): - charset = Charset(charset) - h.append(s, charset) - return h - - - -class Header: - def __init__(self, s=None, charset=None, - maxlinelen=None, header_name=None, - continuation_ws=' ', errors='strict'): - """Create a MIME-compliant header that can contain many character sets. - - Optional s is the initial header value. If None, the initial header - value is not set. You can later append to the header with .append() - method calls. s may be a byte string or a Unicode string, but see the - .append() documentation for semantics. - - Optional charset serves two purposes: it has the same meaning as the - charset argument to the .append() method. It also sets the default - character set for all subsequent .append() calls that omit the charset - argument. If charset is not provided in the constructor, the us-ascii - charset is used both as s's initial charset and as the default for - subsequent .append() calls. - - The maximum line length can be specified explicit via maxlinelen. For - splitting the first line to a shorter value (to account for the field - header which isn't included in s, e.g. `Subject') pass in the name of - the field in header_name. The default maxlinelen is 76. - - continuation_ws must be RFC 2822 compliant folding whitespace (usually - either a space or a hard tab) which will be prepended to continuation - lines. - - errors is passed through to the .append() call. - """ - if charset is None: - charset = USASCII - if not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - self._continuation_ws = continuation_ws - cws_expanded_len = len(continuation_ws.replace('\t', SPACE8)) - # BAW: I believe `chunks' and `maxlinelen' should be non-public. - self._chunks = [] - if s is not None: - self.append(s, charset, errors) - if maxlinelen is None: - maxlinelen = MAXLINELEN - if header_name is None: - # We don't know anything about the field header so the first line - # is the same length as subsequent lines. - self._firstlinelen = maxlinelen - else: - # The first line should be shorter to take into account the field - # header. Also subtract off 2 extra for the colon and space. - self._firstlinelen = maxlinelen - len(header_name) - 2 - # Second and subsequent lines should subtract off the length in - # columns of the continuation whitespace prefix. - self._maxlinelen = maxlinelen - cws_expanded_len - - def __str__(self): - """A synonym for self.encode().""" - return self.encode() - - def __unicode__(self): - """Helper for the built-in unicode function.""" - uchunks = [] - lastcs = None - for s, charset in self._chunks: - # We must preserve spaces between encoded and non-encoded word - # boundaries, which means for us we need to add a space when we go - # from a charset to None/us-ascii, or from None/us-ascii to a - # charset. Only do this for the second and subsequent chunks. - nextcs = charset - if uchunks: - if lastcs not in (None, 'us-ascii'): - if nextcs in (None, 'us-ascii'): - uchunks.append(USPACE) - nextcs = None - elif nextcs not in (None, 'us-ascii'): - uchunks.append(USPACE) - lastcs = nextcs - uchunks.append(unicode(s, str(charset))) - return UEMPTYSTRING.join(uchunks) - - # Rich comparison operators for equality only. BAW: does it make sense to - # have or explicitly disable <, <=, >, >= operators? - def __eq__(self, other): - # other may be a Header or a string. Both are fine so coerce - # ourselves to a string, swap the args and do another comparison. - return other == self.encode() - - def __ne__(self, other): - return not self == other - - def append(self, s, charset=None, errors='strict'): - """Append a string to the MIME header. - - Optional charset, if given, should be a Charset instance or the name - of a character set (which will be converted to a Charset instance). A - value of None (the default) means that the charset given in the - constructor is used. - - s may be a byte string or a Unicode string. If it is a byte string - (i.e. isinstance(s, str) is true), then charset is the encoding of - that byte string, and a UnicodeError will be raised if the string - cannot be decoded with that charset. If s is a Unicode string, then - charset is a hint specifying the character set of the characters in - the string. In this case, when producing an RFC 2822 compliant header - using RFC 2047 rules, the Unicode string will be encoded using the - following charsets in order: us-ascii, the charset hint, utf-8. The - first character set not to provoke a UnicodeError is used. - - Optional `errors' is passed as the third argument to any unicode() or - ustr.encode() call. - """ - if charset is None: - charset = self._charset - elif not isinstance(charset, Charset): - charset = Charset(charset) - # If the charset is our faux 8bit charset, leave the string unchanged - if charset <> '8bit': - # We need to test that the string can be converted to unicode and - # back to a byte string, given the input and output codecs of the - # charset. - if isinstance(s, str): - # Possibly raise UnicodeError if the byte string can't be - # converted to a unicode with the input codec of the charset. - incodec = charset.input_codec or 'us-ascii' - ustr = unicode(s, incodec, errors) - # Now make sure that the unicode could be converted back to a - # byte string with the output codec, which may be different - # than the iput coded. Still, use the original byte string. - outcodec = charset.output_codec or 'us-ascii' - ustr.encode(outcodec, errors) - elif isinstance(s, unicode): - # Now we have to be sure the unicode string can be converted - # to a byte string with a reasonable output codec. We want to - # use the byte string in the chunk. - for charset in USASCII, charset, UTF8: - try: - outcodec = charset.output_codec or 'us-ascii' - s = s.encode(outcodec, errors) - break - except UnicodeError: - pass - else: - assert False, 'utf-8 conversion failed' - self._chunks.append((s, charset)) - - def _split(self, s, charset, maxlinelen, splitchars): - # Split up a header safely for use with encode_chunks. - splittable = charset.to_splittable(s) - encoded = charset.from_splittable(splittable, True) - elen = charset.encoded_header_len(encoded) - # If the line's encoded length first, just return it - if elen <= maxlinelen: - return [(encoded, charset)] - # If we have undetermined raw 8bit characters sitting in a byte - # string, we really don't know what the right thing to do is. We - # can't really split it because it might be multibyte data which we - # could break if we split it between pairs. The least harm seems to - # be to not split the header at all, but that means they could go out - # longer than maxlinelen. - if charset == '8bit': - return [(s, charset)] - # BAW: I'm not sure what the right test here is. What we're trying to - # do is be faithful to RFC 2822's recommendation that ($2.2.3): - # - # "Note: Though structured field bodies are defined in such a way that - # folding can take place between many of the lexical tokens (and even - # within some of the lexical tokens), folding SHOULD be limited to - # placing the CRLF at higher-level syntactic breaks." - # - # For now, I can only imagine doing this when the charset is us-ascii, - # although it's possible that other charsets may also benefit from the - # higher-level syntactic breaks. - elif charset == 'us-ascii': - return self._split_ascii(s, charset, maxlinelen, splitchars) - # BAW: should we use encoded? - elif elen == len(s): - # We can split on _maxlinelen boundaries because we know that the - # encoding won't change the size of the string - splitpnt = maxlinelen - first = charset.from_splittable(splittable[:splitpnt], False) - last = charset.from_splittable(splittable[splitpnt:], False) - else: - # Binary search for split point - first, last = _binsplit(splittable, charset, maxlinelen) - # first is of the proper length so just wrap it in the appropriate - # chrome. last must be recursively split. - fsplittable = charset.to_splittable(first) - fencoded = charset.from_splittable(fsplittable, True) - chunk = [(fencoded, charset)] - return chunk + self._split(last, charset, self._maxlinelen, splitchars) - - def _split_ascii(self, s, charset, firstlen, splitchars): - chunks = _split_ascii(s, firstlen, self._maxlinelen, - self._continuation_ws, splitchars) - return zip(chunks, [charset]*len(chunks)) - - def _encode_chunks(self, newchunks, maxlinelen): - # MIME-encode a header with many different charsets and/or encodings. - # - # Given a list of pairs (string, charset), return a MIME-encoded - # string suitable for use in a header field. Each pair may have - # different charsets and/or encodings, and the resulting header will - # accurately reflect each setting. - # - # Each encoding can be email.Utils.QP (quoted-printable, for - # ASCII-like character sets like iso-8859-1), email.Utils.BASE64 - # (Base64, for non-ASCII like character sets like KOI8-R and - # iso-2022-jp), or None (no encoding). - # - # Each pair will be represented on a separate line; the resulting - # string will be in the format: - # - # =?charset1?q?Mar=EDa_Gonz=E1lez_Alonso?=\n - # =?charset2?b?SvxyZ2VuIEL2aW5n?=" - chunks = [] - for header, charset in newchunks: - if not header: - continue - if charset is None or charset.header_encoding is None: - s = header - else: - s = charset.header_encode(header) - # Don't add more folding whitespace than necessary - if chunks and chunks[-1].endswith(' '): - extra = '' - else: - extra = ' ' - _max_append(chunks, s, maxlinelen, extra) - joiner = NL + self._continuation_ws - return joiner.join(chunks) - - def encode(self, splitchars=';, '): - """Encode a message header into an RFC-compliant format. - - There are many issues involved in converting a given string for use in - an email header. Only certain character sets are readable in most - email clients, and as header strings can only contain a subset of - 7-bit ASCII, care must be taken to properly convert and encode (with - Base64 or quoted-printable) header strings. In addition, there is a - 75-character length limit on any given encoded header field, so - line-wrapping must be performed, even with double-byte character sets. - - This method will do its best to convert the string to the correct - character set used in email, and encode and line wrap it safely with - the appropriate scheme for that character set. - - If the given charset is not known or an error occurs during - conversion, this function will return the header untouched. - - Optional splitchars is a string containing characters to split long - ASCII lines on, in rough support of RFC 2822's `highest level - syntactic breaks'. This doesn't affect RFC 2047 encoded lines. - """ - newchunks = [] - maxlinelen = self._firstlinelen - lastlen = 0 - for s, charset in self._chunks: - # The first bit of the next chunk should be just long enough to - # fill the next line. Don't forget the space separating the - # encoded words. - targetlen = maxlinelen - lastlen - 1 - if targetlen < charset.encoded_header_len(''): - # Stick it on the next line - targetlen = maxlinelen - newchunks += self._split(s, charset, targetlen, splitchars) - lastchunk, lastcharset = newchunks[-1] - lastlen = lastcharset.encoded_header_len(lastchunk) - return self._encode_chunks(newchunks, maxlinelen) - - - -def _split_ascii(s, firstlen, restlen, continuation_ws, splitchars): - lines = [] - maxlen = firstlen - for line in s.splitlines(): - # Ignore any leading whitespace (i.e. continuation whitespace) already - # on the line, since we'll be adding our own. - line = line.lstrip() - if len(line) < maxlen: - lines.append(line) - maxlen = restlen - continue - # Attempt to split the line at the highest-level syntactic break - # possible. Note that we don't have a lot of smarts about field - # syntax; we just try to break on semi-colons, then commas, then - # whitespace. - for ch in splitchars: - if ch in line: - break - else: - # There's nothing useful to split the line on, not even spaces, so - # just append this line unchanged - lines.append(line) - maxlen = restlen - continue - # Now split the line on the character plus trailing whitespace - cre = re.compile(r'%s\s*' % ch) - if ch in ';,': - eol = ch - else: - eol = '' - joiner = eol + ' ' - joinlen = len(joiner) - wslen = len(continuation_ws.replace('\t', SPACE8)) - this = [] - linelen = 0 - for part in cre.split(line): - curlen = linelen + max(0, len(this)-1) * joinlen - partlen = len(part) - onfirstline = not lines - # We don't want to split after the field name, if we're on the - # first line and the field name is present in the header string. - if ch == ' ' and onfirstline and \ - len(this) == 1 and fcre.match(this[0]): - this.append(part) - linelen += partlen - elif curlen + partlen > maxlen: - if this: - lines.append(joiner.join(this) + eol) - # If this part is longer than maxlen and we aren't already - # splitting on whitespace, try to recursively split this line - # on whitespace. - if partlen > maxlen and ch <> ' ': - subl = _split_ascii(part, maxlen, restlen, - continuation_ws, ' ') - lines.extend(subl[:-1]) - this = [subl[-1]] - else: - this = [part] - linelen = wslen + len(this[-1]) - maxlen = restlen - else: - this.append(part) - linelen += partlen - # Put any left over parts on a line by themselves - if this: - lines.append(joiner.join(this)) - return lines - - - -def _binsplit(splittable, charset, maxlinelen): - i = 0 - j = len(splittable) - while i < j: - # Invariants: - # 1. splittable[:k] fits for all k <= i (note that we *assume*, - # at the start, that splittable[:0] fits). - # 2. splittable[:k] does not fit for any k > j (at the start, - # this means we shouldn't look at any k > len(splittable)). - # 3. We don't know about splittable[:k] for k in i+1..j. - # 4. We want to set i to the largest k that fits, with i <= k <= j. - # - m = (i+j+1) >> 1 # ceiling((i+j)/2); i < m <= j - chunk = charset.from_splittable(splittable[:m], True) - chunklen = charset.encoded_header_len(chunk) - if chunklen <= maxlinelen: - # m is acceptable, so is a new lower bound. - i = m - else: - # m is not acceptable, so final i must be < m. - j = m - 1 - # i == j. Invariant #1 implies that splittable[:i] fits, and - # invariant #2 implies that splittable[:i+1] does not fit, so i - # is what we're looking for. - first = charset.from_splittable(splittable[:i], False) - last = charset.from_splittable(splittable[i:], False) - return first, last diff --git a/Lib/email/Iterators.py b/Lib/email/Iterators.py deleted file mode 100644 index 74a93c7..0000000 --- a/Lib/email/Iterators.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Various types of useful iterators and generators.""" - -import sys -from cStringIO import StringIO - - - -# This function will become a method of the Message class -def walk(self): - """Walk over the message tree, yielding each subpart. - - The walk is performed in depth-first order. This method is a - generator. - """ - yield self - if self.is_multipart(): - for subpart in self.get_payload(): - for subsubpart in subpart.walk(): - yield subsubpart - - - -# These two functions are imported into the Iterators.py interface module. -# The Python 2.2 version uses generators for efficiency. -def body_line_iterator(msg, decode=False): - """Iterate over the parts, returning string payloads line-by-line. - - Optional decode (default False) is passed through to .get_payload(). - """ - for subpart in msg.walk(): - payload = subpart.get_payload(decode=decode) - if isinstance(payload, basestring): - for line in StringIO(payload): - yield line - - -def typed_subpart_iterator(msg, maintype='text', subtype=None): - """Iterate over the subparts with a given MIME type. - - Use `maintype' as the main MIME type to match against; this defaults to - "text". Optional `subtype' is the MIME subtype to match against; if - omitted, only the main type is matched. - """ - for subpart in msg.walk(): - if subpart.get_content_maintype() == maintype: - if subtype is None or subpart.get_content_subtype() == subtype: - yield subpart - - - -def _structure(msg, fp=None, level=0, include_default=False): - """A handy debugging aid""" - if fp is None: - fp = sys.stdout - tab = ' ' * (level * 4) - print >> fp, tab + msg.get_content_type(), - if include_default: - print >> fp, '[%s]' % msg.get_default_type() - else: - print >> fp - if msg.is_multipart(): - for subpart in msg.get_payload(): - _structure(subpart, fp, level+1, include_default) diff --git a/Lib/email/MIMEAudio.py b/Lib/email/MIMEAudio.py deleted file mode 100644 index 266ec4c..0000000 --- a/Lib/email/MIMEAudio.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Anthony Baxter -# Contact: email-sig@python.org - -"""Class representing audio/* type MIME documents.""" - -import sndhdr -from cStringIO import StringIO - -from email import Errors -from email import Encoders -from email.MIMENonMultipart import MIMENonMultipart - - - -_sndhdr_MIMEmap = {'au' : 'basic', - 'wav' :'x-wav', - 'aiff':'x-aiff', - 'aifc':'x-aiff', - } - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = StringIO(hdr) - for testfn in sndhdr.tests: - res = testfn(hdr, fakefile) - if res is not None: - return _sndhdr_MIMEmap.get(res[0]) - return None - - - -class MIMEAudio(MIMENonMultipart): - """Class for generating audio/* MIME documents.""" - - def __init__(self, _audiodata, _subtype=None, - _encoder=Encoders.encode_base64, **_params): - """Create an audio/* type MIME document. - - _audiodata is a string containing the raw audio data. If this data - can be decoded by the standard Python `sndhdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific audio subtype via the - _subtype parameter. If _subtype is not given, and no subtype can be - guessed, a TypeError is raised. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = _whatsnd(_audiodata) - if _subtype is None: - raise TypeError('Could not find audio MIME subtype') - MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) - self.set_payload(_audiodata) - _encoder(self) diff --git a/Lib/email/MIMEBase.py b/Lib/email/MIMEBase.py deleted file mode 100644 index 88691f8..0000000 --- a/Lib/email/MIMEBase.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME specializations.""" - -from email import Message - - - -class MIMEBase(Message.Message): - """Base class for MIME specializations.""" - - def __init__(self, _maintype, _subtype, **_params): - """This constructor adds a Content-Type: and a MIME-Version: header. - - The Content-Type: header is taken from the _maintype and _subtype - arguments. Additional parameters for this header are taken from the - keyword arguments. - """ - Message.Message.__init__(self) - ctype = '%s/%s' % (_maintype, _subtype) - self.add_header('Content-Type', ctype, **_params) - self['MIME-Version'] = '1.0' diff --git a/Lib/email/MIMEImage.py b/Lib/email/MIMEImage.py deleted file mode 100644 index a658067..0000000 --- a/Lib/email/MIMEImage.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing image/* type MIME documents.""" - -import imghdr - -from email import Errors -from email import Encoders -from email.MIMENonMultipart import MIMENonMultipart - - - -class MIMEImage(MIMENonMultipart): - """Class for generating image/* type MIME documents.""" - - def __init__(self, _imagedata, _subtype=None, - _encoder=Encoders.encode_base64, **_params): - """Create an image/* type MIME document. - - _imagedata is a string containing the raw image data. If this data - can be decoded by the standard Python `imghdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific image subtype via the _subtype - parameter. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = imghdr.what(None, _imagedata) - if _subtype is None: - raise TypeError('Could not guess image MIME subtype') - MIMENonMultipart.__init__(self, 'image', _subtype, **_params) - self.set_payload(_imagedata) - _encoder(self) diff --git a/Lib/email/MIMEMessage.py b/Lib/email/MIMEMessage.py deleted file mode 100644 index 3021934..0000000 --- a/Lib/email/MIMEMessage.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing message/* MIME documents.""" - -from email import Message -from email.MIMENonMultipart import MIMENonMultipart - - - -class MIMEMessage(MIMENonMultipart): - """Class representing message/* MIME documents.""" - - def __init__(self, _msg, _subtype='rfc822'): - """Create a message/* type MIME document. - - _msg is a message object and must be an instance of Message, or a - derived class of Message, otherwise a TypeError is raised. - - Optional _subtype defines the subtype of the contained message. The - default is "rfc822" (this is defined by the MIME standard, even though - the term "rfc822" is technically outdated by RFC 2822). - """ - MIMENonMultipart.__init__(self, 'message', _subtype) - if not isinstance(_msg, Message.Message): - raise TypeError('Argument is not an instance of Message') - # It's convenient to use this base class method. We need to do it - # this way or we'll get an exception - Message.Message.attach(self, _msg) - # And be sure our default type is set correctly - self.set_default_type('message/rfc822') diff --git a/Lib/email/MIMEMultipart.py b/Lib/email/MIMEMultipart.py deleted file mode 100644 index 9072a64..0000000 --- a/Lib/email/MIMEMultipart.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2002-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME multipart/* type messages.""" - -from email import MIMEBase - - - -class MIMEMultipart(MIMEBase.MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def __init__(self, _subtype='mixed', boundary=None, _subparts=None, - **_params): - """Creates a multipart/* type message. - - By default, creates a multipart/mixed message, with proper - Content-Type and MIME-Version headers. - - _subtype is the subtype of the multipart content type, defaulting to - `mixed'. - - boundary is the multipart boundary string. By default it is - calculated as needed. - - _subparts is a sequence of initial subparts for the payload. It - must be an iterable object, such as a list. You can always - attach new subparts to the message by using the attach() method. - - Additional parameters for the Content-Type header are taken from the - keyword arguments (or passed into the _params argument). - """ - MIMEBase.MIMEBase.__init__(self, 'multipart', _subtype, **_params) - if _subparts: - for p in _subparts: - self.attach(p) - if boundary: - self.set_boundary(boundary) diff --git a/Lib/email/MIMENonMultipart.py b/Lib/email/MIMENonMultipart.py deleted file mode 100644 index 4195d2a..0000000 --- a/Lib/email/MIMENonMultipart.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2002-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME type messages that are not multipart.""" - -from email import Errors -from email import MIMEBase - - - -class MIMENonMultipart(MIMEBase.MIMEBase): - """Base class for MIME multipart/* type messages.""" - - __pychecker__ = 'unusednames=payload' - - def attach(self, payload): - # The public API prohibits attaching multiple subparts to MIMEBase - # derived subtypes since none of them are, by definition, of content - # type multipart/* - raise Errors.MultipartConversionError( - 'Cannot attach additional subparts to non-multipart/*') - - del __pychecker__ diff --git a/Lib/email/MIMEText.py b/Lib/email/MIMEText.py deleted file mode 100644 index 5ef1876..0000000 --- a/Lib/email/MIMEText.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing text/* type MIME documents.""" - -from email.MIMENonMultipart import MIMENonMultipart -from email.Encoders import encode_7or8bit - - - -class MIMEText(MIMENonMultipart): - """Class for generating text/* type MIME documents.""" - - def __init__(self, _text, _subtype='plain', _charset='us-ascii'): - """Create a text/* type MIME document. - - _text is the string for this message object. - - _subtype is the MIME sub content type, defaulting to "plain". - - _charset is the character set parameter added to the Content-Type - header. This defaults to "us-ascii". Note that as a side-effect, the - Content-Transfer-Encoding header will also be set. - """ - MIMENonMultipart.__init__(self, 'text', _subtype, - **{'charset': _charset}) - self.set_payload(_text, _charset) diff --git a/Lib/email/Message.py b/Lib/email/Message.py deleted file mode 100644 index bc76416..0000000 --- a/Lib/email/Message.py +++ /dev/null @@ -1,814 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Basic message object for the email package object model.""" - -import re -import uu -import binascii -import warnings -from cStringIO import StringIO - -# Intrapackage imports -from email import Utils -from email import Errors -from email import Charset - -SEMISPACE = '; ' - -# Regular expression used to split header parameters. BAW: this may be too -# simple. It isn't strictly RFC 2045 (section 5.1) compliant, but it catches -# most headers found in the wild. We may eventually need a full fledged -# parser eventually. -paramre = re.compile(r'\s*;\s*') -# Regular expression that matches `special' characters in parameters, the -# existance of which force quoting of the parameter value. -tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') - - - -# Helper functions -def _formatparam(param, value=None, quote=True): - """Convenience function to format and return a key=value pair. - - This will quote the value if needed or if quote is true. - """ - if value is not None and len(value) > 0: - # A tuple is used for RFC 2231 encoded parameter values where items - # are (charset, language, value). charset is a string, not a Charset - # instance. - if isinstance(value, tuple): - # Encode as per RFC 2231 - param += '*' - value = Utils.encode_rfc2231(value[2], value[0], value[1]) - # BAW: Please check this. I think that if quote is set it should - # force quoting even if not necessary. - if quote or tspecials.search(value): - return '%s="%s"' % (param, Utils.quote(value)) - else: - return '%s=%s' % (param, value) - else: - return param - -def _parseparam(s): - plist = [] - while s[:1] == ';': - s = s[1:] - end = s.find(';') - while end > 0 and s.count('"', 0, end) % 2: - end = s.find(';', end + 1) - if end < 0: - end = len(s) - f = s[:end] - if '=' in f: - i = f.index('=') - f = f[:i].strip().lower() + '=' + f[i+1:].strip() - plist.append(f.strip()) - s = s[end:] - return plist - - -def _unquotevalue(value): - # This is different than Utils.collapse_rfc2231_value() because it doesn't - # try to convert the value to a unicode. Message.get_param() and - # Message.get_params() are both currently defined to return the tuple in - # the face of RFC 2231 parameters. - if isinstance(value, tuple): - return value[0], value[1], Utils.unquote(value[2]) - else: - return Utils.unquote(value) - - - -class Message: - """Basic message object. - - A message object is defined as something that has a bunch of RFC 2822 - headers and a payload. It may optionally have an envelope header - (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a - multipart or a message/rfc822), then the payload is a list of Message - objects, otherwise it is a string. - - Message objects implement part of the `mapping' interface, which assumes - there is exactly one occurrance of the header per message. Some headers - do in fact appear multiple times (e.g. Received) and for those headers, - you must use the explicit API to set or get all the headers. Not all of - the mapping methods are implemented. - """ - def __init__(self): - self._headers = [] - self._unixfrom = None - self._payload = None - self._charset = None - # Defaults for multipart messages - self.preamble = self.epilogue = None - self.defects = [] - # Default content type - self._default_type = 'text/plain' - - def __str__(self): - """Return the entire formatted message as a string. - This includes the headers, body, and envelope header. - """ - return self.as_string(unixfrom=True) - - def as_string(self, unixfrom=False): - """Return the entire formatted message as a string. - Optional `unixfrom' when True, means include the Unix From_ envelope - header. - - This is a convenience method and may not generate the message exactly - as you intend because by default it mangles lines that begin with - "From ". For more flexibility, use the flatten() method of a - Generator instance. - """ - from email.Generator import Generator - fp = StringIO() - g = Generator(fp) - g.flatten(self, unixfrom=unixfrom) - return fp.getvalue() - - def is_multipart(self): - """Return True if the message consists of multiple parts.""" - return isinstance(self._payload, list) - - # - # Unix From_ line - # - def set_unixfrom(self, unixfrom): - self._unixfrom = unixfrom - - def get_unixfrom(self): - return self._unixfrom - - # - # Payload manipulation. - # - def attach(self, payload): - """Add the given payload to the current payload. - - The current payload will always be a list of objects after this method - is called. If you want to set the payload to a scalar object, use - set_payload() instead. - """ - if self._payload is None: - self._payload = [payload] - else: - self._payload.append(payload) - - def get_payload(self, i=None, decode=False): - """Return a reference to the payload. - - The payload will either be a list object or a string. If you mutate - the list object, you modify the message's payload in place. Optional - i returns that index into the payload. - - Optional decode is a flag indicating whether the payload should be - decoded or not, according to the Content-Transfer-Encoding header - (default is False). - - When True and the message is not a multipart, the payload will be - decoded if this header's value is `quoted-printable' or `base64'. If - some other encoding is used, or the header is missing, or if the - payload has bogus data (i.e. bogus base64 or uuencoded data), the - payload is returned as-is. - - If the message is a multipart and the decode flag is True, then None - is returned. - """ - if i is None: - payload = self._payload - elif not isinstance(self._payload, list): - raise TypeError('Expected list, got %s' % type(self._payload)) - else: - payload = self._payload[i] - if decode: - if self.is_multipart(): - return None - cte = self.get('content-transfer-encoding', '').lower() - if cte == 'quoted-printable': - return Utils._qdecode(payload) - elif cte == 'base64': - try: - return Utils._bdecode(payload) - except binascii.Error: - # Incorrect padding - return payload - elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - sfp = StringIO() - try: - uu.decode(StringIO(payload+'\n'), sfp) - payload = sfp.getvalue() - except uu.Error: - # Some decoding problem - return payload - # Everything else, including encodings with 8bit or 7bit are returned - # unchanged. - return payload - - def set_payload(self, payload, charset=None): - """Set the payload to the given value. - - Optional charset sets the message's default character set. See - set_charset() for details. - """ - self._payload = payload - if charset is not None: - self.set_charset(charset) - - def set_charset(self, charset): - """Set the charset of the payload to a given character set. - - charset can be a Charset instance, a string naming a character set, or - None. If it is a string it will be converted to a Charset instance. - If charset is None, the charset parameter will be removed from the - Content-Type field. Anything else will generate a TypeError. - - The message will be assumed to be of type text/* encoded with - charset.input_charset. It will be converted to charset.output_charset - and encoded properly, if needed, when generating the plain text - representation of the message. MIME headers (MIME-Version, - Content-Type, Content-Transfer-Encoding) will be added as needed. - - """ - if charset is None: - self.del_param('charset') - self._charset = None - return - if isinstance(charset, str): - charset = Charset.Charset(charset) - if not isinstance(charset, Charset.Charset): - raise TypeError(charset) - # BAW: should we accept strings that can serve as arguments to the - # Charset constructor? - self._charset = charset - if not self.has_key('MIME-Version'): - self.add_header('MIME-Version', '1.0') - if not self.has_key('Content-Type'): - self.add_header('Content-Type', 'text/plain', - charset=charset.get_output_charset()) - else: - self.set_param('charset', charset.get_output_charset()) - if str(charset) <> charset.get_output_charset(): - self._payload = charset.body_encode(self._payload) - if not self.has_key('Content-Transfer-Encoding'): - cte = charset.get_body_encoding() - try: - cte(self) - except TypeError: - self._payload = charset.body_encode(self._payload) - self.add_header('Content-Transfer-Encoding', cte) - - def get_charset(self): - """Return the Charset instance associated with the message's payload. - """ - return self._charset - - # - # MAPPING INTERFACE (partial) - # - def __len__(self): - """Return the total number of headers, including duplicates.""" - return len(self._headers) - - def __getitem__(self, name): - """Get a header value. - - Return None if the header is missing instead of raising an exception. - - Note that if the header appeared multiple times, exactly which - occurrance gets returned is undefined. Use get_all() to get all - the values matching a header field name. - """ - return self.get(name) - - def __setitem__(self, name, val): - """Set the value of a header. - - Note: this does not overwrite an existing header with the same field - name. Use __delitem__() first to delete any existing headers. - """ - self._headers.append((name, val)) - - def __delitem__(self, name): - """Delete all occurrences of a header, if present. - - Does not raise an exception if the header is missing. - """ - name = name.lower() - newheaders = [] - for k, v in self._headers: - if k.lower() <> name: - newheaders.append((k, v)) - self._headers = newheaders - - def __contains__(self, name): - return name.lower() in [k.lower() for k, v in self._headers] - - def has_key(self, name): - """Return true if the message contains the header.""" - missing = object() - return self.get(name, missing) is not missing - - def keys(self): - """Return a list of all the message's header field names. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [k for k, v in self._headers] - - def values(self): - """Return a list of all the message's header values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [v for k, v in self._headers] - - def items(self): - """Get all the message's header fields and values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return self._headers[:] - - def get(self, name, failobj=None): - """Get a header value. - - Like __getitem__() but return failobj instead of None when the field - is missing. - """ - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - return v - return failobj - - # - # Additional useful stuff - # - - def get_all(self, name, failobj=None): - """Return a list of all the values for the named field. - - These will be sorted in the order they appeared in the original - message, and may contain duplicates. Any fields deleted and - re-inserted are always appended to the header list. - - If no such fields exist, failobj is returned (defaults to None). - """ - values = [] - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - values.append(v) - if not values: - return failobj - return values - - def add_header(self, _name, _value, **_params): - """Extended header setting. - - name is the header field to add. keyword arguments can be used to set - additional parameters for the header field, with underscores converted - to dashes. Normally the parameter will be added as key="value" unless - value is None, in which case only the key will be added. - - Example: - - msg.add_header('content-disposition', 'attachment', filename='bud.gif') - """ - parts = [] - for k, v in _params.items(): - if v is None: - parts.append(k.replace('_', '-')) - else: - parts.append(_formatparam(k.replace('_', '-'), v)) - if _value is not None: - parts.insert(0, _value) - self._headers.append((_name, SEMISPACE.join(parts))) - - def replace_header(self, _name, _value): - """Replace a header. - - Replace the first matching header found in the message, retaining - header order and case. If no matching header was found, a KeyError is - raised. - """ - _name = _name.lower() - for i, (k, v) in zip(range(len(self._headers)), self._headers): - if k.lower() == _name: - self._headers[i] = (k, _value) - break - else: - raise KeyError(_name) - - # - # Deprecated methods. These will be removed in email 3.1. - # - - def get_type(self, failobj=None): - """Returns the message's content type. - - The returned string is coerced to lowercase and returned as a single - string of the form `maintype/subtype'. If there was no Content-Type - header in the message, failobj is returned (defaults to None). - """ - warnings.warn('get_type() deprecated; use get_content_type()', - DeprecationWarning, 2) - missing = object() - value = self.get('content-type', missing) - if value is missing: - return failobj - return paramre.split(value)[0].lower().strip() - - def get_main_type(self, failobj=None): - """Return the message's main content type if present.""" - warnings.warn('get_main_type() deprecated; use get_content_maintype()', - DeprecationWarning, 2) - missing = object() - ctype = self.get_type(missing) - if ctype is missing: - return failobj - if ctype.count('/') <> 1: - return failobj - return ctype.split('/')[0] - - def get_subtype(self, failobj=None): - """Return the message's content subtype if present.""" - warnings.warn('get_subtype() deprecated; use get_content_subtype()', - DeprecationWarning, 2) - missing = object() - ctype = self.get_type(missing) - if ctype is missing: - return failobj - if ctype.count('/') <> 1: - return failobj - return ctype.split('/')[1] - - # - # Use these three methods instead of the three above. - # - - def get_content_type(self): - """Return the message's content type. - - The returned string is coerced to lower case of the form - `maintype/subtype'. If there was no Content-Type header in the - message, the default type as given by get_default_type() will be - returned. Since according to RFC 2045, messages always have a default - type this will always return a value. - - RFC 2045 defines a message's default type to be text/plain unless it - appears inside a multipart/digest container, in which case it would be - message/rfc822. - """ - missing = object() - value = self.get('content-type', missing) - if value is missing: - # This should have no parameters - return self.get_default_type() - ctype = paramre.split(value)[0].lower().strip() - # RFC 2045, section 5.2 says if its invalid, use text/plain - if ctype.count('/') <> 1: - return 'text/plain' - return ctype - - def get_content_maintype(self): - """Return the message's main content type. - - This is the `maintype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[0] - - def get_content_subtype(self): - """Returns the message's sub-content type. - - This is the `subtype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[1] - - def get_default_type(self): - """Return the `default' content type. - - Most messages have a default content type of text/plain, except for - messages that are subparts of multipart/digest containers. Such - subparts have a default content type of message/rfc822. - """ - return self._default_type - - def set_default_type(self, ctype): - """Set the `default' content type. - - ctype should be either "text/plain" or "message/rfc822", although this - is not enforced. The default content type is not stored in the - Content-Type header. - """ - self._default_type = ctype - - def _get_params_preserve(self, failobj, header): - # Like get_params() but preserves the quoting of values. BAW: - # should this be part of the public interface? - missing = object() - value = self.get(header, missing) - if value is missing: - return failobj - params = [] - for p in _parseparam(';' + value): - try: - name, val = p.split('=', 1) - name = name.strip() - val = val.strip() - except ValueError: - # Must have been a bare attribute - name = p.strip() - val = '' - params.append((name, val)) - params = Utils.decode_params(params) - return params - - def get_params(self, failobj=None, header='content-type', unquote=True): - """Return the message's Content-Type parameters, as a list. - - The elements of the returned list are 2-tuples of key/value pairs, as - split on the `=' sign. The left hand side of the `=' is the key, - while the right hand side is the value. If there is no `=' sign in - the parameter the value is the empty string. The value is as - described in the get_param() method. - - Optional failobj is the object to return if there is no Content-Type - header. Optional header is the header to search instead of - Content-Type. If unquote is True, the value is unquoted. - """ - missing = object() - params = self._get_params_preserve(missing, header) - if params is missing: - return failobj - if unquote: - return [(k, _unquotevalue(v)) for k, v in params] - else: - return params - - def get_param(self, param, failobj=None, header='content-type', - unquote=True): - """Return the parameter value if found in the Content-Type header. - - Optional failobj is the object to return if there is no Content-Type - header, or the Content-Type header has no such parameter. Optional - header is the header to search instead of Content-Type. - - Parameter keys are always compared case insensitively. The return - value can either be a string, or a 3-tuple if the parameter was RFC - 2231 encoded. When it's a 3-tuple, the elements of the value are of - the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and - LANGUAGE can be None, in which case you should consider VALUE to be - encoded in the us-ascii charset. You can usually ignore LANGUAGE. - - Your application should be prepared to deal with 3-tuple return - values, and can convert the parameter to a Unicode string like so: - - param = msg.get_param('foo') - if isinstance(param, tuple): - param = unicode(param[2], param[0] or 'us-ascii') - - In any case, the parameter value (either the returned string, or the - VALUE item in the 3-tuple) is always unquoted, unless unquote is set - to False. - """ - if not self.has_key(header): - return failobj - for k, v in self._get_params_preserve(failobj, header): - if k.lower() == param.lower(): - if unquote: - return _unquotevalue(v) - else: - return v - return failobj - - def set_param(self, param, value, header='Content-Type', requote=True, - charset=None, language=''): - """Set a parameter in the Content-Type header. - - If the parameter already exists in the header, its value will be - replaced with the new value. - - If header is Content-Type and has not yet been defined for this - message, it will be set to "text/plain" and the new parameter and - value will be appended as per RFC 2045. - - An alternate header can specified in the header argument, and all - parameters will be quoted as necessary unless requote is False. - - If charset is specified, the parameter will be encoded according to RFC - 2231. Optional language specifies the RFC 2231 language, defaulting - to the empty string. Both charset and language should be strings. - """ - if not isinstance(value, tuple) and charset: - value = (charset, language, value) - - if not self.has_key(header) and header.lower() == 'content-type': - ctype = 'text/plain' - else: - ctype = self.get(header) - if not self.get_param(param, header=header): - if not ctype: - ctype = _formatparam(param, value, requote) - else: - ctype = SEMISPACE.join( - [ctype, _formatparam(param, value, requote)]) - else: - ctype = '' - for old_param, old_value in self.get_params(header=header, - unquote=requote): - append_param = '' - if old_param.lower() == param.lower(): - append_param = _formatparam(param, value, requote) - else: - append_param = _formatparam(old_param, old_value, requote) - if not ctype: - ctype = append_param - else: - ctype = SEMISPACE.join([ctype, append_param]) - if ctype <> self.get(header): - del self[header] - self[header] = ctype - - def del_param(self, param, header='content-type', requote=True): - """Remove the given parameter completely from the Content-Type header. - - The header will be re-written in place without the parameter or its - value. All values will be quoted as necessary unless requote is - False. Optional header specifies an alternative to the Content-Type - header. - """ - if not self.has_key(header): - return - new_ctype = '' - for p, v in self.get_params(header=header, unquote=requote): - if p.lower() <> param.lower(): - if not new_ctype: - new_ctype = _formatparam(p, v, requote) - else: - new_ctype = SEMISPACE.join([new_ctype, - _formatparam(p, v, requote)]) - if new_ctype <> self.get(header): - del self[header] - self[header] = new_ctype - - def set_type(self, type, header='Content-Type', requote=True): - """Set the main type and subtype for the Content-Type header. - - type must be a string in the form "maintype/subtype", otherwise a - ValueError is raised. - - This method replaces the Content-Type header, keeping all the - parameters in place. If requote is False, this leaves the existing - header's quoting as is. Otherwise, the parameters will be quoted (the - default). - - An alternative header can be specified in the header argument. When - the Content-Type header is set, we'll always also add a MIME-Version - header. - """ - # BAW: should we be strict? - if not type.count('/') == 1: - raise ValueError - # Set the Content-Type, you get a MIME-Version - if header.lower() == 'content-type': - del self['mime-version'] - self['MIME-Version'] = '1.0' - if not self.has_key(header): - self[header] = type - return - params = self.get_params(header=header, unquote=requote) - del self[header] - self[header] = type - # Skip the first param; it's the old type. - for p, v in params[1:]: - self.set_param(p, v, header, requote) - - def get_filename(self, failobj=None): - """Return the filename associated with the payload if present. - - The filename is extracted from the Content-Disposition header's - `filename' parameter, and it is unquoted. If that header is missing - the `filename' parameter, this method falls back to looking for the - `name' parameter. - """ - missing = object() - filename = self.get_param('filename', missing, 'content-disposition') - if filename is missing: - filename = self.get_param('name', missing, 'content-disposition') - if filename is missing: - return failobj - return Utils.collapse_rfc2231_value(filename).strip() - - def get_boundary(self, failobj=None): - """Return the boundary associated with the payload if present. - - The boundary is extracted from the Content-Type header's `boundary' - parameter, and it is unquoted. - """ - missing = object() - boundary = self.get_param('boundary', missing) - if boundary is missing: - return failobj - # RFC 2046 says that boundaries may begin but not end in w/s - return Utils.collapse_rfc2231_value(boundary).rstrip() - - def set_boundary(self, boundary): - """Set the boundary parameter in Content-Type to 'boundary'. - - This is subtly different than deleting the Content-Type header and - adding a new one with a new boundary parameter via add_header(). The - main difference is that using the set_boundary() method preserves the - order of the Content-Type header in the original message. - - HeaderParseError is raised if the message has no Content-Type header. - """ - missing = object() - params = self._get_params_preserve(missing, 'content-type') - if params is missing: - # There was no Content-Type header, and we don't know what type - # to set it to, so raise an exception. - raise Errors.HeaderParseError, 'No Content-Type header found' - newparams = [] - foundp = False - for pk, pv in params: - if pk.lower() == 'boundary': - newparams.append(('boundary', '"%s"' % boundary)) - foundp = True - else: - newparams.append((pk, pv)) - if not foundp: - # The original Content-Type header had no boundary attribute. - # Tack one on the end. BAW: should we raise an exception - # instead??? - newparams.append(('boundary', '"%s"' % boundary)) - # Replace the existing Content-Type header with the new value - newheaders = [] - for h, v in self._headers: - if h.lower() == 'content-type': - parts = [] - for k, v in newparams: - if v == '': - parts.append(k) - else: - parts.append('%s=%s' % (k, v)) - newheaders.append((h, SEMISPACE.join(parts))) - - else: - newheaders.append((h, v)) - self._headers = newheaders - - def get_content_charset(self, failobj=None): - """Return the charset parameter of the Content-Type header. - - The returned string is always coerced to lower case. If there is no - Content-Type header, or if that header has no charset parameter, - failobj is returned. - """ - missing = object() - charset = self.get_param('charset', missing) - if charset is missing: - return failobj - if isinstance(charset, tuple): - # RFC 2231 encoded, so decode it, and it better end up as ascii. - pcharset = charset[0] or 'us-ascii' - charset = unicode(charset[2], pcharset).encode('us-ascii') - # RFC 2046, $4.1.2 says charsets are not case sensitive - return charset.lower() - - def get_charsets(self, failobj=None): - """Return a list containing the charset(s) used in this message. - - The returned list of items describes the Content-Type headers' - charset parameter for this message and all the subparts in its - payload. - - Each item will either be a string (the value of the charset parameter - in the Content-Type header of that part) or the value of the - 'failobj' parameter (defaults to None), if the part does not have a - main MIME type of "text", or the charset is not defined. - - The list will contain one string for each part of the message, plus - one for the container message (i.e. self), so that a non-multipart - message will still return a list of length 1. - """ - return [part.get_content_charset(failobj) for part in self.walk()] - - # I.e. def walk(self): ... - from email.Iterators import walk diff --git a/Lib/email/Parser.py b/Lib/email/Parser.py deleted file mode 100644 index 0c05224..0000000 --- a/Lib/email/Parser.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter -# Contact: email-sig@python.org - -"""A parser of RFC 2822 and MIME email messages.""" - -import warnings -from cStringIO import StringIO -from email.FeedParser import FeedParser -from email.Message import Message - - - -class Parser: - def __init__(self, *args, **kws): - """Parser of RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The string must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the string or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - """ - if len(args) >= 1: - if '_class' in kws: - raise TypeError("Multiple values for keyword arg '_class'") - kws['_class'] = args[0] - if len(args) == 2: - if 'strict' in kws: - raise TypeError("Multiple values for keyword arg 'strict'") - kws['strict'] = args[1] - if len(args) > 2: - raise TypeError('Too many arguments') - if '_class' in kws: - self._class = kws['_class'] - del kws['_class'] - else: - self._class = Message - if 'strict' in kws: - warnings.warn("'strict' argument is deprecated (and ignored)", - DeprecationWarning, 2) - del kws['strict'] - if kws: - raise TypeError('Unexpected keyword arguments') - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - feedparser = FeedParser(self._class) - if headersonly: - feedparser._set_headersonly() - while True: - data = fp.read(8192) - if not data: - break - feedparser.feed(data) - return feedparser.close() - - def parsestr(self, text, headersonly=False): - """Create a message structure from a string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - return self.parse(StringIO(text), headersonly=headersonly) - - - -class HeaderParser(Parser): - def parse(self, fp, headersonly=True): - return Parser.parse(self, fp, True) - - def parsestr(self, text, headersonly=True): - return Parser.parsestr(self, text, True) diff --git a/Lib/email/Utils.py b/Lib/email/Utils.py deleted file mode 100644 index 9ba7601..0000000 --- a/Lib/email/Utils.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Miscellaneous utilities.""" - -import os -import re -import time -import base64 -import random -import socket -import warnings -from cStringIO import StringIO - -from email._parseaddr import quote -from email._parseaddr import AddressList as _AddressList -from email._parseaddr import mktime_tz - -# We need wormarounds for bugs in these methods in older Pythons (see below) -from email._parseaddr import parsedate as _parsedate -from email._parseaddr import parsedate_tz as _parsedate_tz - -from quopri import decodestring as _qdecode - -# Intrapackage imports -from email.Encoders import _bencode, _qencode - -COMMASPACE = ', ' -EMPTYSTRING = '' -UEMPTYSTRING = u'' -CRLF = '\r\n' - -specialsre = re.compile(r'[][\\()<>@,:;".]') -escapesre = re.compile(r'[][\\()"]') - - - -# Helpers - -def _identity(s): - return s - - -def _bdecode(s): - # We can't quite use base64.encodestring() since it tacks on a "courtesy - # newline". Blech! - if not s: - return s - value = base64.decodestring(s) - if not s.endswith('\n') and value.endswith('\n'): - return value[:-1] - return value - - - -def fix_eols(s): - """Replace all line-ending characters with \r\n.""" - # Fix newlines with no preceding carriage return - s = re.sub(r'(?<!\r)\n', CRLF, s) - # Fix carriage returns with no following newline - s = re.sub(r'\r(?!\n)', CRLF, s) - return s - - - -def formataddr(pair): - """The inverse of parseaddr(), this takes a 2-tuple of the form - (realname, email_address) and returns the string value suitable - for an RFC 2822 From, To or Cc header. - - If the first element of pair is false, then the second element is - returned unmodified. - """ - name, address = pair - if name: - quotes = '' - if specialsre.search(name): - quotes = '"' - name = escapesre.sub(r'\\\g<0>', name) - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - - - -def getaddresses(fieldvalues): - """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" - all = COMMASPACE.join(fieldvalues) - a = _AddressList(all) - return a.addresslist - - - -ecre = re.compile(r''' - =\? # literal =? - (?P<charset>[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P<encoding>[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P<atom>.*?) # non-greedy up to the next ?= is the atom - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) - - - -def formatdate(timeval=None, localtime=False, usegmt=False): - """Returns a date string as specified by RFC 2822, e.g.: - - Fri, 09 Nov 2001 01:08:47 -0000 - - Optional timeval if given is a floating point time value as accepted by - gmtime() and localtime(), otherwise the current time is used. - - Optional localtime is a flag that when True, interprets timeval, and - returns a date relative to the local timezone instead of UTC, properly - taking daylight savings time into account. - - Optional argument usegmt means that the timezone is written out as - an ascii string, not numeric one (so "GMT" instead of "+0000"). This - is needed for HTTP, and is only used when localtime==False. - """ - # Note: we cannot use strftime() because that honors the locale and RFC - # 2822 requires that day and month names be the English abbreviations. - if timeval is None: - timeval = time.time() - if localtime: - now = time.localtime(timeval) - # Calculate timezone offset, based on whether the local zone has - # daylight savings time, and whether DST is in effect. - if time.daylight and now[-1]: - offset = time.altzone - else: - offset = time.timezone - hours, minutes = divmod(abs(offset), 3600) - # Remember offset is in seconds west of UTC, but the timezone is in - # minutes east of UTC, so the signs differ. - if offset > 0: - sign = '-' - else: - sign = '+' - zone = '%s%02d%02d' % (sign, hours, minutes // 60) - else: - now = time.gmtime(timeval) - # Timezone offset is always -0000 - if usegmt: - zone = 'GMT' - else: - zone = '-0000' - return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( - ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]], - now[2], - ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1], - now[0], now[3], now[4], now[5], - zone) - - - -def make_msgid(idstring=None): - """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: - - <20020201195627.33539.96671@nightshade.la.mastaler.com> - - Optional idstring if given is a string used to strengthen the - uniqueness of the message id. - """ - timeval = time.time() - utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) - pid = os.getpid() - randint = random.randrange(100000) - if idstring is None: - idstring = '' - else: - idstring = '.' + idstring - idhost = socket.getfqdn() - msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost) - return msgid - - - -# These functions are in the standalone mimelib version only because they've -# subsequently been fixed in the latest Python versions. We use this to worm -# around broken older Pythons. -def parsedate(data): - if not data: - return None - return _parsedate(data) - - -def parsedate_tz(data): - if not data: - return None - return _parsedate_tz(data) - - -def parseaddr(addr): - addrs = _AddressList(addr).addresslist - if not addrs: - return '', '' - return addrs[0] - - -# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. -def unquote(str): - """Remove quotes from a string.""" - if len(str) > 1: - if str.startswith('"') and str.endswith('"'): - return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') - if str.startswith('<') and str.endswith('>'): - return str[1:-1] - return str - - - -# RFC2231-related functions - parameter encoding and decoding -def decode_rfc2231(s): - """Decode string according to RFC 2231""" - import urllib - parts = s.split("'", 2) - if len(parts) == 1: - return None, None, urllib.unquote(s) - charset, language, s = parts - return charset, language, urllib.unquote(s) - - -def encode_rfc2231(s, charset=None, language=None): - """Encode string according to RFC 2231. - - If neither charset nor language is given, then s is returned as-is. If - charset is given but not language, the string is encoded using the empty - string for language. - """ - import urllib - s = urllib.quote(s, safe='') - if charset is None and language is None: - return s - if language is None: - language = '' - return "%s'%s'%s" % (charset, language, s) - - -rfc2231_continuation = re.compile(r'^(?P<name>\w+)\*((?P<num>[0-9]+)\*?)?$') - -def decode_params(params): - """Decode parameters list according to RFC 2231. - - params is a sequence of 2-tuples containing (content type, string value). - """ - new_params = [] - # maps parameter's name to a list of continuations - rfc2231_params = {} - # params is a sequence of 2-tuples containing (content_type, string value) - name, value = params[0] - new_params.append((name, value)) - # Cycle through each of the rest of the parameters. - for name, value in params[1:]: - value = unquote(value) - mo = rfc2231_continuation.match(name) - if mo: - name, num = mo.group('name', 'num') - if num is not None: - num = int(num) - rfc2231_param1 = rfc2231_params.setdefault(name, []) - rfc2231_param1.append((num, value)) - else: - new_params.append((name, '"%s"' % quote(value))) - if rfc2231_params: - for name, continuations in rfc2231_params.items(): - value = [] - # Sort by number - continuations.sort() - # And now append all values in num order - for num, continuation in continuations: - value.append(continuation) - charset, language, value = decode_rfc2231(EMPTYSTRING.join(value)) - new_params.append( - (name, (charset, language, '"%s"' % quote(value)))) - return new_params - -def collapse_rfc2231_value(value, errors='replace', - fallback_charset='us-ascii'): - if isinstance(value, tuple): - rawval = unquote(value[2]) - charset = value[0] or 'us-ascii' - try: - return unicode(rawval, charset, errors) - except LookupError: - # XXX charset is unknown to Python. - return unicode(rawval, fallback_charset, errors) - else: - return unquote(value) diff --git a/Lib/email/__init__.py b/Lib/email/__init__.py index e5c0e2e..f01260f 100644 --- a/Lib/email/__init__.py +++ b/Lib/email/__init__.py @@ -4,9 +4,10 @@ """A package for parsing, handling, and generating email messages.""" -__version__ = '3.0.1' +__version__ = '4.0a2' __all__ = [ + # Old names 'base64MIME', 'Charset', 'Encoders', @@ -27,6 +28,19 @@ __all__ = [ 'Utils', 'message_from_string', 'message_from_file', + # new names + 'base64mime', + 'charset', + 'encoders', + 'errors', + 'generator', + 'header', + 'iterators', + 'message', + 'mime', + 'parser', + 'quoprimime', + 'utils', ] @@ -39,7 +53,7 @@ def message_from_string(s, *args, **kws): Optional _class and strict are passed to the Parser constructor. """ - from email.Parser import Parser + from email.parser import Parser return Parser(*args, **kws).parsestr(s) @@ -48,5 +62,62 @@ def message_from_file(fp, *args, **kws): Optional _class and strict are passed to the Parser constructor. """ - from email.Parser import Parser + from email.parser import Parser return Parser(*args, **kws).parse(fp) + + + +# Lazy loading to provide name mapping from new-style names (PEP 8 compatible +# email 4.0 module names), to old-style names (email 3.0 module names). +import sys + +class LazyImporter(object): + def __init__(self, module_name): + self.__name__ = 'email.' + module_name + + def __getattr__(self, name): + __import__(self.__name__) + mod = sys.modules[self.__name__] + self.__dict__.update(mod.__dict__) + return getattr(mod, name) + + +_LOWERNAMES = [ + # email.<old name> -> email.<new name is lowercased old name> + 'Charset', + 'Encoders', + 'Errors', + 'FeedParser', + 'Generator', + 'Header', + 'Iterators', + 'Message', + 'Parser', + 'Utils', + 'base64MIME', + 'quopriMIME', + ] + +_MIMENAMES = [ + # email.MIME<old name> -> email.mime.<new name is lowercased old name> + 'Audio', + 'Base', + 'Image', + 'Message', + 'Multipart', + 'NonMultipart', + 'Text', + ] + +for _name in _LOWERNAMES: + importer = LazyImporter(_name.lower()) + sys.modules['email.' + _name] = importer + setattr(sys.modules['email'], _name, importer) + + +import email.mime +for _name in _MIMENAMES: + importer = LazyImporter('mime.' + _name.lower()) + sys.modules['email.MIME' + _name] = importer + setattr(sys.modules['email'], 'MIME' + _name, importer) + setattr(sys.modules['email.mime'], _name, importer) diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 7d759ef..5821ddf 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -6,6 +6,13 @@ Lifted directly from rfc822.py. This should eventually be rewritten. """ +__all__ = [ + 'mktime_tz', + 'parsedate', + 'parsedate_tz', + 'quote', + ] + import time SPACE = ' ' @@ -117,7 +124,8 @@ def parsedate_tz(data): else: tzsign = 1 tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) - return yy, mm, dd, thh, tmm, tss, 0, 1, 0, tzoffset + # Daylight Saving Time flag is set to -1, since DST is unknown. + return yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset def parsedate(data): diff --git a/Lib/email/base64MIME.py b/Lib/email/base64MIME.py deleted file mode 100644 index 6ed1d53..0000000 --- a/Lib/email/base64MIME.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (C) 2002-2004 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Base64 content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode arbitrary 8-bit data using the three 8-bit bytes in four 7-bit -characters encoding known as Base64. - -It is used in the MIME standards for email to attach images, audio, and text -using some 8-bit character sets to messages. - -This module provides an interface to encode and decode both headers and bodies -with Base64 encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:, From:, Cc:, etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character conversion -necessary for proper internationalized headers; it only does dumb encoding and -decoding. To deal with the various line wrapping issues, use the email.Header -module. -""" - -import re -from binascii import b2a_base64, a2b_base64 -from email.Utils import fix_eols - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# See also Charset.py -MISC_LEN = 7 - - - -# Helpers -def base64_len(s): - """Return the length of s when it is encoded with base64.""" - groups_of_3, leftover = divmod(len(s), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - # Thanks, Tim! - n = groups_of_3 * 4 - if leftover: - n += 4 - return n - - - -def header_encode(header, charset='iso-8859-1', keep_eols=False, - maxlinelen=76, eol=NL): - """Encode a single header line with Base64 encoding in a given charset. - - Defined in RFC 2045, this Base64 encoding is identical to normal Base64 - encoding, except that each line must be intelligently wrapped (respecting - the Base64 encoding), and subsequent lines must start with a space. - - charset names the character set to use to encode the header. It defaults - to iso-8859-1. - - End-of-line characters (\\r, \\n, \\r\\n) will be automatically converted - to the canonical email line separator \\r\\n unless the keep_eols - parameter is True (the default is False). - - Each line of the header will be terminated in the value of eol, which - defaults to "\\n". Set this to "\\r\\n" if you are using the result of - this function directly in email. - - The resulting string will be in the form: - - "=?charset?b?WW/5ciBtYXp66XLrIHf8eiBhIGhhbXBzdGHuciBBIFlv+XIgbWF6euly?=\\n - =?charset?b?6yB3/HogYSBoYW1wc3Rh7nIgQkMgWW/5ciBtYXp66XLrIHf8eiBhIGhh?=" - - with each line wrapped at, at most, maxlinelen characters (defaults to 76 - characters). - """ - # Return empty headers unchanged - if not header: - return header - - if not keep_eols: - header = fix_eols(header) - - # Base64 encode each line, in encoded chunks no greater than maxlinelen in - # length, after the RFC chrome is added in. - base64ed = [] - max_encoded = maxlinelen - len(charset) - MISC_LEN - max_unencoded = max_encoded * 3 // 4 - - for i in range(0, len(header), max_unencoded): - base64ed.append(b2a_base64(header[i:i+max_unencoded])) - - # Now add the RFC chrome to each encoded chunk - lines = [] - for line in base64ed: - # Ignore the last character of each line if it is a newline - if line.endswith(NL): - line = line[:-1] - # Add the chrome - lines.append('=?%s?b?%s?=' % (charset, line)) - # Glue the lines together and return it. BAW: should we be able to - # specify the leading whitespace in the joiner? - joiner = eol + ' ' - return joiner.join(lines) - - - -def encode(s, binary=True, maxlinelen=76, eol=NL): - """Encode a string with base64. - - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). - - If binary is False, end-of-line characters will be converted to the - canonical email end-of-line sequence \\r\\n. Otherwise they will be left - verbatim (this is the default). - - Each line of encoded text will end with eol, which defaults to "\\n". Set - this to "\r\n" if you will be using the result of this function directly - in an email. - """ - if not s: - return s - - if not binary: - s = fix_eols(s) - - encvec = [] - max_unencoded = maxlinelen * 3 // 4 - for i in range(0, len(s), max_unencoded): - # BAW: should encode() inherit b2a_base64()'s dubious behavior in - # adding a newline to the encoded string? - enc = b2a_base64(s[i:i + max_unencoded]) - if enc.endswith(NL) and eol <> NL: - enc = enc[:-1] + eol - encvec.append(enc) - return EMPTYSTRING.join(encvec) - - -# For convenience and backwards compatibility w/ standard base64 module -body_encode = encode -encodestring = encode - - - -def decode(s, convert_eols=None): - """Decode a raw base64 string. - - If convert_eols is set to a string value, all canonical email linefeeds, - e.g. "\\r\\n", in the decoded text will be converted to the value of - convert_eols. os.linesep is a good choice for convert_eols if you are - decoding a text attachment. - - This function does not parse a full MIME header value encoded with - base64 (like =?iso-8895-1?b?bmloISBuaWgh?=) -- please use the high - level email.Header class for that functionality. - """ - if not s: - return s - - dec = a2b_base64(s) - if convert_eols: - return dec.replace(CRLF, convert_eols) - return dec - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode diff --git a/Lib/email/base64mime.py b/Lib/email/base64mime.py new file mode 100644 index 0000000..0129d9d --- /dev/null +++ b/Lib/email/base64mime.py @@ -0,0 +1,184 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Author: Ben Gertzfield +# Contact: email-sig@python.org + +"""Base64 content transfer encoding per RFCs 2045-2047. + +This module handles the content transfer encoding method defined in RFC 2045 +to encode arbitrary 8-bit data using the three 8-bit bytes in four 7-bit +characters encoding known as Base64. + +It is used in the MIME standards for email to attach images, audio, and text +using some 8-bit character sets to messages. + +This module provides an interface to encode and decode both headers and bodies +with Base64 encoding. + +RFC 2045 defines a method for including character set information in an +`encoded-word' in a header. This method is commonly used for 8-bit real names +in To:, From:, Cc:, etc. fields, as well as Subject: lines. + +This module does not do the line wrapping or end-of-line character conversion +necessary for proper internationalized headers; it only does dumb encoding and +decoding. To deal with the various line wrapping issues, use the email.Header +module. +""" + +__all__ = [ + 'base64_len', + 'body_decode', + 'body_encode', + 'decode', + 'decodestring', + 'encode', + 'encodestring', + 'header_encode', + ] + +import re + +from binascii import b2a_base64, a2b_base64 +from email.utils import fix_eols + +CRLF = '\r\n' +NL = '\n' +EMPTYSTRING = '' + +# See also Charset.py +MISC_LEN = 7 + + + +# Helpers +def base64_len(s): + """Return the length of s when it is encoded with base64.""" + groups_of_3, leftover = divmod(len(s), 3) + # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. + # Thanks, Tim! + n = groups_of_3 * 4 + if leftover: + n += 4 + return n + + + +def header_encode(header, charset='iso-8859-1', keep_eols=False, + maxlinelen=76, eol=NL): + """Encode a single header line with Base64 encoding in a given charset. + + Defined in RFC 2045, this Base64 encoding is identical to normal Base64 + encoding, except that each line must be intelligently wrapped (respecting + the Base64 encoding), and subsequent lines must start with a space. + + charset names the character set to use to encode the header. It defaults + to iso-8859-1. + + End-of-line characters (\\r, \\n, \\r\\n) will be automatically converted + to the canonical email line separator \\r\\n unless the keep_eols + parameter is True (the default is False). + + Each line of the header will be terminated in the value of eol, which + defaults to "\\n". Set this to "\\r\\n" if you are using the result of + this function directly in email. + + The resulting string will be in the form: + + "=?charset?b?WW/5ciBtYXp66XLrIHf8eiBhIGhhbXBzdGHuciBBIFlv+XIgbWF6euly?=\\n + =?charset?b?6yB3/HogYSBoYW1wc3Rh7nIgQkMgWW/5ciBtYXp66XLrIHf8eiBhIGhh?=" + + with each line wrapped at, at most, maxlinelen characters (defaults to 76 + characters). + """ + # Return empty headers unchanged + if not header: + return header + + if not keep_eols: + header = fix_eols(header) + + # Base64 encode each line, in encoded chunks no greater than maxlinelen in + # length, after the RFC chrome is added in. + base64ed = [] + max_encoded = maxlinelen - len(charset) - MISC_LEN + max_unencoded = max_encoded * 3 // 4 + + for i in range(0, len(header), max_unencoded): + base64ed.append(b2a_base64(header[i:i+max_unencoded])) + + # Now add the RFC chrome to each encoded chunk + lines = [] + for line in base64ed: + # Ignore the last character of each line if it is a newline + if line.endswith(NL): + line = line[:-1] + # Add the chrome + lines.append('=?%s?b?%s?=' % (charset, line)) + # Glue the lines together and return it. BAW: should we be able to + # specify the leading whitespace in the joiner? + joiner = eol + ' ' + return joiner.join(lines) + + + +def encode(s, binary=True, maxlinelen=76, eol=NL): + """Encode a string with base64. + + Each line will be wrapped at, at most, maxlinelen characters (defaults to + 76 characters). + + If binary is False, end-of-line characters will be converted to the + canonical email end-of-line sequence \\r\\n. Otherwise they will be left + verbatim (this is the default). + + Each line of encoded text will end with eol, which defaults to "\\n". Set + this to "\r\n" if you will be using the result of this function directly + in an email. + """ + if not s: + return s + + if not binary: + s = fix_eols(s) + + encvec = [] + max_unencoded = maxlinelen * 3 // 4 + for i in range(0, len(s), max_unencoded): + # BAW: should encode() inherit b2a_base64()'s dubious behavior in + # adding a newline to the encoded string? + enc = b2a_base64(s[i:i + max_unencoded]) + if enc.endswith(NL) and eol <> NL: + enc = enc[:-1] + eol + encvec.append(enc) + return EMPTYSTRING.join(encvec) + + +# For convenience and backwards compatibility w/ standard base64 module +body_encode = encode +encodestring = encode + + + +def decode(s, convert_eols=None): + """Decode a raw base64 string. + + If convert_eols is set to a string value, all canonical email linefeeds, + e.g. "\\r\\n", in the decoded text will be converted to the value of + convert_eols. os.linesep is a good choice for convert_eols if you are + decoding a text attachment. + + This function does not parse a full MIME header value encoded with + base64 (like =?iso-8895-1?b?bmloISBuaWgh?=) -- please use the high + level email.Header class for that functionality. + """ + if not s: + return s + + dec = a2b_base64(s) + if convert_eols: + return dec.replace(CRLF, convert_eols) + return dec + + +# For convenience and backwards compatibility w/ standard base64 module +body_decode = decode +decodestring = decode diff --git a/Lib/email/charset.py b/Lib/email/charset.py new file mode 100644 index 0000000..8f218b2 --- /dev/null +++ b/Lib/email/charset.py @@ -0,0 +1,388 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Ben Gertzfield, Barry Warsaw +# Contact: email-sig@python.org + +__all__ = [ + 'Charset', + 'add_alias', + 'add_charset', + 'add_codec', + ] + +import email.base64mime +import email.quoprimime + +from email import errors +from email.encoders import encode_7or8bit + + + +# Flags for types of header encodings +QP = 1 # Quoted-Printable +BASE64 = 2 # Base64 +SHORTEST = 3 # the shorter of QP and base64, but only for headers + +# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 +MISC_LEN = 7 + +DEFAULT_CHARSET = 'us-ascii' + + + +# Defaults +CHARSETS = { + # input header enc body enc output conv + 'iso-8859-1': (QP, QP, None), + 'iso-8859-2': (QP, QP, None), + 'iso-8859-3': (QP, QP, None), + 'iso-8859-4': (QP, QP, None), + # iso-8859-5 is Cyrillic, and not especially used + # iso-8859-6 is Arabic, also not particularly used + # iso-8859-7 is Greek, QP will not make it readable + # iso-8859-8 is Hebrew, QP will not make it readable + 'iso-8859-9': (QP, QP, None), + 'iso-8859-10': (QP, QP, None), + # iso-8859-11 is Thai, QP will not make it readable + 'iso-8859-13': (QP, QP, None), + 'iso-8859-14': (QP, QP, None), + 'iso-8859-15': (QP, QP, None), + 'windows-1252':(QP, QP, None), + 'viscii': (QP, QP, None), + 'us-ascii': (None, None, None), + 'big5': (BASE64, BASE64, None), + 'gb2312': (BASE64, BASE64, None), + 'euc-jp': (BASE64, None, 'iso-2022-jp'), + 'shift_jis': (BASE64, None, 'iso-2022-jp'), + 'iso-2022-jp': (BASE64, None, None), + 'koi8-r': (BASE64, BASE64, None), + 'utf-8': (SHORTEST, BASE64, 'utf-8'), + # We're making this one up to represent raw unencoded 8-bit + '8bit': (None, BASE64, 'utf-8'), + } + +# Aliases for other commonly-used names for character sets. Map +# them to the real ones used in email. +ALIASES = { + 'latin_1': 'iso-8859-1', + 'latin-1': 'iso-8859-1', + 'latin_2': 'iso-8859-2', + 'latin-2': 'iso-8859-2', + 'latin_3': 'iso-8859-3', + 'latin-3': 'iso-8859-3', + 'latin_4': 'iso-8859-4', + 'latin-4': 'iso-8859-4', + 'latin_5': 'iso-8859-9', + 'latin-5': 'iso-8859-9', + 'latin_6': 'iso-8859-10', + 'latin-6': 'iso-8859-10', + 'latin_7': 'iso-8859-13', + 'latin-7': 'iso-8859-13', + 'latin_8': 'iso-8859-14', + 'latin-8': 'iso-8859-14', + 'latin_9': 'iso-8859-15', + 'latin-9': 'iso-8859-15', + 'cp949': 'ks_c_5601-1987', + 'euc_jp': 'euc-jp', + 'euc_kr': 'euc-kr', + 'ascii': 'us-ascii', + } + + +# Map charsets to their Unicode codec strings. +CODEC_MAP = { + 'gb2312': 'eucgb2312_cn', + 'big5': 'big5_tw', + # Hack: We don't want *any* conversion for stuff marked us-ascii, as all + # sorts of garbage might be sent to us in the guise of 7-bit us-ascii. + # Let that stuff pass through without conversion to/from Unicode. + 'us-ascii': None, + } + + + +# Convenience functions for extending the above mappings +def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): + """Add character set properties to the global registry. + + charset is the input character set, and must be the canonical name of a + character set. + + Optional header_enc and body_enc is either Charset.QP for + quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for + the shortest of qp or base64 encoding, or None for no encoding. SHORTEST + is only valid for header_enc. It describes how message headers and + message bodies in the input charset are to be encoded. Default is no + encoding. + + Optional output_charset is the character set that the output should be + in. Conversions will proceed from input charset, to Unicode, to the + output charset when the method Charset.convert() is called. The default + is to output in the same character set as the input. + + Both input_charset and output_charset must have Unicode codec entries in + the module's charset-to-codec mapping; use add_codec(charset, codecname) + to add codecs the module does not know about. See the codecs module's + documentation for more information. + """ + if body_enc == SHORTEST: + raise ValueError('SHORTEST not allowed for body_enc') + CHARSETS[charset] = (header_enc, body_enc, output_charset) + + +def add_alias(alias, canonical): + """Add a character set alias. + + alias is the alias name, e.g. latin-1 + canonical is the character set's canonical name, e.g. iso-8859-1 + """ + ALIASES[alias] = canonical + + +def add_codec(charset, codecname): + """Add a codec that map characters in the given charset to/from Unicode. + + charset is the canonical name of a character set. codecname is the name + of a Python codec, as appropriate for the second argument to the unicode() + built-in, or to the encode() method of a Unicode string. + """ + CODEC_MAP[charset] = codecname + + + +class Charset: + """Map character sets to their email properties. + + This class provides information about the requirements imposed on email + for a specific character set. It also provides convenience routines for + converting between character sets, given the availability of the + applicable codecs. Given a character set, it will do its best to provide + information on how to use that character set in an email in an + RFC-compliant way. + + Certain character sets must be encoded with quoted-printable or base64 + when used in email headers or bodies. Certain character sets must be + converted outright, and are not allowed in email. Instances of this + module expose the following information about a character set: + + input_charset: The initial character set specified. Common aliases + are converted to their `official' email names (e.g. latin_1 + is converted to iso-8859-1). Defaults to 7-bit us-ascii. + + header_encoding: If the character set must be encoded before it can be + used in an email header, this attribute will be set to + Charset.QP (for quoted-printable), Charset.BASE64 (for + base64 encoding), or Charset.SHORTEST for the shortest of + QP or BASE64 encoding. Otherwise, it will be None. + + body_encoding: Same as header_encoding, but describes the encoding for the + mail message's body, which indeed may be different than the + header encoding. Charset.SHORTEST is not allowed for + body_encoding. + + output_charset: Some character sets must be converted before the can be + used in email headers or bodies. If the input_charset is + one of them, this attribute will contain the name of the + charset output will be converted to. Otherwise, it will + be None. + + input_codec: The name of the Python codec used to convert the + input_charset to Unicode. If no conversion codec is + necessary, this attribute will be None. + + output_codec: The name of the Python codec used to convert Unicode + to the output_charset. If no conversion codec is necessary, + this attribute will have the same value as the input_codec. + """ + def __init__(self, input_charset=DEFAULT_CHARSET): + # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to + # unicode because its .lower() is locale insensitive. If the argument + # is already a unicode, we leave it at that, but ensure that the + # charset is ASCII, as the standard (RFC XXX) requires. + try: + if isinstance(input_charset, unicode): + input_charset.encode('ascii') + else: + input_charset = unicode(input_charset, 'ascii') + except UnicodeError: + raise errors.CharsetError(input_charset) + input_charset = input_charset.lower() + # Set the input charset after filtering through the aliases + self.input_charset = ALIASES.get(input_charset, input_charset) + # We can try to guess which encoding and conversion to use by the + # charset_map dictionary. Try that first, but let the user override + # it. + henc, benc, conv = CHARSETS.get(self.input_charset, + (SHORTEST, BASE64, None)) + if not conv: + conv = self.input_charset + # Set the attributes, allowing the arguments to override the default. + self.header_encoding = henc + self.body_encoding = benc + self.output_charset = ALIASES.get(conv, conv) + # Now set the codecs. If one isn't defined for input_charset, + # guess and try a Unicode codec with the same name as input_codec. + self.input_codec = CODEC_MAP.get(self.input_charset, + self.input_charset) + self.output_codec = CODEC_MAP.get(self.output_charset, + self.output_charset) + + def __str__(self): + return self.input_charset.lower() + + __repr__ = __str__ + + def __eq__(self, other): + return str(self) == str(other).lower() + + def __ne__(self, other): + return not self.__eq__(other) + + def get_body_encoding(self): + """Return the content-transfer-encoding used for body encoding. + + This is either the string `quoted-printable' or `base64' depending on + the encoding used, or it is a function in which case you should call + the function with a single argument, the Message object being + encoded. The function should then set the Content-Transfer-Encoding + header itself to whatever is appropriate. + + Returns "quoted-printable" if self.body_encoding is QP. + Returns "base64" if self.body_encoding is BASE64. + Returns "7bit" otherwise. + """ + assert self.body_encoding <> SHORTEST + if self.body_encoding == QP: + return 'quoted-printable' + elif self.body_encoding == BASE64: + return 'base64' + else: + return encode_7or8bit + + def convert(self, s): + """Convert a string from the input_codec to the output_codec.""" + if self.input_codec <> self.output_codec: + return unicode(s, self.input_codec).encode(self.output_codec) + else: + return s + + def to_splittable(self, s): + """Convert a possibly multibyte string to a safely splittable format. + + Uses the input_codec to try and convert the string to Unicode, so it + can be safely split on character boundaries (even for multibyte + characters). + + Returns the string as-is if it isn't known how to convert it to + Unicode with the input_charset. + + Characters that could not be converted to Unicode will be replaced + with the Unicode replacement character U+FFFD. + """ + if isinstance(s, unicode) or self.input_codec is None: + return s + try: + return unicode(s, self.input_codec, 'replace') + except LookupError: + # Input codec not installed on system, so return the original + # string unchanged. + return s + + def from_splittable(self, ustr, to_output=True): + """Convert a splittable string back into an encoded string. + + Uses the proper codec to try and convert the string from Unicode back + into an encoded format. Return the string as-is if it is not Unicode, + or if it could not be converted from Unicode. + + Characters that could not be converted from Unicode will be replaced + with an appropriate character (usually '?'). + + If to_output is True (the default), uses output_codec to convert to an + encoded format. If to_output is False, uses input_codec. + """ + if to_output: + codec = self.output_codec + else: + codec = self.input_codec + if not isinstance(ustr, unicode) or codec is None: + return ustr + try: + return ustr.encode(codec, 'replace') + except LookupError: + # Output codec not installed + return ustr + + def get_output_charset(self): + """Return the output character set. + + This is self.output_charset if that is not None, otherwise it is + self.input_charset. + """ + return self.output_charset or self.input_charset + + def encoded_header_len(self, s): + """Return the length of the encoded header string.""" + cset = self.get_output_charset() + # The len(s) of a 7bit encoding is len(s) + if self.header_encoding == BASE64: + return email.base64mime.base64_len(s) + len(cset) + MISC_LEN + elif self.header_encoding == QP: + return email.quoprimime.header_quopri_len(s) + len(cset) + MISC_LEN + elif self.header_encoding == SHORTEST: + lenb64 = email.base64mime.base64_len(s) + lenqp = email.quoprimime.header_quopri_len(s) + return min(lenb64, lenqp) + len(cset) + MISC_LEN + else: + return len(s) + + def header_encode(self, s, convert=False): + """Header-encode a string, optionally converting it to output_charset. + + If convert is True, the string will be converted from the input + charset to the output charset automatically. This is not useful for + multibyte character sets, which have line length issues (multibyte + characters must be split on a character, not a byte boundary); use the + high-level Header class to deal with these issues. convert defaults + to False. + + The type of encoding (base64 or quoted-printable) will be based on + self.header_encoding. + """ + cset = self.get_output_charset() + if convert: + s = self.convert(s) + # 7bit/8bit encodings return the string unchanged (modulo conversions) + if self.header_encoding == BASE64: + return email.base64mime.header_encode(s, cset) + elif self.header_encoding == QP: + return email.quoprimime.header_encode(s, cset, maxlinelen=None) + elif self.header_encoding == SHORTEST: + lenb64 = email.base64mime.base64_len(s) + lenqp = email.quoprimime.header_quopri_len(s) + if lenb64 < lenqp: + return email.base64mime.header_encode(s, cset) + else: + return email.quoprimime.header_encode(s, cset, maxlinelen=None) + else: + return s + + def body_encode(self, s, convert=True): + """Body-encode a string and convert it to output_charset. + + If convert is True (the default), the string will be converted from + the input charset to output charset automatically. Unlike + header_encode(), there are no issues with byte boundaries and + multibyte charsets in email bodies, so this is usually pretty safe. + + The type of encoding (base64 or quoted-printable) will be based on + self.body_encoding. + """ + if convert: + s = self.convert(s) + # 7bit/8bit encodings return the string unchanged (module conversions) + if self.body_encoding is BASE64: + return email.base64mime.body_encode(s) + elif self.body_encoding is QP: + return email.quoprimime.body_encode(s) + else: + return s diff --git a/Lib/email/encoders.py b/Lib/email/encoders.py new file mode 100644 index 0000000..06016cd --- /dev/null +++ b/Lib/email/encoders.py @@ -0,0 +1,88 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Encodings and related functions.""" + +__all__ = [ + 'encode_7or8bit', + 'encode_base64', + 'encode_noop', + 'encode_quopri', + ] + +import base64 + +from quopri import encodestring as _encodestring + + + +def _qencode(s): + enc = _encodestring(s, quotetabs=True) + # Must encode spaces, which quopri.encodestring() doesn't do + return enc.replace(' ', '=20') + + +def _bencode(s): + # We can't quite use base64.encodestring() since it tacks on a "courtesy + # newline". Blech! + if not s: + return s + hasnewline = (s[-1] == '\n') + value = base64.encodestring(s) + if not hasnewline and value[-1] == '\n': + return value[:-1] + return value + + + +def encode_base64(msg): + """Encode the message's payload in Base64. + + Also, add an appropriate Content-Transfer-Encoding header. + """ + orig = msg.get_payload() + encdata = _bencode(orig) + msg.set_payload(encdata) + msg['Content-Transfer-Encoding'] = 'base64' + + + +def encode_quopri(msg): + """Encode the message's payload in quoted-printable. + + Also, add an appropriate Content-Transfer-Encoding header. + """ + orig = msg.get_payload() + encdata = _qencode(orig) + msg.set_payload(encdata) + msg['Content-Transfer-Encoding'] = 'quoted-printable' + + + +def encode_7or8bit(msg): + """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" + orig = msg.get_payload() + if orig is None: + # There's no payload. For backwards compatibility we use 7bit + msg['Content-Transfer-Encoding'] = '7bit' + return + # We play a trick to make this go fast. If encoding to ASCII succeeds, we + # know the data must be 7bit, otherwise treat it as 8bit. + try: + orig.encode('ascii') + except UnicodeError: + # iso-2022-* is non-ASCII but still 7-bit + charset = msg.get_charset() + output_cset = charset and charset.output_charset + if output_cset and output_cset.lower().startswith('iso-2202-'): + msg['Content-Transfer-Encoding'] = '7bit' + else: + msg['Content-Transfer-Encoding'] = '8bit' + else: + msg['Content-Transfer-Encoding'] = '7bit' + + + +def encode_noop(msg): + """Do nothing.""" diff --git a/Lib/email/errors.py b/Lib/email/errors.py new file mode 100644 index 0000000..d52a624 --- /dev/null +++ b/Lib/email/errors.py @@ -0,0 +1,57 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""email package exception classes.""" + + + +class MessageError(Exception): + """Base class for errors in the email package.""" + + +class MessageParseError(MessageError): + """Base class for message parsing errors.""" + + +class HeaderParseError(MessageParseError): + """Error while parsing headers.""" + + +class BoundaryError(MessageParseError): + """Couldn't find terminating boundary.""" + + +class MultipartConversionError(MessageError, TypeError): + """Conversion to a multipart is prohibited.""" + + +class CharsetError(MessageError): + """An illegal charset was given.""" + + + +# These are parsing defects which the parser was able to work around. +class MessageDefect: + """Base class for a message defect.""" + + def __init__(self, line=None): + self.line = line + +class NoBoundaryInMultipartDefect(MessageDefect): + """A message claimed to be a multipart but had no boundary parameter.""" + +class StartBoundaryNotFoundDefect(MessageDefect): + """The claimed start boundary was never found.""" + +class FirstHeaderLineIsContinuationDefect(MessageDefect): + """A message had a continuation line as its first header line.""" + +class MisplacedEnvelopeHeaderDefect(MessageDefect): + """A 'Unix-from' header was found in the middle of a header block.""" + +class MalformedHeaderDefect(MessageDefect): + """Found a header that was missing a colon, or was otherwise malformed.""" + +class MultipartInvariantViolationDefect(MessageDefect): + """A message claimed to be a multipart but no subparts were found.""" diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py new file mode 100644 index 0000000..afb02b3 --- /dev/null +++ b/Lib/email/feedparser.py @@ -0,0 +1,480 @@ +# Copyright (C) 2004-2006 Python Software Foundation +# Authors: Baxter, Wouters and Warsaw +# Contact: email-sig@python.org + +"""FeedParser - An email feed parser. + +The feed parser implements an interface for incrementally parsing an email +message, line by line. This has advantages for certain applications, such as +those reading email messages off a socket. + +FeedParser.feed() is the primary interface for pushing new data into the +parser. It returns when there's nothing more it can do with the available +data. When you have no more data to push into the parser, call .close(). +This completes the parsing and returns the root message object. + +The other advantage of this parser is that it will never throw a parsing +exception. Instead, when it finds something unexpected, it adds a 'defect' to +the current message. Defects are just instances that live on the message +object's .defects attribute. +""" + +__all__ = ['FeedParser'] + +import re + +from email import errors +from email import message + +NLCRE = re.compile('\r\n|\r|\n') +NLCRE_bol = re.compile('(\r\n|\r|\n)') +NLCRE_eol = re.compile('(\r\n|\r|\n)$') +NLCRE_crack = re.compile('(\r\n|\r|\n)') +# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character +# except controls, SP, and ":". +headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])') +EMPTYSTRING = '' +NL = '\n' + +NeedMoreData = object() + + + +class BufferedSubFile(object): + """A file-ish object that can have new data loaded into it. + + You can also push and pop line-matching predicates onto a stack. When the + current predicate matches the current line, a false EOF response + (i.e. empty string) is returned instead. This lets the parser adhere to a + simple abstraction -- it parses until EOF closes the current message. + """ + def __init__(self): + # The last partial line pushed into this object. + self._partial = '' + # The list of full, pushed lines, in reverse order + self._lines = [] + # The stack of false-EOF checking predicates. + self._eofstack = [] + # A flag indicating whether the file has been closed or not. + self._closed = False + + def push_eof_matcher(self, pred): + self._eofstack.append(pred) + + def pop_eof_matcher(self): + return self._eofstack.pop() + + def close(self): + # Don't forget any trailing partial line. + self._lines.append(self._partial) + self._partial = '' + self._closed = True + + def readline(self): + if not self._lines: + if self._closed: + return '' + return NeedMoreData + # Pop the line off the stack and see if it matches the current + # false-EOF predicate. + line = self._lines.pop() + # RFC 2046, section 5.1.2 requires us to recognize outer level + # boundaries at any level of inner nesting. Do this, but be sure it's + # in the order of most to least nested. + for ateof in self._eofstack[::-1]: + if ateof(line): + # We're at the false EOF. But push the last line back first. + self._lines.append(line) + return '' + return line + + def unreadline(self, line): + # Let the consumer push a line back into the buffer. + assert line is not NeedMoreData + self._lines.append(line) + + def push(self, data): + """Push some new data into this object.""" + # Handle any previous leftovers + data, self._partial = self._partial + data, '' + # Crack into lines, but preserve the newlines on the end of each + parts = NLCRE_crack.split(data) + # The *ahem* interesting behaviour of re.split when supplied grouping + # parentheses is that the last element of the resulting list is the + # data after the final RE. In the case of a NL/CR terminated string, + # this is the empty string. + self._partial = parts.pop() + # parts is a list of strings, alternating between the line contents + # and the eol character(s). Gather up a list of lines after + # re-attaching the newlines. + lines = [] + for i in range(len(parts) // 2): + lines.append(parts[i*2] + parts[i*2+1]) + self.pushlines(lines) + + def pushlines(self, lines): + # Reverse and insert at the front of the lines. + self._lines[:0] = lines[::-1] + + def is_closed(self): + return self._closed + + def __iter__(self): + return self + + def next(self): + line = self.readline() + if line == '': + raise StopIteration + return line + + + +class FeedParser: + """A feed-style parser of email.""" + + def __init__(self, _factory=message.Message): + """_factory is called with no arguments to create a new message obj""" + self._factory = _factory + self._input = BufferedSubFile() + self._msgstack = [] + self._parse = self._parsegen().next + self._cur = None + self._last = None + self._headersonly = False + + # Non-public interface for supporting Parser's headersonly flag + def _set_headersonly(self): + self._headersonly = True + + def feed(self, data): + """Push more data into the parser.""" + self._input.push(data) + self._call_parse() + + def _call_parse(self): + try: + self._parse() + except StopIteration: + pass + + def close(self): + """Parse all remaining data and return the root message object.""" + self._input.close() + self._call_parse() + root = self._pop_message() + assert not self._msgstack + # Look for final set of defects + if root.get_content_maintype() == 'multipart' \ + and not root.is_multipart(): + root.defects.append(errors.MultipartInvariantViolationDefect()) + return root + + def _new_message(self): + msg = self._factory() + if self._cur and self._cur.get_content_type() == 'multipart/digest': + msg.set_default_type('message/rfc822') + if self._msgstack: + self._msgstack[-1].attach(msg) + self._msgstack.append(msg) + self._cur = msg + self._last = msg + + def _pop_message(self): + retval = self._msgstack.pop() + if self._msgstack: + self._cur = self._msgstack[-1] + else: + self._cur = None + return retval + + def _parsegen(self): + # Create a new message and start by parsing headers. + self._new_message() + headers = [] + # Collect the headers, searching for a line that doesn't match the RFC + # 2822 header or continuation pattern (including an empty line). + for line in self._input: + if line is NeedMoreData: + yield NeedMoreData + continue + if not headerRE.match(line): + # If we saw the RFC defined header/body separator + # (i.e. newline), just throw it away. Otherwise the line is + # part of the body so push it back. + if not NLCRE.match(line): + self._input.unreadline(line) + break + headers.append(line) + # Done with the headers, so parse them and figure out what we're + # supposed to see in the body of the message. + self._parse_headers(headers) + # Headers-only parsing is a backwards compatibility hack, which was + # necessary in the older parser, which could throw errors. All + # remaining lines in the input are thrown into the message body. + if self._headersonly: + lines = [] + while True: + line = self._input.readline() + if line is NeedMoreData: + yield NeedMoreData + continue + if line == '': + break + lines.append(line) + self._cur.set_payload(EMPTYSTRING.join(lines)) + return + if self._cur.get_content_type() == 'message/delivery-status': + # message/delivery-status contains blocks of headers separated by + # a blank line. We'll represent each header block as a separate + # nested message object, but the processing is a bit different + # than standard message/* types because there is no body for the + # nested messages. A blank line separates the subparts. + while True: + self._input.push_eof_matcher(NLCRE.match) + for retval in self._parsegen(): + if retval is NeedMoreData: + yield NeedMoreData + continue + break + msg = self._pop_message() + # We need to pop the EOF matcher in order to tell if we're at + # the end of the current file, not the end of the last block + # of message headers. + self._input.pop_eof_matcher() + # The input stream must be sitting at the newline or at the + # EOF. We want to see if we're at the end of this subpart, so + # first consume the blank line, then test the next line to see + # if we're at this subpart's EOF. + while True: + line = self._input.readline() + if line is NeedMoreData: + yield NeedMoreData + continue + break + while True: + line = self._input.readline() + if line is NeedMoreData: + yield NeedMoreData + continue + break + if line == '': + break + # Not at EOF so this is a line we're going to need. + self._input.unreadline(line) + return + if self._cur.get_content_maintype() == 'message': + # The message claims to be a message/* type, then what follows is + # another RFC 2822 message. + for retval in self._parsegen(): + if retval is NeedMoreData: + yield NeedMoreData + continue + break + self._pop_message() + return + if self._cur.get_content_maintype() == 'multipart': + boundary = self._cur.get_boundary() + if boundary is None: + # The message /claims/ to be a multipart but it has not + # defined a boundary. That's a problem which we'll handle by + # reading everything until the EOF and marking the message as + # defective. + self._cur.defects.append(errors.NoBoundaryInMultipartDefect()) + lines = [] + for line in self._input: + if line is NeedMoreData: + yield NeedMoreData + continue + lines.append(line) + self._cur.set_payload(EMPTYSTRING.join(lines)) + return + # Create a line match predicate which matches the inter-part + # boundary as well as the end-of-multipart boundary. Don't push + # this onto the input stream until we've scanned past the + # preamble. + separator = '--' + boundary + boundaryre = re.compile( + '(?P<sep>' + re.escape(separator) + + r')(?P<end>--)?(?P<ws>[ \t]*)(?P<linesep>\r\n|\r|\n)?$') + capturing_preamble = True + preamble = [] + linesep = False + while True: + line = self._input.readline() + if line is NeedMoreData: + yield NeedMoreData + continue + if line == '': + break + mo = boundaryre.match(line) + if mo: + # If we're looking at the end boundary, we're done with + # this multipart. If there was a newline at the end of + # the closing boundary, then we need to initialize the + # epilogue with the empty string (see below). + if mo.group('end'): + linesep = mo.group('linesep') + break + # We saw an inter-part boundary. Were we in the preamble? + if capturing_preamble: + if preamble: + # According to RFC 2046, the last newline belongs + # to the boundary. + lastline = preamble[-1] + eolmo = NLCRE_eol.search(lastline) + if eolmo: + preamble[-1] = lastline[:-len(eolmo.group(0))] + self._cur.preamble = EMPTYSTRING.join(preamble) + capturing_preamble = False + self._input.unreadline(line) + continue + # We saw a boundary separating two parts. Consume any + # multiple boundary lines that may be following. Our + # interpretation of RFC 2046 BNF grammar does not produce + # body parts within such double boundaries. + while True: + line = self._input.readline() + if line is NeedMoreData: + yield NeedMoreData + continue + mo = boundaryre.match(line) + if not mo: + self._input.unreadline(line) + break + # Recurse to parse this subpart; the input stream points + # at the subpart's first line. + self._input.push_eof_matcher(boundaryre.match) + for retval in self._parsegen(): + if retval is NeedMoreData: + yield NeedMoreData + continue + break + # Because of RFC 2046, the newline preceding the boundary + # separator actually belongs to the boundary, not the + # previous subpart's payload (or epilogue if the previous + # part is a multipart). + if self._last.get_content_maintype() == 'multipart': + epilogue = self._last.epilogue + if epilogue == '': + self._last.epilogue = None + elif epilogue is not None: + mo = NLCRE_eol.search(epilogue) + if mo: + end = len(mo.group(0)) + self._last.epilogue = epilogue[:-end] + else: + payload = self._last.get_payload() + if isinstance(payload, basestring): + mo = NLCRE_eol.search(payload) + if mo: + payload = payload[:-len(mo.group(0))] + self._last.set_payload(payload) + self._input.pop_eof_matcher() + self._pop_message() + # Set the multipart up for newline cleansing, which will + # happen if we're in a nested multipart. + self._last = self._cur + else: + # I think we must be in the preamble + assert capturing_preamble + preamble.append(line) + # We've seen either the EOF or the end boundary. If we're still + # capturing the preamble, we never saw the start boundary. Note + # that as a defect and store the captured text as the payload. + # Everything from here to the EOF is epilogue. + if capturing_preamble: + self._cur.defects.append(errors.StartBoundaryNotFoundDefect()) + self._cur.set_payload(EMPTYSTRING.join(preamble)) + epilogue = [] + for line in self._input: + if line is NeedMoreData: + yield NeedMoreData + continue + self._cur.epilogue = EMPTYSTRING.join(epilogue) + return + # If the end boundary ended in a newline, we'll need to make sure + # the epilogue isn't None + if linesep: + epilogue = [''] + else: + epilogue = [] + for line in self._input: + if line is NeedMoreData: + yield NeedMoreData + continue + epilogue.append(line) + # Any CRLF at the front of the epilogue is not technically part of + # the epilogue. Also, watch out for an empty string epilogue, + # which means a single newline. + if epilogue: + firstline = epilogue[0] + bolmo = NLCRE_bol.match(firstline) + if bolmo: + epilogue[0] = firstline[len(bolmo.group(0)):] + self._cur.epilogue = EMPTYSTRING.join(epilogue) + return + # Otherwise, it's some non-multipart type, so the entire rest of the + # file contents becomes the payload. + lines = [] + for line in self._input: + if line is NeedMoreData: + yield NeedMoreData + continue + lines.append(line) + self._cur.set_payload(EMPTYSTRING.join(lines)) + + def _parse_headers(self, lines): + # Passed a list of lines that make up the headers for the current msg + lastheader = '' + lastvalue = [] + for lineno, line in enumerate(lines): + # Check for continuation + if line[0] in ' \t': + if not lastheader: + # The first line of the headers was a continuation. This + # is illegal, so let's note the defect, store the illegal + # line, and ignore it for purposes of headers. + defect = errors.FirstHeaderLineIsContinuationDefect(line) + self._cur.defects.append(defect) + continue + lastvalue.append(line) + continue + if lastheader: + # XXX reconsider the joining of folded lines + lhdr = EMPTYSTRING.join(lastvalue)[:-1].rstrip('\r\n') + self._cur[lastheader] = lhdr + lastheader, lastvalue = '', [] + # Check for envelope header, i.e. unix-from + if line.startswith('From '): + if lineno == 0: + # Strip off the trailing newline + mo = NLCRE_eol.search(line) + if mo: + line = line[:-len(mo.group(0))] + self._cur.set_unixfrom(line) + continue + elif lineno == len(lines) - 1: + # Something looking like a unix-from at the end - it's + # probably the first line of the body, so push back the + # line and stop. + self._input.unreadline(line) + return + else: + # Weirdly placed unix-from line. Note this as a defect + # and ignore it. + defect = errors.MisplacedEnvelopeHeaderDefect(line) + self._cur.defects.append(defect) + continue + # Split the line on the colon separating field name from value. + i = line.find(':') + if i < 0: + defect = errors.MalformedHeaderDefect(line) + self._cur.defects.append(defect) + continue + lastheader = line[:i] + lastvalue = [line[i+1:].lstrip()] + # Done with all the lines, so handle the last header. + if lastheader: + # XXX reconsider the joining of folded lines + self._cur[lastheader] = EMPTYSTRING.join(lastvalue).rstrip('\r\n') diff --git a/Lib/email/generator.py b/Lib/email/generator.py new file mode 100644 index 0000000..6e7a515 --- /dev/null +++ b/Lib/email/generator.py @@ -0,0 +1,348 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Classes to generate plain text from a message object tree.""" + +__all__ = ['Generator', 'DecodedGenerator'] + +import re +import sys +import time +import random +import warnings + +from cStringIO import StringIO +from email.header import Header + +UNDERSCORE = '_' +NL = '\n' + +fcre = re.compile(r'^From ', re.MULTILINE) + +def _is8bitstring(s): + if isinstance(s, str): + try: + unicode(s, 'us-ascii') + except UnicodeError: + return True + return False + + + +class Generator: + """Generates output from a Message object tree. + + This basic generator writes the message to the given file object as plain + text. + """ + # + # Public interface + # + + def __init__(self, outfp, mangle_from_=True, maxheaderlen=78): + """Create the generator for message flattening. + + outfp is the output file-like object for writing the message to. It + must have a write() method. + + Optional mangle_from_ is a flag that, when True (the default), escapes + From_ lines in the body of the message by putting a `>' in front of + them. + + Optional maxheaderlen specifies the longest length for a non-continued + header. When a header line is longer (in characters, with tabs + expanded to 8 spaces) than maxheaderlen, the header will split as + defined in the Header class. Set maxheaderlen to zero to disable + header wrapping. The default is 78, as recommended (but not required) + by RFC 2822. + """ + self._fp = outfp + self._mangle_from_ = mangle_from_ + self._maxheaderlen = maxheaderlen + + def write(self, s): + # Just delegate to the file object + self._fp.write(s) + + def flatten(self, msg, unixfrom=False): + """Print the message object tree rooted at msg to the output file + specified when the Generator instance was created. + + unixfrom is a flag that forces the printing of a Unix From_ delimiter + before the first object in the message tree. If the original message + has no From_ delimiter, a `standard' one is crafted. By default, this + is False to inhibit the printing of any From_ delimiter. + + Note that for subobjects, no From_ line is printed. + """ + if unixfrom: + ufrom = msg.get_unixfrom() + if not ufrom: + ufrom = 'From nobody ' + time.ctime(time.time()) + print >> self._fp, ufrom + self._write(msg) + + def clone(self, fp): + """Clone this generator with the exact same options.""" + return self.__class__(fp, self._mangle_from_, self._maxheaderlen) + + # + # Protected interface - undocumented ;/ + # + + def _write(self, msg): + # We can't write the headers yet because of the following scenario: + # say a multipart message includes the boundary string somewhere in + # its body. We'd have to calculate the new boundary /before/ we write + # the headers so that we can write the correct Content-Type: + # parameter. + # + # The way we do this, so as to make the _handle_*() methods simpler, + # is to cache any subpart writes into a StringIO. The we write the + # headers and the StringIO contents. That way, subpart handlers can + # Do The Right Thing, and can still modify the Content-Type: header if + # necessary. + oldfp = self._fp + try: + self._fp = sfp = StringIO() + self._dispatch(msg) + finally: + self._fp = oldfp + # Write the headers. First we see if the message object wants to + # handle that itself. If not, we'll do it generically. + meth = getattr(msg, '_write_headers', None) + if meth is None: + self._write_headers(msg) + else: + meth(self) + self._fp.write(sfp.getvalue()) + + def _dispatch(self, msg): + # Get the Content-Type: for the message, then try to dispatch to + # self._handle_<maintype>_<subtype>(). If there's no handler for the + # full MIME type, then dispatch to self._handle_<maintype>(). If + # that's missing too, then dispatch to self._writeBody(). + main = msg.get_content_maintype() + sub = msg.get_content_subtype() + specific = UNDERSCORE.join((main, sub)).replace('-', '_') + meth = getattr(self, '_handle_' + specific, None) + if meth is None: + generic = main.replace('-', '_') + meth = getattr(self, '_handle_' + generic, None) + if meth is None: + meth = self._writeBody + meth(msg) + + # + # Default handlers + # + + def _write_headers(self, msg): + for h, v in msg.items(): + print >> self._fp, '%s:' % h, + if self._maxheaderlen == 0: + # Explicit no-wrapping + print >> self._fp, v + elif isinstance(v, Header): + # Header instances know what to do + print >> self._fp, v.encode() + elif _is8bitstring(v): + # If we have raw 8bit data in a byte string, we have no idea + # what the encoding is. There is no safe way to split this + # string. If it's ascii-subset, then we could do a normal + # ascii split, but if it's multibyte then we could break the + # string. There's no way to know so the least harm seems to + # be to not split the string and risk it being too long. + print >> self._fp, v + else: + # Header's got lots of smarts, so use it. + print >> self._fp, Header( + v, maxlinelen=self._maxheaderlen, + header_name=h, continuation_ws='\t').encode() + # A blank line always separates headers from body + print >> self._fp + + # + # Handlers for writing types and subtypes + # + + def _handle_text(self, msg): + payload = msg.get_payload() + if payload is None: + return + if not isinstance(payload, basestring): + raise TypeError('string payload expected: %s' % type(payload)) + if self._mangle_from_: + payload = fcre.sub('>From ', payload) + self._fp.write(payload) + + # Default body handler + _writeBody = _handle_text + + def _handle_multipart(self, msg): + # The trick here is to write out each part separately, merge them all + # together, and then make sure that the boundary we've chosen isn't + # present in the payload. + msgtexts = [] + subparts = msg.get_payload() + if subparts is None: + subparts = [] + elif isinstance(subparts, basestring): + # e.g. a non-strict parse of a message with no starting boundary. + self._fp.write(subparts) + return + elif not isinstance(subparts, list): + # Scalar payload + subparts = [subparts] + for part in subparts: + s = StringIO() + g = self.clone(s) + g.flatten(part, unixfrom=False) + msgtexts.append(s.getvalue()) + # Now make sure the boundary we've selected doesn't appear in any of + # the message texts. + alltext = NL.join(msgtexts) + # BAW: What about boundaries that are wrapped in double-quotes? + boundary = msg.get_boundary(failobj=_make_boundary(alltext)) + # If we had to calculate a new boundary because the body text + # contained that string, set the new boundary. We don't do it + # unconditionally because, while set_boundary() preserves order, it + # doesn't preserve newlines/continuations in headers. This is no big + # deal in practice, but turns out to be inconvenient for the unittest + # suite. + if msg.get_boundary() <> boundary: + msg.set_boundary(boundary) + # If there's a preamble, write it out, with a trailing CRLF + if msg.preamble is not None: + print >> self._fp, msg.preamble + # dash-boundary transport-padding CRLF + print >> self._fp, '--' + boundary + # body-part + if msgtexts: + self._fp.write(msgtexts.pop(0)) + # *encapsulation + # --> delimiter transport-padding + # --> CRLF body-part + for body_part in msgtexts: + # delimiter transport-padding CRLF + print >> self._fp, '\n--' + boundary + # body-part + self._fp.write(body_part) + # close-delimiter transport-padding + self._fp.write('\n--' + boundary + '--') + if msg.epilogue is not None: + print >> self._fp + self._fp.write(msg.epilogue) + + def _handle_message_delivery_status(self, msg): + # We can't just write the headers directly to self's file object + # because this will leave an extra newline between the last header + # block and the boundary. Sigh. + blocks = [] + for part in msg.get_payload(): + s = StringIO() + g = self.clone(s) + g.flatten(part, unixfrom=False) + text = s.getvalue() + lines = text.split('\n') + # Strip off the unnecessary trailing empty line + if lines and lines[-1] == '': + blocks.append(NL.join(lines[:-1])) + else: + blocks.append(text) + # Now join all the blocks with an empty line. This has the lovely + # effect of separating each block with an empty line, but not adding + # an extra one after the last one. + self._fp.write(NL.join(blocks)) + + def _handle_message(self, msg): + s = StringIO() + g = self.clone(s) + # The payload of a message/rfc822 part should be a multipart sequence + # of length 1. The zeroth element of the list should be the Message + # object for the subpart. Extract that object, stringify it, and + # write it out. + g.flatten(msg.get_payload(0), unixfrom=False) + self._fp.write(s.getvalue()) + + + +_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' + +class DecodedGenerator(Generator): + """Generator a text representation of a message. + + Like the Generator base class, except that non-text parts are substituted + with a format string representing the part. + """ + def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): + """Like Generator.__init__() except that an additional optional + argument is allowed. + + Walks through all subparts of a message. If the subpart is of main + type `text', then it prints the decoded payload of the subpart. + + Otherwise, fmt is a format string that is used instead of the message + payload. fmt is expanded with the following keywords (in + %(keyword)s format): + + type : Full MIME type of the non-text part + maintype : Main MIME type of the non-text part + subtype : Sub-MIME type of the non-text part + filename : Filename of the non-text part + description: Description associated with the non-text part + encoding : Content transfer encoding of the non-text part + + The default value for fmt is None, meaning + + [Non-text (%(type)s) part of message omitted, filename %(filename)s] + """ + Generator.__init__(self, outfp, mangle_from_, maxheaderlen) + if fmt is None: + self._fmt = _FMT + else: + self._fmt = fmt + + def _dispatch(self, msg): + for part in msg.walk(): + maintype = part.get_content_maintype() + if maintype == 'text': + print >> self, part.get_payload(decode=True) + elif maintype == 'multipart': + # Just skip this + pass + else: + print >> self, self._fmt % { + 'type' : part.get_content_type(), + 'maintype' : part.get_content_maintype(), + 'subtype' : part.get_content_subtype(), + 'filename' : part.get_filename('[no filename]'), + 'description': part.get('Content-Description', + '[no description]'), + 'encoding' : part.get('Content-Transfer-Encoding', + '[no encoding]'), + } + + + +# Helper +_width = len(repr(sys.maxint-1)) +_fmt = '%%0%dd' % _width + +def _make_boundary(text=None): + # Craft a random boundary. If text is given, ensure that the chosen + # boundary doesn't appear in the text. + token = random.randrange(sys.maxint) + boundary = ('=' * 15) + (_fmt % token) + '==' + if text is None: + return boundary + b = boundary + counter = 0 + while True: + cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE) + if not cre.search(text): + break + b = boundary + '.' + str(counter) + counter += 1 + return b diff --git a/Lib/email/header.py b/Lib/email/header.py new file mode 100644 index 0000000..183c337 --- /dev/null +++ b/Lib/email/header.py @@ -0,0 +1,502 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Author: Ben Gertzfield, Barry Warsaw +# Contact: email-sig@python.org + +"""Header encoding and decoding functionality.""" + +__all__ = [ + 'Header', + 'decode_header', + 'make_header', + ] + +import re +import binascii + +import email.quoprimime +import email.base64mime + +from email.errors import HeaderParseError +from email.charset import Charset + +NL = '\n' +SPACE = ' ' +USPACE = u' ' +SPACE8 = ' ' * 8 +UEMPTYSTRING = u'' + +MAXLINELEN = 76 + +USASCII = Charset('us-ascii') +UTF8 = Charset('utf-8') + +# Match encoded-word strings in the form =?charset?q?Hello_World?= +ecre = re.compile(r''' + =\? # literal =? + (?P<charset>[^?]*?) # non-greedy up to the next ? is the charset + \? # literal ? + (?P<encoding>[qb]) # either a "q" or a "b", case insensitive + \? # literal ? + (?P<encoded>.*?) # non-greedy up to the next ?= is the encoded string + \?= # literal ?= + ''', re.VERBOSE | re.IGNORECASE) + +# Field name regexp, including trailing colon, but not separating whitespace, +# according to RFC 2822. Character range is from tilde to exclamation mark. +# For use with .match() +fcre = re.compile(r'[\041-\176]+:$') + + + +# Helpers +_max_append = email.quoprimime._max_append + + + +def decode_header(header): + """Decode a message header value without converting charset. + + Returns a list of (decoded_string, charset) pairs containing each of the + decoded parts of the header. Charset is None for non-encoded parts of the + header, otherwise a lower-case string containing the name of the character + set specified in the encoded string. + + An email.Errors.HeaderParseError may be raised when certain decoding error + occurs (e.g. a base64 decoding exception). + """ + # If no encoding, just return the header + header = str(header) + if not ecre.search(header): + return [(header, None)] + decoded = [] + dec = '' + for line in header.splitlines(): + # This line might not have an encoding in it + if not ecre.search(line): + decoded.append((line, None)) + continue + parts = ecre.split(line) + while parts: + unenc = parts.pop(0).strip() + if unenc: + # Should we continue a long line? + if decoded and decoded[-1][1] is None: + decoded[-1] = (decoded[-1][0] + SPACE + unenc, None) + else: + decoded.append((unenc, None)) + if parts: + charset, encoding = [s.lower() for s in parts[0:2]] + encoded = parts[2] + dec = None + if encoding == 'q': + dec = email.quoprimime.header_decode(encoded) + elif encoding == 'b': + try: + dec = email.base64mime.decode(encoded) + except binascii.Error: + # Turn this into a higher level exception. BAW: Right + # now we throw the lower level exception away but + # when/if we get exception chaining, we'll preserve it. + raise HeaderParseError + if dec is None: + dec = encoded + + if decoded and decoded[-1][1] == charset: + decoded[-1] = (decoded[-1][0] + dec, decoded[-1][1]) + else: + decoded.append((dec, charset)) + del parts[0:3] + return decoded + + + +def make_header(decoded_seq, maxlinelen=None, header_name=None, + continuation_ws=' '): + """Create a Header from a sequence of pairs as returned by decode_header() + + decode_header() takes a header value string and returns a sequence of + pairs of the format (decoded_string, charset) where charset is the string + name of the character set. + + This function takes one of those sequence of pairs and returns a Header + instance. Optional maxlinelen, header_name, and continuation_ws are as in + the Header constructor. + """ + h = Header(maxlinelen=maxlinelen, header_name=header_name, + continuation_ws=continuation_ws) + for s, charset in decoded_seq: + # None means us-ascii but we can simply pass it on to h.append() + if charset is not None and not isinstance(charset, Charset): + charset = Charset(charset) + h.append(s, charset) + return h + + + +class Header: + def __init__(self, s=None, charset=None, + maxlinelen=None, header_name=None, + continuation_ws=' ', errors='strict'): + """Create a MIME-compliant header that can contain many character sets. + + Optional s is the initial header value. If None, the initial header + value is not set. You can later append to the header with .append() + method calls. s may be a byte string or a Unicode string, but see the + .append() documentation for semantics. + + Optional charset serves two purposes: it has the same meaning as the + charset argument to the .append() method. It also sets the default + character set for all subsequent .append() calls that omit the charset + argument. If charset is not provided in the constructor, the us-ascii + charset is used both as s's initial charset and as the default for + subsequent .append() calls. + + The maximum line length can be specified explicit via maxlinelen. For + splitting the first line to a shorter value (to account for the field + header which isn't included in s, e.g. `Subject') pass in the name of + the field in header_name. The default maxlinelen is 76. + + continuation_ws must be RFC 2822 compliant folding whitespace (usually + either a space or a hard tab) which will be prepended to continuation + lines. + + errors is passed through to the .append() call. + """ + if charset is None: + charset = USASCII + if not isinstance(charset, Charset): + charset = Charset(charset) + self._charset = charset + self._continuation_ws = continuation_ws + cws_expanded_len = len(continuation_ws.replace('\t', SPACE8)) + # BAW: I believe `chunks' and `maxlinelen' should be non-public. + self._chunks = [] + if s is not None: + self.append(s, charset, errors) + if maxlinelen is None: + maxlinelen = MAXLINELEN + if header_name is None: + # We don't know anything about the field header so the first line + # is the same length as subsequent lines. + self._firstlinelen = maxlinelen + else: + # The first line should be shorter to take into account the field + # header. Also subtract off 2 extra for the colon and space. + self._firstlinelen = maxlinelen - len(header_name) - 2 + # Second and subsequent lines should subtract off the length in + # columns of the continuation whitespace prefix. + self._maxlinelen = maxlinelen - cws_expanded_len + + def __str__(self): + """A synonym for self.encode().""" + return self.encode() + + def __unicode__(self): + """Helper for the built-in unicode function.""" + uchunks = [] + lastcs = None + for s, charset in self._chunks: + # We must preserve spaces between encoded and non-encoded word + # boundaries, which means for us we need to add a space when we go + # from a charset to None/us-ascii, or from None/us-ascii to a + # charset. Only do this for the second and subsequent chunks. + nextcs = charset + if uchunks: + if lastcs not in (None, 'us-ascii'): + if nextcs in (None, 'us-ascii'): + uchunks.append(USPACE) + nextcs = None + elif nextcs not in (None, 'us-ascii'): + uchunks.append(USPACE) + lastcs = nextcs + uchunks.append(unicode(s, str(charset))) + return UEMPTYSTRING.join(uchunks) + + # Rich comparison operators for equality only. BAW: does it make sense to + # have or explicitly disable <, <=, >, >= operators? + def __eq__(self, other): + # other may be a Header or a string. Both are fine so coerce + # ourselves to a string, swap the args and do another comparison. + return other == self.encode() + + def __ne__(self, other): + return not self == other + + def append(self, s, charset=None, errors='strict'): + """Append a string to the MIME header. + + Optional charset, if given, should be a Charset instance or the name + of a character set (which will be converted to a Charset instance). A + value of None (the default) means that the charset given in the + constructor is used. + + s may be a byte string or a Unicode string. If it is a byte string + (i.e. isinstance(s, str) is true), then charset is the encoding of + that byte string, and a UnicodeError will be raised if the string + cannot be decoded with that charset. If s is a Unicode string, then + charset is a hint specifying the character set of the characters in + the string. In this case, when producing an RFC 2822 compliant header + using RFC 2047 rules, the Unicode string will be encoded using the + following charsets in order: us-ascii, the charset hint, utf-8. The + first character set not to provoke a UnicodeError is used. + + Optional `errors' is passed as the third argument to any unicode() or + ustr.encode() call. + """ + if charset is None: + charset = self._charset + elif not isinstance(charset, Charset): + charset = Charset(charset) + # If the charset is our faux 8bit charset, leave the string unchanged + if charset <> '8bit': + # We need to test that the string can be converted to unicode and + # back to a byte string, given the input and output codecs of the + # charset. + if isinstance(s, str): + # Possibly raise UnicodeError if the byte string can't be + # converted to a unicode with the input codec of the charset. + incodec = charset.input_codec or 'us-ascii' + ustr = unicode(s, incodec, errors) + # Now make sure that the unicode could be converted back to a + # byte string with the output codec, which may be different + # than the iput coded. Still, use the original byte string. + outcodec = charset.output_codec or 'us-ascii' + ustr.encode(outcodec, errors) + elif isinstance(s, unicode): + # Now we have to be sure the unicode string can be converted + # to a byte string with a reasonable output codec. We want to + # use the byte string in the chunk. + for charset in USASCII, charset, UTF8: + try: + outcodec = charset.output_codec or 'us-ascii' + s = s.encode(outcodec, errors) + break + except UnicodeError: + pass + else: + assert False, 'utf-8 conversion failed' + self._chunks.append((s, charset)) + + def _split(self, s, charset, maxlinelen, splitchars): + # Split up a header safely for use with encode_chunks. + splittable = charset.to_splittable(s) + encoded = charset.from_splittable(splittable, True) + elen = charset.encoded_header_len(encoded) + # If the line's encoded length first, just return it + if elen <= maxlinelen: + return [(encoded, charset)] + # If we have undetermined raw 8bit characters sitting in a byte + # string, we really don't know what the right thing to do is. We + # can't really split it because it might be multibyte data which we + # could break if we split it between pairs. The least harm seems to + # be to not split the header at all, but that means they could go out + # longer than maxlinelen. + if charset == '8bit': + return [(s, charset)] + # BAW: I'm not sure what the right test here is. What we're trying to + # do is be faithful to RFC 2822's recommendation that ($2.2.3): + # + # "Note: Though structured field bodies are defined in such a way that + # folding can take place between many of the lexical tokens (and even + # within some of the lexical tokens), folding SHOULD be limited to + # placing the CRLF at higher-level syntactic breaks." + # + # For now, I can only imagine doing this when the charset is us-ascii, + # although it's possible that other charsets may also benefit from the + # higher-level syntactic breaks. + elif charset == 'us-ascii': + return self._split_ascii(s, charset, maxlinelen, splitchars) + # BAW: should we use encoded? + elif elen == len(s): + # We can split on _maxlinelen boundaries because we know that the + # encoding won't change the size of the string + splitpnt = maxlinelen + first = charset.from_splittable(splittable[:splitpnt], False) + last = charset.from_splittable(splittable[splitpnt:], False) + else: + # Binary search for split point + first, last = _binsplit(splittable, charset, maxlinelen) + # first is of the proper length so just wrap it in the appropriate + # chrome. last must be recursively split. + fsplittable = charset.to_splittable(first) + fencoded = charset.from_splittable(fsplittable, True) + chunk = [(fencoded, charset)] + return chunk + self._split(last, charset, self._maxlinelen, splitchars) + + def _split_ascii(self, s, charset, firstlen, splitchars): + chunks = _split_ascii(s, firstlen, self._maxlinelen, + self._continuation_ws, splitchars) + return zip(chunks, [charset]*len(chunks)) + + def _encode_chunks(self, newchunks, maxlinelen): + # MIME-encode a header with many different charsets and/or encodings. + # + # Given a list of pairs (string, charset), return a MIME-encoded + # string suitable for use in a header field. Each pair may have + # different charsets and/or encodings, and the resulting header will + # accurately reflect each setting. + # + # Each encoding can be email.Utils.QP (quoted-printable, for + # ASCII-like character sets like iso-8859-1), email.Utils.BASE64 + # (Base64, for non-ASCII like character sets like KOI8-R and + # iso-2022-jp), or None (no encoding). + # + # Each pair will be represented on a separate line; the resulting + # string will be in the format: + # + # =?charset1?q?Mar=EDa_Gonz=E1lez_Alonso?=\n + # =?charset2?b?SvxyZ2VuIEL2aW5n?=" + chunks = [] + for header, charset in newchunks: + if not header: + continue + if charset is None or charset.header_encoding is None: + s = header + else: + s = charset.header_encode(header) + # Don't add more folding whitespace than necessary + if chunks and chunks[-1].endswith(' '): + extra = '' + else: + extra = ' ' + _max_append(chunks, s, maxlinelen, extra) + joiner = NL + self._continuation_ws + return joiner.join(chunks) + + def encode(self, splitchars=';, '): + """Encode a message header into an RFC-compliant format. + + There are many issues involved in converting a given string for use in + an email header. Only certain character sets are readable in most + email clients, and as header strings can only contain a subset of + 7-bit ASCII, care must be taken to properly convert and encode (with + Base64 or quoted-printable) header strings. In addition, there is a + 75-character length limit on any given encoded header field, so + line-wrapping must be performed, even with double-byte character sets. + + This method will do its best to convert the string to the correct + character set used in email, and encode and line wrap it safely with + the appropriate scheme for that character set. + + If the given charset is not known or an error occurs during + conversion, this function will return the header untouched. + + Optional splitchars is a string containing characters to split long + ASCII lines on, in rough support of RFC 2822's `highest level + syntactic breaks'. This doesn't affect RFC 2047 encoded lines. + """ + newchunks = [] + maxlinelen = self._firstlinelen + lastlen = 0 + for s, charset in self._chunks: + # The first bit of the next chunk should be just long enough to + # fill the next line. Don't forget the space separating the + # encoded words. + targetlen = maxlinelen - lastlen - 1 + if targetlen < charset.encoded_header_len(''): + # Stick it on the next line + targetlen = maxlinelen + newchunks += self._split(s, charset, targetlen, splitchars) + lastchunk, lastcharset = newchunks[-1] + lastlen = lastcharset.encoded_header_len(lastchunk) + return self._encode_chunks(newchunks, maxlinelen) + + + +def _split_ascii(s, firstlen, restlen, continuation_ws, splitchars): + lines = [] + maxlen = firstlen + for line in s.splitlines(): + # Ignore any leading whitespace (i.e. continuation whitespace) already + # on the line, since we'll be adding our own. + line = line.lstrip() + if len(line) < maxlen: + lines.append(line) + maxlen = restlen + continue + # Attempt to split the line at the highest-level syntactic break + # possible. Note that we don't have a lot of smarts about field + # syntax; we just try to break on semi-colons, then commas, then + # whitespace. + for ch in splitchars: + if ch in line: + break + else: + # There's nothing useful to split the line on, not even spaces, so + # just append this line unchanged + lines.append(line) + maxlen = restlen + continue + # Now split the line on the character plus trailing whitespace + cre = re.compile(r'%s\s*' % ch) + if ch in ';,': + eol = ch + else: + eol = '' + joiner = eol + ' ' + joinlen = len(joiner) + wslen = len(continuation_ws.replace('\t', SPACE8)) + this = [] + linelen = 0 + for part in cre.split(line): + curlen = linelen + max(0, len(this)-1) * joinlen + partlen = len(part) + onfirstline = not lines + # We don't want to split after the field name, if we're on the + # first line and the field name is present in the header string. + if ch == ' ' and onfirstline and \ + len(this) == 1 and fcre.match(this[0]): + this.append(part) + linelen += partlen + elif curlen + partlen > maxlen: + if this: + lines.append(joiner.join(this) + eol) + # If this part is longer than maxlen and we aren't already + # splitting on whitespace, try to recursively split this line + # on whitespace. + if partlen > maxlen and ch <> ' ': + subl = _split_ascii(part, maxlen, restlen, + continuation_ws, ' ') + lines.extend(subl[:-1]) + this = [subl[-1]] + else: + this = [part] + linelen = wslen + len(this[-1]) + maxlen = restlen + else: + this.append(part) + linelen += partlen + # Put any left over parts on a line by themselves + if this: + lines.append(joiner.join(this)) + return lines + + + +def _binsplit(splittable, charset, maxlinelen): + i = 0 + j = len(splittable) + while i < j: + # Invariants: + # 1. splittable[:k] fits for all k <= i (note that we *assume*, + # at the start, that splittable[:0] fits). + # 2. splittable[:k] does not fit for any k > j (at the start, + # this means we shouldn't look at any k > len(splittable)). + # 3. We don't know about splittable[:k] for k in i+1..j. + # 4. We want to set i to the largest k that fits, with i <= k <= j. + # + m = (i+j+1) >> 1 # ceiling((i+j)/2); i < m <= j + chunk = charset.from_splittable(splittable[:m], True) + chunklen = charset.encoded_header_len(chunk) + if chunklen <= maxlinelen: + # m is acceptable, so is a new lower bound. + i = m + else: + # m is not acceptable, so final i must be < m. + j = m - 1 + # i == j. Invariant #1 implies that splittable[:i] fits, and + # invariant #2 implies that splittable[:i+1] does not fit, so i + # is what we're looking for. + first = charset.from_splittable(splittable[:i], False) + last = charset.from_splittable(splittable[i:], False) + return first, last diff --git a/Lib/email/iterators.py b/Lib/email/iterators.py new file mode 100644 index 0000000..e99f228 --- /dev/null +++ b/Lib/email/iterators.py @@ -0,0 +1,73 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Various types of useful iterators and generators.""" + +__all__ = [ + 'body_line_iterator', + 'typed_subpart_iterator', + 'walk', + # Do not include _structure() since it's part of the debugging API. + ] + +import sys +from cStringIO import StringIO + + + +# This function will become a method of the Message class +def walk(self): + """Walk over the message tree, yielding each subpart. + + The walk is performed in depth-first order. This method is a + generator. + """ + yield self + if self.is_multipart(): + for subpart in self.get_payload(): + for subsubpart in subpart.walk(): + yield subsubpart + + + +# These two functions are imported into the Iterators.py interface module. +def body_line_iterator(msg, decode=False): + """Iterate over the parts, returning string payloads line-by-line. + + Optional decode (default False) is passed through to .get_payload(). + """ + for subpart in msg.walk(): + payload = subpart.get_payload(decode=decode) + if isinstance(payload, basestring): + for line in StringIO(payload): + yield line + + +def typed_subpart_iterator(msg, maintype='text', subtype=None): + """Iterate over the subparts with a given MIME type. + + Use `maintype' as the main MIME type to match against; this defaults to + "text". Optional `subtype' is the MIME subtype to match against; if + omitted, only the main type is matched. + """ + for subpart in msg.walk(): + if subpart.get_content_maintype() == maintype: + if subtype is None or subpart.get_content_subtype() == subtype: + yield subpart + + + +def _structure(msg, fp=None, level=0, include_default=False): + """A handy debugging aid""" + if fp is None: + fp = sys.stdout + tab = ' ' * (level * 4) + print >> fp, tab + msg.get_content_type(), + if include_default: + print >> fp, '[%s]' % msg.get_default_type() + else: + print >> fp + if msg.is_multipart(): + for subpart in msg.get_payload(): + _structure(subpart, fp, level+1, include_default) diff --git a/Lib/email/message.py b/Lib/email/message.py new file mode 100644 index 0000000..50d90b4 --- /dev/null +++ b/Lib/email/message.py @@ -0,0 +1,773 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Basic message object for the email package object model.""" + +__all__ = ['Message'] + +import re +import uu +import binascii +import warnings +from cStringIO import StringIO + +# Intrapackage imports +import email.charset +from email import utils +from email import errors + +SEMISPACE = '; ' + +# Regular expression used to split header parameters. BAW: this may be too +# simple. It isn't strictly RFC 2045 (section 5.1) compliant, but it catches +# most headers found in the wild. We may eventually need a full fledged +# parser eventually. +paramre = re.compile(r'\s*;\s*') +# Regular expression that matches `special' characters in parameters, the +# existance of which force quoting of the parameter value. +tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') + + + +# Helper functions +def _formatparam(param, value=None, quote=True): + """Convenience function to format and return a key=value pair. + + This will quote the value if needed or if quote is true. + """ + if value is not None and len(value) > 0: + # A tuple is used for RFC 2231 encoded parameter values where items + # are (charset, language, value). charset is a string, not a Charset + # instance. + if isinstance(value, tuple): + # Encode as per RFC 2231 + param += '*' + value = utils.encode_rfc2231(value[2], value[0], value[1]) + # BAW: Please check this. I think that if quote is set it should + # force quoting even if not necessary. + if quote or tspecials.search(value): + return '%s="%s"' % (param, utils.quote(value)) + else: + return '%s=%s' % (param, value) + else: + return param + +def _parseparam(s): + plist = [] + while s[:1] == ';': + s = s[1:] + end = s.find(';') + while end > 0 and s.count('"', 0, end) % 2: + end = s.find(';', end + 1) + if end < 0: + end = len(s) + f = s[:end] + if '=' in f: + i = f.index('=') + f = f[:i].strip().lower() + '=' + f[i+1:].strip() + plist.append(f.strip()) + s = s[end:] + return plist + + +def _unquotevalue(value): + # This is different than utils.collapse_rfc2231_value() because it doesn't + # try to convert the value to a unicode. Message.get_param() and + # Message.get_params() are both currently defined to return the tuple in + # the face of RFC 2231 parameters. + if isinstance(value, tuple): + return value[0], value[1], utils.unquote(value[2]) + else: + return utils.unquote(value) + + + +class Message: + """Basic message object. + + A message object is defined as something that has a bunch of RFC 2822 + headers and a payload. It may optionally have an envelope header + (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a + multipart or a message/rfc822), then the payload is a list of Message + objects, otherwise it is a string. + + Message objects implement part of the `mapping' interface, which assumes + there is exactly one occurrance of the header per message. Some headers + do in fact appear multiple times (e.g. Received) and for those headers, + you must use the explicit API to set or get all the headers. Not all of + the mapping methods are implemented. + """ + def __init__(self): + self._headers = [] + self._unixfrom = None + self._payload = None + self._charset = None + # Defaults for multipart messages + self.preamble = self.epilogue = None + self.defects = [] + # Default content type + self._default_type = 'text/plain' + + def __str__(self): + """Return the entire formatted message as a string. + This includes the headers, body, and envelope header. + """ + return self.as_string(unixfrom=True) + + def as_string(self, unixfrom=False): + """Return the entire formatted message as a string. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. + + This is a convenience method and may not generate the message exactly + as you intend because by default it mangles lines that begin with + "From ". For more flexibility, use the flatten() method of a + Generator instance. + """ + from email.Generator import Generator + fp = StringIO() + g = Generator(fp) + g.flatten(self, unixfrom=unixfrom) + return fp.getvalue() + + def is_multipart(self): + """Return True if the message consists of multiple parts.""" + return isinstance(self._payload, list) + + # + # Unix From_ line + # + def set_unixfrom(self, unixfrom): + self._unixfrom = unixfrom + + def get_unixfrom(self): + return self._unixfrom + + # + # Payload manipulation. + # + def attach(self, payload): + """Add the given payload to the current payload. + + The current payload will always be a list of objects after this method + is called. If you want to set the payload to a scalar object, use + set_payload() instead. + """ + if self._payload is None: + self._payload = [payload] + else: + self._payload.append(payload) + + def get_payload(self, i=None, decode=False): + """Return a reference to the payload. + + The payload will either be a list object or a string. If you mutate + the list object, you modify the message's payload in place. Optional + i returns that index into the payload. + + Optional decode is a flag indicating whether the payload should be + decoded or not, according to the Content-Transfer-Encoding header + (default is False). + + When True and the message is not a multipart, the payload will be + decoded if this header's value is `quoted-printable' or `base64'. If + some other encoding is used, or the header is missing, or if the + payload has bogus data (i.e. bogus base64 or uuencoded data), the + payload is returned as-is. + + If the message is a multipart and the decode flag is True, then None + is returned. + """ + if i is None: + payload = self._payload + elif not isinstance(self._payload, list): + raise TypeError('Expected list, got %s' % type(self._payload)) + else: + payload = self._payload[i] + if decode: + if self.is_multipart(): + return None + cte = self.get('content-transfer-encoding', '').lower() + if cte == 'quoted-printable': + return utils._qdecode(payload) + elif cte == 'base64': + try: + return utils._bdecode(payload) + except binascii.Error: + # Incorrect padding + return payload + elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): + sfp = StringIO() + try: + uu.decode(StringIO(payload+'\n'), sfp, quiet=True) + payload = sfp.getvalue() + except uu.Error: + # Some decoding problem + return payload + # Everything else, including encodings with 8bit or 7bit are returned + # unchanged. + return payload + + def set_payload(self, payload, charset=None): + """Set the payload to the given value. + + Optional charset sets the message's default character set. See + set_charset() for details. + """ + self._payload = payload + if charset is not None: + self.set_charset(charset) + + def set_charset(self, charset): + """Set the charset of the payload to a given character set. + + charset can be a Charset instance, a string naming a character set, or + None. If it is a string it will be converted to a Charset instance. + If charset is None, the charset parameter will be removed from the + Content-Type field. Anything else will generate a TypeError. + + The message will be assumed to be of type text/* encoded with + charset.input_charset. It will be converted to charset.output_charset + and encoded properly, if needed, when generating the plain text + representation of the message. MIME headers (MIME-Version, + Content-Type, Content-Transfer-Encoding) will be added as needed. + + """ + if charset is None: + self.del_param('charset') + self._charset = None + return + if isinstance(charset, str): + charset = email.charset.Charset(charset) + if not isinstance(charset, email.charset.Charset): + raise TypeError(charset) + # BAW: should we accept strings that can serve as arguments to the + # Charset constructor? + self._charset = charset + if not self.has_key('MIME-Version'): + self.add_header('MIME-Version', '1.0') + if not self.has_key('Content-Type'): + self.add_header('Content-Type', 'text/plain', + charset=charset.get_output_charset()) + else: + self.set_param('charset', charset.get_output_charset()) + if str(charset) <> charset.get_output_charset(): + self._payload = charset.body_encode(self._payload) + if not self.has_key('Content-Transfer-Encoding'): + cte = charset.get_body_encoding() + try: + cte(self) + except TypeError: + self._payload = charset.body_encode(self._payload) + self.add_header('Content-Transfer-Encoding', cte) + + def get_charset(self): + """Return the Charset instance associated with the message's payload. + """ + return self._charset + + # + # MAPPING INTERFACE (partial) + # + def __len__(self): + """Return the total number of headers, including duplicates.""" + return len(self._headers) + + def __getitem__(self, name): + """Get a header value. + + Return None if the header is missing instead of raising an exception. + + Note that if the header appeared multiple times, exactly which + occurrance gets returned is undefined. Use get_all() to get all + the values matching a header field name. + """ + return self.get(name) + + def __setitem__(self, name, val): + """Set the value of a header. + + Note: this does not overwrite an existing header with the same field + name. Use __delitem__() first to delete any existing headers. + """ + self._headers.append((name, val)) + + def __delitem__(self, name): + """Delete all occurrences of a header, if present. + + Does not raise an exception if the header is missing. + """ + name = name.lower() + newheaders = [] + for k, v in self._headers: + if k.lower() <> name: + newheaders.append((k, v)) + self._headers = newheaders + + def __contains__(self, name): + return name.lower() in [k.lower() for k, v in self._headers] + + def has_key(self, name): + """Return true if the message contains the header.""" + missing = object() + return self.get(name, missing) is not missing + + def keys(self): + """Return a list of all the message's header field names. + + These will be sorted in the order they appeared in the original + message, or were added to the message, and may contain duplicates. + Any fields deleted and re-inserted are always appended to the header + list. + """ + return [k for k, v in self._headers] + + def values(self): + """Return a list of all the message's header values. + + These will be sorted in the order they appeared in the original + message, or were added to the message, and may contain duplicates. + Any fields deleted and re-inserted are always appended to the header + list. + """ + return [v for k, v in self._headers] + + def items(self): + """Get all the message's header fields and values. + + These will be sorted in the order they appeared in the original + message, or were added to the message, and may contain duplicates. + Any fields deleted and re-inserted are always appended to the header + list. + """ + return self._headers[:] + + def get(self, name, failobj=None): + """Get a header value. + + Like __getitem__() but return failobj instead of None when the field + is missing. + """ + name = name.lower() + for k, v in self._headers: + if k.lower() == name: + return v + return failobj + + # + # Additional useful stuff + # + + def get_all(self, name, failobj=None): + """Return a list of all the values for the named field. + + These will be sorted in the order they appeared in the original + message, and may contain duplicates. Any fields deleted and + re-inserted are always appended to the header list. + + If no such fields exist, failobj is returned (defaults to None). + """ + values = [] + name = name.lower() + for k, v in self._headers: + if k.lower() == name: + values.append(v) + if not values: + return failobj + return values + + def add_header(self, _name, _value, **_params): + """Extended header setting. + + name is the header field to add. keyword arguments can be used to set + additional parameters for the header field, with underscores converted + to dashes. Normally the parameter will be added as key="value" unless + value is None, in which case only the key will be added. + + Example: + + msg.add_header('content-disposition', 'attachment', filename='bud.gif') + """ + parts = [] + for k, v in _params.items(): + if v is None: + parts.append(k.replace('_', '-')) + else: + parts.append(_formatparam(k.replace('_', '-'), v)) + if _value is not None: + parts.insert(0, _value) + self._headers.append((_name, SEMISPACE.join(parts))) + + def replace_header(self, _name, _value): + """Replace a header. + + Replace the first matching header found in the message, retaining + header order and case. If no matching header was found, a KeyError is + raised. + """ + _name = _name.lower() + for i, (k, v) in zip(range(len(self._headers)), self._headers): + if k.lower() == _name: + self._headers[i] = (k, _value) + break + else: + raise KeyError(_name) + + # + # Use these three methods instead of the three above. + # + + def get_content_type(self): + """Return the message's content type. + + The returned string is coerced to lower case of the form + `maintype/subtype'. If there was no Content-Type header in the + message, the default type as given by get_default_type() will be + returned. Since according to RFC 2045, messages always have a default + type this will always return a value. + + RFC 2045 defines a message's default type to be text/plain unless it + appears inside a multipart/digest container, in which case it would be + message/rfc822. + """ + missing = object() + value = self.get('content-type', missing) + if value is missing: + # This should have no parameters + return self.get_default_type() + ctype = paramre.split(value)[0].lower().strip() + # RFC 2045, section 5.2 says if its invalid, use text/plain + if ctype.count('/') <> 1: + return 'text/plain' + return ctype + + def get_content_maintype(self): + """Return the message's main content type. + + This is the `maintype' part of the string returned by + get_content_type(). + """ + ctype = self.get_content_type() + return ctype.split('/')[0] + + def get_content_subtype(self): + """Returns the message's sub-content type. + + This is the `subtype' part of the string returned by + get_content_type(). + """ + ctype = self.get_content_type() + return ctype.split('/')[1] + + def get_default_type(self): + """Return the `default' content type. + + Most messages have a default content type of text/plain, except for + messages that are subparts of multipart/digest containers. Such + subparts have a default content type of message/rfc822. + """ + return self._default_type + + def set_default_type(self, ctype): + """Set the `default' content type. + + ctype should be either "text/plain" or "message/rfc822", although this + is not enforced. The default content type is not stored in the + Content-Type header. + """ + self._default_type = ctype + + def _get_params_preserve(self, failobj, header): + # Like get_params() but preserves the quoting of values. BAW: + # should this be part of the public interface? + missing = object() + value = self.get(header, missing) + if value is missing: + return failobj + params = [] + for p in _parseparam(';' + value): + try: + name, val = p.split('=', 1) + name = name.strip() + val = val.strip() + except ValueError: + # Must have been a bare attribute + name = p.strip() + val = '' + params.append((name, val)) + params = utils.decode_params(params) + return params + + def get_params(self, failobj=None, header='content-type', unquote=True): + """Return the message's Content-Type parameters, as a list. + + The elements of the returned list are 2-tuples of key/value pairs, as + split on the `=' sign. The left hand side of the `=' is the key, + while the right hand side is the value. If there is no `=' sign in + the parameter the value is the empty string. The value is as + described in the get_param() method. + + Optional failobj is the object to return if there is no Content-Type + header. Optional header is the header to search instead of + Content-Type. If unquote is True, the value is unquoted. + """ + missing = object() + params = self._get_params_preserve(missing, header) + if params is missing: + return failobj + if unquote: + return [(k, _unquotevalue(v)) for k, v in params] + else: + return params + + def get_param(self, param, failobj=None, header='content-type', + unquote=True): + """Return the parameter value if found in the Content-Type header. + + Optional failobj is the object to return if there is no Content-Type + header, or the Content-Type header has no such parameter. Optional + header is the header to search instead of Content-Type. + + Parameter keys are always compared case insensitively. The return + value can either be a string, or a 3-tuple if the parameter was RFC + 2231 encoded. When it's a 3-tuple, the elements of the value are of + the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and + LANGUAGE can be None, in which case you should consider VALUE to be + encoded in the us-ascii charset. You can usually ignore LANGUAGE. + + Your application should be prepared to deal with 3-tuple return + values, and can convert the parameter to a Unicode string like so: + + param = msg.get_param('foo') + if isinstance(param, tuple): + param = unicode(param[2], param[0] or 'us-ascii') + + In any case, the parameter value (either the returned string, or the + VALUE item in the 3-tuple) is always unquoted, unless unquote is set + to False. + """ + if not self.has_key(header): + return failobj + for k, v in self._get_params_preserve(failobj, header): + if k.lower() == param.lower(): + if unquote: + return _unquotevalue(v) + else: + return v + return failobj + + def set_param(self, param, value, header='Content-Type', requote=True, + charset=None, language=''): + """Set a parameter in the Content-Type header. + + If the parameter already exists in the header, its value will be + replaced with the new value. + + If header is Content-Type and has not yet been defined for this + message, it will be set to "text/plain" and the new parameter and + value will be appended as per RFC 2045. + + An alternate header can specified in the header argument, and all + parameters will be quoted as necessary unless requote is False. + + If charset is specified, the parameter will be encoded according to RFC + 2231. Optional language specifies the RFC 2231 language, defaulting + to the empty string. Both charset and language should be strings. + """ + if not isinstance(value, tuple) and charset: + value = (charset, language, value) + + if not self.has_key(header) and header.lower() == 'content-type': + ctype = 'text/plain' + else: + ctype = self.get(header) + if not self.get_param(param, header=header): + if not ctype: + ctype = _formatparam(param, value, requote) + else: + ctype = SEMISPACE.join( + [ctype, _formatparam(param, value, requote)]) + else: + ctype = '' + for old_param, old_value in self.get_params(header=header, + unquote=requote): + append_param = '' + if old_param.lower() == param.lower(): + append_param = _formatparam(param, value, requote) + else: + append_param = _formatparam(old_param, old_value, requote) + if not ctype: + ctype = append_param + else: + ctype = SEMISPACE.join([ctype, append_param]) + if ctype <> self.get(header): + del self[header] + self[header] = ctype + + def del_param(self, param, header='content-type', requote=True): + """Remove the given parameter completely from the Content-Type header. + + The header will be re-written in place without the parameter or its + value. All values will be quoted as necessary unless requote is + False. Optional header specifies an alternative to the Content-Type + header. + """ + if not self.has_key(header): + return + new_ctype = '' + for p, v in self.get_params(header=header, unquote=requote): + if p.lower() <> param.lower(): + if not new_ctype: + new_ctype = _formatparam(p, v, requote) + else: + new_ctype = SEMISPACE.join([new_ctype, + _formatparam(p, v, requote)]) + if new_ctype <> self.get(header): + del self[header] + self[header] = new_ctype + + def set_type(self, type, header='Content-Type', requote=True): + """Set the main type and subtype for the Content-Type header. + + type must be a string in the form "maintype/subtype", otherwise a + ValueError is raised. + + This method replaces the Content-Type header, keeping all the + parameters in place. If requote is False, this leaves the existing + header's quoting as is. Otherwise, the parameters will be quoted (the + default). + + An alternative header can be specified in the header argument. When + the Content-Type header is set, we'll always also add a MIME-Version + header. + """ + # BAW: should we be strict? + if not type.count('/') == 1: + raise ValueError + # Set the Content-Type, you get a MIME-Version + if header.lower() == 'content-type': + del self['mime-version'] + self['MIME-Version'] = '1.0' + if not self.has_key(header): + self[header] = type + return + params = self.get_params(header=header, unquote=requote) + del self[header] + self[header] = type + # Skip the first param; it's the old type. + for p, v in params[1:]: + self.set_param(p, v, header, requote) + + def get_filename(self, failobj=None): + """Return the filename associated with the payload if present. + + The filename is extracted from the Content-Disposition header's + `filename' parameter, and it is unquoted. If that header is missing + the `filename' parameter, this method falls back to looking for the + `name' parameter. + """ + missing = object() + filename = self.get_param('filename', missing, 'content-disposition') + if filename is missing: + filename = self.get_param('name', missing, 'content-disposition') + if filename is missing: + return failobj + return utils.collapse_rfc2231_value(filename).strip() + + def get_boundary(self, failobj=None): + """Return the boundary associated with the payload if present. + + The boundary is extracted from the Content-Type header's `boundary' + parameter, and it is unquoted. + """ + missing = object() + boundary = self.get_param('boundary', missing) + if boundary is missing: + return failobj + # RFC 2046 says that boundaries may begin but not end in w/s + return utils.collapse_rfc2231_value(boundary).rstrip() + + def set_boundary(self, boundary): + """Set the boundary parameter in Content-Type to 'boundary'. + + This is subtly different than deleting the Content-Type header and + adding a new one with a new boundary parameter via add_header(). The + main difference is that using the set_boundary() method preserves the + order of the Content-Type header in the original message. + + HeaderParseError is raised if the message has no Content-Type header. + """ + missing = object() + params = self._get_params_preserve(missing, 'content-type') + if params is missing: + # There was no Content-Type header, and we don't know what type + # to set it to, so raise an exception. + raise errors.HeaderParseError('No Content-Type header found') + newparams = [] + foundp = False + for pk, pv in params: + if pk.lower() == 'boundary': + newparams.append(('boundary', '"%s"' % boundary)) + foundp = True + else: + newparams.append((pk, pv)) + if not foundp: + # The original Content-Type header had no boundary attribute. + # Tack one on the end. BAW: should we raise an exception + # instead??? + newparams.append(('boundary', '"%s"' % boundary)) + # Replace the existing Content-Type header with the new value + newheaders = [] + for h, v in self._headers: + if h.lower() == 'content-type': + parts = [] + for k, v in newparams: + if v == '': + parts.append(k) + else: + parts.append('%s=%s' % (k, v)) + newheaders.append((h, SEMISPACE.join(parts))) + + else: + newheaders.append((h, v)) + self._headers = newheaders + + def get_content_charset(self, failobj=None): + """Return the charset parameter of the Content-Type header. + + The returned string is always coerced to lower case. If there is no + Content-Type header, or if that header has no charset parameter, + failobj is returned. + """ + missing = object() + charset = self.get_param('charset', missing) + if charset is missing: + return failobj + if isinstance(charset, tuple): + # RFC 2231 encoded, so decode it, and it better end up as ascii. + pcharset = charset[0] or 'us-ascii' + charset = unicode(charset[2], pcharset).encode('us-ascii') + # RFC 2046, $4.1.2 says charsets are not case sensitive + return charset.lower() + + def get_charsets(self, failobj=None): + """Return a list containing the charset(s) used in this message. + + The returned list of items describes the Content-Type headers' + charset parameter for this message and all the subparts in its + payload. + + Each item will either be a string (the value of the charset parameter + in the Content-Type header of that part) or the value of the + 'failobj' parameter (defaults to None), if the part does not have a + main MIME type of "text", or the charset is not defined. + + The list will contain one string for each part of the message, plus + one for the container message (i.e. self), so that a non-multipart + message will still return a list of length 1. + """ + return [part.get_content_charset(failobj) for part in self.walk()] + + # I.e. def walk(self): ... + from email.Iterators import walk diff --git a/Lib/email/mime/__init__.py b/Lib/email/mime/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py new file mode 100644 index 0000000..6f8bb8a --- /dev/null +++ b/Lib/email/mime/application.py @@ -0,0 +1,36 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Keith Dart +# Contact: email-sig@python.org + +"""Class representing application/* type MIME documents.""" + +__all__ = ["MIMEApplication"] + +from email import encoders +from email.mime.nonmultipart import MIMENonMultipart + + +class MIMEApplication(MIMENonMultipart): + """Class for generating application/* MIME documents.""" + + def __init__(self, _data, _subtype='octet-stream', + _encoder=encoders.encode_base64, **_params): + """Create an application/* type MIME document. + + _data is a string containing the raw applicatoin data. + + _subtype is the MIME content type subtype, defaulting to + 'octet-stream'. + + _encoder is a function which will perform the actual encoding for + transport of the application data, defaulting to base64 encoding. + + Any additional keyword arguments are passed to the base class + constructor, which turns them into parameters on the Content-Type + header. + """ + if _subtype is None: + raise TypeError('Invalid application MIME subtype') + MIMENonMultipart.__init__(self, 'application', _subtype, **_params) + self.set_payload(_data) + _encoder(self) diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py new file mode 100644 index 0000000..c7290c4 --- /dev/null +++ b/Lib/email/mime/audio.py @@ -0,0 +1,73 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Anthony Baxter +# Contact: email-sig@python.org + +"""Class representing audio/* type MIME documents.""" + +__all__ = ['MIMEAudio'] + +import sndhdr + +from cStringIO import StringIO +from email import encoders +from email.mime.nonmultipart import MIMENonMultipart + + + +_sndhdr_MIMEmap = {'au' : 'basic', + 'wav' :'x-wav', + 'aiff':'x-aiff', + 'aifc':'x-aiff', + } + +# There are others in sndhdr that don't have MIME types. :( +# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? +def _whatsnd(data): + """Try to identify a sound file type. + + sndhdr.what() has a pretty cruddy interface, unfortunately. This is why + we re-do it here. It would be easier to reverse engineer the Unix 'file' + command and use the standard 'magic' file, as shipped with a modern Unix. + """ + hdr = data[:512] + fakefile = StringIO(hdr) + for testfn in sndhdr.tests: + res = testfn(hdr, fakefile) + if res is not None: + return _sndhdr_MIMEmap.get(res[0]) + return None + + + +class MIMEAudio(MIMENonMultipart): + """Class for generating audio/* MIME documents.""" + + def __init__(self, _audiodata, _subtype=None, + _encoder=encoders.encode_base64, **_params): + """Create an audio/* type MIME document. + + _audiodata is a string containing the raw audio data. If this data + can be decoded by the standard Python `sndhdr' module, then the + subtype will be automatically included in the Content-Type header. + Otherwise, you can specify the specific audio subtype via the + _subtype parameter. If _subtype is not given, and no subtype can be + guessed, a TypeError is raised. + + _encoder is a function which will perform the actual encoding for + transport of the image data. It takes one argument, which is this + Image instance. It should use get_payload() and set_payload() to + change the payload to the encoded form. It should also add any + Content-Transfer-Encoding or other headers to the message as + necessary. The default encoding is Base64. + + Any additional keyword arguments are passed to the base class + constructor, which turns them into parameters on the Content-Type + header. + """ + if _subtype is None: + _subtype = _whatsnd(_audiodata) + if _subtype is None: + raise TypeError('Could not find audio MIME subtype') + MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) + self.set_payload(_audiodata) + _encoder(self) diff --git a/Lib/email/mime/base.py b/Lib/email/mime/base.py new file mode 100644 index 0000000..ac91925 --- /dev/null +++ b/Lib/email/mime/base.py @@ -0,0 +1,26 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Base class for MIME specializations.""" + +__all__ = ['MIMEBase'] + +from email import message + + + +class MIMEBase(message.Message): + """Base class for MIME specializations.""" + + def __init__(self, _maintype, _subtype, **_params): + """This constructor adds a Content-Type: and a MIME-Version: header. + + The Content-Type: header is taken from the _maintype and _subtype + arguments. Additional parameters for this header are taken from the + keyword arguments. + """ + message.Message.__init__(self) + ctype = '%s/%s' % (_maintype, _subtype) + self.add_header('Content-Type', ctype, **_params) + self['MIME-Version'] = '1.0' diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py new file mode 100644 index 0000000..5563823 --- /dev/null +++ b/Lib/email/mime/image.py @@ -0,0 +1,46 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Class representing image/* type MIME documents.""" + +__all__ = ['MIMEImage'] + +import imghdr + +from email import encoders +from email.mime.nonmultipart import MIMENonMultipart + + + +class MIMEImage(MIMENonMultipart): + """Class for generating image/* type MIME documents.""" + + def __init__(self, _imagedata, _subtype=None, + _encoder=encoders.encode_base64, **_params): + """Create an image/* type MIME document. + + _imagedata is a string containing the raw image data. If this data + can be decoded by the standard Python `imghdr' module, then the + subtype will be automatically included in the Content-Type header. + Otherwise, you can specify the specific image subtype via the _subtype + parameter. + + _encoder is a function which will perform the actual encoding for + transport of the image data. It takes one argument, which is this + Image instance. It should use get_payload() and set_payload() to + change the payload to the encoded form. It should also add any + Content-Transfer-Encoding or other headers to the message as + necessary. The default encoding is Base64. + + Any additional keyword arguments are passed to the base class + constructor, which turns them into parameters on the Content-Type + header. + """ + if _subtype is None: + _subtype = imghdr.what(None, _imagedata) + if _subtype is None: + raise TypeError('Could not guess image MIME subtype') + MIMENonMultipart.__init__(self, 'image', _subtype, **_params) + self.set_payload(_imagedata) + _encoder(self) diff --git a/Lib/email/mime/message.py b/Lib/email/mime/message.py new file mode 100644 index 0000000..275dbfd --- /dev/null +++ b/Lib/email/mime/message.py @@ -0,0 +1,34 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Class representing message/* MIME documents.""" + +__all__ = ['MIMEMessage'] + +from email import message +from email.mime.nonmultipart import MIMENonMultipart + + + +class MIMEMessage(MIMENonMultipart): + """Class representing message/* MIME documents.""" + + def __init__(self, _msg, _subtype='rfc822'): + """Create a message/* type MIME document. + + _msg is a message object and must be an instance of Message, or a + derived class of Message, otherwise a TypeError is raised. + + Optional _subtype defines the subtype of the contained message. The + default is "rfc822" (this is defined by the MIME standard, even though + the term "rfc822" is technically outdated by RFC 2822). + """ + MIMENonMultipart.__init__(self, 'message', _subtype) + if not isinstance(_msg, message.Message): + raise TypeError('Argument is not an instance of Message') + # It's convenient to use this base class method. We need to do it + # this way or we'll get an exception + message.Message.attach(self, _msg) + # And be sure our default type is set correctly + self.set_default_type('message/rfc822') diff --git a/Lib/email/mime/multipart.py b/Lib/email/mime/multipart.py new file mode 100644 index 0000000..5c8c9db --- /dev/null +++ b/Lib/email/mime/multipart.py @@ -0,0 +1,41 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Base class for MIME multipart/* type messages.""" + +__all__ = ['MIMEMultipart'] + +from email.mime.base import MIMEBase + + + +class MIMEMultipart(MIMEBase): + """Base class for MIME multipart/* type messages.""" + + def __init__(self, _subtype='mixed', boundary=None, _subparts=None, + **_params): + """Creates a multipart/* type message. + + By default, creates a multipart/mixed message, with proper + Content-Type and MIME-Version headers. + + _subtype is the subtype of the multipart content type, defaulting to + `mixed'. + + boundary is the multipart boundary string. By default it is + calculated as needed. + + _subparts is a sequence of initial subparts for the payload. It + must be an iterable object, such as a list. You can always + attach new subparts to the message by using the attach() method. + + Additional parameters for the Content-Type header are taken from the + keyword arguments (or passed into the _params argument). + """ + MIMEBase.__init__(self, 'multipart', _subtype, **_params) + if _subparts: + for p in _subparts: + self.attach(p) + if boundary: + self.set_boundary(boundary) diff --git a/Lib/email/mime/nonmultipart.py b/Lib/email/mime/nonmultipart.py new file mode 100644 index 0000000..dd280b5 --- /dev/null +++ b/Lib/email/mime/nonmultipart.py @@ -0,0 +1,26 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Base class for MIME type messages that are not multipart.""" + +__all__ = ['MIMENonMultipart'] + +from email import errors +from email.mime.base import MIMEBase + + + +class MIMENonMultipart(MIMEBase): + """Base class for MIME multipart/* type messages.""" + + __pychecker__ = 'unusednames=payload' + + def attach(self, payload): + # The public API prohibits attaching multiple subparts to MIMEBase + # derived subtypes since none of them are, by definition, of content + # type multipart/* + raise errors.MultipartConversionError( + 'Cannot attach additional subparts to non-multipart/*') + + del __pychecker__ diff --git a/Lib/email/mime/text.py b/Lib/email/mime/text.py new file mode 100644 index 0000000..5747db5 --- /dev/null +++ b/Lib/email/mime/text.py @@ -0,0 +1,30 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Class representing text/* type MIME documents.""" + +__all__ = ['MIMEText'] + +from email.encoders import encode_7or8bit +from email.mime.nonmultipart import MIMENonMultipart + + + +class MIMEText(MIMENonMultipart): + """Class for generating text/* type MIME documents.""" + + def __init__(self, _text, _subtype='plain', _charset='us-ascii'): + """Create a text/* type MIME document. + + _text is the string for this message object. + + _subtype is the MIME sub content type, defaulting to "plain". + + _charset is the character set parameter added to the Content-Type + header. This defaults to "us-ascii". Note that as a side-effect, the + Content-Transfer-Encoding header will also be set. + """ + MIMENonMultipart.__init__(self, 'text', _subtype, + **{'charset': _charset}) + self.set_payload(_text, _charset) diff --git a/Lib/email/parser.py b/Lib/email/parser.py new file mode 100644 index 0000000..2fcaf25 --- /dev/null +++ b/Lib/email/parser.py @@ -0,0 +1,91 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter +# Contact: email-sig@python.org + +"""A parser of RFC 2822 and MIME email messages.""" + +__all__ = ['Parser', 'HeaderParser'] + +import warnings +from cStringIO import StringIO + +from email.feedparser import FeedParser +from email.message import Message + + + +class Parser: + def __init__(self, *args, **kws): + """Parser of RFC 2822 and MIME email messages. + + Creates an in-memory object tree representing the email message, which + can then be manipulated and turned over to a Generator to return the + textual representation of the message. + + The string must be formatted as a block of RFC 2822 headers and header + continuation lines, optionally preceeded by a `Unix-from' header. The + header block is terminated either by the end of the string or by a + blank line. + + _class is the class to instantiate for new message objects when they + must be created. This class must have a constructor that can take + zero arguments. Default is Message.Message. + """ + if len(args) >= 1: + if '_class' in kws: + raise TypeError("Multiple values for keyword arg '_class'") + kws['_class'] = args[0] + if len(args) == 2: + if 'strict' in kws: + raise TypeError("Multiple values for keyword arg 'strict'") + kws['strict'] = args[1] + if len(args) > 2: + raise TypeError('Too many arguments') + if '_class' in kws: + self._class = kws['_class'] + del kws['_class'] + else: + self._class = Message + if 'strict' in kws: + warnings.warn("'strict' argument is deprecated (and ignored)", + DeprecationWarning, 2) + del kws['strict'] + if kws: + raise TypeError('Unexpected keyword arguments') + + def parse(self, fp, headersonly=False): + """Create a message structure from the data in a file. + + Reads all the data from the file and returns the root of the message + structure. Optional headersonly is a flag specifying whether to stop + parsing after reading the headers or not. The default is False, + meaning it parses the entire contents of the file. + """ + feedparser = FeedParser(self._class) + if headersonly: + feedparser._set_headersonly() + while True: + data = fp.read(8192) + if not data: + break + feedparser.feed(data) + return feedparser.close() + + def parsestr(self, text, headersonly=False): + """Create a message structure from a string. + + Returns the root of the message structure. Optional headersonly is a + flag specifying whether to stop parsing after reading the headers or + not. The default is False, meaning it parses the entire contents of + the file. + """ + return self.parse(StringIO(text), headersonly=headersonly) + + + +class HeaderParser(Parser): + def parse(self, fp, headersonly=True): + return Parser.parse(self, fp, True) + + def parsestr(self, text, headersonly=True): + return Parser.parsestr(self, text, True) diff --git a/Lib/email/quopriMIME.py b/Lib/email/quopriMIME.py deleted file mode 100644 index a9b5d49..0000000 --- a/Lib/email/quopriMIME.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (C) 2001-2004 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Quoted-printable content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode US ASCII-like 8-bit data called `quoted-printable'. It is used to -safely encode text that is in a character set similar to the 7-bit US ASCII -character set, but that includes some 8-bit characters that are normally not -allowed in email bodies or headers. - -Quoted-printable is very space-inefficient for encoding binary files; use the -email.base64MIME module for that instead. - -This module provides an interface to encode and decode both headers and bodies -with quoted-printable encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:/From:/Cc: etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character -conversion necessary for proper internationalized headers; it only -does dumb encoding and decoding. To deal with the various line -wrapping issues, use the email.Header module. -""" - -import re -from string import hexdigits -from email.Utils import fix_eols - -CRLF = '\r\n' -NL = '\n' - -# See also Charset.py -MISC_LEN = 7 - -hqre = re.compile(r'[^-a-zA-Z0-9!*+/ ]') -bqre = re.compile(r'[^ !-<>-~\t]') - - - -# Helpers -def header_quopri_check(c): - """Return True if the character should be escaped with header quopri.""" - return bool(hqre.match(c)) - - -def body_quopri_check(c): - """Return True if the character should be escaped with body quopri.""" - return bool(bqre.match(c)) - - -def header_quopri_len(s): - """Return the length of str when it is encoded with header quopri.""" - count = 0 - for c in s: - if hqre.match(c): - count += 3 - else: - count += 1 - return count - - -def body_quopri_len(str): - """Return the length of str when it is encoded with body quopri.""" - count = 0 - for c in str: - if bqre.match(c): - count += 3 - else: - count += 1 - return count - - -def _max_append(L, s, maxlen, extra=''): - if not L: - L.append(s.lstrip()) - elif len(L[-1]) + len(s) <= maxlen: - L[-1] += extra + s - else: - L.append(s.lstrip()) - - -def unquote(s): - """Turn a string in the form =AB to the ASCII character with value 0xab""" - return chr(int(s[1:3], 16)) - - -def quote(c): - return "=%02X" % ord(c) - - - -def header_encode(header, charset="iso-8859-1", keep_eols=False, - maxlinelen=76, eol=NL): - """Encode a single header line with quoted-printable (like) encoding. - - Defined in RFC 2045, this `Q' encoding is similar to quoted-printable, but - used specifically for email header fields to allow charsets with mostly 7 - bit characters (and some 8 bit) to remain more or less readable in non-RFC - 2045 aware mail clients. - - charset names the character set to use to encode the header. It defaults - to iso-8859-1. - - The resulting string will be in the form: - - "=?charset?q?I_f=E2rt_in_your_g=E8n=E8ral_dire=E7tion?\\n - =?charset?q?Silly_=C8nglish_Kn=EEghts?=" - - with each line wrapped safely at, at most, maxlinelen characters (defaults - to 76 characters). If maxlinelen is None, the entire string is encoded in - one chunk with no splitting. - - End-of-line characters (\\r, \\n, \\r\\n) will be automatically converted - to the canonical email line separator \\r\\n unless the keep_eols - parameter is True (the default is False). - - Each line of the header will be terminated in the value of eol, which - defaults to "\\n". Set this to "\\r\\n" if you are using the result of - this function directly in email. - """ - # Return empty headers unchanged - if not header: - return header - - if not keep_eols: - header = fix_eols(header) - - # Quopri encode each line, in encoded chunks no greater than maxlinelen in - # length, after the RFC chrome is added in. - quoted = [] - if maxlinelen is None: - # An obnoxiously large number that's good enough - max_encoded = 100000 - else: - max_encoded = maxlinelen - len(charset) - MISC_LEN - 1 - - for c in header: - # Space may be represented as _ instead of =20 for readability - if c == ' ': - _max_append(quoted, '_', max_encoded) - # These characters can be included verbatim - elif not hqre.match(c): - _max_append(quoted, c, max_encoded) - # Otherwise, replace with hex value like =E2 - else: - _max_append(quoted, "=%02X" % ord(c), max_encoded) - - # Now add the RFC chrome to each encoded chunk and glue the chunks - # together. BAW: should we be able to specify the leading whitespace in - # the joiner? - joiner = eol + ' ' - return joiner.join(['=?%s?q?%s?=' % (charset, line) for line in quoted]) - - - -def encode(body, binary=False, maxlinelen=76, eol=NL): - """Encode with quoted-printable, wrapping at maxlinelen characters. - - If binary is False (the default), end-of-line characters will be converted - to the canonical email end-of-line sequence \\r\\n. Otherwise they will - be left verbatim. - - Each line of encoded text will end with eol, which defaults to "\\n". Set - this to "\\r\\n" if you will be using the result of this function directly - in an email. - - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). Long lines will have the `soft linefeed' quoted-printable - character "=" appended to them, so the decoded text will be identical to - the original text. - """ - if not body: - return body - - if not binary: - body = fix_eols(body) - - # BAW: We're accumulating the body text by string concatenation. That - # can't be very efficient, but I don't have time now to rewrite it. It - # just feels like this algorithm could be more efficient. - encoded_body = '' - lineno = -1 - # Preserve line endings here so we can check later to see an eol needs to - # be added to the output later. - lines = body.splitlines(1) - for line in lines: - # But strip off line-endings for processing this line. - if line.endswith(CRLF): - line = line[:-2] - elif line[-1] in CRLF: - line = line[:-1] - - lineno += 1 - encoded_line = '' - prev = None - linelen = len(line) - # Now we need to examine every character to see if it needs to be - # quopri encoded. BAW: again, string concatenation is inefficient. - for j in range(linelen): - c = line[j] - prev = c - if bqre.match(c): - c = quote(c) - elif j+1 == linelen: - # Check for whitespace at end of line; special case - if c not in ' \t': - encoded_line += c - prev = c - continue - # Check to see to see if the line has reached its maximum length - if len(encoded_line) + len(c) >= maxlinelen: - encoded_body += encoded_line + '=' + eol - encoded_line = '' - encoded_line += c - # Now at end of line.. - if prev and prev in ' \t': - # Special case for whitespace at end of file - if lineno + 1 == len(lines): - prev = quote(prev) - if len(encoded_line) + len(prev) > maxlinelen: - encoded_body += encoded_line + '=' + eol + prev - else: - encoded_body += encoded_line + prev - # Just normal whitespace at end of line - else: - encoded_body += encoded_line + prev + '=' + eol - encoded_line = '' - # Now look at the line we just finished and it has a line ending, we - # need to add eol to the end of the line. - if lines[lineno].endswith(CRLF) or lines[lineno][-1] in CRLF: - encoded_body += encoded_line + eol - else: - encoded_body += encoded_line - encoded_line = '' - return encoded_body - - -# For convenience and backwards compatibility w/ standard base64 module -body_encode = encode -encodestring = encode - - - -# BAW: I'm not sure if the intent was for the signature of this function to be -# the same as base64MIME.decode() or not... -def decode(encoded, eol=NL): - """Decode a quoted-printable string. - - Lines are separated with eol, which defaults to \\n. - """ - if not encoded: - return encoded - # BAW: see comment in encode() above. Again, we're building up the - # decoded string with string concatenation, which could be done much more - # efficiently. - decoded = '' - - for line in encoded.splitlines(): - line = line.rstrip() - if not line: - decoded += eol - continue - - i = 0 - n = len(line) - while i < n: - c = line[i] - if c <> '=': - decoded += c - i += 1 - # Otherwise, c == "=". Are we at the end of the line? If so, add - # a soft line break. - elif i+1 == n: - i += 1 - continue - # Decode if in form =AB - elif i+2 < n and line[i+1] in hexdigits and line[i+2] in hexdigits: - decoded += unquote(line[i:i+3]) - i += 3 - # Otherwise, not in form =AB, pass literally - else: - decoded += c - i += 1 - - if i == n: - decoded += eol - # Special case if original string did not end with eol - if not encoded.endswith(eol) and decoded.endswith(eol): - decoded = decoded[:-1] - return decoded - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode - - - -def _unquote_match(match): - """Turn a match in the form =AB to the ASCII character with value 0xab""" - s = match.group(0) - return unquote(s) - - -# Header decoding is done a bit differently -def header_decode(s): - """Decode a string encoded with RFC 2045 MIME header `Q' encoding. - - This function does not parse a full MIME header value encoded with - quoted-printable (like =?iso-8895-1?q?Hello_World?=) -- please use - the high level email.Header class for that functionality. - """ - s = s.replace('_', ' ') - return re.sub(r'=\w{2}', _unquote_match, s) diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py new file mode 100644 index 0000000..a5658dd --- /dev/null +++ b/Lib/email/quoprimime.py @@ -0,0 +1,336 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Ben Gertzfield +# Contact: email-sig@python.org + +"""Quoted-printable content transfer encoding per RFCs 2045-2047. + +This module handles the content transfer encoding method defined in RFC 2045 +to encode US ASCII-like 8-bit data called `quoted-printable'. It is used to +safely encode text that is in a character set similar to the 7-bit US ASCII +character set, but that includes some 8-bit characters that are normally not +allowed in email bodies or headers. + +Quoted-printable is very space-inefficient for encoding binary files; use the +email.base64MIME module for that instead. + +This module provides an interface to encode and decode both headers and bodies +with quoted-printable encoding. + +RFC 2045 defines a method for including character set information in an +`encoded-word' in a header. This method is commonly used for 8-bit real names +in To:/From:/Cc: etc. fields, as well as Subject: lines. + +This module does not do the line wrapping or end-of-line character +conversion necessary for proper internationalized headers; it only +does dumb encoding and decoding. To deal with the various line +wrapping issues, use the email.Header module. +""" + +__all__ = [ + 'body_decode', + 'body_encode', + 'body_quopri_check', + 'body_quopri_len', + 'decode', + 'decodestring', + 'encode', + 'encodestring', + 'header_decode', + 'header_encode', + 'header_quopri_check', + 'header_quopri_len', + 'quote', + 'unquote', + ] + +import re + +from string import hexdigits +from email.utils import fix_eols + +CRLF = '\r\n' +NL = '\n' + +# See also Charset.py +MISC_LEN = 7 + +hqre = re.compile(r'[^-a-zA-Z0-9!*+/ ]') +bqre = re.compile(r'[^ !-<>-~\t]') + + + +# Helpers +def header_quopri_check(c): + """Return True if the character should be escaped with header quopri.""" + return bool(hqre.match(c)) + + +def body_quopri_check(c): + """Return True if the character should be escaped with body quopri.""" + return bool(bqre.match(c)) + + +def header_quopri_len(s): + """Return the length of str when it is encoded with header quopri.""" + count = 0 + for c in s: + if hqre.match(c): + count += 3 + else: + count += 1 + return count + + +def body_quopri_len(str): + """Return the length of str when it is encoded with body quopri.""" + count = 0 + for c in str: + if bqre.match(c): + count += 3 + else: + count += 1 + return count + + +def _max_append(L, s, maxlen, extra=''): + if not L: + L.append(s.lstrip()) + elif len(L[-1]) + len(s) <= maxlen: + L[-1] += extra + s + else: + L.append(s.lstrip()) + + +def unquote(s): + """Turn a string in the form =AB to the ASCII character with value 0xab""" + return chr(int(s[1:3], 16)) + + +def quote(c): + return "=%02X" % ord(c) + + + +def header_encode(header, charset="iso-8859-1", keep_eols=False, + maxlinelen=76, eol=NL): + """Encode a single header line with quoted-printable (like) encoding. + + Defined in RFC 2045, this `Q' encoding is similar to quoted-printable, but + used specifically for email header fields to allow charsets with mostly 7 + bit characters (and some 8 bit) to remain more or less readable in non-RFC + 2045 aware mail clients. + + charset names the character set to use to encode the header. It defaults + to iso-8859-1. + + The resulting string will be in the form: + + "=?charset?q?I_f=E2rt_in_your_g=E8n=E8ral_dire=E7tion?\\n + =?charset?q?Silly_=C8nglish_Kn=EEghts?=" + + with each line wrapped safely at, at most, maxlinelen characters (defaults + to 76 characters). If maxlinelen is None, the entire string is encoded in + one chunk with no splitting. + + End-of-line characters (\\r, \\n, \\r\\n) will be automatically converted + to the canonical email line separator \\r\\n unless the keep_eols + parameter is True (the default is False). + + Each line of the header will be terminated in the value of eol, which + defaults to "\\n". Set this to "\\r\\n" if you are using the result of + this function directly in email. + """ + # Return empty headers unchanged + if not header: + return header + + if not keep_eols: + header = fix_eols(header) + + # Quopri encode each line, in encoded chunks no greater than maxlinelen in + # length, after the RFC chrome is added in. + quoted = [] + if maxlinelen is None: + # An obnoxiously large number that's good enough + max_encoded = 100000 + else: + max_encoded = maxlinelen - len(charset) - MISC_LEN - 1 + + for c in header: + # Space may be represented as _ instead of =20 for readability + if c == ' ': + _max_append(quoted, '_', max_encoded) + # These characters can be included verbatim + elif not hqre.match(c): + _max_append(quoted, c, max_encoded) + # Otherwise, replace with hex value like =E2 + else: + _max_append(quoted, "=%02X" % ord(c), max_encoded) + + # Now add the RFC chrome to each encoded chunk and glue the chunks + # together. BAW: should we be able to specify the leading whitespace in + # the joiner? + joiner = eol + ' ' + return joiner.join(['=?%s?q?%s?=' % (charset, line) for line in quoted]) + + + +def encode(body, binary=False, maxlinelen=76, eol=NL): + """Encode with quoted-printable, wrapping at maxlinelen characters. + + If binary is False (the default), end-of-line characters will be converted + to the canonical email end-of-line sequence \\r\\n. Otherwise they will + be left verbatim. + + Each line of encoded text will end with eol, which defaults to "\\n". Set + this to "\\r\\n" if you will be using the result of this function directly + in an email. + + Each line will be wrapped at, at most, maxlinelen characters (defaults to + 76 characters). Long lines will have the `soft linefeed' quoted-printable + character "=" appended to them, so the decoded text will be identical to + the original text. + """ + if not body: + return body + + if not binary: + body = fix_eols(body) + + # BAW: We're accumulating the body text by string concatenation. That + # can't be very efficient, but I don't have time now to rewrite it. It + # just feels like this algorithm could be more efficient. + encoded_body = '' + lineno = -1 + # Preserve line endings here so we can check later to see an eol needs to + # be added to the output later. + lines = body.splitlines(1) + for line in lines: + # But strip off line-endings for processing this line. + if line.endswith(CRLF): + line = line[:-2] + elif line[-1] in CRLF: + line = line[:-1] + + lineno += 1 + encoded_line = '' + prev = None + linelen = len(line) + # Now we need to examine every character to see if it needs to be + # quopri encoded. BAW: again, string concatenation is inefficient. + for j in range(linelen): + c = line[j] + prev = c + if bqre.match(c): + c = quote(c) + elif j+1 == linelen: + # Check for whitespace at end of line; special case + if c not in ' \t': + encoded_line += c + prev = c + continue + # Check to see to see if the line has reached its maximum length + if len(encoded_line) + len(c) >= maxlinelen: + encoded_body += encoded_line + '=' + eol + encoded_line = '' + encoded_line += c + # Now at end of line.. + if prev and prev in ' \t': + # Special case for whitespace at end of file + if lineno + 1 == len(lines): + prev = quote(prev) + if len(encoded_line) + len(prev) > maxlinelen: + encoded_body += encoded_line + '=' + eol + prev + else: + encoded_body += encoded_line + prev + # Just normal whitespace at end of line + else: + encoded_body += encoded_line + prev + '=' + eol + encoded_line = '' + # Now look at the line we just finished and it has a line ending, we + # need to add eol to the end of the line. + if lines[lineno].endswith(CRLF) or lines[lineno][-1] in CRLF: + encoded_body += encoded_line + eol + else: + encoded_body += encoded_line + encoded_line = '' + return encoded_body + + +# For convenience and backwards compatibility w/ standard base64 module +body_encode = encode +encodestring = encode + + + +# BAW: I'm not sure if the intent was for the signature of this function to be +# the same as base64MIME.decode() or not... +def decode(encoded, eol=NL): + """Decode a quoted-printable string. + + Lines are separated with eol, which defaults to \\n. + """ + if not encoded: + return encoded + # BAW: see comment in encode() above. Again, we're building up the + # decoded string with string concatenation, which could be done much more + # efficiently. + decoded = '' + + for line in encoded.splitlines(): + line = line.rstrip() + if not line: + decoded += eol + continue + + i = 0 + n = len(line) + while i < n: + c = line[i] + if c <> '=': + decoded += c + i += 1 + # Otherwise, c == "=". Are we at the end of the line? If so, add + # a soft line break. + elif i+1 == n: + i += 1 + continue + # Decode if in form =AB + elif i+2 < n and line[i+1] in hexdigits and line[i+2] in hexdigits: + decoded += unquote(line[i:i+3]) + i += 3 + # Otherwise, not in form =AB, pass literally + else: + decoded += c + i += 1 + + if i == n: + decoded += eol + # Special case if original string did not end with eol + if not encoded.endswith(eol) and decoded.endswith(eol): + decoded = decoded[:-1] + return decoded + + +# For convenience and backwards compatibility w/ standard base64 module +body_decode = decode +decodestring = decode + + + +def _unquote_match(match): + """Turn a match in the form =AB to the ASCII character with value 0xab""" + s = match.group(0) + return unquote(s) + + +# Header decoding is done a bit differently +def header_decode(s): + """Decode a string encoded with RFC 2045 MIME header `Q' encoding. + + This function does not parse a full MIME header value encoded with + quoted-printable (like =?iso-8895-1?q?Hello_World?=) -- please use + the high level email.Header class for that functionality. + """ + s = s.replace('_', ' ') + return re.sub(r'=\w{2}', _unquote_match, s) diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py index 5a42c227..d977693 100644 --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -39,9 +39,6 @@ NL = '\n' EMPTYSTRING = '' SPACE = ' ' -# We don't care about DeprecationWarnings -warnings.filterwarnings('ignore', '', DeprecationWarning, __name__) - def openfile(filename, mode='r'): @@ -87,7 +84,7 @@ class TestMessageAPI(TestEmailBase): charset = Charset('iso-8859-1') msg.set_charset(charset) eq(msg['mime-version'], '1.0') - eq(msg.get_type(), 'text/plain') + eq(msg.get_content_type(), 'text/plain') eq(msg['content-type'], 'text/plain; charset="iso-8859-1"') eq(msg.get_param('charset'), 'iso-8859-1') eq(msg['content-transfer-encoding'], 'quoted-printable') @@ -211,6 +208,19 @@ class TestMessageAPI(TestEmailBase): msg.set_payload('foo') eq(msg.get_payload(decode=True), 'foo') + def test_decode_bogus_uu_payload_quietly(self): + msg = Message() + msg.set_payload('begin 664 foo.txt\n%<W1F=0000H \n \nend\n') + msg['Content-Transfer-Encoding'] = 'x-uuencode' + old_stderr = sys.stderr + try: + sys.stderr = sfp = StringIO() + # We don't care about the payload + msg.get_payload(decode=True) + finally: + sys.stderr = old_stderr + self.assertEqual(sfp.getvalue(), '') + def test_decoded_generator(self): eq = self.assertEqual msg = self._msgobj('msg_07.txt') @@ -893,7 +903,7 @@ class TestMIMEAudio(unittest.TestCase): self._au = MIMEAudio(self._audiodata) def test_guess_minor_type(self): - self.assertEqual(self._au.get_type(), 'audio/basic') + self.assertEqual(self._au.get_content_type(), 'audio/basic') def test_encoding(self): payload = self._au.get_payload() @@ -901,7 +911,7 @@ class TestMIMEAudio(unittest.TestCase): def test_checkSetMinor(self): au = MIMEAudio(self._audiodata, 'fish') - self.assertEqual(au.get_type(), 'audio/fish') + self.assertEqual(au.get_content_type(), 'audio/fish') def test_add_header(self): eq = self.assertEqual @@ -936,7 +946,7 @@ class TestMIMEImage(unittest.TestCase): self._im = MIMEImage(self._imgdata) def test_guess_minor_type(self): - self.assertEqual(self._im.get_type(), 'image/gif') + self.assertEqual(self._im.get_content_type(), 'image/gif') def test_encoding(self): payload = self._im.get_payload() @@ -944,7 +954,7 @@ class TestMIMEImage(unittest.TestCase): def test_checkSetMinor(self): im = MIMEImage(self._imgdata, 'fish') - self.assertEqual(im.get_type(), 'image/fish') + self.assertEqual(im.get_content_type(), 'image/fish') def test_add_header(self): eq = self.assertEqual @@ -976,7 +986,7 @@ class TestMIMEText(unittest.TestCase): def test_types(self): eq = self.assertEqual unless = self.failUnless - eq(self._msg.get_type(), 'text/plain') + eq(self._msg.get_content_type(), 'text/plain') eq(self._msg.get_param('charset'), 'us-ascii') missing = [] unless(self._msg.get_param('foobar', missing) is missing) @@ -1045,7 +1055,7 @@ This is the dingus fish. # tests m = self._msg unless(m.is_multipart()) - eq(m.get_type(), 'multipart/mixed') + eq(m.get_content_type(), 'multipart/mixed') eq(len(m.get_payload()), 2) raises(IndexError, m.get_payload, 2) m0 = m.get_payload(0) @@ -1379,7 +1389,7 @@ class TestNonConformant(TestEmailBase): def test_parse_missing_minor_type(self): eq = self.assertEqual msg = self._msgobj('msg_14.txt') - eq(msg.get_type(), 'text') + eq(msg.get_content_type(), 'text/plain') eq(msg.get_content_maintype(), 'text') eq(msg.get_content_subtype(), 'plain') @@ -1531,7 +1541,7 @@ class TestMIMEMessage(TestEmailBase): m = Message() m['Subject'] = subject r = MIMEMessage(m) - eq(r.get_type(), 'message/rfc822') + eq(r.get_content_type(), 'message/rfc822') payload = r.get_payload() unless(isinstance(payload, list)) eq(len(payload), 1) @@ -1572,7 +1582,7 @@ Here is the body of the message. eq = self.assertEqual unless = self.failUnless msg = self._msgobj('msg_11.txt') - eq(msg.get_type(), 'message/rfc822') + eq(msg.get_content_type(), 'message/rfc822') payload = msg.get_payload() unless(isinstance(payload, list)) eq(len(payload), 1) @@ -1586,12 +1596,12 @@ Here is the body of the message. unless = self.failUnless # msg 16 is a Delivery Status Notification, see RFC 1894 msg = self._msgobj('msg_16.txt') - eq(msg.get_type(), 'multipart/report') + eq(msg.get_content_type(), 'multipart/report') unless(msg.is_multipart()) eq(len(msg.get_payload()), 3) # Subpart 1 is a text/plain, human readable section subpart = msg.get_payload(0) - eq(subpart.get_type(), 'text/plain') + eq(subpart.get_content_type(), 'text/plain') eq(subpart.get_payload(), """\ This report relates to a message you sent with the following header fields: @@ -1611,7 +1621,7 @@ Your message cannot be delivered to the following recipients: # consists of two blocks of headers, represented by two nested Message # objects. subpart = msg.get_payload(1) - eq(subpart.get_type(), 'message/delivery-status') + eq(subpart.get_content_type(), 'message/delivery-status') eq(len(subpart.get_payload()), 2) # message/delivery-status should treat each block as a bunch of # headers, i.e. a bunch of Message objects. @@ -1629,13 +1639,13 @@ Your message cannot be delivered to the following recipients: eq(dsn2.get_param('rfc822', header='final-recipient'), '') # Subpart 3 is the original message subpart = msg.get_payload(2) - eq(subpart.get_type(), 'message/rfc822') + eq(subpart.get_content_type(), 'message/rfc822') payload = subpart.get_payload() unless(isinstance(payload, list)) eq(len(payload), 1) subsubpart = payload[0] unless(isinstance(subsubpart, Message)) - eq(subsubpart.get_type(), 'text/plain') + eq(subsubpart.get_content_type(), 'text/plain') eq(subsubpart['message-id'], '<002001c144a6$8752e060$56104586@oxy.edu>') @@ -1706,16 +1716,16 @@ Two fp.close() container1 = msg.get_payload(0) eq(container1.get_default_type(), 'message/rfc822') - eq(container1.get_type(), None) + eq(container1.get_content_type(), 'message/rfc822') container2 = msg.get_payload(1) eq(container2.get_default_type(), 'message/rfc822') - eq(container2.get_type(), None) + eq(container2.get_content_type(), 'message/rfc822') container1a = container1.get_payload(0) eq(container1a.get_default_type(), 'text/plain') - eq(container1a.get_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') container2a = container2.get_payload(0) eq(container2a.get_default_type(), 'text/plain') - eq(container2a.get_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') def test_default_type_with_explicit_container_type(self): eq = self.assertEqual @@ -1726,16 +1736,16 @@ Two fp.close() container1 = msg.get_payload(0) eq(container1.get_default_type(), 'message/rfc822') - eq(container1.get_type(), 'message/rfc822') + eq(container1.get_content_type(), 'message/rfc822') container2 = msg.get_payload(1) eq(container2.get_default_type(), 'message/rfc822') - eq(container2.get_type(), 'message/rfc822') + eq(container2.get_content_type(), 'message/rfc822') container1a = container1.get_payload(0) eq(container1a.get_default_type(), 'text/plain') - eq(container1a.get_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') container2a = container2.get_payload(0) eq(container2a.get_default_type(), 'text/plain') - eq(container2a.get_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') def test_default_type_non_parsed(self): eq = self.assertEqual @@ -1750,9 +1760,9 @@ Two subpart2 = MIMEMessage(subpart2a) container.attach(subpart1) container.attach(subpart2) - eq(subpart1.get_type(), 'message/rfc822') + eq(subpart1.get_content_type(), 'message/rfc822') eq(subpart1.get_default_type(), 'message/rfc822') - eq(subpart2.get_type(), 'message/rfc822') + eq(subpart2.get_content_type(), 'message/rfc822') eq(subpart2.get_default_type(), 'message/rfc822') neq(container.as_string(0), '''\ Content-Type: multipart/digest; boundary="BOUNDARY" @@ -1784,9 +1794,9 @@ message 2 del subpart1['mime-version'] del subpart2['content-type'] del subpart2['mime-version'] - eq(subpart1.get_type(), None) + eq(subpart1.get_content_type(), 'message/rfc822') eq(subpart1.get_default_type(), 'message/rfc822') - eq(subpart2.get_type(), None) + eq(subpart2.get_content_type(), 'message/rfc822') eq(subpart2.get_default_type(), 'message/rfc822') neq(container.as_string(0), '''\ Content-Type: multipart/digest; boundary="BOUNDARY" @@ -1847,7 +1857,7 @@ class TestIdempotent(TestEmailBase): def test_parse_text_message(self): eq = self.assertEquals msg, text = self._msgobj('msg_01.txt') - eq(msg.get_type(), 'text/plain') + eq(msg.get_content_type(), 'text/plain') eq(msg.get_content_maintype(), 'text') eq(msg.get_content_subtype(), 'plain') eq(msg.get_params()[1], ('charset', 'us-ascii')) @@ -1859,7 +1869,7 @@ class TestIdempotent(TestEmailBase): def test_parse_untyped_message(self): eq = self.assertEquals msg, text = self._msgobj('msg_03.txt') - eq(msg.get_type(), None) + eq(msg.get_content_type(), 'text/plain') eq(msg.get_params(), None) eq(msg.get_param('charset'), None) self._idempotent(msg, text) @@ -1933,7 +1943,7 @@ class TestIdempotent(TestEmailBase): unless = self.failUnless # Get a message object and reset the seek pointer for other tests msg, text = self._msgobj('msg_05.txt') - eq(msg.get_type(), 'multipart/report') + eq(msg.get_content_type(), 'multipart/report') # Test the Content-Type: parameters params = {} for pk, pv in msg.get_params(): @@ -1945,13 +1955,13 @@ class TestIdempotent(TestEmailBase): eq(len(msg.get_payload()), 3) # Make sure the subparts are what we expect msg1 = msg.get_payload(0) - eq(msg1.get_type(), 'text/plain') + eq(msg1.get_content_type(), 'text/plain') eq(msg1.get_payload(), 'Yadda yadda yadda\n') msg2 = msg.get_payload(1) - eq(msg2.get_type(), None) + eq(msg2.get_content_type(), 'text/plain') eq(msg2.get_payload(), 'Yadda yadda yadda\n') msg3 = msg.get_payload(2) - eq(msg3.get_type(), 'message/rfc822') + eq(msg3.get_content_type(), 'message/rfc822') self.failUnless(isinstance(msg3, Message)) payload = msg3.get_payload() unless(isinstance(payload, list)) @@ -1965,7 +1975,7 @@ class TestIdempotent(TestEmailBase): unless = self.failUnless msg, text = self._msgobj('msg_06.txt') # Check some of the outer headers - eq(msg.get_type(), 'message/rfc822') + eq(msg.get_content_type(), 'message/rfc822') # Make sure the payload is a list of exactly one sub-Message, and that # that submessage has a type of text/plain payload = msg.get_payload() @@ -1973,7 +1983,7 @@ class TestIdempotent(TestEmailBase): eq(len(payload), 1) msg1 = payload[0] self.failUnless(isinstance(msg1, Message)) - eq(msg1.get_type(), 'text/plain') + eq(msg1.get_content_type(), 'text/plain') self.failUnless(isinstance(msg1.get_payload(), str)) eq(msg1.get_payload(), '\n') @@ -2058,13 +2068,19 @@ class TestMiscellaneous(TestEmailBase): module = __import__('email') all = module.__all__ all.sort() - self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator', - 'Header', 'Iterators', 'MIMEAudio', 'MIMEBase', - 'MIMEImage', 'MIMEMessage', 'MIMEMultipart', - 'MIMENonMultipart', 'MIMEText', 'Message', - 'Parser', 'Utils', 'base64MIME', - 'message_from_file', 'message_from_string', - 'quopriMIME']) + self.assertEqual(all, [ + # Old names + 'Charset', 'Encoders', 'Errors', 'Generator', + 'Header', 'Iterators', 'MIMEAudio', 'MIMEBase', + 'MIMEImage', 'MIMEMessage', 'MIMEMultipart', + 'MIMENonMultipart', 'MIMEText', 'Message', + 'Parser', 'Utils', 'base64MIME', + # new names + 'base64mime', 'charset', 'encoders', 'errors', 'generator', + 'header', 'iterators', 'message', 'message_from_file', + 'message_from_string', 'mime', 'parser', + 'quopriMIME', 'quoprimime', 'utils', + ]) def test_formatdate(self): now = time.time() @@ -2097,12 +2113,12 @@ class TestMiscellaneous(TestEmailBase): def test_parsedate_no_dayofweek(self): eq = self.assertEqual eq(Utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), - (2003, 2, 25, 13, 47, 26, 0, 1, 0, -28800)) + (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) def test_parsedate_compact_no_dayofweek(self): eq = self.assertEqual eq(Utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), - (2003, 2, 5, 13, 47, 26, 0, 1, 0, -28800)) + (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) def test_parsedate_acceptable_to_time_functions(self): eq = self.assertEqual @@ -2356,7 +2372,7 @@ class TestParsers(TestEmailBase): fp.close() eq(msg['from'], 'ppp-request@zzz.org') eq(msg['to'], 'ppp@zzz.org') - eq(msg.get_type(), 'multipart/mixed') + eq(msg.get_content_type(), 'multipart/mixed') self.failIf(msg.is_multipart()) self.failUnless(isinstance(msg.get_payload(), str)) @@ -2405,10 +2421,10 @@ Here's the message body fp.close() eq(len(msg.get_payload()), 2) part1 = msg.get_payload(0) - eq(part1.get_type(), 'text/plain') + eq(part1.get_content_type(), 'text/plain') eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n') part2 = msg.get_payload(1) - eq(part2.get_type(), 'application/riscos') + eq(part2.get_content_type(), 'application/riscos') def test_multipart_digest_with_extra_mime_headers(self): eq = self.assertEqual @@ -2427,21 +2443,21 @@ Here's the message body eq(msg.is_multipart(), 1) eq(len(msg.get_payload()), 2) part1 = msg.get_payload(0) - eq(part1.get_type(), 'message/rfc822') + eq(part1.get_content_type(), 'message/rfc822') eq(part1.is_multipart(), 1) eq(len(part1.get_payload()), 1) part1a = part1.get_payload(0) eq(part1a.is_multipart(), 0) - eq(part1a.get_type(), 'text/plain') + eq(part1a.get_content_type(), 'text/plain') neq(part1a.get_payload(), 'message 1\n') # next message/rfc822 part2 = msg.get_payload(1) - eq(part2.get_type(), 'message/rfc822') + eq(part2.get_content_type(), 'message/rfc822') eq(part2.is_multipart(), 1) eq(len(part2.get_payload()), 1) part2a = part2.get_payload(0) eq(part2a.is_multipart(), 0) - eq(part2a.get_type(), 'text/plain') + eq(part2a.get_content_type(), 'text/plain') neq(part2a.get_payload(), 'message 2\n') def test_three_lines(self): @@ -2723,6 +2739,11 @@ class TestCharset(unittest.TestCase): c = Charset('fake') eq('hello w\xf6rld', c.body_encode('hello w\xf6rld')) + def test_unicode_charset_name(self): + charset = Charset(u'us-ascii') + self.assertEqual(str(charset), 'us-ascii') + self.assertRaises(Errors.CharsetError, Charset, 'asc\xffii') + # Test multilingual MIME headers. diff --git a/Lib/email/test/test_email_codecs.py b/Lib/email/test/test_email_codecs.py index 159989c..38b7d95 100644 --- a/Lib/email/test/test_email_codecs.py +++ b/Lib/email/test/test_email_codecs.py @@ -10,6 +10,13 @@ from email.Charset import Charset from email.Header import Header, decode_header from email.Message import Message +# We're compatible with Python 2.3, but it doesn't have the built-in Asian +# codecs, so we have to skip all these tests. +try: + unicode('foo', 'euc-jp') +except LookupError: + raise TestSkipped + class TestEmailAsianCodecs(TestEmailBase): diff --git a/Lib/email/test/test_email_codecs_renamed.py b/Lib/email/test/test_email_codecs_renamed.py new file mode 100644 index 0000000..56baccd --- /dev/null +++ b/Lib/email/test/test_email_codecs_renamed.py @@ -0,0 +1,77 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Contact: email-sig@python.org +# email package unit tests for (optional) Asian codecs + +import unittest +from test.test_support import TestSkipped, run_unittest + +from email.test.test_email import TestEmailBase +from email.charset import Charset +from email.header import Header, decode_header +from email.message import Message + +# We're compatible with Python 2.3, but it doesn't have the built-in Asian +# codecs, so we have to skip all these tests. +try: + unicode('foo', 'euc-jp') +except LookupError: + raise TestSkipped + + + +class TestEmailAsianCodecs(TestEmailBase): + def test_japanese_codecs(self): + eq = self.ndiffAssertEqual + j = Charset("euc-jp") + g = Charset("iso-8859-1") + h = Header("Hello World!") + jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa' + ghello = 'Gr\xfc\xdf Gott!' + h.append(jhello, j) + h.append(ghello, g) + # BAW: This used to -- and maybe should -- fold the two iso-8859-1 + # chunks into a single encoded word. However it doesn't violate the + # standard to have them as two encoded chunks and maybe it's + # reasonable <wink> for each .append() call to result in a separate + # encoded word. + eq(h.encode(), """\ +Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?= + =?iso-8859-1?q?Gr=FC=DF?= =?iso-8859-1?q?_Gott!?=""") + eq(decode_header(h.encode()), + [('Hello World!', None), + ('\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'), + ('Gr\xfc\xdf Gott!', 'iso-8859-1')]) + long = 'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9' + h = Header(long, j, header_name="Subject") + # test a very long header + enc = h.encode() + # TK: splitting point may differ by codec design and/or Header encoding + eq(enc , """\ +=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?= + =?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""") + # TK: full decode comparison + eq(h.__unicode__().encode('euc-jp'), long) + + def test_payload_encoding(self): + jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa' + jcode = 'euc-jp' + msg = Message() + msg.set_payload(jhello, jcode) + ustr = unicode(msg.get_payload(), msg.get_content_charset()) + self.assertEqual(jhello, ustr.encode(jcode)) + + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestEmailAsianCodecs)) + return suite + + +def test_main(): + run_unittest(TestEmailAsianCodecs) + + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/Lib/email/test/test_email_renamed.py b/Lib/email/test/test_email_renamed.py new file mode 100644 index 0000000..4ac2ee9 --- /dev/null +++ b/Lib/email/test/test_email_renamed.py @@ -0,0 +1,3078 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Contact: email-sig@python.org +# email package unit tests + +import os +import sys +import time +import base64 +import difflib +import unittest +import warnings +from cStringIO import StringIO + +import email + +from email.charset import Charset +from email.header import Header, decode_header, make_header +from email.parser import Parser, HeaderParser +from email.generator import Generator, DecodedGenerator +from email.message import Message +from email.mime.application import MIMEApplication +from email.mime.audio import MIMEAudio +from email.mime.text import MIMEText +from email.mime.image import MIMEImage +from email.mime.base import MIMEBase +from email.mime.message import MIMEMessage +from email.mime.multipart import MIMEMultipart +from email import utils +from email import errors +from email import encoders +from email import iterators +from email import base64mime +from email import quoprimime + +from test.test_support import findfile, run_unittest +from email.test import __file__ as landmark + + +NL = '\n' +EMPTYSTRING = '' +SPACE = ' ' + + + +def openfile(filename, mode='r'): + path = os.path.join(os.path.dirname(landmark), 'data', filename) + return open(path, mode) + + + +# Base test class +class TestEmailBase(unittest.TestCase): + def ndiffAssertEqual(self, first, second): + """Like failUnlessEqual except use ndiff for readable output.""" + if first <> second: + sfirst = str(first) + ssecond = str(second) + diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines()) + fp = StringIO() + print >> fp, NL, NL.join(diff) + raise self.failureException, fp.getvalue() + + def _msgobj(self, filename): + fp = openfile(findfile(filename)) + try: + msg = email.message_from_file(fp) + finally: + fp.close() + return msg + + + +# Test various aspects of the Message class's API +class TestMessageAPI(TestEmailBase): + def test_get_all(self): + eq = self.assertEqual + msg = self._msgobj('msg_20.txt') + eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org']) + eq(msg.get_all('xx', 'n/a'), 'n/a') + + def test_getset_charset(self): + eq = self.assertEqual + msg = Message() + eq(msg.get_charset(), None) + charset = Charset('iso-8859-1') + msg.set_charset(charset) + eq(msg['mime-version'], '1.0') + eq(msg.get_content_type(), 'text/plain') + eq(msg['content-type'], 'text/plain; charset="iso-8859-1"') + eq(msg.get_param('charset'), 'iso-8859-1') + eq(msg['content-transfer-encoding'], 'quoted-printable') + eq(msg.get_charset().input_charset, 'iso-8859-1') + # Remove the charset + msg.set_charset(None) + eq(msg.get_charset(), None) + eq(msg['content-type'], 'text/plain') + # Try adding a charset when there's already MIME headers present + msg = Message() + msg['MIME-Version'] = '2.0' + msg['Content-Type'] = 'text/x-weird' + msg['Content-Transfer-Encoding'] = 'quinted-puntable' + msg.set_charset(charset) + eq(msg['mime-version'], '2.0') + eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"') + eq(msg['content-transfer-encoding'], 'quinted-puntable') + + def test_set_charset_from_string(self): + eq = self.assertEqual + msg = Message() + msg.set_charset('us-ascii') + eq(msg.get_charset().input_charset, 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + + def test_set_payload_with_charset(self): + msg = Message() + charset = Charset('iso-8859-1') + msg.set_payload('This is a string payload', charset) + self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') + + def test_get_charsets(self): + eq = self.assertEqual + + msg = self._msgobj('msg_08.txt') + charsets = msg.get_charsets() + eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r']) + + msg = self._msgobj('msg_09.txt') + charsets = msg.get_charsets('dingbat') + eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat', + 'koi8-r']) + + msg = self._msgobj('msg_12.txt') + charsets = msg.get_charsets() + eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2', + 'iso-8859-3', 'us-ascii', 'koi8-r']) + + def test_get_filename(self): + eq = self.assertEqual + + msg = self._msgobj('msg_04.txt') + filenames = [p.get_filename() for p in msg.get_payload()] + eq(filenames, ['msg.txt', 'msg.txt']) + + msg = self._msgobj('msg_07.txt') + subpart = msg.get_payload(1) + eq(subpart.get_filename(), 'dingusfish.gif') + + def test_get_filename_with_name_parameter(self): + eq = self.assertEqual + + msg = self._msgobj('msg_44.txt') + filenames = [p.get_filename() for p in msg.get_payload()] + eq(filenames, ['msg.txt', 'msg.txt']) + + def test_get_boundary(self): + eq = self.assertEqual + msg = self._msgobj('msg_07.txt') + # No quotes! + eq(msg.get_boundary(), 'BOUNDARY') + + def test_set_boundary(self): + eq = self.assertEqual + # This one has no existing boundary parameter, but the Content-Type: + # header appears fifth. + msg = self._msgobj('msg_01.txt') + msg.set_boundary('BOUNDARY') + header, value = msg.items()[4] + eq(header.lower(), 'content-type') + eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"') + # This one has a Content-Type: header, with a boundary, stuck in the + # middle of its headers. Make sure the order is preserved; it should + # be fifth. + msg = self._msgobj('msg_04.txt') + msg.set_boundary('BOUNDARY') + header, value = msg.items()[4] + eq(header.lower(), 'content-type') + eq(value, 'multipart/mixed; boundary="BOUNDARY"') + # And this one has no Content-Type: header at all. + msg = self._msgobj('msg_03.txt') + self.assertRaises(errors.HeaderParseError, + msg.set_boundary, 'BOUNDARY') + + def test_get_decoded_payload(self): + eq = self.assertEqual + msg = self._msgobj('msg_10.txt') + # The outer message is a multipart + eq(msg.get_payload(decode=True), None) + # Subpart 1 is 7bit encoded + eq(msg.get_payload(0).get_payload(decode=True), + 'This is a 7bit encoded message.\n') + # Subpart 2 is quopri + eq(msg.get_payload(1).get_payload(decode=True), + '\xa1This is a Quoted Printable encoded message!\n') + # Subpart 3 is base64 + eq(msg.get_payload(2).get_payload(decode=True), + 'This is a Base64 encoded message.') + # Subpart 4 has no Content-Transfer-Encoding: header. + eq(msg.get_payload(3).get_payload(decode=True), + 'This has no Content-Transfer-Encoding: header.\n') + + def test_get_decoded_uu_payload(self): + eq = self.assertEqual + msg = Message() + msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n') + for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): + msg['content-transfer-encoding'] = cte + eq(msg.get_payload(decode=True), 'hello world') + # Now try some bogus data + msg.set_payload('foo') + eq(msg.get_payload(decode=True), 'foo') + + def test_decoded_generator(self): + eq = self.assertEqual + msg = self._msgobj('msg_07.txt') + fp = openfile('msg_17.txt') + try: + text = fp.read() + finally: + fp.close() + s = StringIO() + g = DecodedGenerator(s) + g.flatten(msg) + eq(s.getvalue(), text) + + def test__contains__(self): + msg = Message() + msg['From'] = 'Me' + msg['to'] = 'You' + # Check for case insensitivity + self.failUnless('from' in msg) + self.failUnless('From' in msg) + self.failUnless('FROM' in msg) + self.failUnless('to' in msg) + self.failUnless('To' in msg) + self.failUnless('TO' in msg) + + def test_as_string(self): + eq = self.assertEqual + msg = self._msgobj('msg_01.txt') + fp = openfile('msg_01.txt') + try: + text = fp.read() + finally: + fp.close() + eq(text, msg.as_string()) + fullrepr = str(msg) + lines = fullrepr.split('\n') + self.failUnless(lines[0].startswith('From ')) + eq(text, NL.join(lines[1:])) + + def test_bad_param(self): + msg = email.message_from_string("Content-Type: blarg; baz; boo\n") + self.assertEqual(msg.get_param('baz'), '') + + def test_missing_filename(self): + msg = email.message_from_string("From: foo\n") + self.assertEqual(msg.get_filename(), None) + + def test_bogus_filename(self): + msg = email.message_from_string( + "Content-Disposition: blarg; filename\n") + self.assertEqual(msg.get_filename(), '') + + def test_missing_boundary(self): + msg = email.message_from_string("From: foo\n") + self.assertEqual(msg.get_boundary(), None) + + def test_get_params(self): + eq = self.assertEqual + msg = email.message_from_string( + 'X-Header: foo=one; bar=two; baz=three\n') + eq(msg.get_params(header='x-header'), + [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]) + msg = email.message_from_string( + 'X-Header: foo; bar=one; baz=two\n') + eq(msg.get_params(header='x-header'), + [('foo', ''), ('bar', 'one'), ('baz', 'two')]) + eq(msg.get_params(), None) + msg = email.message_from_string( + 'X-Header: foo; bar="one"; baz=two\n') + eq(msg.get_params(header='x-header'), + [('foo', ''), ('bar', 'one'), ('baz', 'two')]) + + def test_get_param_liberal(self): + msg = Message() + msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"' + self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG') + + def test_get_param(self): + eq = self.assertEqual + msg = email.message_from_string( + "X-Header: foo=one; bar=two; baz=three\n") + eq(msg.get_param('bar', header='x-header'), 'two') + eq(msg.get_param('quuz', header='x-header'), None) + eq(msg.get_param('quuz'), None) + msg = email.message_from_string( + 'X-Header: foo; bar="one"; baz=two\n') + eq(msg.get_param('foo', header='x-header'), '') + eq(msg.get_param('bar', header='x-header'), 'one') + eq(msg.get_param('baz', header='x-header'), 'two') + # XXX: We are not RFC-2045 compliant! We cannot parse: + # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"' + # msg.get_param("weird") + # yet. + + def test_get_param_funky_continuation_lines(self): + msg = self._msgobj('msg_22.txt') + self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG') + + def test_get_param_with_semis_in_quotes(self): + msg = email.message_from_string( + 'Content-Type: image/pjpeg; name="Jim&&Jill"\n') + self.assertEqual(msg.get_param('name'), 'Jim&&Jill') + self.assertEqual(msg.get_param('name', unquote=False), + '"Jim&&Jill"') + + def test_has_key(self): + msg = email.message_from_string('Header: exists') + self.failUnless(msg.has_key('header')) + self.failUnless(msg.has_key('Header')) + self.failUnless(msg.has_key('HEADER')) + self.failIf(msg.has_key('headeri')) + + def test_set_param(self): + eq = self.assertEqual + msg = Message() + msg.set_param('charset', 'iso-2022-jp') + eq(msg.get_param('charset'), 'iso-2022-jp') + msg.set_param('importance', 'high value') + eq(msg.get_param('importance'), 'high value') + eq(msg.get_param('importance', unquote=False), '"high value"') + eq(msg.get_params(), [('text/plain', ''), + ('charset', 'iso-2022-jp'), + ('importance', 'high value')]) + eq(msg.get_params(unquote=False), [('text/plain', ''), + ('charset', '"iso-2022-jp"'), + ('importance', '"high value"')]) + msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy') + eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx') + + def test_del_param(self): + eq = self.assertEqual + msg = self._msgobj('msg_05.txt') + eq(msg.get_params(), + [('multipart/report', ''), ('report-type', 'delivery-status'), + ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) + old_val = msg.get_param("report-type") + msg.del_param("report-type") + eq(msg.get_params(), + [('multipart/report', ''), + ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) + msg.set_param("report-type", old_val) + eq(msg.get_params(), + [('multipart/report', ''), + ('boundary', 'D1690A7AC1.996856090/mail.example.com'), + ('report-type', old_val)]) + + def test_del_param_on_other_header(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', filename='bud.gif') + msg.del_param('filename', 'content-disposition') + self.assertEqual(msg['content-disposition'], 'attachment') + + def test_set_type(self): + eq = self.assertEqual + msg = Message() + self.assertRaises(ValueError, msg.set_type, 'text') + msg.set_type('text/plain') + eq(msg['content-type'], 'text/plain') + msg.set_param('charset', 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + msg.set_type('text/html') + eq(msg['content-type'], 'text/html; charset="us-ascii"') + + def test_set_type_on_other_header(self): + msg = Message() + msg['X-Content-Type'] = 'text/plain' + msg.set_type('application/octet-stream', 'X-Content-Type') + self.assertEqual(msg['x-content-type'], 'application/octet-stream') + + def test_get_content_type_missing(self): + msg = Message() + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_type_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_type(), 'message/rfc822') + + def test_get_content_type_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_type(), + 'message/rfc822') + + def test_get_content_type_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_type(), + 'message/rfc822') + + def test_get_content_type_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_type_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_maintype_missing(self): + msg = Message() + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_maintype_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_maintype_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_subtype_missing(self): + msg = Message() + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_subtype_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_subtype_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_maintype_error(self): + msg = Message() + msg['Content-Type'] = 'no-slash-in-this-string' + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_subtype_error(self): + msg = Message() + msg['Content-Type'] = 'no-slash-in-this-string' + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_replace_header(self): + eq = self.assertEqual + msg = Message() + msg.add_header('First', 'One') + msg.add_header('Second', 'Two') + msg.add_header('Third', 'Three') + eq(msg.keys(), ['First', 'Second', 'Third']) + eq(msg.values(), ['One', 'Two', 'Three']) + msg.replace_header('Second', 'Twenty') + eq(msg.keys(), ['First', 'Second', 'Third']) + eq(msg.values(), ['One', 'Twenty', 'Three']) + msg.add_header('First', 'Eleven') + msg.replace_header('First', 'One Hundred') + eq(msg.keys(), ['First', 'Second', 'Third', 'First']) + eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven']) + self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing') + + def test_broken_base64_payload(self): + x = 'AwDp0P7//y6LwKEAcPa/6Q=9' + msg = Message() + msg['content-type'] = 'audio/x-midi' + msg['content-transfer-encoding'] = 'base64' + msg.set_payload(x) + self.assertEqual(msg.get_payload(decode=True), x) + + + +# Test the email.encoders module +class TestEncoders(unittest.TestCase): + def test_encode_empty_payload(self): + eq = self.assertEqual + msg = Message() + msg.set_charset('us-ascii') + eq(msg['content-transfer-encoding'], '7bit') + + def test_default_cte(self): + eq = self.assertEqual + msg = MIMEText('hello world') + eq(msg['content-transfer-encoding'], '7bit') + + def test_default_cte(self): + eq = self.assertEqual + # With no explicit _charset its us-ascii, and all are 7-bit + msg = MIMEText('hello world') + eq(msg['content-transfer-encoding'], '7bit') + # Similar, but with 8-bit data + msg = MIMEText('hello \xf8 world') + eq(msg['content-transfer-encoding'], '8bit') + # And now with a different charset + msg = MIMEText('hello \xf8 world', _charset='iso-8859-1') + eq(msg['content-transfer-encoding'], 'quoted-printable') + + + +# Test long header wrapping +class TestLongHeaders(TestEmailBase): + def test_split_long_continuation(self): + eq = self.ndiffAssertEqual + msg = email.message_from_string("""\ +Subject: bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text + +test +""") + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +Subject: bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text + +test +""") + + def test_another_long_almost_unsplittable_header(self): + eq = self.ndiffAssertEqual + hstr = """\ +bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text""" + h = Header(hstr, continuation_ws='\t') + eq(h.encode(), """\ +bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text""") + h = Header(hstr) + eq(h.encode(), """\ +bug demonstration + 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 + more text""") + + def test_long_nonstring(self): + eq = self.ndiffAssertEqual + g = Charset("iso-8859-1") + cz = Charset("iso-8859-2") + utf8 = Charset("utf-8") + g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. " + cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. " + utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8") + h = Header(g_head, g, header_name='Subject') + h.append(cz_head, cz) + h.append(utf8_head, utf8) + msg = Message() + msg['Subject'] = h + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?= + =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?= + =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?= + =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?= + =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= + =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?= + =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?= + =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?= + =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?= + =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?= + =?utf-8?b?44Gm44GE44G+44GZ44CC?= + +""") + eq(h.encode(), """\ +=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?= + =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?= + =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?= + =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?= + =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= + =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?= + =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?= + =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?= + =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?= + =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?= + =?utf-8?b?44Gm44GE44G+44GZ44CC?=""") + + def test_long_header_encode(self): + eq = self.ndiffAssertEqual + h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', + header_name='X-Foobar-Spoink-Defrobnit') + eq(h.encode(), '''\ +wasnipoop; giraffes="very-long-necked-animals"; + spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') + + def test_long_header_encode_with_tab_continuation(self): + eq = self.ndiffAssertEqual + h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', + header_name='X-Foobar-Spoink-Defrobnit', + continuation_ws='\t') + eq(h.encode(), '''\ +wasnipoop; giraffes="very-long-necked-animals"; +\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') + + def test_header_splitter(self): + eq = self.ndiffAssertEqual + msg = MIMEText('') + # It'd be great if we could use add_header() here, but that doesn't + # guarantee an order of the parameters. + msg['X-Foobar-Spoink-Defrobnit'] = ( + 'wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"') + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), '''\ +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; +\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey" + +''') + + def test_no_semis_header_splitter(self): + eq = self.ndiffAssertEqual + msg = Message() + msg['From'] = 'test@dom.ain' + msg['References'] = SPACE.join(['<%d@dom.ain>' % i for i in range(10)]) + msg.set_payload('Test') + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +From: test@dom.ain +References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain> +\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain> + +Test""") + + def test_no_split_long_header(self): + eq = self.ndiffAssertEqual + hstr = 'References: ' + 'x' * 80 + h = Header(hstr, continuation_ws='\t') + eq(h.encode(), """\ +References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""") + + def test_splitting_multiple_long_lines(self): + eq = self.ndiffAssertEqual + hstr = """\ +from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +""" + h = Header(hstr, continuation_ws='\t') + eq(h.encode(), """\ +from babylon.socal-raves.org (localhost [127.0.0.1]); +\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; +\tfor <mailman-admin@babylon.socal-raves.org>; +\tSat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); +\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; +\tfor <mailman-admin@babylon.socal-raves.org>; +\tSat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); +\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; +\tfor <mailman-admin@babylon.socal-raves.org>; +\tSat, 2 Feb 2002 17:00:06 -0800 (PST)""") + + def test_splitting_first_line_only_is_long(self): + eq = self.ndiffAssertEqual + hstr = """\ +from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca) +\tby kronos.mems-exchange.org with esmtp (Exim 4.05) +\tid 17k4h5-00034i-00 +\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""" + h = Header(hstr, maxlinelen=78, header_name='Received', + continuation_ws='\t') + eq(h.encode(), """\ +from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] +\thelo=cthulhu.gerg.ca) +\tby kronos.mems-exchange.org with esmtp (Exim 4.05) +\tid 17k4h5-00034i-00 +\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""") + + def test_long_8bit_header(self): + eq = self.ndiffAssertEqual + msg = Message() + h = Header('Britische Regierung gibt', 'iso-8859-1', + header_name='Subject') + h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte') + msg['Subject'] = h + eq(msg.as_string(), """\ +Subject: =?iso-8859-1?q?Britische_Regierung_gibt?= =?iso-8859-1?q?gr=FCnes?= + =?iso-8859-1?q?_Licht_f=FCr_Offshore-Windkraftprojekte?= + +""") + + def test_long_8bit_header_no_charset(self): + eq = self.ndiffAssertEqual + msg = Message() + msg['Reply-To'] = 'Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com>' + eq(msg.as_string(), """\ +Reply-To: Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com> + +""") + + def test_long_to_header(self): + eq = self.ndiffAssertEqual + to = '"Someone Test #A" <someone@eecs.umich.edu>,<someone@eecs.umich.edu>,"Someone Test #B" <someone@umich.edu>, "Someone Test #C" <someone@eecs.umich.edu>, "Someone Test #D" <someone@eecs.umich.edu>' + msg = Message() + msg['To'] = to + eq(msg.as_string(0), '''\ +To: "Someone Test #A" <someone@eecs.umich.edu>, <someone@eecs.umich.edu>, +\t"Someone Test #B" <someone@umich.edu>, +\t"Someone Test #C" <someone@eecs.umich.edu>, +\t"Someone Test #D" <someone@eecs.umich.edu> + +''') + + def test_long_line_after_append(self): + eq = self.ndiffAssertEqual + s = 'This is an example of string which has almost the limit of header length.' + h = Header(s) + h.append('Add another line.') + eq(h.encode(), """\ +This is an example of string which has almost the limit of header length. + Add another line.""") + + def test_shorter_line_with_append(self): + eq = self.ndiffAssertEqual + s = 'This is a shorter line.' + h = Header(s) + h.append('Add another sentence. (Surprise?)') + eq(h.encode(), + 'This is a shorter line. Add another sentence. (Surprise?)') + + def test_long_field_name(self): + eq = self.ndiffAssertEqual + fn = 'X-Very-Very-Very-Long-Header-Name' + gs = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. " + h = Header(gs, 'iso-8859-1', header_name=fn) + # BAW: this seems broken because the first line is too long + eq(h.encode(), """\ +=?iso-8859-1?q?Die_Mieter_treten_hier_?= + =?iso-8859-1?q?ein_werden_mit_einem_Foerderband_komfortabel_den_Korridor_?= + =?iso-8859-1?q?entlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_g?= + =?iso-8859-1?q?egen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""") + + def test_long_received_header(self): + h = 'from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; Wed, 05 Mar 2003 18:10:18 -0700' + msg = Message() + msg['Received-1'] = Header(h, continuation_ws='\t') + msg['Received-2'] = h + self.assertEqual(msg.as_string(), """\ +Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by +\throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP; +\tWed, 05 Mar 2003 18:10:18 -0700 +Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by +\throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP; +\tWed, 05 Mar 2003 18:10:18 -0700 + +""") + + def test_string_headerinst_eq(self): + h = '<15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner\'s message of "Thu, 6 Mar 2003 13:58:21 +0100")' + msg = Message() + msg['Received-1'] = Header(h, header_name='Received-1', + continuation_ws='\t') + msg['Received-2'] = h + self.assertEqual(msg.as_string(), """\ +Received-1: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> +\t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100") +Received-2: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> +\t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100") + +""") + + def test_long_unbreakable_lines_with_continuation(self): + eq = self.ndiffAssertEqual + msg = Message() + t = """\ + iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 + locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp""" + msg['Face-1'] = t + msg['Face-2'] = Header(t, header_name='Face-2') + eq(msg.as_string(), """\ +Face-1: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 +\tlocQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp +Face-2: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 + locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp + +""") + + def test_another_long_multiline_header(self): + eq = self.ndiffAssertEqual + m = '''\ +Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with Microsoft SMTPSVC(5.0.2195.4905); +\tWed, 16 Oct 2002 07:41:11 -0700''' + msg = email.message_from_string(m) + eq(msg.as_string(), '''\ +Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with +\tMicrosoft SMTPSVC(5.0.2195.4905); Wed, 16 Oct 2002 07:41:11 -0700 + +''') + + def test_long_lines_with_different_header(self): + eq = self.ndiffAssertEqual + h = """\ +List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>, + <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>""" + msg = Message() + msg['List'] = h + msg['List'] = Header(h, header_name='List') + eq(msg.as_string(), """\ +List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>, +\t<mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe> +List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>, + <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe> + +""") + + + +# Test mangling of "From " lines in the body of a message +class TestFromMangling(unittest.TestCase): + def setUp(self): + self.msg = Message() + self.msg['From'] = 'aaa@bbb.org' + self.msg.set_payload("""\ +From the desk of A.A.A.: +Blah blah blah +""") + + def test_mangled_from(self): + s = StringIO() + g = Generator(s, mangle_from_=True) + g.flatten(self.msg) + self.assertEqual(s.getvalue(), """\ +From: aaa@bbb.org + +>From the desk of A.A.A.: +Blah blah blah +""") + + def test_dont_mangle_from(self): + s = StringIO() + g = Generator(s, mangle_from_=False) + g.flatten(self.msg) + self.assertEqual(s.getvalue(), """\ +From: aaa@bbb.org + +From the desk of A.A.A.: +Blah blah blah +""") + + + +# Test the basic MIMEAudio class +class TestMIMEAudio(unittest.TestCase): + def setUp(self): + # Make sure we pick up the audiotest.au that lives in email/test/data. + # In Python, there's an audiotest.au living in Lib/test but that isn't + # included in some binary distros that don't include the test + # package. The trailing empty string on the .join() is significant + # since findfile() will do a dirname(). + datadir = os.path.join(os.path.dirname(landmark), 'data', '') + fp = open(findfile('audiotest.au', datadir), 'rb') + try: + self._audiodata = fp.read() + finally: + fp.close() + self._au = MIMEAudio(self._audiodata) + + def test_guess_minor_type(self): + self.assertEqual(self._au.get_content_type(), 'audio/basic') + + def test_encoding(self): + payload = self._au.get_payload() + self.assertEqual(base64.decodestring(payload), self._audiodata) + + def test_checkSetMinor(self): + au = MIMEAudio(self._audiodata, 'fish') + self.assertEqual(au.get_content_type(), 'audio/fish') + + def test_add_header(self): + eq = self.assertEqual + unless = self.failUnless + self._au.add_header('Content-Disposition', 'attachment', + filename='audiotest.au') + eq(self._au['content-disposition'], + 'attachment; filename="audiotest.au"') + eq(self._au.get_params(header='content-disposition'), + [('attachment', ''), ('filename', 'audiotest.au')]) + eq(self._au.get_param('filename', header='content-disposition'), + 'audiotest.au') + missing = [] + eq(self._au.get_param('attachment', header='content-disposition'), '') + unless(self._au.get_param('foo', failobj=missing, + header='content-disposition') is missing) + # Try some missing stuff + unless(self._au.get_param('foobar', missing) is missing) + unless(self._au.get_param('attachment', missing, + header='foobar') is missing) + + + +# Test the basic MIMEImage class +class TestMIMEImage(unittest.TestCase): + def setUp(self): + fp = openfile('PyBanner048.gif') + try: + self._imgdata = fp.read() + finally: + fp.close() + self._im = MIMEImage(self._imgdata) + + def test_guess_minor_type(self): + self.assertEqual(self._im.get_content_type(), 'image/gif') + + def test_encoding(self): + payload = self._im.get_payload() + self.assertEqual(base64.decodestring(payload), self._imgdata) + + def test_checkSetMinor(self): + im = MIMEImage(self._imgdata, 'fish') + self.assertEqual(im.get_content_type(), 'image/fish') + + def test_add_header(self): + eq = self.assertEqual + unless = self.failUnless + self._im.add_header('Content-Disposition', 'attachment', + filename='dingusfish.gif') + eq(self._im['content-disposition'], + 'attachment; filename="dingusfish.gif"') + eq(self._im.get_params(header='content-disposition'), + [('attachment', ''), ('filename', 'dingusfish.gif')]) + eq(self._im.get_param('filename', header='content-disposition'), + 'dingusfish.gif') + missing = [] + eq(self._im.get_param('attachment', header='content-disposition'), '') + unless(self._im.get_param('foo', failobj=missing, + header='content-disposition') is missing) + # Try some missing stuff + unless(self._im.get_param('foobar', missing) is missing) + unless(self._im.get_param('attachment', missing, + header='foobar') is missing) + + + +# Test the basic MIMEApplication class +class TestMIMEApplication(unittest.TestCase): + def test_headers(self): + eq = self.assertEqual + msg = MIMEApplication('\xfa\xfb\xfc\xfd\xfe\xff') + eq(msg.get_content_type(), 'application/octet-stream') + eq(msg['content-transfer-encoding'], 'base64') + + def test_body(self): + eq = self.assertEqual + bytes = '\xfa\xfb\xfc\xfd\xfe\xff' + msg = MIMEApplication(bytes) + eq(msg.get_payload(), '+vv8/f7/') + eq(msg.get_payload(decode=True), bytes) + + + +# Test the basic MIMEText class +class TestMIMEText(unittest.TestCase): + def setUp(self): + self._msg = MIMEText('hello there') + + def test_types(self): + eq = self.assertEqual + unless = self.failUnless + eq(self._msg.get_content_type(), 'text/plain') + eq(self._msg.get_param('charset'), 'us-ascii') + missing = [] + unless(self._msg.get_param('foobar', missing) is missing) + unless(self._msg.get_param('charset', missing, header='foobar') + is missing) + + def test_payload(self): + self.assertEqual(self._msg.get_payload(), 'hello there') + self.failUnless(not self._msg.is_multipart()) + + def test_charset(self): + eq = self.assertEqual + msg = MIMEText('hello there', _charset='us-ascii') + eq(msg.get_charset().input_charset, 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + + + +# Test complicated multipart/* messages +class TestMultipart(TestEmailBase): + def setUp(self): + fp = openfile('PyBanner048.gif') + try: + data = fp.read() + finally: + fp.close() + + container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY') + image = MIMEImage(data, name='dingusfish.gif') + image.add_header('content-disposition', 'attachment', + filename='dingusfish.gif') + intro = MIMEText('''\ +Hi there, + +This is the dingus fish. +''') + container.attach(intro) + container.attach(image) + container['From'] = 'Barry <barry@digicool.com>' + container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>' + container['Subject'] = 'Here is your dingus fish' + + now = 987809702.54848599 + timetuple = time.localtime(now) + if timetuple[-1] == 0: + tzsecs = time.timezone + else: + tzsecs = time.altzone + if tzsecs > 0: + sign = '-' + else: + sign = '+' + tzoffset = ' %s%04d' % (sign, tzsecs / 36) + container['Date'] = time.strftime( + '%a, %d %b %Y %H:%M:%S', + time.localtime(now)) + tzoffset + self._msg = container + self._im = image + self._txt = intro + + def test_hierarchy(self): + # convenience + eq = self.assertEqual + unless = self.failUnless + raises = self.assertRaises + # tests + m = self._msg + unless(m.is_multipart()) + eq(m.get_content_type(), 'multipart/mixed') + eq(len(m.get_payload()), 2) + raises(IndexError, m.get_payload, 2) + m0 = m.get_payload(0) + m1 = m.get_payload(1) + unless(m0 is self._txt) + unless(m1 is self._im) + eq(m.get_payload(), [m0, m1]) + unless(not m0.is_multipart()) + unless(not m1.is_multipart()) + + def test_empty_multipart_idempotent(self): + text = """\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY + + +--BOUNDARY-- +""" + msg = Parser().parsestr(text) + self.ndiffAssertEqual(text, msg.as_string()) + + def test_no_parts_in_a_multipart_with_none_epilogue(self): + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.set_boundary('BOUNDARY') + self.ndiffAssertEqual(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY + +--BOUNDARY--''') + + def test_no_parts_in_a_multipart_with_empty_epilogue(self): + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = '' + outer.epilogue = '' + outer.set_boundary('BOUNDARY') + self.ndiffAssertEqual(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY + +--BOUNDARY-- +''') + + def test_one_part_in_a_multipart(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.set_boundary('BOUNDARY') + msg = MIMEText('hello world') + outer.attach(msg) + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + def test_seq_parts_in_a_multipart_with_empty_preamble(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = '' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_none_preamble(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = None + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_none_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = None + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_empty_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = '' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY-- +''') + + + def test_seq_parts_in_a_multipart_with_nl_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = '\n' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY-- + +''') + + def test_message_external_body(self): + eq = self.assertEqual + msg = self._msgobj('msg_36.txt') + eq(len(msg.get_payload()), 2) + msg1 = msg.get_payload(1) + eq(msg1.get_content_type(), 'multipart/alternative') + eq(len(msg1.get_payload()), 2) + for subpart in msg1.get_payload(): + eq(subpart.get_content_type(), 'message/external-body') + eq(len(subpart.get_payload()), 1) + subsubpart = subpart.get_payload(0) + eq(subsubpart.get_content_type(), 'text/plain') + + def test_double_boundary(self): + # msg_37.txt is a multipart that contains two dash-boundary's in a + # row. Our interpretation of RFC 2046 calls for ignoring the second + # and subsequent boundaries. + msg = self._msgobj('msg_37.txt') + self.assertEqual(len(msg.get_payload()), 3) + + def test_nested_inner_contains_outer_boundary(self): + eq = self.ndiffAssertEqual + # msg_38.txt has an inner part that contains outer boundaries. My + # interpretation of RFC 2046 (based on sections 5.1 and 5.1.2) say + # these are illegal and should be interpreted as unterminated inner + # parts. + msg = self._msgobj('msg_38.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/mixed + multipart/mixed + multipart/alternative + text/plain + text/plain + text/plain + text/plain +""") + + def test_nested_with_same_boundary(self): + eq = self.ndiffAssertEqual + # msg 39.txt is similarly evil in that it's got inner parts that use + # the same boundary as outer parts. Again, I believe the way this is + # parsed is closest to the spirit of RFC 2046 + msg = self._msgobj('msg_39.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/mixed + multipart/mixed + multipart/alternative + application/octet-stream + application/octet-stream + text/plain +""") + + def test_boundary_in_non_multipart(self): + msg = self._msgobj('msg_40.txt') + self.assertEqual(msg.as_string(), '''\ +MIME-Version: 1.0 +Content-Type: text/html; boundary="--961284236552522269" + +----961284236552522269 +Content-Type: text/html; +Content-Transfer-Encoding: 7Bit + +<html></html> + +----961284236552522269-- +''') + + def test_boundary_with_leading_space(self): + eq = self.assertEqual + msg = email.message_from_string('''\ +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=" XXXX" + +-- XXXX +Content-Type: text/plain + + +-- XXXX +Content-Type: text/plain + +-- XXXX-- +''') + self.failUnless(msg.is_multipart()) + eq(msg.get_boundary(), ' XXXX') + eq(len(msg.get_payload()), 2) + + def test_boundary_without_trailing_newline(self): + m = Parser().parsestr("""\ +Content-Type: multipart/mixed; boundary="===============0012394164==" +MIME-Version: 1.0 + +--===============0012394164== +Content-Type: image/file1.jpg +MIME-Version: 1.0 +Content-Transfer-Encoding: base64 + +YXNkZg== +--===============0012394164==--""") + self.assertEquals(m.get_payload(0).get_payload(), 'YXNkZg==') + + + +# Test some badly formatted messages +class TestNonConformant(TestEmailBase): + def test_parse_missing_minor_type(self): + eq = self.assertEqual + msg = self._msgobj('msg_14.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + + def test_same_boundary_inner_outer(self): + unless = self.failUnless + msg = self._msgobj('msg_15.txt') + # XXX We can probably eventually do better + inner = msg.get_payload(0) + unless(hasattr(inner, 'defects')) + self.assertEqual(len(inner.defects), 1) + unless(isinstance(inner.defects[0], + errors.StartBoundaryNotFoundDefect)) + + def test_multipart_no_boundary(self): + unless = self.failUnless + msg = self._msgobj('msg_25.txt') + unless(isinstance(msg.get_payload(), str)) + self.assertEqual(len(msg.defects), 2) + unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) + unless(isinstance(msg.defects[1], + errors.MultipartInvariantViolationDefect)) + + def test_invalid_content_type(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + msg = Message() + # RFC 2045, $5.2 says invalid yields text/plain + msg['Content-Type'] = 'text' + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_content_type(), 'text/plain') + # Clear the old value and try something /really/ invalid + del msg['content-type'] + msg['Content-Type'] = 'foo' + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_content_type(), 'text/plain') + # Still, make sure that the message is idempotently generated + s = StringIO() + g = Generator(s) + g.flatten(msg) + neq(s.getvalue(), 'Content-Type: foo\n\n') + + def test_no_start_boundary(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_31.txt') + eq(msg.get_payload(), """\ +--BOUNDARY +Content-Type: text/plain + +message 1 + +--BOUNDARY +Content-Type: text/plain + +message 2 + +--BOUNDARY-- +""") + + def test_no_separating_blank_line(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_35.txt') + eq(msg.as_string(), """\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: here's something interesting + +counter to RFC 2822, there's no separating newline here +""") + + def test_lying_multipart(self): + unless = self.failUnless + msg = self._msgobj('msg_41.txt') + unless(hasattr(msg, 'defects')) + self.assertEqual(len(msg.defects), 2) + unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) + unless(isinstance(msg.defects[1], + errors.MultipartInvariantViolationDefect)) + + def test_missing_start_boundary(self): + outer = self._msgobj('msg_42.txt') + # The message structure is: + # + # multipart/mixed + # text/plain + # message/rfc822 + # multipart/mixed [*] + # + # [*] This message is missing its start boundary + bad = outer.get_payload(1).get_payload(0) + self.assertEqual(len(bad.defects), 1) + self.failUnless(isinstance(bad.defects[0], + errors.StartBoundaryNotFoundDefect)) + + + +# Test RFC 2047 header encoding and decoding +class TestRFC2047(unittest.TestCase): + def test_rfc2047_multiline(self): + eq = self.assertEqual + s = """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz + foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""" + dh = decode_header(s) + eq(dh, [ + ('Re:', None), + ('r\x8aksm\x9arg\x8cs', 'mac-iceland'), + ('baz foo bar', None), + ('r\x8aksm\x9arg\x8cs', 'mac-iceland')]) + eq(str(make_header(dh)), + """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar + =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""") + + def test_whitespace_eater_unicode(self): + eq = self.assertEqual + s = '=?ISO-8859-1?Q?Andr=E9?= Pirard <pirard@dom.ain>' + dh = decode_header(s) + eq(dh, [('Andr\xe9', 'iso-8859-1'), ('Pirard <pirard@dom.ain>', None)]) + hu = unicode(make_header(dh)).encode('latin-1') + eq(hu, 'Andr\xe9 Pirard <pirard@dom.ain>') + + def test_whitespace_eater_unicode_2(self): + eq = self.assertEqual + s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?=' + dh = decode_header(s) + eq(dh, [('The', None), ('quick brown fox', 'iso-8859-1'), + ('jumped over the', None), ('lazy dog', 'iso-8859-1')]) + hu = make_header(dh).__unicode__() + eq(hu, u'The quick brown fox jumped over the lazy dog') + + + +# Test the MIMEMessage class +class TestMIMEMessage(TestEmailBase): + def setUp(self): + fp = openfile('msg_11.txt') + try: + self._text = fp.read() + finally: + fp.close() + + def test_type_error(self): + self.assertRaises(TypeError, MIMEMessage, 'a plain string') + + def test_valid_argument(self): + eq = self.assertEqual + unless = self.failUnless + subject = 'A sub-message' + m = Message() + m['Subject'] = subject + r = MIMEMessage(m) + eq(r.get_content_type(), 'message/rfc822') + payload = r.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + subpart = payload[0] + unless(subpart is m) + eq(subpart['subject'], subject) + + def test_bad_multipart(self): + eq = self.assertEqual + msg1 = Message() + msg1['Subject'] = 'subpart 1' + msg2 = Message() + msg2['Subject'] = 'subpart 2' + r = MIMEMessage(msg1) + self.assertRaises(errors.MultipartConversionError, r.attach, msg2) + + def test_generate(self): + # First craft the message to be encapsulated + m = Message() + m['Subject'] = 'An enclosed message' + m.set_payload('Here is the body of the message.\n') + r = MIMEMessage(m) + r['Subject'] = 'The enclosing message' + s = StringIO() + g = Generator(s) + g.flatten(r) + self.assertEqual(s.getvalue(), """\ +Content-Type: message/rfc822 +MIME-Version: 1.0 +Subject: The enclosing message + +Subject: An enclosed message + +Here is the body of the message. +""") + + def test_parse_message_rfc822(self): + eq = self.assertEqual + unless = self.failUnless + msg = self._msgobj('msg_11.txt') + eq(msg.get_content_type(), 'message/rfc822') + payload = msg.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + submsg = payload[0] + self.failUnless(isinstance(submsg, Message)) + eq(submsg['subject'], 'An enclosed message') + eq(submsg.get_payload(), 'Here is the body of the message.\n') + + def test_dsn(self): + eq = self.assertEqual + unless = self.failUnless + # msg 16 is a Delivery Status Notification, see RFC 1894 + msg = self._msgobj('msg_16.txt') + eq(msg.get_content_type(), 'multipart/report') + unless(msg.is_multipart()) + eq(len(msg.get_payload()), 3) + # Subpart 1 is a text/plain, human readable section + subpart = msg.get_payload(0) + eq(subpart.get_content_type(), 'text/plain') + eq(subpart.get_payload(), """\ +This report relates to a message you sent with the following header fields: + + Message-id: <002001c144a6$8752e060$56104586@oxy.edu> + Date: Sun, 23 Sep 2001 20:10:55 -0700 + From: "Ian T. Henry" <henryi@oxy.edu> + To: SoCal Raves <scr@socal-raves.org> + Subject: [scr] yeah for Ians!! + +Your message cannot be delivered to the following recipients: + + Recipient address: jangel1@cougar.noc.ucla.edu + Reason: recipient reached disk quota + +""") + # Subpart 2 contains the machine parsable DSN information. It + # consists of two blocks of headers, represented by two nested Message + # objects. + subpart = msg.get_payload(1) + eq(subpart.get_content_type(), 'message/delivery-status') + eq(len(subpart.get_payload()), 2) + # message/delivery-status should treat each block as a bunch of + # headers, i.e. a bunch of Message objects. + dsn1 = subpart.get_payload(0) + unless(isinstance(dsn1, Message)) + eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu') + eq(dsn1.get_param('dns', header='reporting-mta'), '') + # Try a missing one <wink> + eq(dsn1.get_param('nsd', header='reporting-mta'), None) + dsn2 = subpart.get_payload(1) + unless(isinstance(dsn2, Message)) + eq(dsn2['action'], 'failed') + eq(dsn2.get_params(header='original-recipient'), + [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')]) + eq(dsn2.get_param('rfc822', header='final-recipient'), '') + # Subpart 3 is the original message + subpart = msg.get_payload(2) + eq(subpart.get_content_type(), 'message/rfc822') + payload = subpart.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + subsubpart = payload[0] + unless(isinstance(subsubpart, Message)) + eq(subsubpart.get_content_type(), 'text/plain') + eq(subsubpart['message-id'], + '<002001c144a6$8752e060$56104586@oxy.edu>') + + def test_epilogue(self): + eq = self.ndiffAssertEqual + fp = openfile('msg_21.txt') + try: + text = fp.read() + finally: + fp.close() + msg = Message() + msg['From'] = 'aperson@dom.ain' + msg['To'] = 'bperson@dom.ain' + msg['Subject'] = 'Test' + msg.preamble = 'MIME message' + msg.epilogue = 'End of MIME message\n' + msg1 = MIMEText('One') + msg2 = MIMEText('Two') + msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') + msg.attach(msg1) + msg.attach(msg2) + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), text) + + def test_no_nl_preamble(self): + eq = self.ndiffAssertEqual + msg = Message() + msg['From'] = 'aperson@dom.ain' + msg['To'] = 'bperson@dom.ain' + msg['Subject'] = 'Test' + msg.preamble = 'MIME message' + msg.epilogue = '' + msg1 = MIMEText('One') + msg2 = MIMEText('Two') + msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') + msg.attach(msg1) + msg.attach(msg2) + eq(msg.as_string(), """\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: Test +Content-Type: multipart/mixed; boundary="BOUNDARY" + +MIME message +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +One +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +Two +--BOUNDARY-- +""") + + def test_default_type(self): + eq = self.assertEqual + fp = openfile('msg_30.txt') + try: + msg = email.message_from_file(fp) + finally: + fp.close() + container1 = msg.get_payload(0) + eq(container1.get_default_type(), 'message/rfc822') + eq(container1.get_content_type(), 'message/rfc822') + container2 = msg.get_payload(1) + eq(container2.get_default_type(), 'message/rfc822') + eq(container2.get_content_type(), 'message/rfc822') + container1a = container1.get_payload(0) + eq(container1a.get_default_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') + container2a = container2.get_payload(0) + eq(container2a.get_default_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') + + def test_default_type_with_explicit_container_type(self): + eq = self.assertEqual + fp = openfile('msg_28.txt') + try: + msg = email.message_from_file(fp) + finally: + fp.close() + container1 = msg.get_payload(0) + eq(container1.get_default_type(), 'message/rfc822') + eq(container1.get_content_type(), 'message/rfc822') + container2 = msg.get_payload(1) + eq(container2.get_default_type(), 'message/rfc822') + eq(container2.get_content_type(), 'message/rfc822') + container1a = container1.get_payload(0) + eq(container1a.get_default_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') + container2a = container2.get_payload(0) + eq(container2a.get_default_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') + + def test_default_type_non_parsed(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + # Set up container + container = MIMEMultipart('digest', 'BOUNDARY') + container.epilogue = '' + # Set up subparts + subpart1a = MIMEText('message 1\n') + subpart2a = MIMEText('message 2\n') + subpart1 = MIMEMessage(subpart1a) + subpart2 = MIMEMessage(subpart2a) + container.attach(subpart1) + container.attach(subpart2) + eq(subpart1.get_content_type(), 'message/rfc822') + eq(subpart1.get_default_type(), 'message/rfc822') + eq(subpart2.get_content_type(), 'message/rfc822') + eq(subpart2.get_default_type(), 'message/rfc822') + neq(container.as_string(0), '''\ +Content-Type: multipart/digest; boundary="BOUNDARY" +MIME-Version: 1.0 + +--BOUNDARY +Content-Type: message/rfc822 +MIME-Version: 1.0 + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 1 + +--BOUNDARY +Content-Type: message/rfc822 +MIME-Version: 1.0 + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 2 + +--BOUNDARY-- +''') + del subpart1['content-type'] + del subpart1['mime-version'] + del subpart2['content-type'] + del subpart2['mime-version'] + eq(subpart1.get_content_type(), 'message/rfc822') + eq(subpart1.get_default_type(), 'message/rfc822') + eq(subpart2.get_content_type(), 'message/rfc822') + eq(subpart2.get_default_type(), 'message/rfc822') + neq(container.as_string(0), '''\ +Content-Type: multipart/digest; boundary="BOUNDARY" +MIME-Version: 1.0 + +--BOUNDARY + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 1 + +--BOUNDARY + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 2 + +--BOUNDARY-- +''') + + def test_mime_attachments_in_constructor(self): + eq = self.assertEqual + text1 = MIMEText('') + text2 = MIMEText('') + msg = MIMEMultipart(_subparts=(text1, text2)) + eq(len(msg.get_payload()), 2) + eq(msg.get_payload(0), text1) + eq(msg.get_payload(1), text2) + + + +# A general test of parser->model->generator idempotency. IOW, read a message +# in, parse it into a message object tree, then without touching the tree, +# regenerate the plain text. The original text and the transformed text +# should be identical. Note: that we ignore the Unix-From since that may +# contain a changed date. +class TestIdempotent(TestEmailBase): + def _msgobj(self, filename): + fp = openfile(filename) + try: + data = fp.read() + finally: + fp.close() + msg = email.message_from_string(data) + return msg, data + + def _idempotent(self, msg, text): + eq = self.ndiffAssertEqual + s = StringIO() + g = Generator(s, maxheaderlen=0) + g.flatten(msg) + eq(text, s.getvalue()) + + def test_parse_text_message(self): + eq = self.assertEquals + msg, text = self._msgobj('msg_01.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_params()[1], ('charset', 'us-ascii')) + eq(msg.get_param('charset'), 'us-ascii') + eq(msg.preamble, None) + eq(msg.epilogue, None) + self._idempotent(msg, text) + + def test_parse_untyped_message(self): + eq = self.assertEquals + msg, text = self._msgobj('msg_03.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_params(), None) + eq(msg.get_param('charset'), None) + self._idempotent(msg, text) + + def test_simple_multipart(self): + msg, text = self._msgobj('msg_04.txt') + self._idempotent(msg, text) + + def test_MIME_digest(self): + msg, text = self._msgobj('msg_02.txt') + self._idempotent(msg, text) + + def test_long_header(self): + msg, text = self._msgobj('msg_27.txt') + self._idempotent(msg, text) + + def test_MIME_digest_with_part_headers(self): + msg, text = self._msgobj('msg_28.txt') + self._idempotent(msg, text) + + def test_mixed_with_image(self): + msg, text = self._msgobj('msg_06.txt') + self._idempotent(msg, text) + + def test_multipart_report(self): + msg, text = self._msgobj('msg_05.txt') + self._idempotent(msg, text) + + def test_dsn(self): + msg, text = self._msgobj('msg_16.txt') + self._idempotent(msg, text) + + def test_preamble_epilogue(self): + msg, text = self._msgobj('msg_21.txt') + self._idempotent(msg, text) + + def test_multipart_one_part(self): + msg, text = self._msgobj('msg_23.txt') + self._idempotent(msg, text) + + def test_multipart_no_parts(self): + msg, text = self._msgobj('msg_24.txt') + self._idempotent(msg, text) + + def test_no_start_boundary(self): + msg, text = self._msgobj('msg_31.txt') + self._idempotent(msg, text) + + def test_rfc2231_charset(self): + msg, text = self._msgobj('msg_32.txt') + self._idempotent(msg, text) + + def test_more_rfc2231_parameters(self): + msg, text = self._msgobj('msg_33.txt') + self._idempotent(msg, text) + + def test_text_plain_in_a_multipart_digest(self): + msg, text = self._msgobj('msg_34.txt') + self._idempotent(msg, text) + + def test_nested_multipart_mixeds(self): + msg, text = self._msgobj('msg_12a.txt') + self._idempotent(msg, text) + + def test_message_external_body_idempotent(self): + msg, text = self._msgobj('msg_36.txt') + self._idempotent(msg, text) + + def test_content_type(self): + eq = self.assertEquals + unless = self.failUnless + # Get a message object and reset the seek pointer for other tests + msg, text = self._msgobj('msg_05.txt') + eq(msg.get_content_type(), 'multipart/report') + # Test the Content-Type: parameters + params = {} + for pk, pv in msg.get_params(): + params[pk] = pv + eq(params['report-type'], 'delivery-status') + eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com') + eq(msg.preamble, 'This is a MIME-encapsulated message.\n') + eq(msg.epilogue, '\n') + eq(len(msg.get_payload()), 3) + # Make sure the subparts are what we expect + msg1 = msg.get_payload(0) + eq(msg1.get_content_type(), 'text/plain') + eq(msg1.get_payload(), 'Yadda yadda yadda\n') + msg2 = msg.get_payload(1) + eq(msg2.get_content_type(), 'text/plain') + eq(msg2.get_payload(), 'Yadda yadda yadda\n') + msg3 = msg.get_payload(2) + eq(msg3.get_content_type(), 'message/rfc822') + self.failUnless(isinstance(msg3, Message)) + payload = msg3.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + msg4 = payload[0] + unless(isinstance(msg4, Message)) + eq(msg4.get_payload(), 'Yadda yadda yadda\n') + + def test_parser(self): + eq = self.assertEquals + unless = self.failUnless + msg, text = self._msgobj('msg_06.txt') + # Check some of the outer headers + eq(msg.get_content_type(), 'message/rfc822') + # Make sure the payload is a list of exactly one sub-Message, and that + # that submessage has a type of text/plain + payload = msg.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + msg1 = payload[0] + self.failUnless(isinstance(msg1, Message)) + eq(msg1.get_content_type(), 'text/plain') + self.failUnless(isinstance(msg1.get_payload(), str)) + eq(msg1.get_payload(), '\n') + + + +# Test various other bits of the package's functionality +class TestMiscellaneous(TestEmailBase): + def test_message_from_string(self): + fp = openfile('msg_01.txt') + try: + text = fp.read() + finally: + fp.close() + msg = email.message_from_string(text) + s = StringIO() + # Don't wrap/continue long headers since we're trying to test + # idempotency. + g = Generator(s, maxheaderlen=0) + g.flatten(msg) + self.assertEqual(text, s.getvalue()) + + def test_message_from_file(self): + fp = openfile('msg_01.txt') + try: + text = fp.read() + fp.seek(0) + msg = email.message_from_file(fp) + s = StringIO() + # Don't wrap/continue long headers since we're trying to test + # idempotency. + g = Generator(s, maxheaderlen=0) + g.flatten(msg) + self.assertEqual(text, s.getvalue()) + finally: + fp.close() + + def test_message_from_string_with_class(self): + unless = self.failUnless + fp = openfile('msg_01.txt') + try: + text = fp.read() + finally: + fp.close() + # Create a subclass + class MyMessage(Message): + pass + + msg = email.message_from_string(text, MyMessage) + unless(isinstance(msg, MyMessage)) + # Try something more complicated + fp = openfile('msg_02.txt') + try: + text = fp.read() + finally: + fp.close() + msg = email.message_from_string(text, MyMessage) + for subpart in msg.walk(): + unless(isinstance(subpart, MyMessage)) + + def test_message_from_file_with_class(self): + unless = self.failUnless + # Create a subclass + class MyMessage(Message): + pass + + fp = openfile('msg_01.txt') + try: + msg = email.message_from_file(fp, MyMessage) + finally: + fp.close() + unless(isinstance(msg, MyMessage)) + # Try something more complicated + fp = openfile('msg_02.txt') + try: + msg = email.message_from_file(fp, MyMessage) + finally: + fp.close() + for subpart in msg.walk(): + unless(isinstance(subpart, MyMessage)) + + def test__all__(self): + module = __import__('email') + # Can't use sorted() here due to Python 2.3 compatibility + all = module.__all__[:] + all.sort() + self.assertEqual(all, [ + # Old names + 'Charset', 'Encoders', 'Errors', 'Generator', + 'Header', 'Iterators', 'MIMEAudio', 'MIMEBase', + 'MIMEImage', 'MIMEMessage', 'MIMEMultipart', + 'MIMENonMultipart', 'MIMEText', 'Message', + 'Parser', 'Utils', 'base64MIME', + # new names + 'base64mime', 'charset', 'encoders', 'errors', 'generator', + 'header', 'iterators', 'message', 'message_from_file', + 'message_from_string', 'mime', 'parser', + 'quopriMIME', 'quoprimime', 'utils', + ]) + + def test_formatdate(self): + now = time.time() + self.assertEqual(utils.parsedate(utils.formatdate(now))[:6], + time.gmtime(now)[:6]) + + def test_formatdate_localtime(self): + now = time.time() + self.assertEqual( + utils.parsedate(utils.formatdate(now, localtime=True))[:6], + time.localtime(now)[:6]) + + def test_formatdate_usegmt(self): + now = time.time() + self.assertEqual( + utils.formatdate(now, localtime=False), + time.strftime('%a, %d %b %Y %H:%M:%S -0000', time.gmtime(now))) + self.assertEqual( + utils.formatdate(now, localtime=False, usegmt=True), + time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(now))) + + def test_parsedate_none(self): + self.assertEqual(utils.parsedate(''), None) + + def test_parsedate_compact(self): + # The FWS after the comma is optional + self.assertEqual(utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'), + utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800')) + + def test_parsedate_no_dayofweek(self): + eq = self.assertEqual + eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), + (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) + + def test_parsedate_compact_no_dayofweek(self): + eq = self.assertEqual + eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), + (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) + + def test_parsedate_acceptable_to_time_functions(self): + eq = self.assertEqual + timetup = utils.parsedate('5 Feb 2003 13:47:26 -0800') + t = int(time.mktime(timetup)) + eq(time.localtime(t)[:6], timetup[:6]) + eq(int(time.strftime('%Y', timetup)), 2003) + timetup = utils.parsedate_tz('5 Feb 2003 13:47:26 -0800') + t = int(time.mktime(timetup[:9])) + eq(time.localtime(t)[:6], timetup[:6]) + eq(int(time.strftime('%Y', timetup[:9])), 2003) + + def test_parseaddr_empty(self): + self.assertEqual(utils.parseaddr('<>'), ('', '')) + self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '') + + def test_noquote_dump(self): + self.assertEqual( + utils.formataddr(('A Silly Person', 'person@dom.ain')), + 'A Silly Person <person@dom.ain>') + + def test_escape_dump(self): + self.assertEqual( + utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')), + r'"A \(Very\) Silly Person" <person@dom.ain>') + a = r'A \(Special\) Person' + b = 'person@dom.ain' + self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) + + def test_escape_backslashes(self): + self.assertEqual( + utils.formataddr(('Arthur \Backslash\ Foobar', 'person@dom.ain')), + r'"Arthur \\Backslash\\ Foobar" <person@dom.ain>') + a = r'Arthur \Backslash\ Foobar' + b = 'person@dom.ain' + self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) + + def test_name_with_dot(self): + x = 'John X. Doe <jxd@example.com>' + y = '"John X. Doe" <jxd@example.com>' + a, b = ('John X. Doe', 'jxd@example.com') + self.assertEqual(utils.parseaddr(x), (a, b)) + self.assertEqual(utils.parseaddr(y), (a, b)) + # formataddr() quotes the name if there's a dot in it + self.assertEqual(utils.formataddr((a, b)), y) + + def test_quote_dump(self): + self.assertEqual( + utils.formataddr(('A Silly; Person', 'person@dom.ain')), + r'"A Silly; Person" <person@dom.ain>') + + def test_fix_eols(self): + eq = self.assertEqual + eq(utils.fix_eols('hello'), 'hello') + eq(utils.fix_eols('hello\n'), 'hello\r\n') + eq(utils.fix_eols('hello\r'), 'hello\r\n') + eq(utils.fix_eols('hello\r\n'), 'hello\r\n') + eq(utils.fix_eols('hello\n\r'), 'hello\r\n\r\n') + + def test_charset_richcomparisons(self): + eq = self.assertEqual + ne = self.failIfEqual + cset1 = Charset() + cset2 = Charset() + eq(cset1, 'us-ascii') + eq(cset1, 'US-ASCII') + eq(cset1, 'Us-AsCiI') + eq('us-ascii', cset1) + eq('US-ASCII', cset1) + eq('Us-AsCiI', cset1) + ne(cset1, 'usascii') + ne(cset1, 'USASCII') + ne(cset1, 'UsAsCiI') + ne('usascii', cset1) + ne('USASCII', cset1) + ne('UsAsCiI', cset1) + eq(cset1, cset2) + eq(cset2, cset1) + + def test_getaddresses(self): + eq = self.assertEqual + eq(utils.getaddresses(['aperson@dom.ain (Al Person)', + 'Bud Person <bperson@dom.ain>']), + [('Al Person', 'aperson@dom.ain'), + ('Bud Person', 'bperson@dom.ain')]) + + def test_getaddresses_nasty(self): + eq = self.assertEqual + eq(utils.getaddresses(['foo: ;']), [('', '')]) + eq(utils.getaddresses( + ['[]*-- =~$']), + [('', ''), ('', ''), ('', '*--')]) + eq(utils.getaddresses( + ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']), + [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) + + def test_utils_quote_unquote(self): + eq = self.assertEqual + msg = Message() + msg.add_header('content-disposition', 'attachment', + filename='foo\\wacky"name') + eq(msg.get_filename(), 'foo\\wacky"name') + + def test_get_body_encoding_with_bogus_charset(self): + charset = Charset('not a charset') + self.assertEqual(charset.get_body_encoding(), 'base64') + + def test_get_body_encoding_with_uppercase_charset(self): + eq = self.assertEqual + msg = Message() + msg['Content-Type'] = 'text/plain; charset=UTF-8' + eq(msg['content-type'], 'text/plain; charset=UTF-8') + charsets = msg.get_charsets() + eq(len(charsets), 1) + eq(charsets[0], 'utf-8') + charset = Charset(charsets[0]) + eq(charset.get_body_encoding(), 'base64') + msg.set_payload('hello world', charset=charset) + eq(msg.get_payload(), 'aGVsbG8gd29ybGQ=\n') + eq(msg.get_payload(decode=True), 'hello world') + eq(msg['content-transfer-encoding'], 'base64') + # Try another one + msg = Message() + msg['Content-Type'] = 'text/plain; charset="US-ASCII"' + charsets = msg.get_charsets() + eq(len(charsets), 1) + eq(charsets[0], 'us-ascii') + charset = Charset(charsets[0]) + eq(charset.get_body_encoding(), encoders.encode_7or8bit) + msg.set_payload('hello world', charset=charset) + eq(msg.get_payload(), 'hello world') + eq(msg['content-transfer-encoding'], '7bit') + + def test_charsets_case_insensitive(self): + lc = Charset('us-ascii') + uc = Charset('US-ASCII') + self.assertEqual(lc.get_body_encoding(), uc.get_body_encoding()) + + def test_partial_falls_inside_message_delivery_status(self): + eq = self.ndiffAssertEqual + # The Parser interface provides chunks of data to FeedParser in 8192 + # byte gulps. SF bug #1076485 found one of those chunks inside + # message/delivery-status header block, which triggered an + # unreadline() of NeedMoreData. + msg = self._msgobj('msg_43.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/report + text/plain + message/delivery-status + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/rfc822-headers +""") + + + +# Test the iterator/generators +class TestIterators(TestEmailBase): + def test_body_line_iterator(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + # First a simple non-multipart message + msg = self._msgobj('msg_01.txt') + it = iterators.body_line_iterator(msg) + lines = list(it) + eq(len(lines), 6) + neq(EMPTYSTRING.join(lines), msg.get_payload()) + # Now a more complicated multipart + msg = self._msgobj('msg_02.txt') + it = iterators.body_line_iterator(msg) + lines = list(it) + eq(len(lines), 43) + fp = openfile('msg_19.txt') + try: + neq(EMPTYSTRING.join(lines), fp.read()) + finally: + fp.close() + + def test_typed_subpart_iterator(self): + eq = self.assertEqual + msg = self._msgobj('msg_04.txt') + it = iterators.typed_subpart_iterator(msg, 'text') + lines = [] + subparts = 0 + for subpart in it: + subparts += 1 + lines.append(subpart.get_payload()) + eq(subparts, 2) + eq(EMPTYSTRING.join(lines), """\ +a simple kind of mirror +to reflect upon our own +a simple kind of mirror +to reflect upon our own +""") + + def test_typed_subpart_iterator_default_type(self): + eq = self.assertEqual + msg = self._msgobj('msg_03.txt') + it = iterators.typed_subpart_iterator(msg, 'text', 'plain') + lines = [] + subparts = 0 + for subpart in it: + subparts += 1 + lines.append(subpart.get_payload()) + eq(subparts, 1) + eq(EMPTYSTRING.join(lines), """\ + +Hi, + +Do you like this message? + +-Me +""") + + + +class TestParsers(TestEmailBase): + def test_header_parser(self): + eq = self.assertEqual + # Parse only the headers of a complex multipart MIME document + fp = openfile('msg_02.txt') + try: + msg = HeaderParser().parse(fp) + finally: + fp.close() + eq(msg['from'], 'ppp-request@zzz.org') + eq(msg['to'], 'ppp@zzz.org') + eq(msg.get_content_type(), 'multipart/mixed') + self.failIf(msg.is_multipart()) + self.failUnless(isinstance(msg.get_payload(), str)) + + def test_whitespace_continuation(self): + eq = self.assertEqual + # This message contains a line after the Subject: header that has only + # whitespace, but it is not empty! + msg = email.message_from_string("""\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: the next line has a space on it +\x20 +Date: Mon, 8 Apr 2002 15:09:19 -0400 +Message-ID: spam + +Here's the message body +""") + eq(msg['subject'], 'the next line has a space on it\n ') + eq(msg['message-id'], 'spam') + eq(msg.get_payload(), "Here's the message body\n") + + def test_whitespace_continuation_last_header(self): + eq = self.assertEqual + # Like the previous test, but the subject line is the last + # header. + msg = email.message_from_string("""\ +From: aperson@dom.ain +To: bperson@dom.ain +Date: Mon, 8 Apr 2002 15:09:19 -0400 +Message-ID: spam +Subject: the next line has a space on it +\x20 + +Here's the message body +""") + eq(msg['subject'], 'the next line has a space on it\n ') + eq(msg['message-id'], 'spam') + eq(msg.get_payload(), "Here's the message body\n") + + def test_crlf_separation(self): + eq = self.assertEqual + fp = openfile('msg_26.txt', mode='rb') + try: + msg = Parser().parse(fp) + finally: + fp.close() + eq(len(msg.get_payload()), 2) + part1 = msg.get_payload(0) + eq(part1.get_content_type(), 'text/plain') + eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n') + part2 = msg.get_payload(1) + eq(part2.get_content_type(), 'application/riscos') + + def test_multipart_digest_with_extra_mime_headers(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + fp = openfile('msg_28.txt') + try: + msg = email.message_from_file(fp) + finally: + fp.close() + # Structure is: + # multipart/digest + # message/rfc822 + # text/plain + # message/rfc822 + # text/plain + eq(msg.is_multipart(), 1) + eq(len(msg.get_payload()), 2) + part1 = msg.get_payload(0) + eq(part1.get_content_type(), 'message/rfc822') + eq(part1.is_multipart(), 1) + eq(len(part1.get_payload()), 1) + part1a = part1.get_payload(0) + eq(part1a.is_multipart(), 0) + eq(part1a.get_content_type(), 'text/plain') + neq(part1a.get_payload(), 'message 1\n') + # next message/rfc822 + part2 = msg.get_payload(1) + eq(part2.get_content_type(), 'message/rfc822') + eq(part2.is_multipart(), 1) + eq(len(part2.get_payload()), 1) + part2a = part2.get_payload(0) + eq(part2a.is_multipart(), 0) + eq(part2a.get_content_type(), 'text/plain') + neq(part2a.get_payload(), 'message 2\n') + + def test_three_lines(self): + # A bug report by Andrew McNamara + lines = ['From: Andrew Person <aperson@dom.ain', + 'Subject: Test', + 'Date: Tue, 20 Aug 2002 16:43:45 +1000'] + msg = email.message_from_string(NL.join(lines)) + self.assertEqual(msg['date'], 'Tue, 20 Aug 2002 16:43:45 +1000') + + def test_strip_line_feed_and_carriage_return_in_headers(self): + eq = self.assertEqual + # For [ 1002475 ] email message parser doesn't handle \r\n correctly + value1 = 'text' + value2 = 'more text' + m = 'Header: %s\r\nNext-Header: %s\r\n\r\nBody\r\n\r\n' % ( + value1, value2) + msg = email.message_from_string(m) + eq(msg.get('Header'), value1) + eq(msg.get('Next-Header'), value2) + + def test_rfc2822_header_syntax(self): + eq = self.assertEqual + m = '>From: foo\nFrom: bar\n!"#QUX;~: zoo\n\nbody' + msg = email.message_from_string(m) + eq(len(msg.keys()), 3) + keys = msg.keys() + keys.sort() + eq(keys, ['!"#QUX;~', '>From', 'From']) + eq(msg.get_payload(), 'body') + + def test_rfc2822_space_not_allowed_in_header(self): + eq = self.assertEqual + m = '>From foo@example.com 11:25:53\nFrom: bar\n!"#QUX;~: zoo\n\nbody' + msg = email.message_from_string(m) + eq(len(msg.keys()), 0) + + def test_rfc2822_one_character_header(self): + eq = self.assertEqual + m = 'A: first header\nB: second header\nCC: third header\n\nbody' + msg = email.message_from_string(m) + headers = msg.keys() + headers.sort() + eq(headers, ['A', 'B', 'CC']) + eq(msg.get_payload(), 'body') + + + +class TestBase64(unittest.TestCase): + def test_len(self): + eq = self.assertEqual + eq(base64mime.base64_len('hello'), + len(base64mime.encode('hello', eol=''))) + for size in range(15): + if size == 0 : bsize = 0 + elif size <= 3 : bsize = 4 + elif size <= 6 : bsize = 8 + elif size <= 9 : bsize = 12 + elif size <= 12: bsize = 16 + else : bsize = 20 + eq(base64mime.base64_len('x'*size), bsize) + + def test_decode(self): + eq = self.assertEqual + eq(base64mime.decode(''), '') + eq(base64mime.decode('aGVsbG8='), 'hello') + eq(base64mime.decode('aGVsbG8=', 'X'), 'hello') + eq(base64mime.decode('aGVsbG8NCndvcmxk\n', 'X'), 'helloXworld') + + def test_encode(self): + eq = self.assertEqual + eq(base64mime.encode(''), '') + eq(base64mime.encode('hello'), 'aGVsbG8=\n') + # Test the binary flag + eq(base64mime.encode('hello\n'), 'aGVsbG8K\n') + eq(base64mime.encode('hello\n', 0), 'aGVsbG8NCg==\n') + # Test the maxlinelen arg + eq(base64mime.encode('xxxx ' * 20, maxlinelen=40), """\ +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg +eHh4eCB4eHh4IA== +""") + # Test the eol argument + eq(base64mime.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\ +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r +eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r +eHh4eCB4eHh4IA==\r +""") + + def test_header_encode(self): + eq = self.assertEqual + he = base64mime.header_encode + eq(he('hello'), '=?iso-8859-1?b?aGVsbG8=?=') + eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8NCndvcmxk?=') + # Test the charset option + eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?b?aGVsbG8=?=') + # Test the keep_eols flag + eq(he('hello\nworld', keep_eols=True), + '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=') + # Test the maxlinelen argument + eq(he('xxxx ' * 20, maxlinelen=40), """\ +=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?= + =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?= + =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?= + =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?= + =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?= + =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""") + # Test the eol argument + eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\ +=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=\r + =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=\r + =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=\r + =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=\r + =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=\r + =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""") + + + +class TestQuopri(unittest.TestCase): + def setUp(self): + self.hlit = [chr(x) for x in range(ord('a'), ord('z')+1)] + \ + [chr(x) for x in range(ord('A'), ord('Z')+1)] + \ + [chr(x) for x in range(ord('0'), ord('9')+1)] + \ + ['!', '*', '+', '-', '/', ' '] + self.hnon = [chr(x) for x in range(256) if chr(x) not in self.hlit] + assert len(self.hlit) + len(self.hnon) == 256 + self.blit = [chr(x) for x in range(ord(' '), ord('~')+1)] + ['\t'] + self.blit.remove('=') + self.bnon = [chr(x) for x in range(256) if chr(x) not in self.blit] + assert len(self.blit) + len(self.bnon) == 256 + + def test_header_quopri_check(self): + for c in self.hlit: + self.failIf(quoprimime.header_quopri_check(c)) + for c in self.hnon: + self.failUnless(quoprimime.header_quopri_check(c)) + + def test_body_quopri_check(self): + for c in self.blit: + self.failIf(quoprimime.body_quopri_check(c)) + for c in self.bnon: + self.failUnless(quoprimime.body_quopri_check(c)) + + def test_header_quopri_len(self): + eq = self.assertEqual + hql = quoprimime.header_quopri_len + enc = quoprimime.header_encode + for s in ('hello', 'h@e@l@l@o@'): + # Empty charset and no line-endings. 7 == RFC chrome + eq(hql(s), len(enc(s, charset='', eol=''))-7) + for c in self.hlit: + eq(hql(c), 1) + for c in self.hnon: + eq(hql(c), 3) + + def test_body_quopri_len(self): + eq = self.assertEqual + bql = quoprimime.body_quopri_len + for c in self.blit: + eq(bql(c), 1) + for c in self.bnon: + eq(bql(c), 3) + + def test_quote_unquote_idempotent(self): + for x in range(256): + c = chr(x) + self.assertEqual(quoprimime.unquote(quoprimime.quote(c)), c) + + def test_header_encode(self): + eq = self.assertEqual + he = quoprimime.header_encode + eq(he('hello'), '=?iso-8859-1?q?hello?=') + eq(he('hello\nworld'), '=?iso-8859-1?q?hello=0D=0Aworld?=') + # Test the charset option + eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=') + # Test the keep_eols flag + eq(he('hello\nworld', keep_eols=True), '=?iso-8859-1?q?hello=0Aworld?=') + # Test a non-ASCII character + eq(he('hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=') + # Test the maxlinelen argument + eq(he('xxxx ' * 20, maxlinelen=40), """\ +=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?= + =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?= + =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?= + =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?= + =?iso-8859-1?q?x_xxxx_xxxx_?=""") + # Test the eol argument + eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\ +=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=\r + =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=\r + =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=\r + =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=\r + =?iso-8859-1?q?x_xxxx_xxxx_?=""") + + def test_decode(self): + eq = self.assertEqual + eq(quoprimime.decode(''), '') + eq(quoprimime.decode('hello'), 'hello') + eq(quoprimime.decode('hello', 'X'), 'hello') + eq(quoprimime.decode('hello\nworld', 'X'), 'helloXworld') + + def test_encode(self): + eq = self.assertEqual + eq(quoprimime.encode(''), '') + eq(quoprimime.encode('hello'), 'hello') + # Test the binary flag + eq(quoprimime.encode('hello\r\nworld'), 'hello\nworld') + eq(quoprimime.encode('hello\r\nworld', 0), 'hello\nworld') + # Test the maxlinelen arg + eq(quoprimime.encode('xxxx ' * 20, maxlinelen=40), """\ +xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx= + xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx= +x xxxx xxxx xxxx xxxx=20""") + # Test the eol argument + eq(quoprimime.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\ +xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=\r + xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=\r +x xxxx xxxx xxxx xxxx=20""") + eq(quoprimime.encode("""\ +one line + +two line"""), """\ +one line + +two line""") + + + +# Test the Charset class +class TestCharset(unittest.TestCase): + def tearDown(self): + from email import charset as CharsetModule + try: + del CharsetModule.CHARSETS['fake'] + except KeyError: + pass + + def test_idempotent(self): + eq = self.assertEqual + # Make sure us-ascii = no Unicode conversion + c = Charset('us-ascii') + s = 'Hello World!' + sp = c.to_splittable(s) + eq(s, c.from_splittable(sp)) + # test 8-bit idempotency with us-ascii + s = '\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa' + sp = c.to_splittable(s) + eq(s, c.from_splittable(sp)) + + def test_body_encode(self): + eq = self.assertEqual + # Try a charset with QP body encoding + c = Charset('iso-8859-1') + eq('hello w=F6rld', c.body_encode('hello w\xf6rld')) + # Try a charset with Base64 body encoding + c = Charset('utf-8') + eq('aGVsbG8gd29ybGQ=\n', c.body_encode('hello world')) + # Try a charset with None body encoding + c = Charset('us-ascii') + eq('hello world', c.body_encode('hello world')) + # Try the convert argument, where input codec <> output codec + c = Charset('euc-jp') + # With apologies to Tokio Kikuchi ;) + try: + eq('\x1b$B5FCO;~IW\x1b(B', + c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7')) + eq('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', + c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', False)) + except LookupError: + # We probably don't have the Japanese codecs installed + pass + # Testing SF bug #625509, which we have to fake, since there are no + # built-in encodings where the header encoding is QP but the body + # encoding is not. + from email import charset as CharsetModule + CharsetModule.add_charset('fake', CharsetModule.QP, None) + c = Charset('fake') + eq('hello w\xf6rld', c.body_encode('hello w\xf6rld')) + + def test_unicode_charset_name(self): + charset = Charset(u'us-ascii') + self.assertEqual(str(charset), 'us-ascii') + self.assertRaises(errors.CharsetError, Charset, 'asc\xffii') + + + +# Test multilingual MIME headers. +class TestHeader(TestEmailBase): + def test_simple(self): + eq = self.ndiffAssertEqual + h = Header('Hello World!') + eq(h.encode(), 'Hello World!') + h.append(' Goodbye World!') + eq(h.encode(), 'Hello World! Goodbye World!') + + def test_simple_surprise(self): + eq = self.ndiffAssertEqual + h = Header('Hello World!') + eq(h.encode(), 'Hello World!') + h.append('Goodbye World!') + eq(h.encode(), 'Hello World! Goodbye World!') + + def test_header_needs_no_decoding(self): + h = 'no decoding needed' + self.assertEqual(decode_header(h), [(h, None)]) + + def test_long(self): + h = Header("I am the very model of a modern Major-General; I've information vegetable, animal, and mineral; I know the kings of England, and I quote the fights historical from Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical; I understand equations, both the simple and quadratical; about binomial theorem I'm teeming with a lot o' news, with many cheerful facts about the square of the hypotenuse.", + maxlinelen=76) + for l in h.encode(splitchars=' ').split('\n '): + self.failUnless(len(l) <= 76) + + def test_multilingual(self): + eq = self.ndiffAssertEqual + g = Charset("iso-8859-1") + cz = Charset("iso-8859-2") + utf8 = Charset("utf-8") + g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. " + cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. " + utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8") + h = Header(g_head, g) + h.append(cz_head, cz) + h.append(utf8_head, utf8) + enc = h.encode() + eq(enc, """\ +=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderband_ko?= + =?iso-8859-1?q?mfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen_Wan?= + =?iso-8859-1?q?dgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef=F6?= + =?iso-8859-1?q?rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutily?= + =?iso-8859-2?q?_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?= + =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC?= + =?utf-8?b?5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn?= + =?utf-8?b?44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFz?= + =?utf-8?q?_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das_Oder_die_Fl?= + =?utf-8?b?aXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBo+OBpuOBhOOBvuOBmQ==?= + =?utf-8?b?44CC?=""") + eq(decode_header(enc), + [(g_head, "iso-8859-1"), (cz_head, "iso-8859-2"), + (utf8_head, "utf-8")]) + ustr = unicode(h) + eq(ustr.encode('utf-8'), + 'Die Mieter treten hier ein werden mit einem Foerderband ' + 'komfortabel den Korridor entlang, an s\xc3\xbcdl\xc3\xbcndischen ' + 'Wandgem\xc3\xa4lden vorbei, gegen die rotierenden Klingen ' + 'bef\xc3\xb6rdert. Finan\xc4\x8dni metropole se hroutily pod ' + 'tlakem jejich d\xc5\xafvtipu.. \xe6\xad\xa3\xe7\xa2\xba\xe3\x81' + '\xab\xe8\xa8\x80\xe3\x81\x86\xe3\x81\xa8\xe7\xbf\xbb\xe8\xa8\xb3' + '\xe3\x81\xaf\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3' + '\x81\xbe\xe3\x81\x9b\xe3\x82\x93\xe3\x80\x82\xe4\xb8\x80\xe9\x83' + '\xa8\xe3\x81\xaf\xe3\x83\x89\xe3\x82\xa4\xe3\x83\x84\xe8\xaa\x9e' + '\xe3\x81\xa7\xe3\x81\x99\xe3\x81\x8c\xe3\x80\x81\xe3\x81\x82\xe3' + '\x81\xa8\xe3\x81\xaf\xe3\x81\xa7\xe3\x81\x9f\xe3\x82\x89\xe3\x82' + '\x81\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b' + '\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x8cWenn ist das Nunstuck git ' + 'und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt ' + 'gersput.\xe3\x80\x8d\xe3\x81\xa8\xe8\xa8\x80\xe3\x81\xa3\xe3\x81' + '\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82') + # Test make_header() + newh = make_header(decode_header(enc)) + eq(newh, enc) + + def test_header_ctor_default_args(self): + eq = self.ndiffAssertEqual + h = Header() + eq(h, '') + h.append('foo', Charset('iso-8859-1')) + eq(h, '=?iso-8859-1?q?foo?=') + + def test_explicit_maxlinelen(self): + eq = self.ndiffAssertEqual + hstr = 'A very long line that must get split to something other than at the 76th character boundary to test the non-default behavior' + h = Header(hstr) + eq(h.encode(), '''\ +A very long line that must get split to something other than at the 76th + character boundary to test the non-default behavior''') + h = Header(hstr, header_name='Subject') + eq(h.encode(), '''\ +A very long line that must get split to something other than at the + 76th character boundary to test the non-default behavior''') + h = Header(hstr, maxlinelen=1024, header_name='Subject') + eq(h.encode(), hstr) + + def test_us_ascii_header(self): + eq = self.assertEqual + s = 'hello' + x = decode_header(s) + eq(x, [('hello', None)]) + h = make_header(x) + eq(s, h.encode()) + + def test_string_charset(self): + eq = self.assertEqual + h = Header() + h.append('hello', 'iso-8859-1') + eq(h, '=?iso-8859-1?q?hello?=') + +## def test_unicode_error(self): +## raises = self.assertRaises +## raises(UnicodeError, Header, u'[P\xf6stal]', 'us-ascii') +## raises(UnicodeError, Header, '[P\xf6stal]', 'us-ascii') +## h = Header() +## raises(UnicodeError, h.append, u'[P\xf6stal]', 'us-ascii') +## raises(UnicodeError, h.append, '[P\xf6stal]', 'us-ascii') +## raises(UnicodeError, Header, u'\u83ca\u5730\u6642\u592b', 'iso-8859-1') + + def test_utf8_shortest(self): + eq = self.assertEqual + h = Header(u'p\xf6stal', 'utf-8') + eq(h.encode(), '=?utf-8?q?p=C3=B6stal?=') + h = Header(u'\u83ca\u5730\u6642\u592b', 'utf-8') + eq(h.encode(), '=?utf-8?b?6I+K5Zyw5pmC5aSr?=') + + def test_bad_8bit_header(self): + raises = self.assertRaises + eq = self.assertEqual + x = 'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big' + raises(UnicodeError, Header, x) + h = Header() + raises(UnicodeError, h.append, x) + eq(str(Header(x, errors='replace')), x) + h.append(x, errors='replace') + eq(str(h), x) + + def test_encoded_adjacent_nonencoded(self): + eq = self.assertEqual + h = Header() + h.append('hello', 'iso-8859-1') + h.append('world') + s = h.encode() + eq(s, '=?iso-8859-1?q?hello?= world') + h = make_header(decode_header(s)) + eq(h.encode(), s) + + def test_whitespace_eater(self): + eq = self.assertEqual + s = 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztk=?= =?koi8-r?q?=CA?= zz.' + parts = decode_header(s) + eq(parts, [('Subject:', None), ('\xf0\xd2\xcf\xd7\xc5\xd2\xcb\xc1 \xce\xc1 \xc6\xc9\xce\xc1\xcc\xd8\xce\xd9\xca', 'koi8-r'), ('zz.', None)]) + hdr = make_header(parts) + eq(hdr.encode(), + 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztnK?= zz.') + + def test_broken_base64_header(self): + raises = self.assertRaises + s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?=' + raises(errors.HeaderParseError, decode_header, s) + + + +# Test RFC 2231 header parameters (en/de)coding +class TestRFC2231(TestEmailBase): + def test_get_param(self): + eq = self.assertEqual + msg = self._msgobj('msg_29.txt') + eq(msg.get_param('title'), + ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!')) + eq(msg.get_param('title', unquote=False), + ('us-ascii', 'en', '"This is even more ***fun*** isn\'t it!"')) + + def test_set_param(self): + eq = self.assertEqual + msg = Message() + msg.set_param('title', 'This is even more ***fun*** isn\'t it!', + charset='us-ascii') + eq(msg.get_param('title'), + ('us-ascii', '', 'This is even more ***fun*** isn\'t it!')) + msg.set_param('title', 'This is even more ***fun*** isn\'t it!', + charset='us-ascii', language='en') + eq(msg.get_param('title'), + ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!')) + msg = self._msgobj('msg_01.txt') + msg.set_param('title', 'This is even more ***fun*** isn\'t it!', + charset='us-ascii', language='en') + eq(msg.as_string(), """\ +Return-Path: <bbb@zzz.org> +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) +\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 +Content-Type: text/plain; charset=us-ascii; +\ttitle*="us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21" + + +Hi, + +Do you like this message? + +-Me +""") + + def test_del_param(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_01.txt') + msg.set_param('foo', 'bar', charset='us-ascii', language='en') + msg.set_param('title', 'This is even more ***fun*** isn\'t it!', + charset='us-ascii', language='en') + msg.del_param('foo', header='Content-Type') + eq(msg.as_string(), """\ +Return-Path: <bbb@zzz.org> +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) +\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 +Content-Type: text/plain; charset="us-ascii"; +\ttitle*="us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21" + + +Hi, + +Do you like this message? + +-Me +""") + + def test_rfc2231_get_content_charset(self): + eq = self.assertEqual + msg = self._msgobj('msg_32.txt') + eq(msg.get_content_charset(), 'us-ascii') + + def test_rfc2231_no_language_or_charset(self): + m = '''\ +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm" +Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEM; NAME*1=P_nsmail.htm + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_param('NAME'), + (None, None, 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm')) + + def test_rfc2231_no_language_or_charset_in_filename(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0="This%20is%20even%20more%20"; +\tfilename*1="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_no_language_or_charset_in_boundary(self): + m = '''\ +Content-Type: multipart/alternative; +\tboundary*0="This%20is%20even%20more%20"; +\tboundary*1="%2A%2A%2Afun%2A%2A%2A%20"; +\tboundary*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_boundary(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_no_language_or_charset_in_charset(self): + # This is a nonsensical charset value, but tests the code anyway + m = '''\ +Content-Type: text/plain; +\tcharset*0="This%20is%20even%20more%20"; +\tcharset*1="%2A%2A%2Afun%2A%2A%2A%20"; +\tcharset*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_content_charset(), + 'this is even more ***fun*** is it not.pdf') + + def test_rfc2231_unknown_encoding(self): + m = """\ +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename*0=X-UNKNOWN''myfile.txt + +""" + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), 'myfile.txt') + + + +def _testclasses(): + mod = sys.modules[__name__] + return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] + + +def suite(): + suite = unittest.TestSuite() + for testclass in _testclasses(): + suite.addTest(unittest.makeSuite(testclass)) + return suite + + +def test_main(): + for testclass in _testclasses(): + run_unittest(testclass) + + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/Lib/email/utils.py b/Lib/email/utils.py new file mode 100644 index 0000000..250eb19 --- /dev/null +++ b/Lib/email/utils.py @@ -0,0 +1,306 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# Author: Barry Warsaw +# Contact: email-sig@python.org + +"""Miscellaneous utilities.""" + +__all__ = [ + 'collapse_rfc2231_value', + 'decode_params', + 'decode_rfc2231', + 'encode_rfc2231', + 'formataddr', + 'formatdate', + 'getaddresses', + 'make_msgid', + 'parseaddr', + 'parsedate', + 'parsedate_tz', + 'unquote', + ] + +import os +import re +import time +import base64 +import random +import socket +import warnings +from cStringIO import StringIO + +from email._parseaddr import quote +from email._parseaddr import AddressList as _AddressList +from email._parseaddr import mktime_tz + +# We need wormarounds for bugs in these methods in older Pythons (see below) +from email._parseaddr import parsedate as _parsedate +from email._parseaddr import parsedate_tz as _parsedate_tz + +from quopri import decodestring as _qdecode + +# Intrapackage imports +from email.encoders import _bencode, _qencode + +COMMASPACE = ', ' +EMPTYSTRING = '' +UEMPTYSTRING = u'' +CRLF = '\r\n' + +specialsre = re.compile(r'[][\\()<>@,:;".]') +escapesre = re.compile(r'[][\\()"]') + + + +# Helpers + +def _identity(s): + return s + + +def _bdecode(s): + # We can't quite use base64.encodestring() since it tacks on a "courtesy + # newline". Blech! + if not s: + return s + value = base64.decodestring(s) + if not s.endswith('\n') and value.endswith('\n'): + return value[:-1] + return value + + + +def fix_eols(s): + """Replace all line-ending characters with \r\n.""" + # Fix newlines with no preceding carriage return + s = re.sub(r'(?<!\r)\n', CRLF, s) + # Fix carriage returns with no following newline + s = re.sub(r'\r(?!\n)', CRLF, s) + return s + + + +def formataddr(pair): + """The inverse of parseaddr(), this takes a 2-tuple of the form + (realname, email_address) and returns the string value suitable + for an RFC 2822 From, To or Cc header. + + If the first element of pair is false, then the second element is + returned unmodified. + """ + name, address = pair + if name: + quotes = '' + if specialsre.search(name): + quotes = '"' + name = escapesre.sub(r'\\\g<0>', name) + return '%s%s%s <%s>' % (quotes, name, quotes, address) + return address + + + +def getaddresses(fieldvalues): + """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" + all = COMMASPACE.join(fieldvalues) + a = _AddressList(all) + return a.addresslist + + + +ecre = re.compile(r''' + =\? # literal =? + (?P<charset>[^?]*?) # non-greedy up to the next ? is the charset + \? # literal ? + (?P<encoding>[qb]) # either a "q" or a "b", case insensitive + \? # literal ? + (?P<atom>.*?) # non-greedy up to the next ?= is the atom + \?= # literal ?= + ''', re.VERBOSE | re.IGNORECASE) + + + +def formatdate(timeval=None, localtime=False, usegmt=False): + """Returns a date string as specified by RFC 2822, e.g.: + + Fri, 09 Nov 2001 01:08:47 -0000 + + Optional timeval if given is a floating point time value as accepted by + gmtime() and localtime(), otherwise the current time is used. + + Optional localtime is a flag that when True, interprets timeval, and + returns a date relative to the local timezone instead of UTC, properly + taking daylight savings time into account. + + Optional argument usegmt means that the timezone is written out as + an ascii string, not numeric one (so "GMT" instead of "+0000"). This + is needed for HTTP, and is only used when localtime==False. + """ + # Note: we cannot use strftime() because that honors the locale and RFC + # 2822 requires that day and month names be the English abbreviations. + if timeval is None: + timeval = time.time() + if localtime: + now = time.localtime(timeval) + # Calculate timezone offset, based on whether the local zone has + # daylight savings time, and whether DST is in effect. + if time.daylight and now[-1]: + offset = time.altzone + else: + offset = time.timezone + hours, minutes = divmod(abs(offset), 3600) + # Remember offset is in seconds west of UTC, but the timezone is in + # minutes east of UTC, so the signs differ. + if offset > 0: + sign = '-' + else: + sign = '+' + zone = '%s%02d%02d' % (sign, hours, minutes // 60) + else: + now = time.gmtime(timeval) + # Timezone offset is always -0000 + if usegmt: + zone = 'GMT' + else: + zone = '-0000' + return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( + ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]], + now[2], + ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1], + now[0], now[3], now[4], now[5], + zone) + + + +def make_msgid(idstring=None): + """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: + + <20020201195627.33539.96671@nightshade.la.mastaler.com> + + Optional idstring if given is a string used to strengthen the + uniqueness of the message id. + """ + timeval = time.time() + utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) + pid = os.getpid() + randint = random.randrange(100000) + if idstring is None: + idstring = '' + else: + idstring = '.' + idstring + idhost = socket.getfqdn() + msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost) + return msgid + + + +# These functions are in the standalone mimelib version only because they've +# subsequently been fixed in the latest Python versions. We use this to worm +# around broken older Pythons. +def parsedate(data): + if not data: + return None + return _parsedate(data) + + +def parsedate_tz(data): + if not data: + return None + return _parsedate_tz(data) + + +def parseaddr(addr): + addrs = _AddressList(addr).addresslist + if not addrs: + return '', '' + return addrs[0] + + +# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. +def unquote(str): + """Remove quotes from a string.""" + if len(str) > 1: + if str.startswith('"') and str.endswith('"'): + return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') + if str.startswith('<') and str.endswith('>'): + return str[1:-1] + return str + + + +# RFC2231-related functions - parameter encoding and decoding +def decode_rfc2231(s): + """Decode string according to RFC 2231""" + import urllib + parts = s.split("'", 2) + if len(parts) == 1: + return None, None, urllib.unquote(s) + charset, language, s = parts + return charset, language, urllib.unquote(s) + + +def encode_rfc2231(s, charset=None, language=None): + """Encode string according to RFC 2231. + + If neither charset nor language is given, then s is returned as-is. If + charset is given but not language, the string is encoded using the empty + string for language. + """ + import urllib + s = urllib.quote(s, safe='') + if charset is None and language is None: + return s + if language is None: + language = '' + return "%s'%s'%s" % (charset, language, s) + + +rfc2231_continuation = re.compile(r'^(?P<name>\w+)\*((?P<num>[0-9]+)\*?)?$') + +def decode_params(params): + """Decode parameters list according to RFC 2231. + + params is a sequence of 2-tuples containing (content type, string value). + """ + new_params = [] + # maps parameter's name to a list of continuations + rfc2231_params = {} + # params is a sequence of 2-tuples containing (content_type, string value) + name, value = params[0] + new_params.append((name, value)) + # Cycle through each of the rest of the parameters. + for name, value in params[1:]: + value = unquote(value) + mo = rfc2231_continuation.match(name) + if mo: + name, num = mo.group('name', 'num') + if num is not None: + num = int(num) + rfc2231_param1 = rfc2231_params.setdefault(name, []) + rfc2231_param1.append((num, value)) + else: + new_params.append((name, '"%s"' % quote(value))) + if rfc2231_params: + for name, continuations in rfc2231_params.items(): + value = [] + # Sort by number + continuations.sort() + # And now append all values in num order + for num, continuation in continuations: + value.append(continuation) + charset, language, value = decode_rfc2231(EMPTYSTRING.join(value)) + new_params.append( + (name, (charset, language, '"%s"' % quote(value)))) + return new_params + +def collapse_rfc2231_value(value, errors='replace', + fallback_charset='us-ascii'): + if isinstance(value, tuple): + rawval = unquote(value[2]) + charset = value[0] or 'us-ascii' + try: + return unicode(rawval, charset, errors) + except LookupError: + # XXX charset is unknown to Python. + return unicode(rawval, fallback_charset, errors) + else: + return unquote(value) diff --git a/Lib/encodings/big5.py b/Lib/encodings/big5.py index d56aa1b..7adeb0e 100644 --- a/Lib/encodings/big5.py +++ b/Lib/encodings/big5.py @@ -2,10 +2,10 @@ # big5.py: Python Unicode Codec for BIG5 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: big5.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_tw, codecs +import _multibytecodec as mbc codec = _codecs_tw.getcodec('big5') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='big5', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/big5hkscs.py b/Lib/encodings/big5hkscs.py index 443997f..350df37 100644 --- a/Lib/encodings/big5hkscs.py +++ b/Lib/encodings/big5hkscs.py @@ -2,10 +2,10 @@ # big5hkscs.py: Python Unicode Codec for BIG5HKSCS # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: big5hkscs.py,v 1.1 2004/06/29 05:14:27 perky Exp $ # import _codecs_hk, codecs +import _multibytecodec as mbc codec = _codecs_hk.getcodec('big5hkscs') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='big5hkscs', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/cp932.py b/Lib/encodings/cp932.py index 38937f5..e01f59b 100644 --- a/Lib/encodings/cp932.py +++ b/Lib/encodings/cp932.py @@ -2,10 +2,10 @@ # cp932.py: Python Unicode Codec for CP932 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: cp932.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('cp932') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='cp932', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/cp949.py b/Lib/encodings/cp949.py index 0f3c847..627c871 100644 --- a/Lib/encodings/cp949.py +++ b/Lib/encodings/cp949.py @@ -2,10 +2,10 @@ # cp949.py: Python Unicode Codec for CP949 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: cp949.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_kr, codecs +import _multibytecodec as mbc codec = _codecs_kr.getcodec('cp949') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='cp949', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/cp950.py b/Lib/encodings/cp950.py index dab3e28..39eec5e 100644 --- a/Lib/encodings/cp950.py +++ b/Lib/encodings/cp950.py @@ -2,10 +2,10 @@ # cp950.py: Python Unicode Codec for CP950 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: cp950.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_tw, codecs +import _multibytecodec as mbc codec = _codecs_tw.getcodec('cp950') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='cp950', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/euc_jis_2004.py b/Lib/encodings/euc_jis_2004.py index 02d55ca..72b87aea 100644 --- a/Lib/encodings/euc_jis_2004.py +++ b/Lib/encodings/euc_jis_2004.py @@ -2,10 +2,10 @@ # euc_jis_2004.py: Python Unicode Codec for EUC_JIS_2004 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: euc_jis_2004.py,v 1.1 2004/07/07 16:18:25 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('euc_jis_2004') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='euc_jis_2004', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/euc_jisx0213.py b/Lib/encodings/euc_jisx0213.py index 30f173e..cc47d04 100644 --- a/Lib/encodings/euc_jisx0213.py +++ b/Lib/encodings/euc_jisx0213.py @@ -2,10 +2,10 @@ # euc_jisx0213.py: Python Unicode Codec for EUC_JISX0213 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: euc_jisx0213.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('euc_jisx0213') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='euc_jisx0213', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/euc_jp.py b/Lib/encodings/euc_jp.py index a3947a3..7bcbe41 100644 --- a/Lib/encodings/euc_jp.py +++ b/Lib/encodings/euc_jp.py @@ -2,10 +2,10 @@ # euc_jp.py: Python Unicode Codec for EUC_JP # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: euc_jp.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('euc_jp') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='euc_jp', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/euc_kr.py b/Lib/encodings/euc_kr.py index bbebee8..c1fb126 100644 --- a/Lib/encodings/euc_kr.py +++ b/Lib/encodings/euc_kr.py @@ -2,10 +2,10 @@ # euc_kr.py: Python Unicode Codec for EUC_KR # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: euc_kr.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_kr, codecs +import _multibytecodec as mbc codec = _codecs_kr.getcodec('euc_kr') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='euc_kr', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/gb18030.py b/Lib/encodings/gb18030.py index 7eca319..34fb6c3 100644 --- a/Lib/encodings/gb18030.py +++ b/Lib/encodings/gb18030.py @@ -2,10 +2,10 @@ # gb18030.py: Python Unicode Codec for GB18030 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: gb18030.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_cn, codecs +import _multibytecodec as mbc codec = _codecs_cn.getcodec('gb18030') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='gb18030', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/gb2312.py b/Lib/encodings/gb2312.py index 5130efa..3c3b837 100644 --- a/Lib/encodings/gb2312.py +++ b/Lib/encodings/gb2312.py @@ -2,10 +2,10 @@ # gb2312.py: Python Unicode Codec for GB2312 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: gb2312.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_cn, codecs +import _multibytecodec as mbc codec = _codecs_cn.getcodec('gb2312') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='gb2312', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/gbk.py b/Lib/encodings/gbk.py index 67854bc..1b45db8 100644 --- a/Lib/encodings/gbk.py +++ b/Lib/encodings/gbk.py @@ -2,10 +2,10 @@ # gbk.py: Python Unicode Codec for GBK # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: gbk.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_cn, codecs +import _multibytecodec as mbc codec = _codecs_cn.getcodec('gbk') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='gbk', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/hz.py b/Lib/encodings/hz.py index 3940894..383442a 100644 --- a/Lib/encodings/hz.py +++ b/Lib/encodings/hz.py @@ -2,10 +2,10 @@ # hz.py: Python Unicode Codec for HZ # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: hz.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_cn, codecs +import _multibytecodec as mbc codec = _codecs_cn.getcodec('hz') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='hz', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/idna.py b/Lib/encodings/idna.py index 8bdae32..ea90d67 100644 --- a/Lib/encodings/idna.py +++ b/Lib/encodings/idna.py @@ -35,7 +35,7 @@ def nameprep(label): stringprep.in_table_c7(c) or \ stringprep.in_table_c8(c) or \ stringprep.in_table_c9(c): - raise UnicodeError, "Invalid character %s" % repr(c) + raise UnicodeError("Invalid character %r" % c) # Check bidi RandAL = map(stringprep.in_table_d1, label) @@ -48,14 +48,14 @@ def nameprep(label): # 2) If a string contains any RandALCat character, the string # MUST NOT contain any LCat character. if filter(stringprep.in_table_d2, label): - raise UnicodeError, "Violation of BIDI requirement 2" + raise UnicodeError("Violation of BIDI requirement 2") # 3) If a string contains any RandALCat character, a # RandALCat character MUST be the first character of the # string, and a RandALCat character MUST be the last # character of the string. if not RandAL[0] or not RandAL[-1]: - raise UnicodeError, "Violation of BIDI requirement 3" + raise UnicodeError("Violation of BIDI requirement 3") return label @@ -70,7 +70,7 @@ def ToASCII(label): # Skip to step 8. if 0 < len(label) < 64: return label - raise UnicodeError, "label too long" + raise UnicodeError("label empty or too long") # Step 2: nameprep label = nameprep(label) @@ -85,11 +85,11 @@ def ToASCII(label): # Skip to step 8. if 0 < len(label) < 64: return label - raise UnicodeError, "label too long" + raise UnicodeError("label empty or too long") # Step 5: Check ACE prefix if label.startswith(uace_prefix): - raise UnicodeError, "Label starts with ACE prefix" + raise UnicodeError("Label starts with ACE prefix") # Step 6: Encode with PUNYCODE label = label.encode("punycode") @@ -100,7 +100,7 @@ def ToASCII(label): # Step 8: Check size if 0 < len(label) < 64: return label - raise UnicodeError, "label too long" + raise UnicodeError("label empty or too long") def ToUnicode(label): # Step 1: Check for ASCII @@ -119,7 +119,7 @@ def ToUnicode(label): try: label = label.encode("ascii") except UnicodeError: - raise UnicodeError, "Invalid character in IDN label" + raise UnicodeError("Invalid character in IDN label") # Step 3: Check for ACE prefix if not label.startswith(ace_prefix): return unicode(label, "ascii") @@ -136,7 +136,7 @@ def ToUnicode(label): # Step 7: Compare the result of step 6 with the one of step 3 # label2 will already be in lower case. if label.lower() != label2: - raise UnicodeError, ("IDNA does not round-trip", label, label2) + raise UnicodeError("IDNA does not round-trip", label, label2) # Step 8: return the result of step 5 return result @@ -148,7 +148,7 @@ class Codec(codecs.Codec): if errors != 'strict': # IDNA is quite clear that implementations must be strict - raise UnicodeError, "unsupported error handling "+errors + raise UnicodeError("unsupported error handling "+errors) if not input: return "", 0 @@ -168,7 +168,7 @@ class Codec(codecs.Codec): def decode(self,input,errors='strict'): if errors != 'strict': - raise UnicodeError, "Unsupported error handling "+errors + raise UnicodeError("Unsupported error handling "+errors) if not input: return u"", 0 @@ -194,13 +194,79 @@ class Codec(codecs.Codec): return u".".join(result)+trailing_dot, len(input) -class IncrementalEncoder(codecs.IncrementalEncoder): - def encode(self, input, final=False): - return Codec().encode(input, self.errors)[0] +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, input, errors, final): + if errors != 'strict': + # IDNA is quite clear that implementations must be strict + raise UnicodeError("unsupported error handling "+errors) + + if not input: + return ("", 0) + + labels = dots.split(input) + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = '.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = '.' + + result = [] + size = 0 + for label in labels: + result.append(ToASCII(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result = ".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input, errors, final): + if errors != 'strict': + raise UnicodeError("Unsupported error handling "+errors) + + if not input: + return (u"", 0) + + # IDNA allows decoding to operate on Unicode strings, too. + if isinstance(input, unicode): + labels = dots.split(input) + else: + # Must be ASCII string + input = str(input) + unicode(input, "ascii") + labels = input.split(".") + + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = u'.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = u'.' + + result = [] + size = 0 + for label in labels: + result.append(ToUnicode(label)) + if size: + size += 1 + size += len(label) -class IncrementalDecoder(codecs.IncrementalDecoder): - def decode(self, input, final=False): - return Codec().decode(input, self.errors)[0] + result = u".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) class StreamWriter(Codec,codecs.StreamWriter): pass diff --git a/Lib/encodings/iso2022_jp.py b/Lib/encodings/iso2022_jp.py index 109658b..ab04060 100644 --- a/Lib/encodings/iso2022_jp.py +++ b/Lib/encodings/iso2022_jp.py @@ -2,10 +2,10 @@ # iso2022_jp.py: Python Unicode Codec for ISO2022_JP # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_jp_1.py b/Lib/encodings/iso2022_jp_1.py index 201bd28..997044d 100644 --- a/Lib/encodings/iso2022_jp_1.py +++ b/Lib/encodings/iso2022_jp_1.py @@ -2,10 +2,10 @@ # iso2022_jp_1.py: Python Unicode Codec for ISO2022_JP_1 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp_1.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp_1') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp_1', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_jp_2.py b/Lib/encodings/iso2022_jp_2.py index 7a61018..9106bf7 100644 --- a/Lib/encodings/iso2022_jp_2.py +++ b/Lib/encodings/iso2022_jp_2.py @@ -2,10 +2,10 @@ # iso2022_jp_2.py: Python Unicode Codec for ISO2022_JP_2 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp_2.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp_2') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp_2', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_jp_2004.py b/Lib/encodings/iso2022_jp_2004.py index 2497124..40198bf 100644 --- a/Lib/encodings/iso2022_jp_2004.py +++ b/Lib/encodings/iso2022_jp_2004.py @@ -2,10 +2,10 @@ # iso2022_jp_2004.py: Python Unicode Codec for ISO2022_JP_2004 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp_2004.py,v 1.1 2004/07/07 16:18:25 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp_2004') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp_2004', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_jp_3.py b/Lib/encodings/iso2022_jp_3.py index 8b2ed00..346e08b 100644 --- a/Lib/encodings/iso2022_jp_3.py +++ b/Lib/encodings/iso2022_jp_3.py @@ -2,10 +2,10 @@ # iso2022_jp_3.py: Python Unicode Codec for ISO2022_JP_3 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp_3.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp_3') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp_3', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_jp_ext.py b/Lib/encodings/iso2022_jp_ext.py index 97cb4e7..752bab9 100644 --- a/Lib/encodings/iso2022_jp_ext.py +++ b/Lib/encodings/iso2022_jp_ext.py @@ -2,10 +2,10 @@ # iso2022_jp_ext.py: Python Unicode Codec for ISO2022_JP_EXT # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_jp_ext.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_jp_ext') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_jp_ext', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/iso2022_kr.py b/Lib/encodings/iso2022_kr.py index f5549ca..bf70187 100644 --- a/Lib/encodings/iso2022_kr.py +++ b/Lib/encodings/iso2022_kr.py @@ -2,10 +2,10 @@ # iso2022_kr.py: Python Unicode Codec for ISO2022_KR # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: iso2022_kr.py,v 1.2 2004/06/28 18:16:03 perky Exp $ # import _codecs_iso2022, codecs +import _multibytecodec as mbc codec = _codecs_iso2022.getcodec('iso2022_kr') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='iso2022_kr', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/johab.py b/Lib/encodings/johab.py index b6a87d7..512aeeb 100644 --- a/Lib/encodings/johab.py +++ b/Lib/encodings/johab.py @@ -2,10 +2,10 @@ # johab.py: Python Unicode Codec for JOHAB # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: johab.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_kr, codecs +import _multibytecodec as mbc codec = _codecs_kr.getcodec('johab') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='johab', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/shift_jis.py b/Lib/encodings/shift_jis.py index ec5e517..8338117 100644 --- a/Lib/encodings/shift_jis.py +++ b/Lib/encodings/shift_jis.py @@ -2,10 +2,10 @@ # shift_jis.py: Python Unicode Codec for SHIFT_JIS # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: shift_jis.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('shift_jis') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='shift_jis', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/shift_jis_2004.py b/Lib/encodings/shift_jis_2004.py index 446cd7c..161b1e8 100644 --- a/Lib/encodings/shift_jis_2004.py +++ b/Lib/encodings/shift_jis_2004.py @@ -2,10 +2,10 @@ # shift_jis_2004.py: Python Unicode Codec for SHIFT_JIS_2004 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: shift_jis_2004.py,v 1.1 2004/07/07 16:18:25 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('shift_jis_2004') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='shift_jis_2004', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/encodings/shift_jisx0213.py b/Lib/encodings/shift_jisx0213.py index 495468b..cb653f5 100644 --- a/Lib/encodings/shift_jisx0213.py +++ b/Lib/encodings/shift_jisx0213.py @@ -2,10 +2,10 @@ # shift_jisx0213.py: Python Unicode Codec for SHIFT_JISX0213 # # Written by Hye-Shik Chang <perky@FreeBSD.org> -# $CJKCodecs: shift_jisx0213.py,v 1.8 2004/06/28 18:16:03 perky Exp $ # import _codecs_jp, codecs +import _multibytecodec as mbc codec = _codecs_jp.getcodec('shift_jisx0213') @@ -13,22 +13,27 @@ class Codec(codecs.Codec): encode = codec.encode decode = codec.decode -class StreamReader(Codec, codecs.StreamReader): - def __init__(self, stream, errors='strict'): - codecs.StreamReader.__init__(self, stream, errors) - __codec = codec.StreamReader(stream, errors) - self.read = __codec.read - self.readline = __codec.readline - self.readlines = __codec.readlines - self.reset = __codec.reset - -class StreamWriter(Codec, codecs.StreamWriter): - def __init__(self, stream, errors='strict'): - codecs.StreamWriter.__init__(self, stream, errors) - __codec = codec.StreamWriter(stream, errors) - self.write = __codec.write - self.writelines = __codec.writelines - self.reset = __codec.reset +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec def getregentry(): - return (codec.encode, codec.decode, StreamReader, StreamWriter) + return codecs.CodecInfo( + name='shift_jisx0213', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/getpass.py b/Lib/getpass.py index a30d3a1..8204a47 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -15,11 +15,14 @@ import sys __all__ = ["getpass","getuser"] -def unix_getpass(prompt='Password: '): +def unix_getpass(prompt='Password: ', stream=None): """Prompt for a password, with echo turned off. + The prompt is written on stream, by default stdout. Restore terminal settings at end. """ + if stream is None: + stream = sys.stdout try: fd = sys.stdin.fileno() @@ -32,18 +35,18 @@ def unix_getpass(prompt='Password: '): new[3] = new[3] & ~termios.ECHO # 3 == 'lflags' try: termios.tcsetattr(fd, termios.TCSADRAIN, new) - passwd = _raw_input(prompt) + passwd = _raw_input(prompt, stream) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old) - sys.stdout.write('\n') + stream.write('\n') return passwd -def win_getpass(prompt='Password: '): +def win_getpass(prompt='Password: ', stream=None): """Prompt for password with echo off, using Windows getch().""" if sys.stdin is not sys.__stdin__: - return default_getpass(prompt) + return default_getpass(prompt, stream) import msvcrt for c in prompt: msvcrt.putch(c) @@ -63,16 +66,18 @@ def win_getpass(prompt='Password: '): return pw -def default_getpass(prompt='Password: '): - print "Warning: Problem with getpass. Passwords may be echoed." - return _raw_input(prompt) +def default_getpass(prompt='Password: ', stream=None): + print >>sys.stderr, "Warning: Problem with getpass. Passwords may be echoed." + return _raw_input(prompt, stream) -def _raw_input(prompt=""): +def _raw_input(prompt="", stream=None): # This doesn't save the string in the GNU readline history. + if stream is None: + stream = sys.stdout prompt = str(prompt) if prompt: - sys.stdout.write(prompt) + stream.write(prompt) line = sys.stdin.readline() if not line: raise EOFError diff --git a/Lib/glob.py b/Lib/glob.py index ecc6d25..95656cc 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -60,7 +60,7 @@ def glob0(dirname, basename): if basename == '': # `os.path.split()` returns an empty basename for paths ending with a # directory separator. 'q*x/' should match only directories. - if os.isdir(dirname): + if os.path.isdir(dirname): return [basename] else: if os.path.lexists(os.path.join(dirname, basename)): diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py index ce1fd2a..deeb5c5 100644 --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -377,6 +377,7 @@ class IOBinding: try: f = open(filename, "wb") f.write(chars) + f.flush() f.close() return True except IOError, msg: diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index bbe9878..8163330 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,7 +1,10 @@ -What's New in IDLE 1.2a0? -======================= +What's New in IDLE 1.2a1? +========================= + +*Release date: 05-APR-2006* -*Release date: XX-XXX-2006* +- Source file f.flush() after writing; trying to avoid lossage if user + kills GUI. - Options / Keys / Advanced dialog made functional. Also, allow binding of 'movement' keys. @@ -70,7 +73,7 @@ What's New in IDLE 1.2a0? - Improve error handling when .idlerc can't be created (warn and exit). -- The GUI was hanging if the shell window was closed while a raw_input() +- The GUI was hanging if the shell window was closed while a raw_input() was pending. Restored the quit() of the readline() mainloop(). http://mail.python.org/pipermail/idle-dev/2004-December/002307.html diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index eef2885..fbde56c 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "1.2a0" +IDLE_VERSION = "1.2a1" diff --git a/Lib/inspect.py b/Lib/inspect.py index 57bf18c..2e4d987 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -353,7 +353,7 @@ def getsourcefile(object): if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix: # Looks like a binary file. We want to only return a text file. return None - if os.path.exists(filename): + if os.path.exists(filename) or hasattr(getmodule(object), '__loader__'): return filename def getabsfile(object): @@ -379,7 +379,7 @@ def getmodule(object): if file in modulesbyfile: return sys.modules.get(modulesbyfile[file]) for module in sys.modules.values(): - if hasattr(module, '__file__'): + if ismodule(module) and hasattr(module, '__file__'): modulesbyfile[ os.path.realpath( getabsfile(module))] = module.__name__ @@ -406,7 +406,7 @@ def findsource(object): in the file and the line number indexes a line in that list. An IOError is raised if the source code cannot be retrieved.""" file = getsourcefile(object) or getfile(object) - lines = linecache.getlines(file) + lines = linecache.getlines(file, getmodule(object).__dict__) if not lines: raise IOError('could not get source code') diff --git a/Lib/lib-old/Para.py b/Lib/lib-old/Para.py deleted file mode 100644 index 2fd8dc6..0000000 --- a/Lib/lib-old/Para.py +++ /dev/null @@ -1,343 +0,0 @@ -# Text formatting abstractions -# Note -- this module is obsolete, it's too slow anyway - - -# Oft-used type object -Int = type(0) - - -# Represent a paragraph. This is a list of words with associated -# font and size information, plus indents and justification for the -# entire paragraph. -# Once the words have been added to a paragraph, it can be laid out -# for different line widths. Once laid out, it can be rendered at -# different screen locations. Once rendered, it can be queried -# for mouse hits, and parts of the text can be highlighted -class Para: - # - def __init__(self): - self.words = [] # The words - self.just = 'l' # Justification: 'l', 'r', 'lr' or 'c' - self.indent_left = self.indent_right = self.indent_hang = 0 - # Final lay-out parameters, may change - self.left = self.top = self.right = self.bottom = \ - self.width = self.height = self.lines = None - # - # Add a word, computing size information for it. - # Words may also be added manually by appending to self.words - # Each word should be a 7-tuple: - # (font, text, width, space, stretch, ascent, descent) - def addword(self, d, font, text, space, stretch): - if font is not None: - d.setfont(font) - width = d.textwidth(text) - ascent = d.baseline() - descent = d.lineheight() - ascent - spw = d.textwidth(' ') - space = space * spw - stretch = stretch * spw - tuple = (font, text, width, space, stretch, ascent, descent) - self.words.append(tuple) - # - # Hooks to begin and end anchors -- insert numbers in the word list! - def bgn_anchor(self, id): - self.words.append(id) - # - def end_anchor(self, id): - self.words.append(0) - # - # Return the total length (width) of the text added so far, in pixels - def getlength(self): - total = 0 - for word in self.words: - if type(word) is not Int: - total = total + word[2] + word[3] - return total - # - # Tab to a given position (relative to the current left indent): - # remove all stretch, add fixed space up to the new indent. - # If the current position is already at the tab stop, - # don't add any new space (but still remove the stretch) - def tabto(self, tab): - total = 0 - as, de = 1, 0 - for i in range(len(self.words)): - word = self.words[i] - if type(word) is Int: continue - (fo, te, wi, sp, st, as, de) = word - self.words[i] = (fo, te, wi, sp, 0, as, de) - total = total + wi + sp - if total < tab: - self.words.append((None, '', 0, tab-total, 0, as, de)) - # - # Make a hanging tag: tab to hang, increment indent_left by hang, - # and reset indent_hang to -hang - def makehangingtag(self, hang): - self.tabto(hang) - self.indent_left = self.indent_left + hang - self.indent_hang = -hang - # - # Decide where the line breaks will be given some screen width - def layout(self, linewidth): - self.width = linewidth - height = 0 - self.lines = lines = [] - avail1 = self.width - self.indent_left - self.indent_right - avail = avail1 - self.indent_hang - words = self.words - i = 0 - n = len(words) - lastfont = None - while i < n: - firstfont = lastfont - charcount = 0 - width = 0 - stretch = 0 - ascent = 0 - descent = 0 - lsp = 0 - j = i - while i < n: - word = words[i] - if type(word) is Int: - if word > 0 and width >= avail: - break - i = i+1 - continue - fo, te, wi, sp, st, as, de = word - if width + wi > avail and width > 0 and wi > 0: - break - if fo is not None: - lastfont = fo - if width == 0: - firstfont = fo - charcount = charcount + len(te) + (sp > 0) - width = width + wi + sp - lsp = sp - stretch = stretch + st - lst = st - ascent = max(ascent, as) - descent = max(descent, de) - i = i+1 - while i > j and type(words[i-1]) is Int and \ - words[i-1] > 0: i = i-1 - width = width - lsp - if i < n: - stretch = stretch - lst - else: - stretch = 0 - tuple = i-j, firstfont, charcount, width, stretch, \ - ascent, descent - lines.append(tuple) - height = height + ascent + descent - avail = avail1 - self.height = height - # - # Call a function for all words in a line - def visit(self, wordfunc, anchorfunc): - avail1 = self.width - self.indent_left - self.indent_right - avail = avail1 - self.indent_hang - v = self.top - i = 0 - for tuple in self.lines: - wordcount, firstfont, charcount, width, stretch, \ - ascent, descent = tuple - h = self.left + self.indent_left - if i == 0: h = h + self.indent_hang - extra = 0 - if self.just == 'r': h = h + avail - width - elif self.just == 'c': h = h + (avail - width) / 2 - elif self.just == 'lr' and stretch > 0: - extra = avail - width - v2 = v + ascent + descent - for j in range(i, i+wordcount): - word = self.words[j] - if type(word) is Int: - ok = anchorfunc(self, tuple, word, \ - h, v) - if ok is not None: return ok - continue - fo, te, wi, sp, st, as, de = word - if extra > 0 and stretch > 0: - ex = extra * st / stretch - extra = extra - ex - stretch = stretch - st - else: - ex = 0 - h2 = h + wi + sp + ex - ok = wordfunc(self, tuple, word, h, v, \ - h2, v2, (j==i), (j==i+wordcount-1)) - if ok is not None: return ok - h = h2 - v = v2 - i = i + wordcount - avail = avail1 - # - # Render a paragraph in "drawing object" d, using the rectangle - # given by (left, top, right) with an unspecified bottom. - # Return the computed bottom of the text. - def render(self, d, left, top, right): - if self.width != right-left: - self.layout(right-left) - self.left = left - self.top = top - self.right = right - self.bottom = self.top + self.height - self.anchorid = 0 - try: - self.d = d - self.visit(self.__class__._renderword, \ - self.__class__._renderanchor) - finally: - self.d = None - return self.bottom - # - def _renderword(self, tuple, word, h, v, h2, v2, isfirst, islast): - if word[0] is not None: self.d.setfont(word[0]) - baseline = v + tuple[5] - self.d.text((h, baseline - word[5]), word[1]) - if self.anchorid > 0: - self.d.line((h, baseline+2), (h2, baseline+2)) - # - def _renderanchor(self, tuple, word, h, v): - self.anchorid = word - # - # Return which anchor(s) was hit by the mouse - def hitcheck(self, mouseh, mousev): - self.mouseh = mouseh - self.mousev = mousev - self.anchorid = 0 - self.hits = [] - self.visit(self.__class__._hitcheckword, \ - self.__class__._hitcheckanchor) - return self.hits - # - def _hitcheckword(self, tuple, word, h, v, h2, v2, isfirst, islast): - if self.anchorid > 0 and h <= self.mouseh <= h2 and \ - v <= self.mousev <= v2: - self.hits.append(self.anchorid) - # - def _hitcheckanchor(self, tuple, word, h, v): - self.anchorid = word - # - # Return whether the given anchor id is present - def hasanchor(self, id): - return id in self.words or -id in self.words - # - # Extract the raw text from the word list, substituting one space - # for non-empty inter-word space, and terminating with '\n' - def extract(self): - text = '' - for w in self.words: - if type(w) is not Int: - word = w[1] - if w[3]: word = word + ' ' - text = text + word - return text + '\n' - # - # Return which character position was hit by the mouse, as - # an offset in the entire text as returned by extract(). - # Return None if the mouse was not in this paragraph - def whereis(self, d, mouseh, mousev): - if mousev < self.top or mousev > self.bottom: - return None - self.mouseh = mouseh - self.mousev = mousev - self.lastfont = None - self.charcount = 0 - try: - self.d = d - return self.visit(self.__class__._whereisword, \ - self.__class__._whereisanchor) - finally: - self.d = None - # - def _whereisword(self, tuple, word, h1, v1, h2, v2, isfirst, islast): - fo, te, wi, sp, st, as, de = word - if fo is not None: self.lastfont = fo - h = h1 - if isfirst: h1 = 0 - if islast: h2 = 999999 - if not (v1 <= self.mousev <= v2 and h1 <= self.mouseh <= h2): - self.charcount = self.charcount + len(te) + (sp > 0) - return - if self.lastfont is not None: - self.d.setfont(self.lastfont) - cc = 0 - for c in te: - cw = self.d.textwidth(c) - if self.mouseh <= h + cw/2: - return self.charcount + cc - cc = cc+1 - h = h+cw - self.charcount = self.charcount + cc - if self.mouseh <= (h+h2) / 2: - return self.charcount - else: - return self.charcount + 1 - # - def _whereisanchor(self, tuple, word, h, v): - pass - # - # Return screen position corresponding to position in paragraph. - # Return tuple (h, vtop, vbaseline, vbottom). - # This is more or less the inverse of whereis() - def screenpos(self, d, pos): - if pos < 0: - ascent, descent = self.lines[0][5:7] - return self.left, self.top, self.top + ascent, \ - self.top + ascent + descent - self.pos = pos - self.lastfont = None - try: - self.d = d - ok = self.visit(self.__class__._screenposword, \ - self.__class__._screenposanchor) - finally: - self.d = None - if ok is None: - ascent, descent = self.lines[-1][5:7] - ok = self.right, self.bottom - ascent - descent, \ - self.bottom - descent, self.bottom - return ok - # - def _screenposword(self, tuple, word, h1, v1, h2, v2, isfirst, islast): - fo, te, wi, sp, st, as, de = word - if fo is not None: self.lastfont = fo - cc = len(te) + (sp > 0) - if self.pos > cc: - self.pos = self.pos - cc - return - if self.pos < cc: - self.d.setfont(self.lastfont) - h = h1 + self.d.textwidth(te[:self.pos]) - else: - h = h2 - ascent, descent = tuple[5:7] - return h, v1, v1+ascent, v2 - # - def _screenposanchor(self, tuple, word, h, v): - pass - # - # Invert the stretch of text between pos1 and pos2. - # If pos1 is None, the beginning is implied; - # if pos2 is None, the end is implied. - # Undoes its own effect when called again with the same arguments - def invert(self, d, pos1, pos2): - if pos1 is None: - pos1 = self.left, self.top, self.top, self.top - else: - pos1 = self.screenpos(d, pos1) - if pos2 is None: - pos2 = self.right, self.bottom,self.bottom,self.bottom - else: - pos2 = self.screenpos(d, pos2) - h1, top1, baseline1, bottom1 = pos1 - h2, top2, baseline2, bottom2 = pos2 - if bottom1 <= top2: - d.invert((h1, top1), (self.right, bottom1)) - h1 = self.left - if bottom1 < top2: - d.invert((h1, bottom1), (self.right, top2)) - top1, bottom1 = top2, bottom2 - d.invert((h1, top1), (h2, bottom2)) diff --git a/Lib/lib-old/addpack.py b/Lib/lib-old/addpack.py deleted file mode 100644 index 2fb2601..0000000 --- a/Lib/lib-old/addpack.py +++ /dev/null @@ -1,67 +0,0 @@ -# This module provides standard support for "packages". -# -# The idea is that large groups of related modules can be placed in -# their own subdirectory, which can be added to the Python search path -# in a relatively easy way. -# -# The current version takes a package name and searches the Python -# search path for a directory by that name, and if found adds it to -# the module search path (sys.path). It maintains a list of packages -# that have already been added so adding the same package many times -# is OK. -# -# It is intended to be used in a fairly stylized manner: each module -# that wants to use a particular package, say 'Foo', is supposed to -# contain the following code: -# -# from addpack import addpack -# addpack('Foo') -# <import modules from package Foo> -# -# Additional arguments, when present, provide additional places where -# to look for the package before trying sys.path (these may be either -# strings or lists/tuples of strings). Also, if the package name is a -# full pathname, first the last component is tried in the usual way, -# then the full pathname is tried last. If the package name is a -# *relative* pathname (UNIX: contains a slash but doesn't start with -# one), then nothing special is done. The packages "/foo/bar/bletch" -# and "bletch" are considered the same, but unrelated to "bar/bletch". -# -# If the algorithm finds more than one suitable subdirectory, all are -# added to the search path -- this makes it possible to override part -# of a package. The same path will not be added more than once. -# -# If no directory is found, ImportError is raised. - -_packs = {} # {pack: [pathname, ...], ...} - -def addpack(pack, *locations): - import os - if os.path.isabs(pack): - base = os.path.basename(pack) - else: - base = pack - if _packs.has_key(base): - return - import sys - path = [] - for loc in _flatten(locations) + sys.path: - fn = os.path.join(loc, base) - if fn not in path and os.path.isdir(fn): - path.append(fn) - if pack != base and pack not in path and os.path.isdir(pack): - path.append(pack) - if not path: raise ImportError, 'package ' + pack + ' not found' - _packs[base] = path - for fn in path: - if fn not in sys.path: - sys.path.append(fn) - -def _flatten(locations): - locs = [] - for loc in locations: - if type(loc) == type(''): - locs.append(loc) - else: - locs = locs + _flatten(loc) - return locs diff --git a/Lib/lib-old/cmp.py b/Lib/lib-old/cmp.py deleted file mode 100644 index 1146a25..0000000 --- a/Lib/lib-old/cmp.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Efficiently compare files, boolean outcome only (equal / not equal). - -Tricks (used in this order): - - Files with identical type, size & mtime are assumed to be clones - - Files with different type or size cannot be identical - - We keep a cache of outcomes of earlier comparisons - - We don't fork a process to run 'cmp' but read the files ourselves -""" - -import os - -cache = {} - -def cmp(f1, f2, shallow=1): - """Compare two files, use the cache if possible. - Return 1 for identical files, 0 for different. - Raise exceptions if either file could not be statted, read, etc.""" - s1, s2 = sig(os.stat(f1)), sig(os.stat(f2)) - if s1[0] != 8 or s2[0] != 8: - # Either is a not a plain file -- always report as different - return 0 - if shallow and s1 == s2: - # type, size & mtime match -- report same - return 1 - if s1[:2] != s2[:2]: # Types or sizes differ, don't bother - # types or sizes differ -- report different - return 0 - # same type and size -- look in the cache - key = (f1, f2) - try: - cs1, cs2, outcome = cache[key] - # cache hit - if s1 == cs1 and s2 == cs2: - # cached signatures match - return outcome - # stale cached signature(s) - except KeyError: - # cache miss - pass - # really compare - outcome = do_cmp(f1, f2) - cache[key] = s1, s2, outcome - return outcome - -def sig(st): - """Return signature (i.e., type, size, mtime) from raw stat data - 0-5: st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid - 6-9: st_size, st_atime, st_mtime, st_ctime""" - type = st[0] / 4096 - size = st[6] - mtime = st[8] - return type, size, mtime - -def do_cmp(f1, f2): - """Compare two files, really.""" - bufsize = 8*1024 # Could be tuned - fp1 = open(f1, 'rb') - fp2 = open(f2, 'rb') - while 1: - b1 = fp1.read(bufsize) - b2 = fp2.read(bufsize) - if b1 != b2: return 0 - if not b1: return 1 diff --git a/Lib/lib-old/cmpcache.py b/Lib/lib-old/cmpcache.py deleted file mode 100644 index 11540f8..0000000 --- a/Lib/lib-old/cmpcache.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Efficiently compare files, boolean outcome only (equal / not equal). - -Tricks (used in this order): - - Use the statcache module to avoid statting files more than once - - Files with identical type, size & mtime are assumed to be clones - - Files with different type or size cannot be identical - - We keep a cache of outcomes of earlier comparisons - - We don't fork a process to run 'cmp' but read the files ourselves -""" - -import os -from stat import * -import statcache - - -# The cache. -# -cache = {} - - -def cmp(f1, f2, shallow=1): - """Compare two files, use the cache if possible. - May raise os.error if a stat or open of either fails. - Return 1 for identical files, 0 for different. - Raise exceptions if either file could not be statted, read, etc.""" - s1, s2 = sig(statcache.stat(f1)), sig(statcache.stat(f2)) - if not S_ISREG(s1[0]) or not S_ISREG(s2[0]): - # Either is a not a plain file -- always report as different - return 0 - if shallow and s1 == s2: - # type, size & mtime match -- report same - return 1 - if s1[:2] != s2[:2]: # Types or sizes differ, don't bother - # types or sizes differ -- report different - return 0 - # same type and size -- look in the cache - key = f1 + ' ' + f2 - if cache.has_key(key): - cs1, cs2, outcome = cache[key] - # cache hit - if s1 == cs1 and s2 == cs2: - # cached signatures match - return outcome - # stale cached signature(s) - # really compare - outcome = do_cmp(f1, f2) - cache[key] = s1, s2, outcome - return outcome - -def sig(st): - """Return signature (i.e., type, size, mtime) from raw stat data.""" - return S_IFMT(st[ST_MODE]), st[ST_SIZE], st[ST_MTIME] - -def do_cmp(f1, f2): - """Compare two files, really.""" - #print ' cmp', f1, f2 # XXX remove when debugged - bufsize = 8*1024 # Could be tuned - fp1 = open(f1, 'rb') - fp2 = open(f2, 'rb') - while 1: - b1 = fp1.read(bufsize) - b2 = fp2.read(bufsize) - if b1 != b2: return 0 - if not b1: return 1 diff --git a/Lib/lib-old/codehack.py b/Lib/lib-old/codehack.py deleted file mode 100644 index 0b5e3a1..0000000 --- a/Lib/lib-old/codehack.py +++ /dev/null @@ -1,81 +0,0 @@ -# A subroutine for extracting a function name from a code object -# (with cache) - -import sys -from stat import * -import string -import os -import linecache - -# XXX The functions getcodename() and getfuncname() are now obsolete -# XXX as code and function objects now have a name attribute -- -# XXX co.co_name and f.func_name. -# XXX getlineno() is now also obsolete because of the new attribute -# XXX of code objects, co.co_firstlineno. - -# Extract the function or class name from a code object. -# This is a bit of a hack, since a code object doesn't contain -# the name directly. So what do we do: -# - get the filename (which *is* in the code object) -# - look in the code string to find the first SET_LINENO instruction -# (this must be the first instruction) -# - get the line from the file -# - if the line starts with 'class' or 'def' (after possible whitespace), -# extract the following identifier -# -# This breaks apart when the function was read from <stdin> -# or constructed by exec(), when the file is not accessible, -# and also when the file has been modified or when a line is -# continued with a backslash before the function or class name. -# -# Because this is a pretty expensive hack, a cache is kept. - -SET_LINENO = 127 # The opcode (see "opcode.h" in the Python source) -identchars = string.ascii_letters + string.digits + '_' # Identifier characters - -_namecache = {} # The cache - -def getcodename(co): - try: - return co.co_name - except AttributeError: - pass - key = `co` # arbitrary but uniquely identifying string - if _namecache.has_key(key): return _namecache[key] - filename = co.co_filename - code = co.co_code - name = '' - if ord(code[0]) == SET_LINENO: - lineno = ord(code[1]) | ord(code[2]) << 8 - line = linecache.getline(filename, lineno) - words = line.split() - if len(words) >= 2 and words[0] in ('def', 'class'): - name = words[1] - for i in range(len(name)): - if name[i] not in identchars: - name = name[:i] - break - _namecache[key] = name - return name - -# Use the above routine to find a function's name. - -def getfuncname(func): - try: - return func.func_name - except AttributeError: - pass - return getcodename(func.func_code) - -# A part of the above code to extract just the line number from a code object. - -def getlineno(co): - try: - return co.co_firstlineno - except AttributeError: - pass - code = co.co_code - if ord(code[0]) == SET_LINENO: - return ord(code[1]) | ord(code[2]) << 8 - else: - return -1 diff --git a/Lib/lib-old/dircmp.py b/Lib/lib-old/dircmp.py deleted file mode 100644 index 1e7bf2a..0000000 --- a/Lib/lib-old/dircmp.py +++ /dev/null @@ -1,202 +0,0 @@ -"""A class to build directory diff tools on.""" - -import os - -import dircache -import cmpcache -import statcache -from stat import * - -class dircmp: - """Directory comparison class.""" - - def new(self, a, b): - """Initialize.""" - self.a = a - self.b = b - # Properties that caller may change before calling self.run(): - self.hide = [os.curdir, os.pardir] # Names never to be shown - self.ignore = ['RCS', 'tags'] # Names ignored in comparison - - return self - - def run(self): - """Compare everything except common subdirectories.""" - self.a_list = filter(dircache.listdir(self.a), self.hide) - self.b_list = filter(dircache.listdir(self.b), self.hide) - self.a_list.sort() - self.b_list.sort() - self.phase1() - self.phase2() - self.phase3() - - def phase1(self): - """Compute common names.""" - self.a_only = [] - self.common = [] - for x in self.a_list: - if x in self.b_list: - self.common.append(x) - else: - self.a_only.append(x) - - self.b_only = [] - for x in self.b_list: - if x not in self.common: - self.b_only.append(x) - - def phase2(self): - """Distinguish files, directories, funnies.""" - self.common_dirs = [] - self.common_files = [] - self.common_funny = [] - - for x in self.common: - a_path = os.path.join(self.a, x) - b_path = os.path.join(self.b, x) - - ok = 1 - try: - a_stat = statcache.stat(a_path) - except os.error, why: - # print 'Can\'t stat', a_path, ':', why[1] - ok = 0 - try: - b_stat = statcache.stat(b_path) - except os.error, why: - # print 'Can\'t stat', b_path, ':', why[1] - ok = 0 - - if ok: - a_type = S_IFMT(a_stat[ST_MODE]) - b_type = S_IFMT(b_stat[ST_MODE]) - if a_type != b_type: - self.common_funny.append(x) - elif S_ISDIR(a_type): - self.common_dirs.append(x) - elif S_ISREG(a_type): - self.common_files.append(x) - else: - self.common_funny.append(x) - else: - self.common_funny.append(x) - - def phase3(self): - """Find out differences between common files.""" - xx = cmpfiles(self.a, self.b, self.common_files) - self.same_files, self.diff_files, self.funny_files = xx - - def phase4(self): - """Find out differences between common subdirectories. - A new dircmp object is created for each common subdirectory, - these are stored in a dictionary indexed by filename. - The hide and ignore properties are inherited from the parent.""" - self.subdirs = {} - for x in self.common_dirs: - a_x = os.path.join(self.a, x) - b_x = os.path.join(self.b, x) - self.subdirs[x] = newdd = dircmp().new(a_x, b_x) - newdd.hide = self.hide - newdd.ignore = self.ignore - newdd.run() - - def phase4_closure(self): - """Recursively call phase4() on subdirectories.""" - self.phase4() - for x in self.subdirs.keys(): - self.subdirs[x].phase4_closure() - - def report(self): - """Print a report on the differences between a and b.""" - # Assume that phases 1 to 3 have been executed - # Output format is purposely lousy - print 'diff', self.a, self.b - if self.a_only: - print 'Only in', self.a, ':', self.a_only - if self.b_only: - print 'Only in', self.b, ':', self.b_only - if self.same_files: - print 'Identical files :', self.same_files - if self.diff_files: - print 'Differing files :', self.diff_files - if self.funny_files: - print 'Trouble with common files :', self.funny_files - if self.common_dirs: - print 'Common subdirectories :', self.common_dirs - if self.common_funny: - print 'Common funny cases :', self.common_funny - - def report_closure(self): - """Print reports on self and on subdirs. - If phase 4 hasn't been done, no subdir reports are printed.""" - self.report() - try: - x = self.subdirs - except AttributeError: - return # No subdirectories computed - for x in self.subdirs.keys(): - print - self.subdirs[x].report_closure() - - def report_phase4_closure(self): - """Report and do phase 4 recursively.""" - self.report() - self.phase4() - for x in self.subdirs.keys(): - print - self.subdirs[x].report_phase4_closure() - - -def cmpfiles(a, b, common): - """Compare common files in two directories. - Return: - - files that compare equal - - files that compare different - - funny cases (can't stat etc.)""" - - res = ([], [], []) - for x in common: - res[cmp(os.path.join(a, x), os.path.join(b, x))].append(x) - return res - - -def cmp(a, b): - """Compare two files. - Return: - 0 for equal - 1 for different - 2 for funny cases (can't stat, etc.)""" - - try: - if cmpcache.cmp(a, b): return 0 - return 1 - except os.error: - return 2 - - -def filter(list, skip): - """Return a copy with items that occur in skip removed.""" - - result = [] - for item in list: - if item not in skip: result.append(item) - return result - - -def demo(): - """Demonstration and testing.""" - - import sys - import getopt - options, args = getopt.getopt(sys.argv[1:], 'r') - if len(args) != 2: - raise getopt.error, 'need exactly two args' - dd = dircmp().new(args[0], args[1]) - dd.run() - if ('-r', '') in options: - dd.report_phase4_closure() - else: - dd.report() - -if __name__ == "__main__": - demo() diff --git a/Lib/lib-old/dump.py b/Lib/lib-old/dump.py deleted file mode 100644 index 60bdba8..0000000 --- a/Lib/lib-old/dump.py +++ /dev/null @@ -1,63 +0,0 @@ -# Module 'dump' -# -# Print python code that reconstructs a variable. -# This only works in certain cases. -# -# It works fine for: -# - ints and floats (except NaNs and other weird things) -# - strings -# - compounds and lists, provided it works for all their elements -# - imported modules, provided their name is the module name -# -# It works for top-level dictionaries but not for dictionaries -# contained in other objects (could be made to work with some hassle -# though). -# -# It does not work for functions (all sorts), classes, class objects, -# windows, files etc. -# -# Finally, objects referenced by more than one name or contained in more -# than one other object lose their sharing property (this is bad for -# strings used as exception identifiers, for instance). - -# Dump a whole symbol table -# -def dumpsymtab(dict): - for key in dict.keys(): - dumpvar(key, dict[key]) - -# Dump a single variable -# -def dumpvar(name, x): - import sys - t = type(x) - if t == type({}): - print name, '= {}' - for key in x.keys(): - item = x[key] - if not printable(item): - print '#', - print name, '[', `key`, '] =', `item` - elif t in (type(''), type(0), type(0.0), type([]), type(())): - if not printable(x): - print '#', - print name, '=', `x` - elif t == type(sys): - print 'import', name, '#', x - else: - print '#', name, '=', x - -# check if a value is printable in a way that can be read back with input() -# -def printable(x): - t = type(x) - if t in (type(''), type(0), type(0.0)): - return 1 - if t in (type([]), type(())): - for item in x: - if not printable(item): - return 0 - return 1 - if x == {}: - return 1 - return 0 diff --git a/Lib/lib-old/find.py b/Lib/lib-old/find.py deleted file mode 100644 index 39ad771..0000000 --- a/Lib/lib-old/find.py +++ /dev/null @@ -1,26 +0,0 @@ -import fnmatch -import os - -_debug = 0 - -_prune = ['(*)'] - -def find(pattern, dir = os.curdir): - list = [] - names = os.listdir(dir) - names.sort() - for name in names: - if name in (os.curdir, os.pardir): - continue - fullname = os.path.join(dir, name) - if fnmatch.fnmatch(name, pattern): - list.append(fullname) - if os.path.isdir(fullname) and not os.path.islink(fullname): - for p in _prune: - if fnmatch.fnmatch(name, p): - if _debug: print "skip", `fullname` - break - else: - if _debug: print "descend into", `fullname` - list = list + find(pattern, fullname) - return list diff --git a/Lib/lib-old/fmt.py b/Lib/lib-old/fmt.py deleted file mode 100644 index 997d37a..0000000 --- a/Lib/lib-old/fmt.py +++ /dev/null @@ -1,623 +0,0 @@ -# Text formatting abstractions -# Note -- this module is obsolete, it's too slow anyway - - -import string -import Para - - -# A formatter back-end object has one method that is called by the formatter: -# addpara(p), where p is a paragraph object. For example: - - -# Formatter back-end to do nothing at all with the paragraphs -class NullBackEnd: - # - def __init__(self): - pass - # - def addpara(self, p): - pass - # - def bgn_anchor(self, id): - pass - # - def end_anchor(self, id): - pass - - -# Formatter back-end to collect the paragraphs in a list -class SavingBackEnd(NullBackEnd): - # - def __init__(self): - self.paralist = [] - # - def addpara(self, p): - self.paralist.append(p) - # - def hitcheck(self, h, v): - hits = [] - for p in self.paralist: - if p.top <= v <= p.bottom: - for id in p.hitcheck(h, v): - if id not in hits: - hits.append(id) - return hits - # - def extract(self): - text = '' - for p in self.paralist: - text = text + (p.extract()) - return text - # - def extractpart(self, long1, long2): - if long1 > long2: long1, long2 = long2, long1 - para1, pos1 = long1 - para2, pos2 = long2 - text = '' - while para1 < para2: - ptext = self.paralist[para1].extract() - text = text + ptext[pos1:] - pos1 = 0 - para1 = para1 + 1 - ptext = self.paralist[para2].extract() - return text + ptext[pos1:pos2] - # - def whereis(self, d, h, v): - total = 0 - for i in range(len(self.paralist)): - p = self.paralist[i] - result = p.whereis(d, h, v) - if result is not None: - return i, result - return None - # - def roundtowords(self, long1, long2): - i, offset = long1 - text = self.paralist[i].extract() - while offset > 0 and text[offset-1] != ' ': offset = offset-1 - long1 = i, offset - # - i, offset = long2 - text = self.paralist[i].extract() - n = len(text) - while offset < n-1 and text[offset] != ' ': offset = offset+1 - long2 = i, offset - # - return long1, long2 - # - def roundtoparagraphs(self, long1, long2): - long1 = long1[0], 0 - long2 = long2[0], len(self.paralist[long2[0]].extract()) - return long1, long2 - - -# Formatter back-end to send the text directly to the drawing object -class WritingBackEnd(NullBackEnd): - # - def __init__(self, d, width): - self.d = d - self.width = width - self.lineno = 0 - # - def addpara(self, p): - self.lineno = p.render(self.d, 0, self.lineno, self.width) - - -# A formatter receives a stream of formatting instructions and assembles -# these into a stream of paragraphs on to a back-end. The assembly is -# parametrized by a text measurement object, which must match the output -# operations of the back-end. The back-end is responsible for splitting -# paragraphs up in lines of a given maximum width. (This is done because -# in a windowing environment, when the window size changes, there is no -# need to redo the assembly into paragraphs, but the splitting into lines -# must be done taking the new window size into account.) - - -# Formatter base class. Initialize it with a text measurement object, -# which is used for text measurements, and a back-end object, -# which receives the completed paragraphs. The formatting methods are: -# setfont(font) -# setleftindent(nspaces) -# setjust(type) where type is 'l', 'c', 'r', or 'lr' -# flush() -# vspace(nlines) -# needvspace(nlines) -# addword(word, nspaces) -class BaseFormatter: - # - def __init__(self, d, b): - # Drawing object used for text measurements - self.d = d - # - # BackEnd object receiving completed paragraphs - self.b = b - # - # Parameters of the formatting model - self.leftindent = 0 - self.just = 'l' - self.font = None - self.blanklines = 0 - # - # Parameters derived from the current font - self.space = d.textwidth(' ') - self.line = d.lineheight() - self.ascent = d.baseline() - self.descent = self.line - self.ascent - # - # Parameter derived from the default font - self.n_space = self.space - # - # Current paragraph being built - self.para = None - self.nospace = 1 - # - # Font to set on the next word - self.nextfont = None - # - def newpara(self): - return Para.Para() - # - def setfont(self, font): - if font is None: return - self.font = self.nextfont = font - d = self.d - d.setfont(font) - self.space = d.textwidth(' ') - self.line = d.lineheight() - self.ascent = d.baseline() - self.descent = self.line - self.ascent - # - def setleftindent(self, nspaces): - self.leftindent = int(self.n_space * nspaces) - if self.para: - hang = self.leftindent - self.para.indent_left - if hang > 0 and self.para.getlength() <= hang: - self.para.makehangingtag(hang) - self.nospace = 1 - else: - self.flush() - # - def setrightindent(self, nspaces): - self.rightindent = int(self.n_space * nspaces) - if self.para: - self.para.indent_right = self.rightindent - self.flush() - # - def setjust(self, just): - self.just = just - if self.para: - self.para.just = self.just - # - def flush(self): - if self.para: - self.b.addpara(self.para) - self.para = None - if self.font is not None: - self.d.setfont(self.font) - self.nospace = 1 - # - def vspace(self, nlines): - self.flush() - if nlines > 0: - self.para = self.newpara() - tuple = None, '', 0, 0, 0, int(nlines*self.line), 0 - self.para.words.append(tuple) - self.flush() - self.blanklines = self.blanklines + nlines - # - def needvspace(self, nlines): - self.flush() # Just to be sure - if nlines > self.blanklines: - self.vspace(nlines - self.blanklines) - # - def addword(self, text, space): - if self.nospace and not text: - return - self.nospace = 0 - self.blanklines = 0 - if not self.para: - self.para = self.newpara() - self.para.indent_left = self.leftindent - self.para.just = self.just - self.nextfont = self.font - space = int(space * self.space) - self.para.words.append((self.nextfont, text, - self.d.textwidth(text), space, space, - self.ascent, self.descent)) - self.nextfont = None - # - def bgn_anchor(self, id): - if not self.para: - self.nospace = 0 - self.addword('', 0) - self.para.bgn_anchor(id) - # - def end_anchor(self, id): - if not self.para: - self.nospace = 0 - self.addword('', 0) - self.para.end_anchor(id) - - -# Measuring object for measuring text as viewed on a tty -class NullMeasurer: - # - def __init__(self): - pass - # - def setfont(self, font): - pass - # - def textwidth(self, text): - return len(text) - # - def lineheight(self): - return 1 - # - def baseline(self): - return 0 - - -# Drawing object for writing plain ASCII text to a file -class FileWriter: - # - def __init__(self, fp): - self.fp = fp - self.lineno, self.colno = 0, 0 - # - def setfont(self, font): - pass - # - def text(self, (h, v), str): - if not str: return - if '\n' in str: - raise ValueError, 'can\'t write \\n' - while self.lineno < v: - self.fp.write('\n') - self.colno, self.lineno = 0, self.lineno + 1 - while self.lineno > v: - # XXX This should never happen... - self.fp.write('\033[A') # ANSI up arrow - self.lineno = self.lineno - 1 - if self.colno < h: - self.fp.write(' ' * (h - self.colno)) - elif self.colno > h: - self.fp.write('\b' * (self.colno - h)) - self.colno = h - self.fp.write(str) - self.colno = h + len(str) - - -# Formatting class to do nothing at all with the data -class NullFormatter(BaseFormatter): - # - def __init__(self): - d = NullMeasurer() - b = NullBackEnd() - BaseFormatter.__init__(self, d, b) - - -# Formatting class to write directly to a file -class WritingFormatter(BaseFormatter): - # - def __init__(self, fp, width): - dm = NullMeasurer() - dw = FileWriter(fp) - b = WritingBackEnd(dw, width) - BaseFormatter.__init__(self, dm, b) - self.blanklines = 1 - # - # Suppress multiple blank lines - def needvspace(self, nlines): - BaseFormatter.needvspace(self, min(1, nlines)) - - -# A "FunnyFormatter" writes ASCII text with a twist: *bold words*, -# _italic text_ and _underlined words_, and `quoted text'. -# It assumes that the fonts are 'r', 'i', 'b', 'u', 'q': (roman, -# italic, bold, underline, quote). -# Moreover, if the font is in upper case, the text is converted to -# UPPER CASE. -class FunnyFormatter(WritingFormatter): - # - def flush(self): - if self.para: finalize(self.para) - WritingFormatter.flush(self) - - -# Surrounds *bold words* and _italic text_ in a paragraph with -# appropriate markers, fixing the size (assuming these characters' -# width is 1). -openchar = \ - {'b':'*', 'i':'_', 'u':'_', 'q':'`', 'B':'*', 'I':'_', 'U':'_', 'Q':'`'} -closechar = \ - {'b':'*', 'i':'_', 'u':'_', 'q':'\'', 'B':'*', 'I':'_', 'U':'_', 'Q':'\''} -def finalize(para): - oldfont = curfont = 'r' - para.words.append(('r', '', 0, 0, 0, 0)) # temporary, deleted at end - for i in range(len(para.words)): - fo, te, wi = para.words[i][:3] - if fo is not None: curfont = fo - if curfont != oldfont: - if closechar.has_key(oldfont): - c = closechar[oldfont] - j = i-1 - while j > 0 and para.words[j][1] == '': j = j-1 - fo1, te1, wi1 = para.words[j][:3] - te1 = te1 + c - wi1 = wi1 + len(c) - para.words[j] = (fo1, te1, wi1) + \ - para.words[j][3:] - if openchar.has_key(curfont) and te: - c = openchar[curfont] - te = c + te - wi = len(c) + wi - para.words[i] = (fo, te, wi) + \ - para.words[i][3:] - if te: oldfont = curfont - else: oldfont = 'r' - if curfont in string.uppercase: - te = string.upper(te) - para.words[i] = (fo, te, wi) + para.words[i][3:] - del para.words[-1] - - -# Formatter back-end to draw the text in a window. -# This has an option to draw while the paragraphs are being added, -# to minimize the delay before the user sees anything. -# This manages the entire "document" of the window. -class StdwinBackEnd(SavingBackEnd): - # - def __init__(self, window, drawnow): - self.window = window - self.drawnow = drawnow - self.width = window.getwinsize()[0] - self.selection = None - self.height = 0 - window.setorigin(0, 0) - window.setdocsize(0, 0) - self.d = window.begindrawing() - SavingBackEnd.__init__(self) - # - def finish(self): - self.d.close() - self.d = None - self.window.setdocsize(0, self.height) - # - def addpara(self, p): - self.paralist.append(p) - if self.drawnow: - self.height = \ - p.render(self.d, 0, self.height, self.width) - else: - p.layout(self.width) - p.left = 0 - p.top = self.height - p.right = self.width - p.bottom = self.height + p.height - self.height = p.bottom - # - def resize(self): - self.window.change((0, 0), (self.width, self.height)) - self.width = self.window.getwinsize()[0] - self.height = 0 - for p in self.paralist: - p.layout(self.width) - p.left = 0 - p.top = self.height - p.right = self.width - p.bottom = self.height + p.height - self.height = p.bottom - self.window.change((0, 0), (self.width, self.height)) - self.window.setdocsize(0, self.height) - # - def redraw(self, area): - d = self.window.begindrawing() - (left, top), (right, bottom) = area - d.erase(area) - d.cliprect(area) - for p in self.paralist: - if top < p.bottom and p.top < bottom: - v = p.render(d, p.left, p.top, p.right) - if self.selection: - self.invert(d, self.selection) - d.close() - # - def setselection(self, new): - if new: - long1, long2 = new - pos1 = long1[:3] - pos2 = long2[:3] - new = pos1, pos2 - if new != self.selection: - d = self.window.begindrawing() - if self.selection: - self.invert(d, self.selection) - if new: - self.invert(d, new) - d.close() - self.selection = new - # - def getselection(self): - return self.selection - # - def extractselection(self): - if self.selection: - a, b = self.selection - return self.extractpart(a, b) - else: - return None - # - def invert(self, d, region): - long1, long2 = region - if long1 > long2: long1, long2 = long2, long1 - para1, pos1 = long1 - para2, pos2 = long2 - while para1 < para2: - self.paralist[para1].invert(d, pos1, None) - pos1 = None - para1 = para1 + 1 - self.paralist[para2].invert(d, pos1, pos2) - # - def search(self, prog): - import re, string - if type(prog) is type(''): - prog = re.compile(string.lower(prog)) - if self.selection: - iold = self.selection[0][0] - else: - iold = -1 - hit = None - for i in range(len(self.paralist)): - if i == iold or i < iold and hit: - continue - p = self.paralist[i] - text = string.lower(p.extract()) - match = prog.search(text) - if match: - a, b = match.group(0) - long1 = i, a - long2 = i, b - hit = long1, long2 - if i > iold: - break - if hit: - self.setselection(hit) - i = hit[0][0] - p = self.paralist[i] - self.window.show((p.left, p.top), (p.right, p.bottom)) - return 1 - else: - return 0 - # - def showanchor(self, id): - for i in range(len(self.paralist)): - p = self.paralist[i] - if p.hasanchor(id): - long1 = i, 0 - long2 = i, len(p.extract()) - hit = long1, long2 - self.setselection(hit) - self.window.show( - (p.left, p.top), (p.right, p.bottom)) - break - - -# GL extensions - -class GLFontCache: - # - def __init__(self): - self.reset() - self.setfont('') - # - def reset(self): - self.fontkey = None - self.fonthandle = None - self.fontinfo = None - self.fontcache = {} - # - def close(self): - self.reset() - # - def setfont(self, fontkey): - if fontkey == '': - fontkey = 'Times-Roman 12' - elif ' ' not in fontkey: - fontkey = fontkey + ' 12' - if fontkey == self.fontkey: - return - if self.fontcache.has_key(fontkey): - handle = self.fontcache[fontkey] - else: - import string - i = string.index(fontkey, ' ') - name, sizestr = fontkey[:i], fontkey[i:] - size = eval(sizestr) - key1 = name + ' 1' - key = name + ' ' + `size` - # NB key may differ from fontkey! - if self.fontcache.has_key(key): - handle = self.fontcache[key] - else: - if self.fontcache.has_key(key1): - handle = self.fontcache[key1] - else: - import fm - handle = fm.findfont(name) - self.fontcache[key1] = handle - handle = handle.scalefont(size) - self.fontcache[fontkey] = \ - self.fontcache[key] = handle - self.fontkey = fontkey - if self.fonthandle != handle: - self.fonthandle = handle - self.fontinfo = handle.getfontinfo() - handle.setfont() - - -class GLMeasurer(GLFontCache): - # - def textwidth(self, text): - return self.fonthandle.getstrwidth(text) - # - def baseline(self): - return self.fontinfo[6] - self.fontinfo[3] - # - def lineheight(self): - return self.fontinfo[6] - - -class GLWriter(GLFontCache): - # - # NOTES: - # (1) Use gl.ortho2 to use X pixel coordinates! - # - def text(self, (h, v), text): - import gl, fm - gl.cmov2i(h, v + self.fontinfo[6] - self.fontinfo[3]) - fm.prstr(text) - # - def setfont(self, fontkey): - oldhandle = self.fonthandle - GLFontCache.setfont(fontkey) - if self.fonthandle != oldhandle: - handle.setfont() - - -class GLMeasurerWriter(GLMeasurer, GLWriter): - pass - - -class GLBackEnd(SavingBackEnd): - # - def __init__(self, wid): - import gl - gl.winset(wid) - self.wid = wid - self.width = gl.getsize()[1] - self.height = 0 - self.d = GLMeasurerWriter() - SavingBackEnd.__init__(self) - # - def finish(self): - pass - # - def addpara(self, p): - self.paralist.append(p) - self.height = p.render(self.d, 0, self.height, self.width) - # - def redraw(self): - import gl - gl.winset(self.wid) - width = gl.getsize()[1] - if width != self.width: - setdocsize = 1 - self.width = width - for p in self.paralist: - p.top = p.bottom = None - d = self.d - v = 0 - for p in self.paralist: - v = p.render(d, 0, v, width) diff --git a/Lib/lib-old/grep.py b/Lib/lib-old/grep.py deleted file mode 100644 index 2926746..0000000 --- a/Lib/lib-old/grep.py +++ /dev/null @@ -1,79 +0,0 @@ -# 'grep' - -import regex -from regex_syntax import * - -opt_show_where = 0 -opt_show_filename = 0 -opt_show_lineno = 1 - -def grep(pat, *files): - return ggrep(RE_SYNTAX_GREP, pat, files) - -def egrep(pat, *files): - return ggrep(RE_SYNTAX_EGREP, pat, files) - -def emgrep(pat, *files): - return ggrep(RE_SYNTAX_EMACS, pat, files) - -def ggrep(syntax, pat, files): - if len(files) == 1 and type(files[0]) == type([]): - files = files[0] - global opt_show_filename - opt_show_filename = (len(files) != 1) - syntax = regex.set_syntax(syntax) - try: - prog = regex.compile(pat) - finally: - syntax = regex.set_syntax(syntax) - for filename in files: - fp = open(filename, 'r') - lineno = 0 - while 1: - line = fp.readline() - if not line: break - lineno = lineno + 1 - if prog.search(line) >= 0: - showline(filename, lineno, line, prog) - fp.close() - -def pgrep(pat, *files): - if len(files) == 1 and type(files[0]) == type([]): - files = files[0] - global opt_show_filename - opt_show_filename = (len(files) != 1) - import re - prog = re.compile(pat) - for filename in files: - fp = open(filename, 'r') - lineno = 0 - while 1: - line = fp.readline() - if not line: break - lineno = lineno + 1 - if prog.search(line): - showline(filename, lineno, line, prog) - fp.close() - -def showline(filename, lineno, line, prog): - if line[-1:] == '\n': line = line[:-1] - if opt_show_lineno: - prefix = `lineno`.rjust(3) + ': ' - else: - prefix = '' - if opt_show_filename: - prefix = filename + ': ' + prefix - print prefix + line - if opt_show_where: - start, end = prog.regs()[0] - line = line[:start] - if '\t' not in line: - prefix = ' ' * (len(prefix) + start) - else: - prefix = ' ' * len(prefix) - for c in line: - if c != '\t': c = ' ' - prefix = prefix + c - if start == end: prefix = prefix + '\\' - else: prefix = prefix + '^'*(end-start) - print prefix diff --git a/Lib/lib-old/lockfile.py b/Lib/lib-old/lockfile.py deleted file mode 100644 index cde9b48..0000000 --- a/Lib/lib-old/lockfile.py +++ /dev/null @@ -1,15 +0,0 @@ -import struct, fcntl - -def writelock(f): - _lock(f, fcntl.F_WRLCK) - -def readlock(f): - _lock(f, fcntl.F_RDLCK) - -def unlock(f): - _lock(f, fcntl.F_UNLCK) - -def _lock(f, op): - dummy = fcntl.fcntl(f.fileno(), fcntl.F_SETLKW, - struct.pack('2h8l', op, - 0, 0, 0, 0, 0, 0, 0, 0, 0)) diff --git a/Lib/lib-old/newdir.py b/Lib/lib-old/newdir.py deleted file mode 100644 index 356becc..0000000 --- a/Lib/lib-old/newdir.py +++ /dev/null @@ -1,73 +0,0 @@ -# New dir() function - - -# This should be the new dir(), except that it should still list -# the current local name space by default - -def listattrs(x): - try: - dictkeys = x.__dict__.keys() - except (AttributeError, TypeError): - dictkeys = [] - # - try: - methods = x.__methods__ - except (AttributeError, TypeError): - methods = [] - # - try: - members = x.__members__ - except (AttributeError, TypeError): - members = [] - # - try: - the_class = x.__class__ - except (AttributeError, TypeError): - the_class = None - # - try: - bases = x.__bases__ - except (AttributeError, TypeError): - bases = () - # - total = dictkeys + methods + members - if the_class: - # It's a class instace; add the class's attributes - # that are functions (methods)... - class_attrs = listattrs(the_class) - class_methods = [] - for name in class_attrs: - if is_function(getattr(the_class, name)): - class_methods.append(name) - total = total + class_methods - elif bases: - # It's a derived class; add the base class attributes - for base in bases: - base_attrs = listattrs(base) - total = total + base_attrs - total.sort() - return total - i = 0 - while i+1 < len(total): - if total[i] == total[i+1]: - del total[i+1] - else: - i = i+1 - return total - - -# Helper to recognize functions - -def is_function(x): - return type(x) == type(is_function) - - -# Approximation of builtin dir(); but note that this lists the user's -# variables by default, not the current local name space. - -def dir(x = None): - if x is not None: - return listattrs(x) - else: - import __main__ - return listattrs(__main__) diff --git a/Lib/lib-old/ni.py b/Lib/lib-old/ni.py deleted file mode 100644 index 074f989..0000000 --- a/Lib/lib-old/ni.py +++ /dev/null @@ -1,433 +0,0 @@ -"""New import scheme with package support. - -Quick Reference ---------------- - -- To enable package support, execute "import ni" before importing any - packages. Importing this module automatically installs the relevant - import hooks. - -- To create a package named spam containing sub-modules ham, bacon and - eggs, create a directory spam somewhere on Python's module search - path (i.e. spam's parent directory must be one of the directories in - sys.path or $PYTHONPATH); then create files ham.py, bacon.py and - eggs.py inside spam. - -- To import module ham from package spam and use function hamneggs() - from that module, you can either do - - import spam.ham # *not* "import spam" !!! - spam.ham.hamneggs() - - or - - from spam import ham - ham.hamneggs() - - or - - from spam.ham import hamneggs - hamneggs() - -- Importing just "spam" does not do what you expect: it creates an - empty package named spam if one does not already exist, but it does - not import spam's submodules. The only submodule that is guaranteed - to be imported is spam.__init__, if it exists. Note that - spam.__init__ is a submodule of package spam. It can reference to - spam's namespace via the '__.' prefix, for instance - - __.spam_inited = 1 # Set a package-level variable - - - -Theory of Operation -------------------- - -A Package is a module that can contain other modules. Packages can be -nested. Package introduce dotted names for modules, like P.Q.M, which -could correspond to a file P/Q/M.py found somewhere on sys.path. It -is possible to import a package itself, though this makes little sense -unless the package contains a module called __init__. - -A package has two variables that control the namespace used for -packages and modules, both initialized to sensible defaults the first -time the package is referenced. - -(1) A package's *module search path*, contained in the per-package -variable __path__, defines a list of *directories* where submodules or -subpackages of the package are searched. It is initialized to the -directory containing the package. Setting this variable to None makes -the module search path default to sys.path (this is not quite the same -as setting it to sys.path, since the latter won't track later -assignments to sys.path). - -(2) A package's *import domain*, contained in the per-package variable -__domain__, defines a list of *packages* that are searched (using -their respective module search paths) to satisfy imports. It is -initialized to the list consisting of the package itself, its parent -package, its parent's parent, and so on, ending with the root package -(the nameless package containing all top-level packages and modules, -whose module search path is None, implying sys.path). - -The default domain implements a search algorithm called "expanding -search". An alternative search algorithm called "explicit search" -fixes the import search path to contain only the root package, -requiring the modules in the package to name all imported modules by -their full name. The convention of using '__' to refer to the current -package (both as a per-module variable and in module names) can be -used by packages using explicit search to refer to modules in the same -package; this combination is known as "explicit-relative search". - -The PackageImporter and PackageLoader classes together implement the -following policies: - -- There is a root package, whose name is ''. It cannot be imported - directly but may be referenced, e.g. by using '__' from a top-level - module. - -- In each module or package, the variable '__' contains a reference to - the parent package; in the root package, '__' points to itself. - -- In the name for imported modules (e.g. M in "import M" or "from M - import ..."), a leading '__' refers to the current package (i.e. - the package containing the current module); leading '__.__' and so - on refer to the current package's parent, and so on. The use of - '__' elsewhere in the module name is not supported. - -- Modules are searched using the "expanding search" algorithm by - virtue of the default value for __domain__. - -- If A.B.C is imported, A is searched using __domain__; then - subpackage B is searched in A using its __path__, and so on. - -- Built-in modules have priority: even if a file sys.py exists in a - package, "import sys" imports the built-in sys module. - -- The same holds for frozen modules, for better or for worse. - -- Submodules and subpackages are not automatically loaded when their - parent packages is loaded. - -- The construct "from package import *" is illegal. (It can still be - used to import names from a module.) - -- When "from package import module1, module2, ..." is used, those - modules are explicitly loaded. - -- When a package is loaded, if it has a submodule __init__, that - module is loaded. This is the place where required submodules can - be loaded, the __path__ variable extended, etc. The __init__ module - is loaded even if the package was loaded only in order to create a - stub for a sub-package: if "import P.Q.R" is the first reference to - P, and P has a submodule __init__, P.__init__ is loaded before P.Q - is even searched. - -Caveats: - -- It is possible to import a package that has no __init__ submodule; - this is not particularly useful but there may be useful applications - for it (e.g. to manipulate its search paths from the outside!). - -- There are no special provisions for os.chdir(). If you plan to use - os.chdir() before you have imported all your modules, it is better - not to have relative pathnames in sys.path. (This could actually be - fixed by changing the implementation of path_join() in the hook to - absolutize paths.) - -- Packages and modules are introduced in sys.modules as soon as their - loading is started. When the loading is terminated by an exception, - the sys.modules entries remain around. - -- There are no special measures to support mutually recursive modules, - but it will work under the same conditions where it works in the - flat module space system. - -- Sometimes dummy entries (whose value is None) are entered in - sys.modules, to indicate that a particular module does not exist -- - this is done to speed up the expanding search algorithm when a - module residing at a higher level is repeatedly imported (Python - promises that importing a previously imported module is cheap!) - -- Although dynamically loaded extensions are allowed inside packages, - the current implementation (hardcoded in the interpreter) of their - initialization may cause problems if an extension invokes the - interpreter during its initialization. - -- reload() may find another version of the module only if it occurs on - the package search path. Thus, it keeps the connection to the - package to which the module belongs, but may find a different file. - -XXX Need to have an explicit name for '', e.g. '__root__'. - -""" - - -import imp -import sys -import __builtin__ - -import ihooks -from ihooks import ModuleLoader, ModuleImporter - - -class PackageLoader(ModuleLoader): - - """A subclass of ModuleLoader with package support. - - find_module_in_dir() will succeed if there's a subdirectory with - the given name; load_module() will create a stub for a package and - load its __init__ module if it exists. - - """ - - def find_module_in_dir(self, name, dir): - if dir is not None: - dirname = self.hooks.path_join(dir, name) - if self.hooks.path_isdir(dirname): - return None, dirname, ('', '', 'PACKAGE') - return ModuleLoader.find_module_in_dir(self, name, dir) - - def load_module(self, name, stuff): - file, filename, info = stuff - suff, mode, type = info - if type == 'PACKAGE': - return self.load_package(name, stuff) - if sys.modules.has_key(name): - m = sys.modules[name] - else: - sys.modules[name] = m = imp.new_module(name) - self.set_parent(m) - if type == imp.C_EXTENSION and '.' in name: - return self.load_dynamic(name, stuff) - else: - return ModuleLoader.load_module(self, name, stuff) - - def load_dynamic(self, name, stuff): - file, filename, (suff, mode, type) = stuff - # Hack around restriction in imp.load_dynamic() - i = name.rfind('.') - tail = name[i+1:] - if sys.modules.has_key(tail): - save = sys.modules[tail] - else: - save = None - sys.modules[tail] = imp.new_module(name) - try: - m = imp.load_dynamic(tail, filename, file) - finally: - if save: - sys.modules[tail] = save - else: - del sys.modules[tail] - sys.modules[name] = m - return m - - def load_package(self, name, stuff): - file, filename, info = stuff - if sys.modules.has_key(name): - package = sys.modules[name] - else: - sys.modules[name] = package = imp.new_module(name) - package.__path__ = [filename] - self.init_package(package) - return package - - def init_package(self, package): - self.set_parent(package) - self.set_domain(package) - self.call_init_module(package) - - def set_parent(self, m): - name = m.__name__ - if '.' in name: - name = name[:name.rfind('.')] - else: - name = '' - m.__ = sys.modules[name] - - def set_domain(self, package): - name = package.__name__ - package.__domain__ = domain = [name] - while '.' in name: - name = name[:name.rfind('.')] - domain.append(name) - if name: - domain.append('') - - def call_init_module(self, package): - stuff = self.find_module('__init__', package.__path__) - if stuff: - m = self.load_module(package.__name__ + '.__init__', stuff) - package.__init__ = m - - -class PackageImporter(ModuleImporter): - - """Importer that understands packages and '__'.""" - - def __init__(self, loader = None, verbose = 0): - ModuleImporter.__init__(self, - loader or PackageLoader(None, verbose), verbose) - - def import_module(self, name, globals={}, locals={}, fromlist=[]): - if globals.has_key('__'): - package = globals['__'] - else: - # No calling context, assume in root package - package = sys.modules[''] - if name[:3] in ('__.', '__'): - p = package - name = name[3:] - while name[:3] in ('__.', '__'): - p = p.__ - name = name[3:] - if not name: - return self.finish(package, p, '', fromlist) - if '.' in name: - i = name.find('.') - name, tail = name[:i], name[i:] - else: - tail = '' - mname = p.__name__ and p.__name__+'.'+name or name - m = self.get1(mname) - return self.finish(package, m, tail, fromlist) - if '.' in name: - i = name.find('.') - name, tail = name[:i], name[i:] - else: - tail = '' - for pname in package.__domain__: - mname = pname and pname+'.'+name or name - m = self.get0(mname) - if m: break - else: - raise ImportError, "No such module %s" % name - return self.finish(m, m, tail, fromlist) - - def finish(self, module, m, tail, fromlist): - # Got ....A; now get ....A.B.C.D - yname = m.__name__ - if tail and sys.modules.has_key(yname + tail): # Fast path - yname, tail = yname + tail, '' - m = self.get1(yname) - while tail: - i = tail.find('.', 1) - if i > 0: - head, tail = tail[:i], tail[i:] - else: - head, tail = tail, '' - yname = yname + head - m = self.get1(yname) - - # Got ....A.B.C.D; now finalize things depending on fromlist - if not fromlist: - return module - if '__' in fromlist: - raise ImportError, "Can't import __ from anywhere" - if not hasattr(m, '__path__'): return m - if '*' in fromlist: - raise ImportError, "Can't import * from a package" - for f in fromlist: - if hasattr(m, f): continue - fname = yname + '.' + f - self.get1(fname) - return m - - def get1(self, name): - m = self.get(name) - if not m: - raise ImportError, "No module named %s" % name - return m - - def get0(self, name): - m = self.get(name) - if not m: - sys.modules[name] = None - return m - - def get(self, name): - # Internal routine to get or load a module when its parent exists - if sys.modules.has_key(name): - return sys.modules[name] - if '.' in name: - i = name.rfind('.') - head, tail = name[:i], name[i+1:] - else: - head, tail = '', name - path = sys.modules[head].__path__ - stuff = self.loader.find_module(tail, path) - if not stuff: - return None - sys.modules[name] = m = self.loader.load_module(name, stuff) - if head: - setattr(sys.modules[head], tail, m) - return m - - def reload(self, module): - name = module.__name__ - if '.' in name: - i = name.rfind('.') - head, tail = name[:i], name[i+1:] - path = sys.modules[head].__path__ - else: - tail = name - path = sys.modules[''].__path__ - stuff = self.loader.find_module(tail, path) - if not stuff: - raise ImportError, "No module named %s" % name - return self.loader.load_module(name, stuff) - - def unload(self, module): - if hasattr(module, '__path__'): - raise ImportError, "don't know how to unload packages yet" - PackageImporter.unload(self, module) - - def install(self): - if not sys.modules.has_key(''): - sys.modules[''] = package = imp.new_module('') - package.__path__ = None - self.loader.init_package(package) - for m in sys.modules.values(): - if not m: continue - if not hasattr(m, '__'): - self.loader.set_parent(m) - ModuleImporter.install(self) - - -def install(v = 0): - ihooks.install(PackageImporter(None, v)) - -def uninstall(): - ihooks.uninstall() - -def ni(v = 0): - install(v) - -def no(): - uninstall() - -def test(): - import pdb - try: - testproper() - except: - sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() - print - print sys.last_type, ':', sys.last_value - print - pdb.pm() - -def testproper(): - install(1) - try: - import mactest - print dir(mactest) - raw_input('OK?') - finally: - uninstall() - - -if __name__ == '__main__': - test() -else: - install() diff --git a/Lib/lib-old/packmail.py b/Lib/lib-old/packmail.py deleted file mode 100644 index e569108..0000000 --- a/Lib/lib-old/packmail.py +++ /dev/null @@ -1,111 +0,0 @@ -# Module 'packmail' -- create a self-unpacking shell archive. - -# This module works on UNIX and on the Mac; the archives can unpack -# themselves only on UNIX. - -import os -from stat import ST_MTIME - -# Print help -def help(): - print 'All fns have a file open for writing as first parameter' - print 'pack(f, fullname, name): pack fullname as name' - print 'packsome(f, directory, namelist): selected files from directory' - print 'packall(f, directory): pack all files from directory' - print 'packnotolder(f, directory, name): pack all files from directory' - print ' that are not older than a file there' - print 'packtree(f, directory): pack entire directory tree' - -# Pack one file -def pack(outfp, file, name): - fp = open(file, 'r') - outfp.write('echo ' + name + '\n') - outfp.write('sed "s/^X//" >"' + name + '" <<"!"\n') - while 1: - line = fp.readline() - if not line: break - if line[-1:] != '\n': - line = line + '\n' - outfp.write('X' + line) - outfp.write('!\n') - fp.close() - -# Pack some files from a directory -def packsome(outfp, dirname, names): - for name in names: - print name - file = os.path.join(dirname, name) - pack(outfp, file, name) - -# Pack all files from a directory -def packall(outfp, dirname): - names = os.listdir(dirname) - try: - names.remove('.') - except: - pass - try: - names.remove('..') - except: - pass - names.sort() - packsome(outfp, dirname, names) - -# Pack all files from a directory that are not older than a give one -def packnotolder(outfp, dirname, oldest): - names = os.listdir(dirname) - try: - names.remove('.') - except: - pass - try: - names.remove('..') - except: - pass - oldest = os.path.join(dirname, oldest) - st = os.stat(oldest) - mtime = st[ST_MTIME] - todo = [] - for name in names: - print name, '...', - st = os.stat(os.path.join(dirname, name)) - if st[ST_MTIME] >= mtime: - print 'Yes.' - todo.append(name) - else: - print 'No.' - todo.sort() - packsome(outfp, dirname, todo) - -# Pack a whole tree (no exceptions) -def packtree(outfp, dirname): - print 'packtree', dirname - outfp.write('mkdir ' + unixfix(dirname) + '\n') - names = os.listdir(dirname) - try: - names.remove('.') - except: - pass - try: - names.remove('..') - except: - pass - subdirs = [] - for name in names: - fullname = os.path.join(dirname, name) - if os.path.isdir(fullname): - subdirs.append(fullname) - else: - print 'pack', fullname - pack(outfp, fullname, unixfix(fullname)) - for subdirname in subdirs: - packtree(outfp, subdirname) - -def unixfix(name): - comps = name.split(os.sep) - res = '' - for comp in comps: - if comp: - if res: res = res + '/' - res = res + comp - return res diff --git a/Lib/lib-old/poly.py b/Lib/lib-old/poly.py deleted file mode 100644 index fe6a1dc..0000000 --- a/Lib/lib-old/poly.py +++ /dev/null @@ -1,52 +0,0 @@ -# module 'poly' -- Polynomials - -# A polynomial is represented by a list of coefficients, e.g., -# [1, 10, 5] represents 1*x**0 + 10*x**1 + 5*x**2 (or 1 + 10x + 5x**2). -# There is no way to suppress internal zeros; trailing zeros are -# taken out by normalize(). - -def normalize(p): # Strip unnecessary zero coefficients - n = len(p) - while n: - if p[n-1]: return p[:n] - n = n-1 - return [] - -def plus(a, b): - if len(a) < len(b): a, b = b, a # make sure a is the longest - res = a[:] # make a copy - for i in range(len(b)): - res[i] = res[i] + b[i] - return normalize(res) - -def minus(a, b): - neg_b = map(lambda x: -x, b[:]) - return plus(a, neg_b) - -def one(power, coeff): # Representation of coeff * x**power - res = [] - for i in range(power): res.append(0) - return res + [coeff] - -def times(a, b): - res = [] - for i in range(len(a)): - for j in range(len(b)): - res = plus(res, one(i+j, a[i]*b[j])) - return res - -def power(a, n): # Raise polynomial a to the positive integral power n - if n == 0: return [1] - if n == 1: return a - if n/2*2 == n: - b = power(a, n/2) - return times(b, b) - return times(power(a, n-1), a) - -def der(a): # First derivative - res = a[1:] - for i in range(len(res)): - res[i] = res[i] * (i+1) - return res - -# Computing a primitive function would require rational arithmetic... diff --git a/Lib/lib-old/rand.py b/Lib/lib-old/rand.py deleted file mode 100644 index a557b69..0000000 --- a/Lib/lib-old/rand.py +++ /dev/null @@ -1,13 +0,0 @@ -# Module 'rand' -# Don't use unless you want compatibility with C's rand()! - -import whrandom - -def srand(seed): - whrandom.seed(seed%256, seed/256%256, seed/65536%256) - -def rand(): - return int(whrandom.random() * 32768.0) % 32768 - -def choice(seq): - return seq[rand() % len(seq)] diff --git a/Lib/lib-old/statcache.py b/Lib/lib-old/statcache.py deleted file mode 100644 index d478393..0000000 --- a/Lib/lib-old/statcache.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Maintain a cache of stat() information on files. - -There are functions to reset the cache or to selectively remove items. -""" - -import warnings -warnings.warn("The statcache module is obsolete. Use os.stat() instead.", - DeprecationWarning) -del warnings - -import os as _os -from stat import * - -__all__ = ["stat","reset","forget","forget_prefix","forget_dir", - "forget_except_prefix","isdir"] - -# The cache. Keys are pathnames, values are os.stat outcomes. -# Remember that multiple threads may be calling this! So, e.g., that -# path in cache returns 1 doesn't mean the cache will still contain -# path on the next line. Code defensively. - -cache = {} - -def stat(path): - """Stat a file, possibly out of the cache.""" - ret = cache.get(path, None) - if ret is None: - cache[path] = ret = _os.stat(path) - return ret - -def reset(): - """Clear the cache.""" - cache.clear() - -# For thread saftey, always use forget() internally too. -def forget(path): - """Remove a given item from the cache, if it exists.""" - try: - del cache[path] - except KeyError: - pass - -def forget_prefix(prefix): - """Remove all pathnames with a given prefix.""" - for path in cache.keys(): - if path.startswith(prefix): - forget(path) - -def forget_dir(prefix): - """Forget a directory and all entries except for entries in subdirs.""" - - # Remove trailing separator, if any. This is tricky to do in a - # x-platform way. For example, Windows accepts both / and \ as - # separators, and if there's nothing *but* a separator we want to - # preserve that this is the root. Only os.path has the platform - # knowledge we need. - from os.path import split, join - prefix = split(join(prefix, "xxx"))[0] - forget(prefix) - for path in cache.keys(): - # First check that the path at least starts with the prefix, so - # that when it doesn't we can avoid paying for split(). - if path.startswith(prefix) and split(path)[0] == prefix: - forget(path) - -def forget_except_prefix(prefix): - """Remove all pathnames except with a given prefix. - - Normally used with prefix = '/' after a chdir(). - """ - - for path in cache.keys(): - if not path.startswith(prefix): - forget(path) - -def isdir(path): - """Return True if directory, else False.""" - try: - st = stat(path) - except _os.error: - return False - return S_ISDIR(st.st_mode) diff --git a/Lib/lib-old/tb.py b/Lib/lib-old/tb.py deleted file mode 100644 index 9063559..0000000 --- a/Lib/lib-old/tb.py +++ /dev/null @@ -1,177 +0,0 @@ -# Print tracebacks, with a dump of local variables. -# Also an interactive stack trace browser. -# Note -- this module is obsolete -- use pdb.pm() instead. - -import sys -import os -from stat import * -import linecache - -def br(): browser(sys.last_traceback) - -def tb(): printtb(sys.last_traceback) - -def browser(tb): - if not tb: - print 'No traceback.' - return - tblist = [] - while tb: - tblist.append(tb) - tb = tb.tb_next - ptr = len(tblist)-1 - tb = tblist[ptr] - while 1: - if tb != tblist[ptr]: - tb = tblist[ptr] - print `ptr` + ':', - printtbheader(tb) - try: - line = raw_input('TB: ') - except KeyboardInterrupt: - print '\n[Interrupted]' - break - except EOFError: - print '\n[EOF]' - break - cmd = line.strip() - if cmd: - if cmd == 'quit': - break - elif cmd == 'list': - browserlist(tb) - elif cmd == 'up': - if ptr-1 >= 0: ptr = ptr-1 - else: print 'Bottom of stack.' - elif cmd == 'down': - if ptr+1 < len(tblist): ptr = ptr+1 - else: print 'Top of stack.' - elif cmd == 'locals': - printsymbols(tb.tb_frame.f_locals) - elif cmd == 'globals': - printsymbols(tb.tb_frame.f_globals) - elif cmd in ('?', 'help'): - browserhelp() - else: - browserexec(tb, cmd) - -def browserlist(tb): - filename = tb.tb_frame.f_code.co_filename - lineno = tb.tb_lineno - last = lineno - first = max(1, last-10) - for i in range(first, last+1): - if i == lineno: prefix = '***' + `i`.rjust(4) + ':' - else: prefix = `i`.rjust(7) + ':' - line = linecache.getline(filename, i) - if line[-1:] == '\n': line = line[:-1] - print prefix + line - -def browserexec(tb, cmd): - locals = tb.tb_frame.f_locals - globals = tb.tb_frame.f_globals - try: - exec cmd+'\n' in globals, locals - except: - t, v = sys.exc_info()[:2] - print '*** Exception:', - if type(t) is type(''): - print t, - else: - print t.__name__, - if v is not None: - print ':', v, - print - print 'Type help to get help.' - -def browserhelp(): - print - print ' This is the traceback browser. Commands are:' - print ' up : move one level up in the call stack' - print ' down : move one level down in the call stack' - print ' locals : print all local variables at this level' - print ' globals : print all global variables at this level' - print ' list : list source code around the failure' - print ' help : print help (what you are reading now)' - print ' quit : back to command interpreter' - print ' Typing any other 1-line statement will execute it' - print ' using the current level\'s symbol tables' - print - -def printtb(tb): - while tb: - print1tb(tb) - tb = tb.tb_next - -def print1tb(tb): - printtbheader(tb) - if tb.tb_frame.f_locals is not tb.tb_frame.f_globals: - printsymbols(tb.tb_frame.f_locals) - -def printtbheader(tb): - filename = tb.tb_frame.f_code.co_filename - lineno = tb.tb_lineno - info = '"' + filename + '"(' + `lineno` + ')' - line = linecache.getline(filename, lineno) - if line: - info = info + ': ' + line.strip() - print info - -def printsymbols(d): - keys = d.keys() - keys.sort() - for name in keys: - print ' ' + name.ljust(12) + ':', - printobject(d[name], 4) - print - -def printobject(v, maxlevel): - if v is None: - print 'None', - elif type(v) in (type(0), type(0.0)): - print v, - elif type(v) is type(''): - if len(v) > 20: - print `v[:17] + '...'`, - else: - print `v`, - elif type(v) is type(()): - print '(', - printlist(v, maxlevel) - print ')', - elif type(v) is type([]): - print '[', - printlist(v, maxlevel) - print ']', - elif type(v) is type({}): - print '{', - printdict(v, maxlevel) - print '}', - else: - print v, - -def printlist(v, maxlevel): - n = len(v) - if n == 0: return - if maxlevel <= 0: - print '...', - return - for i in range(min(6, n)): - printobject(v[i], maxlevel-1) - if i+1 < n: print ',', - if n > 6: print '...', - -def printdict(v, maxlevel): - keys = v.keys() - n = len(keys) - if n == 0: return - if maxlevel <= 0: - print '...', - return - keys.sort() - for i in range(min(6, n)): - key = keys[i] - print `key` + ':', - printobject(v[key], maxlevel-1) - if i+1 < n: print ',', - if n > 6: print '...', diff --git a/Lib/lib-old/tzparse.py b/Lib/lib-old/tzparse.py deleted file mode 100644 index 12468b5..0000000 --- a/Lib/lib-old/tzparse.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Parse a timezone specification.""" - -# XXX Unfinished. -# XXX Only the typical form "XXXhhYYY;ddd/hh,ddd/hh" is currently supported. - -import warnings -warnings.warn( - "The tzparse module is obsolete and will disappear in the future", - DeprecationWarning) - -tzpat = ('^([A-Z][A-Z][A-Z])([-+]?[0-9]+)([A-Z][A-Z][A-Z]);' - '([0-9]+)/([0-9]+),([0-9]+)/([0-9]+)$') - -tzprog = None - -def tzparse(tzstr): - """Given a timezone spec, return a tuple of information - (tzname, delta, dstname, daystart, hourstart, dayend, hourend), - where 'tzname' is the name of the timezone, 'delta' is the offset - in hours from GMT, 'dstname' is the name of the daylight-saving - timezone, and 'daystart'/'hourstart' and 'dayend'/'hourend' - specify the starting and ending points for daylight saving time.""" - global tzprog - if tzprog is None: - import re - tzprog = re.compile(tzpat) - match = tzprog.match(tzstr) - if not match: - raise ValueError, 'not the TZ syntax I understand' - subs = [] - for i in range(1, 8): - subs.append(match.group(i)) - for i in (1, 3, 4, 5, 6): - subs[i] = eval(subs[i]) - [tzname, delta, dstname, daystart, hourstart, dayend, hourend] = subs - return (tzname, delta, dstname, daystart, hourstart, dayend, hourend) - -def tzlocaltime(secs, params): - """Given a Unix time in seconds and a tuple of information about - a timezone as returned by tzparse(), return the local time in the - form (year, month, day, hour, min, sec, yday, wday, tzname).""" - import time - (tzname, delta, dstname, daystart, hourstart, dayend, hourend) = params - year, month, days, hours, mins, secs, yday, wday, isdst = \ - time.gmtime(secs - delta*3600) - if (daystart, hourstart) <= (yday+1, hours) < (dayend, hourend): - tzname = dstname - hours = hours + 1 - return year, month, days, hours, mins, secs, yday, wday, tzname - -def tzset(): - """Determine the current timezone from the "TZ" environment variable.""" - global tzparams, timezone, altzone, daylight, tzname - import os - tzstr = os.environ['TZ'] - tzparams = tzparse(tzstr) - timezone = tzparams[1] * 3600 - altzone = timezone - 3600 - daylight = 1 - tzname = tzparams[0], tzparams[2] - -def isdst(secs): - """Return true if daylight-saving time is in effect for the given - Unix time in the current timezone.""" - import time - (tzname, delta, dstname, daystart, hourstart, dayend, hourend) = \ - tzparams - year, month, days, hours, mins, secs, yday, wday, isdst = \ - time.gmtime(secs - delta*3600) - return (daystart, hourstart) <= (yday+1, hours) < (dayend, hourend) - -tzset() - -def localtime(secs): - """Get the local time in the current timezone.""" - return tzlocaltime(secs, tzparams) - -def test(): - from time import asctime, gmtime - import time, sys - now = time.time() - x = localtime(now) - tm = x[:-1] + (0,) - print 'now =', now, '=', asctime(tm), x[-1] - now = now - now % (24*3600) - if sys.argv[1:]: now = now + eval(sys.argv[1]) - x = gmtime(now) - tm = x[:-1] + (0,) - print 'gmtime =', now, '=', asctime(tm), 'yday =', x[-2] - jan1 = now - x[-2]*24*3600 - x = localtime(jan1) - tm = x[:-1] + (0,) - print 'jan1 =', jan1, '=', asctime(tm), x[-1] - for d in range(85, 95) + range(265, 275): - t = jan1 + d*24*3600 - x = localtime(t) - tm = x[:-1] + (0,) - print 'd =', d, 't =', t, '=', asctime(tm), x[-1] diff --git a/Lib/lib-old/util.py b/Lib/lib-old/util.py deleted file mode 100644 index 104af1e..0000000 --- a/Lib/lib-old/util.py +++ /dev/null @@ -1,25 +0,0 @@ -# Module 'util' -- some useful functions that don't fit elsewhere - -# NB: These are now built-in functions, but this module is provided -# for compatibility. Don't use in new programs unless you need backward -# compatibility (i.e. need to run with old interpreters). - - -# Remove an item from a list. -# No complaints if it isn't in the list at all. -# If it occurs more than once, remove the first occurrence. -# -def remove(item, list): - if item in list: list.remove(item) - - -# Return a string containing a file's contents. -# -def readfile(fn): - return readopenfile(open(fn, 'r')) - - -# Read an open file until EOF. -# -def readopenfile(fp): - return fp.read() diff --git a/Lib/lib-old/whatsound.py b/Lib/lib-old/whatsound.py deleted file mode 100644 index 1b1df23..0000000 --- a/Lib/lib-old/whatsound.py +++ /dev/null @@ -1 +0,0 @@ -from sndhdr import * diff --git a/Lib/lib-old/whrandom.py b/Lib/lib-old/whrandom.py deleted file mode 100644 index bc0d1a4..0000000 --- a/Lib/lib-old/whrandom.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Wichman-Hill random number generator. - -Wichmann, B. A. & Hill, I. D. (1982) -Algorithm AS 183: -An efficient and portable pseudo-random number generator -Applied Statistics 31 (1982) 188-190 - -see also: - Correction to Algorithm AS 183 - Applied Statistics 33 (1984) 123 - - McLeod, A. I. (1985) - A remark on Algorithm AS 183 - Applied Statistics 34 (1985),198-200 - - -USE: -whrandom.random() yields double precision random numbers - uniformly distributed between 0 and 1. - -whrandom.seed(x, y, z) must be called before whrandom.random() - to seed the generator - -There is also an interface to create multiple independent -random generators, and to choose from other ranges. - - - -Multi-threading note: the random number generator used here is not -thread-safe; it is possible that nearly simultaneous calls in -different theads return the same random value. To avoid this, you -have to use a lock around all calls. (I didn't want to slow this -down in the serial case by using a lock here.) -""" - -import warnings -warnings.warn("the whrandom module is deprecated; please use the random module", - DeprecationWarning) - -# Translated by Guido van Rossum from C source provided by -# Adrian Baddeley. - - -class whrandom: - def __init__(self, x = 0, y = 0, z = 0): - """Initialize an instance. - Without arguments, initialize from current time. - With arguments (x, y, z), initialize from them.""" - self.seed(x, y, z) - - def seed(self, x = 0, y = 0, z = 0): - """Set the seed from (x, y, z). - These must be integers in the range [0, 256).""" - if not type(x) == type(y) == type(z) == type(0): - raise TypeError, 'seeds must be integers' - if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): - raise ValueError, 'seeds must be in range(0, 256)' - if 0 == x == y == z: - # Initialize from current time - import time - t = long(time.time() * 256) - t = int((t&0xffffff) ^ (t>>24)) - t, x = divmod(t, 256) - t, y = divmod(t, 256) - t, z = divmod(t, 256) - # Zero is a poor seed, so substitute 1 - self._seed = (x or 1, y or 1, z or 1) - - def random(self): - """Get the next random number in the range [0.0, 1.0).""" - # This part is thread-unsafe: - # BEGIN CRITICAL SECTION - x, y, z = self._seed - # - x = (171 * x) % 30269 - y = (172 * y) % 30307 - z = (170 * z) % 30323 - # - self._seed = x, y, z - # END CRITICAL SECTION - # - return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 - - def uniform(self, a, b): - """Get a random number in the range [a, b).""" - return a + (b-a) * self.random() - - def randint(self, a, b): - """Get a random integer in the range [a, b] including - both end points. - - (Deprecated; use randrange below.)""" - return self.randrange(a, b+1) - - def choice(self, seq): - """Choose a random element from a non-empty sequence.""" - return seq[int(self.random() * len(seq))] - - def randrange(self, start, stop=None, step=1, int=int, default=None): - """Choose a random item from range(start, stop[, step]). - - 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 'default' arguments.""" - # This code is a bit messy to make it fast for the - # common case while still doing adequate error checking - istart = int(start) - if istart != start: - raise ValueError, "non-integer arg 1 for randrange()" - if stop is default: - if istart > 0: - return int(self.random() * istart) - raise ValueError, "empty range for randrange()" - istop = int(stop) - if istop != stop: - raise ValueError, "non-integer stop for randrange()" - if step == 1: - if istart < istop: - return istart + int(self.random() * - (istop - istart)) - raise ValueError, "empty range for randrange()" - istep = int(step) - if istep != step: - raise ValueError, "non-integer step for randrange()" - if istep > 0: - n = (istop - istart + istep - 1) / istep - elif istep < 0: - n = (istop - istart + istep + 1) / istep - else: - raise ValueError, "zero step for randrange()" - - if n <= 0: - raise ValueError, "empty range for randrange()" - return istart + istep*int(self.random() * n) - - -# Initialize from the current time -_inst = whrandom() -seed = _inst.seed -random = _inst.random -uniform = _inst.uniform -randint = _inst.randint -choice = _inst.choice -randrange = _inst.randrange diff --git a/Lib/lib-old/zmod.py b/Lib/lib-old/zmod.py deleted file mode 100644 index 55f49df..0000000 --- a/Lib/lib-old/zmod.py +++ /dev/null @@ -1,94 +0,0 @@ -# module 'zmod' - -# Compute properties of mathematical "fields" formed by taking -# Z/n (the whole numbers modulo some whole number n) and an -# irreducible polynomial (i.e., a polynomial with only complex zeros), -# e.g., Z/5 and X**2 + 2. -# -# The field is formed by taking all possible linear combinations of -# a set of d base vectors (where d is the degree of the polynomial). -# -# Note that this procedure doesn't yield a field for all combinations -# of n and p: it may well be that some numbers have more than one -# inverse and others have none. This is what we check. -# -# Remember that a field is a ring where each element has an inverse. -# A ring has commutative addition and multiplication, a zero and a one: -# 0*x = x*0 = 0, 0+x = x+0 = x, 1*x = x*1 = x. Also, the distributive -# property holds: a*(b+c) = a*b + b*c. -# (XXX I forget if this is an axiom or follows from the rules.) - -import poly - - -# Example N and polynomial - -N = 5 -P = poly.plus(poly.one(0, 2), poly.one(2, 1)) # 2 + x**2 - - -# Return x modulo y. Returns >= 0 even if x < 0. - -def mod(x, y): - return divmod(x, y)[1] - - -# Normalize a polynomial modulo n and modulo p. - -def norm(a, n, p): - a = poly.modulo(a, p) - a = a[:] - for i in range(len(a)): a[i] = mod(a[i], n) - a = poly.normalize(a) - return a - - -# Make a list of all n^d elements of the proposed field. - -def make_all(mat): - all = [] - for row in mat: - for a in row: - all.append(a) - return all - -def make_elements(n, d): - if d == 0: return [poly.one(0, 0)] - sub = make_elements(n, d-1) - all = [] - for a in sub: - for i in range(n): - all.append(poly.plus(a, poly.one(d-1, i))) - return all - -def make_inv(all, n, p): - x = poly.one(1, 1) - inv = [] - for a in all: - inv.append(norm(poly.times(a, x), n, p)) - return inv - -def checkfield(n, p): - all = make_elements(n, len(p)-1) - inv = make_inv(all, n, p) - all1 = all[:] - inv1 = inv[:] - all1.sort() - inv1.sort() - if all1 == inv1: print 'BINGO!' - else: - print 'Sorry:', n, p - print all - print inv - -def rj(s, width): - if type(s) is not type(''): s = `s` - n = len(s) - if n >= width: return s - return ' '*(width - n) + s - -def lj(s, width): - if type(s) is not type(''): s = `s` - n = len(s) - if n >= width: return s - return s + ' '*(width - n) diff --git a/Lib/lib-tk/Tix.py b/Lib/lib-tk/Tix.py index 2fb1307..14c3c24 100755 --- a/Lib/lib-tk/Tix.py +++ b/Lib/lib-tk/Tix.py @@ -1541,8 +1541,8 @@ class Tree(TixWidget): '''This command is used to indicate whether the entry given by entryPath has children entries and whether the children are visible. mode must be one of open, close or none. If mode is set to open, a (+) - indicator is drawn next to the entry. If mode is set to close, a (-) - indicator is drawn next to the entry. If mode is set to none, no + indicator is drawn next the the entry. If mode is set to close, a (-) + indicator is drawn next the the entry. If mode is set to none, no indicators will be drawn for this entry. The default mode is none. The open mode indicates the entry has hidden children and this entry can be opened by the user. The close mode indicates that all the children of the @@ -1773,6 +1773,7 @@ class CObjView(TixWidget): # FIXME: It should inherit -superclass tixScrolledWidget pass + class Grid(TixWidget): '''The Tix Grid command creates a new window and makes it into a tixGrid widget. Additional options, may be specified on the command @@ -1787,26 +1788,101 @@ class Grid(TixWidget): border. Subwidgets - None''' - pass - + # valid specific resources as of Tk 8.4 + # editdonecmd, editnotifycmd, floatingcols, floatingrows, formatcmd, + # highlightbackground, highlightcolor, leftmargin, itemtype, selectmode, + # selectunit, topmargin, + def __init__(self, master=None, cnf={}, **kw): + static= [] + self.cnf= cnf + TixWidget.__init__(self, master, 'tixGrid', static, cnf, kw) + + # valid options as of Tk 8.4 + # anchor, bdtype, cget, configure, delete, dragsite, dropsite, entrycget, edit + # entryconfigure, format, geometryinfo, info, index, move, nearest, selection + # set, size, unset, xview, yview # def anchor option ?args ...? + def anchor_get(self): + "Get the (x,y) coordinate of the current anchor cell" + return self._getints(self.tk.call(self, 'anchor', 'get')) + # def bdtype # def delete dim from ?to? + def delete_row(self, from_, to=None): + """Delete rows between from_ and to inclusive. + If to is not provided, delete only row at from_""" + if to is None: + self.tk.call(self, 'delete', 'row', from_) + else: + self.tk.call(self, 'delete', 'row', from_, to) + def delete_column(self, from_, to=None): + """Delete columns between from_ and to inclusive. + If to is not provided, delete only column at from_""" + if to is None: + self.tk.call(self, 'delete', 'column', from_) + else: + self.tk.call(self, 'delete', 'column', from_, to) # def edit apply # def edit set x y - # def entrycget x y option - # def entryconfigure x y ?option? ?value option value ...? + + def entrycget(self, x, y, option): + "Get the option value for cell at (x,y)" + return self.tk.call(self, 'entrycget', x, y, option) + + def entryconfigure(self, x, y, **kw): + return self.tk.call(self, 'entryconfigure', x, y, *self._options(None, kw)) # def format # def index + + def info_exists(self, x, y): + "Return True if display item exists at (x,y)" + return bool(int(self.tk.call(self, 'info', 'exists', x, y))) + + def info_bbox(self, x, y): + # This seems to always return '', at least for 'text' displayitems + return self.tk.call(self, 'info', 'bbox', x, y) + + def nearest(self, x, y): + "Return coordinate of cell nearest pixel coordinate (x,y)" + return self._getints(self.tk.call(self, 'nearest', x, y)) + + # def selection adjust + # def selection clear + # def selection includes + # def selection set + # def selection toggle # def move dim from to offset - # def set x y ?-itemtype type? ?option value...? + + def set(self, x, y, itemtype=None, **kw): + args= self._options(self.cnf, kw) + if itemtype is not None: + args= ('-itemtype', itemtype) + args + self.tk.call(self, 'set', x, y, *args) + # def size dim index ?option value ...? # def unset x y - # def xview - # def yview -class ScrolledGrid(TixWidget): + def xview(self): + return self._getdoubles(self.tk.call(self, 'xview')) + def xview_moveto(self, fraction): + self.tk.call(self,'xview', 'moveto', fraction) + def xview_scroll(self, count, what="units"): + "Scroll right (count>0) or left <count> of units|pages" + self.tk.call(self, 'xview', 'scroll', count, what) + + def yview(self): + return self._getdoubles(self.tk.call(self, 'yview')) + def yview_moveto(self, fraction): + self.tk.call(self,'ysview', 'moveto', fraction) + def yview_scroll(self, count, what="units"): + "Scroll down (count>0) or up <count> of units|pages" + self.tk.call(self, 'yview', 'scroll', count, what) + +class ScrolledGrid(Grid): '''Scrolled Grid widgets''' # FIXME: It should inherit -superclass tixScrolledWidget - pass + def __init__(self, master=None, cnf={}, **kw): + static= [] + self.cnf= cnf + TixWidget.__init__(self, master, 'tixScrolledGrid', static, cnf, kw) diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py index d600cd7..0ba954e 100644 --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -449,18 +449,15 @@ class Misc: # I'd rather use time.sleep(ms*0.001) self.tk.call('after', ms) else: - # XXX Disgusting hack to clean up after calling func - tmp = [] - def callit(func=func, args=args, self=self, tmp=tmp): + def callit(): try: func(*args) finally: try: - self.deletecommand(tmp[0]) + self.deletecommand(name) except TclError: pass name = self._register(callit) - tmp.append(name) return self.tk.call('after', ms, name) def after_idle(self, func, *args): """Call FUNC once if the Tcl main loop has no event to @@ -486,7 +483,24 @@ class Misc: def bell(self, displayof=0): """Ring a display's bell.""" self.tk.call(('bell',) + self._displayof(displayof)) + # Clipboard handling: + def clipboard_get(self, **kw): + """Retrieve data from the clipboard on window's display. + + The window keyword defaults to the root window of the Tkinter + application. + + The type keyword specifies the form in which the data is + to be returned and should be an atom name such as STRING + or FILE_NAME. Type defaults to STRING. + + This command is equivalent to: + + selection_get(CLIPBOARD) + """ + return self.tk.call(('clipboard', 'get') + self._options(kw)) + def clipboard_clear(self, **kw): """Clear the data in the Tk clipboard. diff --git a/Lib/lib-tk/tkFont.py b/Lib/lib-tk/tkFont.py index 5b5a6ba..15dea2e 100644 --- a/Lib/lib-tk/tkFont.py +++ b/Lib/lib-tk/tkFont.py @@ -108,7 +108,9 @@ class Font: try: if self.delete_font: self._call("font", "delete", self.name) - except (AttributeError, Tkinter.TclError): + except (KeyboardInterrupt, SystemExit): + raise + except Exception: pass def copy(self): diff --git a/Lib/linecache.py b/Lib/linecache.py index 2ccc6c6..f49695a 100644 --- a/Lib/linecache.py +++ b/Lib/linecache.py @@ -10,8 +10,8 @@ import os __all__ = ["getline", "clearcache", "checkcache"] -def getline(filename, lineno): - lines = getlines(filename) +def getline(filename, lineno, module_globals=None): + lines = getlines(filename, module_globals) if 1 <= lineno <= len(lines): return lines[lineno-1] else: @@ -30,14 +30,14 @@ def clearcache(): cache = {} -def getlines(filename): +def getlines(filename, module_globals=None): """Get the lines for a file from the cache. Update the cache if it doesn't contain an entry for this file already.""" if filename in cache: return cache[filename][2] else: - return updatecache(filename) + return updatecache(filename, module_globals) def checkcache(filename=None): @@ -54,6 +54,8 @@ def checkcache(filename=None): for filename in filenames: size, mtime, lines, fullname = cache[filename] + if mtime is None: + continue # no-op for files loaded via a __loader__ try: stat = os.stat(fullname) except os.error: @@ -63,7 +65,7 @@ def checkcache(filename=None): del cache[filename] -def updatecache(filename): +def updatecache(filename, module_globals=None): """Update a cache entry and return its list of lines. If something's wrong, print a message, discard the cache entry, and return an empty list.""" @@ -72,12 +74,34 @@ def updatecache(filename): del cache[filename] if not filename or filename[0] + filename[-1] == '<>': return [] + fullname = filename try: stat = os.stat(fullname) except os.error, msg: - # Try looking through the module search path. basename = os.path.split(filename)[1] + + # Try for a __loader__, if available + if module_globals and '__loader__' in module_globals: + name = module_globals.get('__name__') + loader = module_globals['__loader__'] + get_source = getattr(loader, 'get_source', None) + + if name and get_source: + if basename.startswith(name.split('.')[-1]+'.'): + try: + data = get_source(name) + except (ImportError, IOError): + pass + else: + cache[filename] = ( + len(data), None, + [line+'\n' for line in data.splitlines()], fullname + ) + return cache[filename][2] + + # Try looking through the module search path. + for dirname in sys.path: # When using imputil, sys.path may contain things other than # strings; ignore them when it happens. diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 7db0dab..9798931 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -719,7 +719,7 @@ class StreamHandler(Handler): If strm is not specified, sys.stderr is used. """ Handler.__init__(self) - if not strm: + if strm is None: strm = sys.stderr self.stream = strm self.formatter = None diff --git a/Lib/mimetools.py b/Lib/mimetools.py index 0b698ac..8c1cc19 100644 --- a/Lib/mimetools.py +++ b/Lib/mimetools.py @@ -127,7 +127,10 @@ def choose_boundary(): import time if _prefix is None: import socket - hostid = socket.gethostbyname(socket.gethostname()) + try: + hostid = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + hostid = '127.0.0.1' try: uid = repr(os.getuid()) except AttributeError: diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 7a8b765..bee2ff7 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -315,162 +315,171 @@ def read_mime_types(file): return db.types_map[True] -suffix_map = { - '.tgz': '.tar.gz', - '.taz': '.tar.gz', - '.tz': '.tar.gz', - } - -encodings_map = { - '.gz': 'gzip', - '.Z': 'compress', - } - -# Before adding new types, make sure they are either registered with IANA, at -# http://www.isi.edu/in-notes/iana/assignments/media-types -# or extensions, i.e. using the x- prefix - -# If you add to these, please keep them sorted! -types_map = { - '.a' : 'application/octet-stream', - '.ai' : 'application/postscript', - '.aif' : 'audio/x-aiff', - '.aifc' : 'audio/x-aiff', - '.aiff' : 'audio/x-aiff', - '.au' : 'audio/basic', - '.avi' : 'video/x-msvideo', - '.bat' : 'text/plain', - '.bcpio' : 'application/x-bcpio', - '.bin' : 'application/octet-stream', - '.bmp' : 'image/x-ms-bmp', - '.c' : 'text/plain', - # Duplicates :( - '.cdf' : 'application/x-cdf', - '.cdf' : 'application/x-netcdf', - '.cpio' : 'application/x-cpio', - '.csh' : 'application/x-csh', - '.css' : 'text/css', - '.dll' : 'application/octet-stream', - '.doc' : 'application/msword', - '.dot' : 'application/msword', - '.dvi' : 'application/x-dvi', - '.eml' : 'message/rfc822', - '.eps' : 'application/postscript', - '.etx' : 'text/x-setext', - '.exe' : 'application/octet-stream', - '.gif' : 'image/gif', - '.gtar' : 'application/x-gtar', - '.h' : 'text/plain', - '.hdf' : 'application/x-hdf', - '.htm' : 'text/html', - '.html' : 'text/html', - '.ief' : 'image/ief', - '.jpe' : 'image/jpeg', - '.jpeg' : 'image/jpeg', - '.jpg' : 'image/jpeg', - '.js' : 'application/x-javascript', - '.ksh' : 'text/plain', - '.latex' : 'application/x-latex', - '.m1v' : 'video/mpeg', - '.man' : 'application/x-troff-man', - '.me' : 'application/x-troff-me', - '.mht' : 'message/rfc822', - '.mhtml' : 'message/rfc822', - '.mif' : 'application/x-mif', - '.mov' : 'video/quicktime', - '.movie' : 'video/x-sgi-movie', - '.mp2' : 'audio/mpeg', - '.mp3' : 'audio/mpeg', - '.mpa' : 'video/mpeg', - '.mpe' : 'video/mpeg', - '.mpeg' : 'video/mpeg', - '.mpg' : 'video/mpeg', - '.ms' : 'application/x-troff-ms', - '.nc' : 'application/x-netcdf', - '.nws' : 'message/rfc822', - '.o' : 'application/octet-stream', - '.obj' : 'application/octet-stream', - '.oda' : 'application/oda', - '.p12' : 'application/x-pkcs12', - '.p7c' : 'application/pkcs7-mime', - '.pbm' : 'image/x-portable-bitmap', - '.pdf' : 'application/pdf', - '.pfx' : 'application/x-pkcs12', - '.pgm' : 'image/x-portable-graymap', - '.pl' : 'text/plain', - '.png' : 'image/png', - '.pnm' : 'image/x-portable-anymap', - '.pot' : 'application/vnd.ms-powerpoint', - '.ppa' : 'application/vnd.ms-powerpoint', - '.ppm' : 'image/x-portable-pixmap', - '.pps' : 'application/vnd.ms-powerpoint', - '.ppt' : 'application/vnd.ms-powerpoint', - '.ps' : 'application/postscript', - '.pwz' : 'application/vnd.ms-powerpoint', - '.py' : 'text/x-python', - '.pyc' : 'application/x-python-code', - '.pyo' : 'application/x-python-code', - '.qt' : 'video/quicktime', - '.ra' : 'audio/x-pn-realaudio', - '.ram' : 'application/x-pn-realaudio', - '.ras' : 'image/x-cmu-raster', - '.rdf' : 'application/xml', - '.rgb' : 'image/x-rgb', - '.roff' : 'application/x-troff', - '.rtx' : 'text/richtext', - '.sgm' : 'text/x-sgml', - '.sgml' : 'text/x-sgml', - '.sh' : 'application/x-sh', - '.shar' : 'application/x-shar', - '.snd' : 'audio/basic', - '.so' : 'application/octet-stream', - '.src' : 'application/x-wais-source', - '.sv4cpio': 'application/x-sv4cpio', - '.sv4crc' : 'application/x-sv4crc', - '.swf' : 'application/x-shockwave-flash', - '.t' : 'application/x-troff', - '.tar' : 'application/x-tar', - '.tcl' : 'application/x-tcl', - '.tex' : 'application/x-tex', - '.texi' : 'application/x-texinfo', - '.texinfo': 'application/x-texinfo', - '.tif' : 'image/tiff', - '.tiff' : 'image/tiff', - '.tr' : 'application/x-troff', - '.tsv' : 'text/tab-separated-values', - '.txt' : 'text/plain', - '.ustar' : 'application/x-ustar', - '.vcf' : 'text/x-vcard', - '.wav' : 'audio/x-wav', - '.wiz' : 'application/msword', - '.wsdl' : 'application/xml', - '.xbm' : 'image/x-xbitmap', - '.xlb' : 'application/vnd.ms-excel', - # Duplicates :( - '.xls' : 'application/excel', - '.xls' : 'application/vnd.ms-excel', - '.xml' : 'text/xml', - '.xpdl' : 'application/xml', - '.xpm' : 'image/x-xpixmap', - '.xsl' : 'application/xml', - '.xwd' : 'image/x-xwindowdump', - '.zip' : 'application/zip', - } - -# These are non-standard types, commonly found in the wild. They will only -# match if strict=0 flag is given to the API methods. - -# Please sort these too -common_types = { - '.jpg' : 'image/jpg', - '.mid' : 'audio/midi', - '.midi': 'audio/midi', - '.pct' : 'image/pict', - '.pic' : 'image/pict', - '.pict': 'image/pict', - '.rtf' : 'application/rtf', - '.xul' : 'text/xul' - } +def _default_mime_types(): + global suffix_map + global encodings_map + global types_map + global common_types + + suffix_map = { + '.tgz': '.tar.gz', + '.taz': '.tar.gz', + '.tz': '.tar.gz', + } + + encodings_map = { + '.gz': 'gzip', + '.Z': 'compress', + } + + # Before adding new types, make sure they are either registered with IANA, + # at http://www.isi.edu/in-notes/iana/assignments/media-types + # or extensions, i.e. using the x- prefix + + # If you add to these, please keep them sorted! + types_map = { + '.a' : 'application/octet-stream', + '.ai' : 'application/postscript', + '.aif' : 'audio/x-aiff', + '.aifc' : 'audio/x-aiff', + '.aiff' : 'audio/x-aiff', + '.au' : 'audio/basic', + '.avi' : 'video/x-msvideo', + '.bat' : 'text/plain', + '.bcpio' : 'application/x-bcpio', + '.bin' : 'application/octet-stream', + '.bmp' : 'image/x-ms-bmp', + '.c' : 'text/plain', + # Duplicates :( + '.cdf' : 'application/x-cdf', + '.cdf' : 'application/x-netcdf', + '.cpio' : 'application/x-cpio', + '.csh' : 'application/x-csh', + '.css' : 'text/css', + '.dll' : 'application/octet-stream', + '.doc' : 'application/msword', + '.dot' : 'application/msword', + '.dvi' : 'application/x-dvi', + '.eml' : 'message/rfc822', + '.eps' : 'application/postscript', + '.etx' : 'text/x-setext', + '.exe' : 'application/octet-stream', + '.gif' : 'image/gif', + '.gtar' : 'application/x-gtar', + '.h' : 'text/plain', + '.hdf' : 'application/x-hdf', + '.htm' : 'text/html', + '.html' : 'text/html', + '.ief' : 'image/ief', + '.jpe' : 'image/jpeg', + '.jpeg' : 'image/jpeg', + '.jpg' : 'image/jpeg', + '.js' : 'application/x-javascript', + '.ksh' : 'text/plain', + '.latex' : 'application/x-latex', + '.m1v' : 'video/mpeg', + '.man' : 'application/x-troff-man', + '.me' : 'application/x-troff-me', + '.mht' : 'message/rfc822', + '.mhtml' : 'message/rfc822', + '.mif' : 'application/x-mif', + '.mov' : 'video/quicktime', + '.movie' : 'video/x-sgi-movie', + '.mp2' : 'audio/mpeg', + '.mp3' : 'audio/mpeg', + '.mpa' : 'video/mpeg', + '.mpe' : 'video/mpeg', + '.mpeg' : 'video/mpeg', + '.mpg' : 'video/mpeg', + '.ms' : 'application/x-troff-ms', + '.nc' : 'application/x-netcdf', + '.nws' : 'message/rfc822', + '.o' : 'application/octet-stream', + '.obj' : 'application/octet-stream', + '.oda' : 'application/oda', + '.p12' : 'application/x-pkcs12', + '.p7c' : 'application/pkcs7-mime', + '.pbm' : 'image/x-portable-bitmap', + '.pdf' : 'application/pdf', + '.pfx' : 'application/x-pkcs12', + '.pgm' : 'image/x-portable-graymap', + '.pl' : 'text/plain', + '.png' : 'image/png', + '.pnm' : 'image/x-portable-anymap', + '.pot' : 'application/vnd.ms-powerpoint', + '.ppa' : 'application/vnd.ms-powerpoint', + '.ppm' : 'image/x-portable-pixmap', + '.pps' : 'application/vnd.ms-powerpoint', + '.ppt' : 'application/vnd.ms-powerpoint', + '.ps' : 'application/postscript', + '.pwz' : 'application/vnd.ms-powerpoint', + '.py' : 'text/x-python', + '.pyc' : 'application/x-python-code', + '.pyo' : 'application/x-python-code', + '.qt' : 'video/quicktime', + '.ra' : 'audio/x-pn-realaudio', + '.ram' : 'application/x-pn-realaudio', + '.ras' : 'image/x-cmu-raster', + '.rdf' : 'application/xml', + '.rgb' : 'image/x-rgb', + '.roff' : 'application/x-troff', + '.rtx' : 'text/richtext', + '.sgm' : 'text/x-sgml', + '.sgml' : 'text/x-sgml', + '.sh' : 'application/x-sh', + '.shar' : 'application/x-shar', + '.snd' : 'audio/basic', + '.so' : 'application/octet-stream', + '.src' : 'application/x-wais-source', + '.sv4cpio': 'application/x-sv4cpio', + '.sv4crc' : 'application/x-sv4crc', + '.swf' : 'application/x-shockwave-flash', + '.t' : 'application/x-troff', + '.tar' : 'application/x-tar', + '.tcl' : 'application/x-tcl', + '.tex' : 'application/x-tex', + '.texi' : 'application/x-texinfo', + '.texinfo': 'application/x-texinfo', + '.tif' : 'image/tiff', + '.tiff' : 'image/tiff', + '.tr' : 'application/x-troff', + '.tsv' : 'text/tab-separated-values', + '.txt' : 'text/plain', + '.ustar' : 'application/x-ustar', + '.vcf' : 'text/x-vcard', + '.wav' : 'audio/x-wav', + '.wiz' : 'application/msword', + '.wsdl' : 'application/xml', + '.xbm' : 'image/x-xbitmap', + '.xlb' : 'application/vnd.ms-excel', + # Duplicates :( + '.xls' : 'application/excel', + '.xls' : 'application/vnd.ms-excel', + '.xml' : 'text/xml', + '.xpdl' : 'application/xml', + '.xpm' : 'image/x-xpixmap', + '.xsl' : 'application/xml', + '.xwd' : 'image/x-xwindowdump', + '.zip' : 'application/zip', + } + + # These are non-standard types, commonly found in the wild. They will + # only match if strict=0 flag is given to the API methods. + + # Please sort these too + common_types = { + '.jpg' : 'image/jpg', + '.mid' : 'audio/midi', + '.midi': 'audio/midi', + '.pct' : 'image/pict', + '.pic' : 'image/pict', + '.pict': 'image/pict', + '.rtf' : 'application/rtf', + '.xul' : 'text/xul' + } + + +_default_mime_types() if __name__ == '__main__': diff --git a/Lib/pdb.py b/Lib/pdb.py index 1aa2eae..5b7ea99 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -91,6 +91,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): 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 must be disp. after execing the cmd list + self.commands_silent = {} # for each bp num, tells if the stack trace must be disp. after execing the cmd list + self.commands_defining = False # True while in the process of defining a command list + self.commands_bnum = None # The breakpoint number for which we are defining a list + def reset(self): bdb.Bdb.reset(self) self.forget() @@ -137,7 +143,28 @@ class Pdb(bdb.Bdb, cmd.Cmd): or frame.f_lineno<= 0): return self._wait_for_mainpyfile = 0 - self.interaction(frame, None) + if self.bp_commands(frame): + self.interaction(frame, None) + + def bp_commands(self,frame): + """ Call every command that was set for the current active breakpoint (if there is one) + Returns True if the normal interaction function must be called, False otherwise """ + #self.currentbp is set in bdb.py in bdb.break_here if a breakpoint was hit + if getattr(self,"currentbp",False) and self.currentbp in self.commands: + currentbp = self.currentbp + self.currentbp = 0 + lastcmd_back = self.lastcmd + self.setup(frame, None) + for line in self.commands[currentbp]: + self.onecmd(line) + self.lastcmd = lastcmd_back + if not self.commands_silent[currentbp]: + self.print_stack_entry(self.stack[self.curindex]) + if self.commands_doprompt[currentbp]: + self.cmdloop() + self.forget() + return + return 1 def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" @@ -202,12 +229,70 @@ class Pdb(bdb.Bdb, cmd.Cmd): line = line[:marker].rstrip() return line + def onecmd(self, line): + """Interpret the argument as though it had been typed in response + to the prompt. + + Checks wether this line is typed in the normal prompt or in a breakpoint command list definition + """ + if not self.commands_defining: + return cmd.Cmd.onecmd(self, line) + else: + return self.handle_command_def(line) + + def handle_command_def(self,line): + """ Handles one command line during command list definition. """ + cmd, arg, line = self.parseline(line) + if cmd == 'silent': + self.commands_silent[self.commands_bnum] = True + return # continue to handle other cmd def in the cmd list + elif cmd == 'end': + self.cmdqueue = [] + return 1 # end of cmd list + cmdlist = self.commands[self.commands_bnum] + if (arg): + cmdlist.append(cmd+' '+arg) + else: + cmdlist.append(cmd) + # Determine if we must stop + try: + func = getattr(self, 'do_' + cmd) + except AttributeError: + func = self.default + if func.func_name in self.commands_resuming : # one of the resuming commands. + self.commands_doprompt[self.commands_bnum] = False + self.cmdqueue = [] + return 1 + return + # Command definitions, called by cmdloop() # The argument is the remaining string on the command line # Return true to exit from the command loop do_h = cmd.Cmd.do_help + def do_commands(self, arg): + """Defines a list of commands associated to a breakpoint + Those commands will be executed whenever the breakpoint causes the program to stop execution.""" + if not arg: + bnum = len(bdb.Breakpoint.bpbynumber)-1 + else: + try: + bnum = int(arg) + except: + print "Usage : commands [bnum]\n ...\n end" + return + self.commands_bnum = bnum + self.commands[bnum] = [] + self.commands_doprompt[bnum] = True + self.commands_silent[bnum] = False + prompt_back = self.prompt + self.prompt = '(com) ' + self.commands_defining = True + self.cmdloop() + self.commands_defining = False + self.prompt = prompt_back + def do_break(self, arg, temporary = 0): # break [ ([filename:]lineno | function) [, "condition"] ] if not arg: @@ -691,6 +776,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): if args[0] in self.aliases: del self.aliases[args[0]] + #list of all the commands making the program resume execution. + commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return', 'do_quit', 'do_jump'] + # Print a traceback starting at the top stack frame. # The most recently entered frame is printed last; # this is different from dbx and gdb, but consistent with @@ -944,6 +1032,41 @@ alias ps pi self print """unalias name Deletes the specified alias.""" + def help_commands(self): + print """commands [bpnumber] +(com) ... +(com) end +(Pdb) + +Specify a list of commands for breakpoint number bpnumber. The +commands themselves appear on the following lines. Type a line +containing just 'end' to terminate the commands. + +To remove all commands from a breakpoint, type commands and +follow it immediately with end; that is, give no commands. + +With no bpnumber argument, commands refers to the last +breakpoint set. + +You can use breakpoint commands to start your program up again. +Simply use the continue command, or step, or any other +command that resumes execution. + +Specifying any command resuming execution (currently continue, +step, next, return, jump, quit and their abbreviations) terminates +the command list (as if that command was immediately followed by end). +This is because any time you resume execution +(even with a simple next or step), you may encounter +another breakpoint--which could have its own command list, leading to +ambiguities about which list to execute. + + If you use the 'silent' command in the command list, the +usual message about stopping at a breakpoint is not printed. This may +be desirable for breakpoints that are to print a specific message and +then continue. If none of the other commands print anything, you +see no sign that the breakpoint was reached. +""" + def help_pdb(self): help() diff --git a/Lib/pkg_resources.py b/Lib/pkg_resources.py new file mode 100644 index 0000000..db6cc90 --- /dev/null +++ b/Lib/pkg_resources.py @@ -0,0 +1,2377 @@ +"""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, new, pkgutil # XXX +from sets import ImmutableSet +from os import utime, rename, unlink # capture these to bypass sandboxing +from os import open as os_open + +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: + info = os.popen('/usr/bin/sw_vers').read().splitlines() + for line in info: + key, value = line.split(None, 1) + if key == 'ProductVersion:': + _cache.append(value.strip().split(".")) + break + else: + raise ValueError, "What?!" + 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. + """ + from distutils.util 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) + + +try: + from pkgutil import get_importer +except ImportError: + import _pkgutil as pkgutil + get_importer = pkgutil.get_importer +else: + import pkgutil + + +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,False)`` 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,[]) + + 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) + + self._added_new(dist) + + + def resolve(self, requirements, env=None, installer=None): + """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 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: + raise DistributionNotFound(req) # XXX put more info here + 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 "Couldn't 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: + 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. + """ + # XXX + + + 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(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)) + + def get_metadata(self, name): + if not self.egg_info: + return "" + return self._get(self._fn(self.egg_info,name)) + + 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): + return os.path.join(base, *resource_name.split('/')) + + 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: + 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': + return open(self.path,'rU').read() + 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() + + + +_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): + 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 file(os.path.join(path_item, entry)): + if not line.strip(): continue + for item in find_distributions(line.rstrip()): + yield item + +register_finder(pkgutil.ImpImporter, 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] = new.module(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(pkgutil.ImpImporter, 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<name>[^-]+)" + r"( -(?P<ver>[^-]+) (-py(?P<pyver>[^-]+) (-(?P<plat>.+))? )? )?", + re.VERBOSE | re.IGNORECASE +).match + +component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) +replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c'}.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", whic 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. + """ + 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.lstrip(), 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) + + + + + + +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, + -len(self.location or ''), self.location, self.py_version, + self.platform + ) + ) + def __cmp__(self, other): return cmp(self.hashcmp, other) + def __hash__(self): return hash(self.hashcmp) + + # 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) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: declare_namespace(pkg) + + 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 not loc: return + if path is sys.path: + self.check_version_conflict() + best, pos = 0, -1 + for p,item in enumerate(path): + item = _normalize_cached(item) + if loc.startswith(item) and len(item)>best and loc<>item: + best, pos = len(item), p + if pos==-1: + if loc not in path: path.append(loc) + elif loc not in path[:pos+1]: + while loc in path: path.remove(loc) + path.insert(pos,loc) + + + def check_version_conflict(self): + if self.key=='setuptools': + 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 + + fn = getattr(sys.modules[modname], '__file__', None) + if fn and normalize_path(fn).startswith(loc): + 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]), + ImmutableSet(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 + for parsed,trans,op,ver in self.index: + action = trans[cmp(item,parsed)] + 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): + reqs = list(parse_requirements(s)) + if reqs: + if len(reqs)==1: + return reqs[0] + 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 _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/Lib/pkgutil.py b/Lib/pkgutil.py index fbd708a..7316892 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -1,7 +1,432 @@ """Utilities to support packages.""" +# NOTE: This module must remain compatible with Python 2.3, as it is shared +# by setuptools for distribution with Python 2.3 and up. + import os import sys +import imp +import os.path +from types import ModuleType + +__all__ = [ + 'get_importer', 'iter_importers', 'get_loader', 'find_loader', + 'walk_packages', 'iter_modules', + 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', +] + +def read_code(stream): + # This helper is needed in order for the PEP 302 emulation to + # correctly handle compiled files + import marshal + + magic = stream.read(4) + if magic != imp.get_magic(): + return None + + stream.read(4) # Skip timestamp + return marshal.load(stream) + + +def simplegeneric(func): + """Make a trivial single-dispatch generic function""" + registry = {} + def wrapper(*args,**kw): + ob = args[0] + try: + cls = ob.__class__ + except AttributeError: + cls = type(ob) + try: + mro = cls.__mro__ + except AttributeError: + try: + class cls(cls,object): pass + mro = cls.__mro__[1:] + except TypeError: + mro = object, # must be an ExtensionClass or some such :( + for t in mro: + if t in registry: + return registry[t](*args,**kw) + else: + return func(*args,**kw) + try: + wrapper.__name__ = func.__name__ + except (TypeError,AttributeError): + pass # Python 2.3 doesn't allow functions to be renamed + + def register(typ, func=None): + if func is None: + return lambda f: register(typ, f) + registry[typ] = func + return func + + wrapper.__dict__ = func.__dict__ + wrapper.__doc__ = func.__doc__ + wrapper.register = register + return wrapper + + +def walk_packages(path=None, prefix='', onerror=None): + """Yield submodule names+loaders recursively, for path or sys.path""" + + def seen(p,m={}): + if p in m: return True + m[p] = True + + for importer, name, ispkg in iter_modules(path, prefix): + yield importer, name, ispkg + + if ispkg: + try: + __import__(name) + except ImportError: + if onerror is not None: + onerror() + else: + path = getattr(sys.modules[name], '__path__', None) or [] + + # don't traverse path items we've seen before + path = [p for p in path if not seen(p)] + + for item in walk_packages(path, name+'.'): + yield item + + +def iter_modules(path=None, prefix=''): + """Yield submodule names+loaders for path or sys.path""" + if path is None: + importers = iter_importers() + else: + importers = map(get_importer, path) + + yielded = {} + for i in importers: + for name, ispkg in iter_importer_modules(i, prefix): + if name not in yielded: + yielded[name] = 1 + yield i, name, ispkg + + +#@simplegeneric +def iter_importer_modules(importer, prefix=''): + if not hasattr(importer,'iter_modules'): + return [] + return importer.iter_modules(prefix) + +iter_importer_modules = simplegeneric(iter_importer_modules) + + +class ImpImporter: + """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. + + Note that ImpImporter does not currently support being used by placement + on sys.meta_path. + """ + + def __init__(self, path=None): + self.path = path + + def find_module(self, fullname, path=None): + # Note: we ignore 'path' argument since it is only used via meta_path + subname = fullname.split(".")[-1] + if subname != fullname and self.path is None: + return None + if self.path is None: + path = None + else: + path = [os.path.realpath(self.path)] + try: + file, filename, etc = imp.find_module(subname, path) + except ImportError: + return None + return ImpLoader(fullname, file, filename, etc) + + def iter_modules(self, prefix=''): + if self.path is None or not os.path.isdir(self.path): + return + + yielded = {} + import inspect + + filenames = os.listdir(self.path) + filenames.sort() # handle packages before same-named modules + + for fn in filenames: + modname = inspect.getmodulename(fn) + if modname=='__init__' or modname in yielded: + continue + + path = os.path.join(self.path, fn) + ispkg = False + + if not modname and os.path.isdir(path) and '.' not in fn: + modname = fn + for fn in os.listdir(path): + subname = inspect.getmodulename(fn) + if subname=='__init__': + ispkg = True + break + else: + continue # not a package + + if modname and '.' not in modname: + yielded[modname] = 1 + yield prefix + modname, ispkg + + +class ImpLoader: + """PEP 302 Loader that wraps Python's "classic" import algorithm + """ + code = source = None + + def __init__(self, fullname, file, filename, etc): + self.file = file + self.filename = filename + self.fullname = fullname + self.etc = etc + + def load_module(self, fullname): + self._reopen() + 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_data(self, pathname): + return open(pathname, "rb").read() + + def _reopen(self): + if self.file and self.file.closed: + if mod_type==imp.PY_SOURCE: + self.file = open(self.filename, 'rU') + elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): + self.file = open(self.filename, 'rb') + + def _fix_name(self, fullname): + if fullname is None: + fullname = self.fullname + elif fullname != self.fullname: + raise ImportError("Loader for module %s cannot handle " + "module %s" % (self.fullname, fullname)) + return fullname + + def is_package(self, fullname): + fullname = self._fix_name(fullname) + return self.etc[2]==imp.PKG_DIRECTORY + + def get_code(self, fullname=None): + fullname = self._fix_name(fullname) + if self.code is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + source = self.get_source(fullname) + self.code = compile(source, self.filename, 'exec') + elif mod_type==imp.PY_COMPILED: + self._reopen() + try: + self.code = read_code(self.file) + finally: + self.file.close() + elif mod_type==imp.PKG_DIRECTORY: + self.code = self._get_delegate().get_code() + return self.code + + def get_source(self, fullname=None): + fullname = self._fix_name(fullname) + if self.source is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + self._reopen() + try: + self.source = self.file.read() + finally: + self.file.close() + elif mod_type==imp.PY_COMPILED: + if os.path.exists(self.filename[:-1]): + f = open(self.filename[:-1], 'rU') + self.source = f.read() + f.close() + elif mod_type==imp.PKG_DIRECTORY: + self.source = self._get_delegate().get_source() + return self.source + + + def _get_delegate(self): + return ImpImporter(self.filename).find_module('__init__') + + def get_filename(self, fullname=None): + fullname = self._fix_name(fullname) + mod_type = self.etc[2] + if self.etc[2]==imp.PKG_DIRECTORY: + return self._get_delegate().get_filename() + elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): + return self.filename + return None + + +try: + import zipimport + from zipimport import zipimporter + + def iter_zipimport_modules(importer, prefix=''): + dirlist = zipimport._zip_directory_cache[importer.archive].keys() + dirlist.sort() + _prefix = importer.prefix + plen = len(_prefix) + yielded = {} + import inspect + for fn in dirlist: + if not fn.startswith(_prefix): + continue + + fn = fn[plen:].split(os.sep) + + if len(fn)==2 and fn[1].startswith('__init__.py'): + if fn[0] not in yielded: + yielded[fn[0]] = 1 + yield fn[0], True + + if len(fn)!=1: + continue + + modname = inspect.getmodulename(fn[0]) + if modname=='__init__': + continue + + if modname and '.' not in modname and modname not in yielded: + yielded[modname] = 1 + yield prefix + modname, False + + iter_importer_modules.register(zipimporter, iter_zipimport_modules) + +except ImportError: + pass + + +def get_importer(path_item): + """Retrieve a PEP 302 importer for the given path item + + 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 cache (or part of it) can be cleared manually if a + rescan of sys.path_hooks is necessary. + """ + try: + importer = sys.path_importer_cache[path_item] + except KeyError: + for path_hook in sys.path_hooks: + try: + importer = path_hook(path_item) + break + except ImportError: + pass + else: + importer = None + sys.path_importer_cache.setdefault(path_item,importer) + + if importer is None: + try: + importer = ImpImporter(path_item) + except ImportError: + pass + return importer + + +def iter_importers(fullname=""): + """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, + sys.path, and Python's "classic" import machinery, in that order. If + 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 + 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 + 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, + 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 + """ + if fullname.startswith('.'): + raise ImportError("Relative module names not supported") + if '.' in fullname: + # Get the containing package's __path__ + pkg = '.'.join(fullname.split('.')[:-1]) + if pkg not in sys.modules: + __import__(pkg) + path = getattr(sys.modules[pkg],'__path__',None) or [] + else: + for importer in sys.meta_path: + yield importer + path = sys.path + for item in path: + yield get_importer(item) + if '.' not in fullname: + yield ImpImporter() + +def get_loader(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__. + + 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 module_or_name in sys.modules: + module_or_name = sys.modules[module_or_name] + if isinstance(module_or_name, ModuleType): + module = module_or_name + loader = getattr(module,'__loader__',None) + if loader is not None: + return loader + fullname = module.__name__ + else: + fullname = module_or_name + return find_loader(fullname) + +def find_loader(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. + """ + for importer in iter_importers(fullname): + loader = importer.find_module(fullname) + if loader is not None: + return loader + + return None + def extend_path(path, name): """Extend a package's path. diff --git a/Lib/plat-mac/applesingle.py b/Lib/plat-mac/applesingle.py index b035d9e..76bdb06 100644 --- a/Lib/plat-mac/applesingle.py +++ b/Lib/plat-mac/applesingle.py @@ -25,7 +25,7 @@ class Error(ValueError): pass # File header format: magic, version, unused, number of entries -AS_HEADER_FORMAT=">ll16sh" +AS_HEADER_FORMAT=">LL16sh" AS_HEADER_LENGTH=26 # The flag words for AppleSingle AS_MAGIC=0x00051600 diff --git a/Lib/platform.py b/Lib/platform.py index 62fdaf4..288bc95 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -607,7 +607,8 @@ def mac_ver(release='',versioninfo=('','',''),machine=''): versioninfo = (version,stage,nonrel) if sysa: machine = {0x1: '68k', - 0x2: 'PowerPC'}.get(sysa,'') + 0x2: 'PowerPC', + 0xa: 'i386'}.get(sysa,'') return release,versioninfo,machine def _java_getprop(name,default): diff --git a/Lib/popen2.py b/Lib/popen2.py index 54543be..67ebd26 100644 --- a/Lib/popen2.py +++ b/Lib/popen2.py @@ -20,7 +20,13 @@ _active = [] def _cleanup(): for inst in _active[:]: - inst.poll() + if inst.poll(_deadstate=sys.maxint) >= 0: + try: + _active.remove(inst) + except ValueError: + # This can happen if two threads create a new Popen instance. + # It's harmless that it was already removed, so ignore. + pass class Popen3: """Class representing a child process. Normally instances are created @@ -39,6 +45,7 @@ class Popen3: specified, it specifies the size of the I/O buffers to/from the child process.""" _cleanup() + self.cmd = cmd p2cread, p2cwrite = os.pipe() c2pread, c2pwrite = os.pipe() if capturestderr: @@ -60,7 +67,13 @@ class Popen3: self.childerr = os.fdopen(errout, 'r', bufsize) else: self.childerr = None - _active.append(self) + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) def _run_child(self, cmd): if isinstance(cmd, basestring): @@ -75,26 +88,28 @@ class Popen3: finally: os._exit(1) - def poll(self): + def poll(self, _deadstate=None): """Return the exit status of the child process if it has finished, or -1 if it hasn't finished yet.""" if self.sts < 0: try: pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated if pid == self.pid: self.sts = sts - _active.remove(self) except os.error: - pass + if _deadstate is not None: + self.sts = _deadstate return self.sts def wait(self): """Wait for and return the exit status of the child process.""" if self.sts < 0: pid, sts = os.waitpid(self.pid, 0) - if pid == self.pid: - self.sts = sts - _active.remove(self) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts return self.sts @@ -103,6 +118,7 @@ class Popen4(Popen3): def __init__(self, cmd, bufsize=-1): _cleanup() + self.cmd = cmd p2cread, p2cwrite = os.pipe() c2pread, c2pwrite = os.pipe() self.pid = os.fork() @@ -116,7 +132,6 @@ class Popen4(Popen3): self.tochild = os.fdopen(p2cwrite, 'w', bufsize) os.close(c2pwrite) self.fromchild = os.fdopen(c2pread, 'r', bufsize) - _active.append(self) if sys.platform[:3] == "win" or sys.platform == "os2emx": @@ -186,6 +201,9 @@ else: __all__.extend(["Popen3", "Popen4"]) def _test(): + # When the test runs, there shouldn't be any open pipes + _cleanup() + assert not _active, "Active pipes when test starts " + repr([c.cmd for c in _active]) cmd = "cat" teststr = "ab cd\n" if os.name == "nt": @@ -216,6 +234,7 @@ def _test(): raise ValueError("unexpected %r on stderr" % (got,)) for inst in _active[:]: inst.wait() + _cleanup() if _active: raise ValueError("_active not empty") print "All OK" diff --git a/Lib/pstats.py b/Lib/pstats.py index 930cc6d..c3a8828 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -32,6 +32,7 @@ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import sys import os import time import marshal @@ -58,18 +59,31 @@ class Stats: printed. The sort_stats() method now processes some additional options (i.e., in - addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted - strings to select the sort order. For example sort_stats('time', 'name') - sorts on the major key of "internal function time", and on the minor - key of 'the name of the function'. Look at the two tables in sort_stats() - and get_sort_arg_defs(self) for more examples. + addition to the old -1, 0, 1, or 2). It takes an arbitrary number of + quoted strings to select the sort order. For example sort_stats('time', + 'name') sorts on the major key of 'internal function time', and on the + minor key of 'the name of the function'. Look at the two tables in + sort_stats() and get_sort_arg_defs(self) for more examples. - All methods now return "self", so you can string together commands like: + All methods return self, so you can string together commands like: Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ print_stats(5).print_callers(5) """ - def __init__(self, *args): + def __init__(self, *args, **kwds): + # I can't figure out how to explictly specify a stream keyword arg + # with *args: + # def __init__(self, *args, stream=sys.stdout): ... + # so I use **kwds and sqauwk if something unexpected is passed in. + self.stream = sys.stdout + if "stream" in kwds: + self.stream = kwds["stream"] + del kwds["stream"] + if kwds: + keys = kwds.keys() + keys.sort() + extras = ", ".join(["%s=%s" % (k, kwds[k]) for k in keys]) + raise ValueError, "unrecognized keyword args: %s" % extras if not len(args): arg = None else: @@ -96,9 +110,9 @@ class Stats: trouble = 0 finally: if trouble: - print "Invalid timing data", - if self.files: print self.files[-1], - print + print >> self.stream, "Invalid timing data", + if self.files: print >> self.stream, self.files[-1], + print >> self.stream def load_stats(self, arg): if not arg: self.stats = {} @@ -320,7 +334,7 @@ class Stats: if not list: return 0, list - print msg + print >> self.stream, msg if count < len(self.stats): width = 0 for func in list: @@ -330,24 +344,24 @@ class Stats: def print_stats(self, *amount): for filename in self.files: - print filename - if self.files: print + print >> self.stream, filename + if self.files: print >> self.stream indent = ' ' * 8 for func in self.top_level: - print indent, func_get_function_name(func) + print >> self.stream, indent, func_get_function_name(func) - print indent, self.total_calls, "function calls", + print >> self.stream, indent, self.total_calls, "function calls", if self.total_calls != self.prim_calls: - print "(%d primitive calls)" % self.prim_calls, - print "in %.3f CPU seconds" % self.total_tt - print + print >> self.stream, "(%d primitive calls)" % self.prim_calls, + print >> self.stream, "in %.3f CPU seconds" % self.total_tt + print >> self.stream width, list = self.get_print_list(amount) if list: self.print_title() for func in list: self.print_line(func) - print - print + print >> self.stream + print >> self.stream return self def print_callees(self, *amount): @@ -361,8 +375,8 @@ class Stats: self.print_call_line(width, func, self.all_callees[func]) else: self.print_call_line(width, func, {}) - print - print + print >> self.stream + print >> self.stream return self def print_callers(self, *amount): @@ -372,12 +386,12 @@ class Stats: for func in list: cc, nc, tt, ct, callers = self.stats[func] self.print_call_line(width, func, callers, "<-") - print - print + print >> self.stream + print >> self.stream return self def print_call_heading(self, name_size, column_title): - print "Function ".ljust(name_size) + column_title + print >> self.stream, "Function ".ljust(name_size) + column_title # print sub-header only if we have new-style callers subheader = False for cc, nc, tt, ct, callers in self.stats.itervalues(): @@ -386,12 +400,12 @@ class Stats: subheader = isinstance(value, tuple) break if subheader: - print " "*name_size + " ncalls tottime cumtime" + print >> self.stream, " "*name_size + " ncalls tottime cumtime" def print_call_line(self, name_size, source, call_dict, arrow="->"): - print func_std_string(source).ljust(name_size) + arrow, + print >> self.stream, func_std_string(source).ljust(name_size) + arrow, if not call_dict: - print + print >> self.stream return clist = call_dict.keys() clist.sort() @@ -411,30 +425,30 @@ class Stats: else: substats = '%s(%r) %s' % (name, value, f8(self.stats[func][3])) left_width = name_size + 3 - print indent*left_width + substats + print >> self.stream, indent*left_width + substats indent = " " def print_title(self): - print ' ncalls tottime percall cumtime percall', \ - 'filename:lineno(function)' + print >> self.stream, ' ncalls tottime percall cumtime percall', + print >> self.stream, 'filename:lineno(function)' def print_line(self, func): # hack : should print percentages cc, nc, tt, ct, callers = self.stats[func] c = str(nc) if nc != cc: c = c + '/' + str(cc) - print c.rjust(9), - print f8(tt), + print >> self.stream, c.rjust(9), + print >> self.stream, f8(tt), if nc == 0: - print ' '*8, + print >> self.stream, ' '*8, else: - print f8(tt/nc), - print f8(ct), + print >> self.stream, f8(tt/nc), + print >> self.stream, f8(ct), if cc == 0: - print ' '*8, + print >> self.stream, ' '*8, else: - print f8(ct/cc), - print func_std_string(func) + print >> self.stream, f8(ct/cc), + print >> self.stream, func_std_string(func) class TupleComp: """This class provides a generic function for comparing any two tuples. @@ -549,7 +563,7 @@ if __name__ == '__main__': try: frac = float(term) if frac > 1 or frac < 0: - print "Fraction argument mus be in [0, 1]" + print >> self.stream, "Fraction argument must be in [0, 1]" continue processed.append(frac) continue @@ -559,93 +573,93 @@ if __name__ == '__main__': if self.stats: getattr(self.stats, fn)(*processed) else: - print "No statistics object is loaded." + print >> self.stream, "No statistics object is loaded." return 0 def generic_help(self): - print "Arguments may be:" - print "* An integer maximum number of entries to print." - print "* A decimal fractional number between 0 and 1, controlling" - print " what fraction of selected entries to print." - print "* A regular expression; only entries with function names" - print " that match it are printed." + print >> self.stream, "Arguments may be:" + print >> self.stream, "* An integer maximum number of entries to print." + print >> self.stream, "* A decimal fractional number between 0 and 1, controlling" + print >> self.stream, " what fraction of selected entries to print." + print >> self.stream, "* A regular expression; only entries with function names" + print >> self.stream, " that match it are printed." def do_add(self, line): self.stats.add(line) return 0 def help_add(self): - print "Add profile info from given file to current statistics object." + print >> self.stream, "Add profile info from given file to current statistics object." def do_callees(self, line): return self.generic('print_callees', line) def help_callees(self): - print "Print callees statistics from the current stat object." + print >> self.stream, "Print callees statistics from the current stat object." self.generic_help() def do_callers(self, line): return self.generic('print_callers', line) def help_callers(self): - print "Print callers statistics from the current stat object." + print >> self.stream, "Print callers statistics from the current stat object." self.generic_help() def do_EOF(self, line): - print "" + print >> self.stream, "" return 1 def help_EOF(self): - print "Leave the profile brower." + print >> self.stream, "Leave the profile brower." def do_quit(self, line): return 1 def help_quit(self): - print "Leave the profile brower." + print >> self.stream, "Leave the profile brower." def do_read(self, line): if line: try: self.stats = Stats(line) except IOError, args: - print args[1] + print >> self.stream, args[1] return self.prompt = line + "% " elif len(self.prompt) > 2: line = self.prompt[-2:] else: - print "No statistics object is current -- cannot reload." + print >> self.stream, "No statistics object is current -- cannot reload." return 0 def help_read(self): - print "Read in profile data from a specified file." + print >> self.stream, "Read in profile data from a specified file." def do_reverse(self, line): self.stats.reverse_order() return 0 def help_reverse(self): - print "Reverse the sort order of the profiling report." + print >> self.stream, "Reverse the sort order of the profiling report." def do_sort(self, line): abbrevs = self.stats.get_sort_arg_defs() if line and not filter(lambda x,a=abbrevs: x not in a,line.split()): self.stats.sort_stats(*line.split()) else: - print "Valid sort keys (unique prefixes are accepted):" + print >> self.stream, "Valid sort keys (unique prefixes are accepted):" for (key, value) in Stats.sort_arg_dict_default.iteritems(): - print "%s -- %s" % (key, value[1]) + print >> self.stream, "%s -- %s" % (key, value[1]) return 0 def help_sort(self): - print "Sort profile data according to specified keys." - print "(Typing `sort' without arguments lists valid keys.)" + print >> self.stream, "Sort profile data according to specified keys." + print >> self.stream, "(Typing `sort' without arguments lists valid keys.)" def complete_sort(self, text, *args): return [a for a in Stats.sort_arg_dict_default if a.startswith(text)] def do_stats(self, line): return self.generic('print_stats', line) def help_stats(self): - print "Print statistics from the current stat object." + print >> self.stream, "Print statistics from the current stat object." self.generic_help() def do_strip(self, line): self.stats.strip_dirs() return 0 def help_strip(self): - print "Strip leading path information from filenames in the report." + print >> self.stream, "Strip leading path information from filenames in the report." def postcmd(self, stop, line): if stop: @@ -653,14 +667,14 @@ if __name__ == '__main__': return None import sys - print "Welcome to the profile statistics browser." + print >> self.stream, "Welcome to the profile statistics browser." if len(sys.argv) > 1: initprofile = sys.argv[1] else: initprofile = None try: ProfileBrowser(initprofile).cmdloop() - print "Goodbye." + print >> self.stream, "Goodbye." except KeyboardInterrupt: pass diff --git a/Lib/pydoc.py b/Lib/pydoc.py index b6afc7f..cf38630 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -52,10 +52,16 @@ Richard Chamberlain, for the first implementation of textdoc. # the current directory is changed with os.chdir(), an incorrect # path will be displayed. -import sys, imp, os, re, types, inspect, __builtin__ +import sys, imp, os, re, types, inspect, __builtin__, pkgutil from repr import Repr from string import expandtabs, find, join, lower, split, strip, rfind, rstrip -from collections import deque +try: + from collections import deque +except ImportError: + # Python 2.3 compatibility + class deque(list): + def popleft(self): + return self.pop(0) # --------------------------------------------------------- common routines @@ -182,6 +188,23 @@ def ispackage(path): return True return False +def source_synopsis(file): + line = file.readline() + while line[:1] == '#' or not strip(line): + line = file.readline() + if not line: break + line = strip(line) + if line[:4] == 'r"""': line = line[1:] + if line[:3] == '"""': + line = line[3:] + if line[-1:] == '\\': line = line[:-1] + while not strip(line): + line = file.readline() + if not line: break + result = strip(split(line, '"""')[0]) + else: result = None + return result + def synopsis(filename, cache={}): """Get the one-line summary out of a module file.""" mtime = os.stat(filename).st_mtime @@ -196,24 +219,11 @@ def synopsis(filename, cache={}): if info and 'b' in info[2]: # binary modules have to be imported try: module = imp.load_module('__temp__', file, filename, info[1:]) except: return None - result = split(module.__doc__ or '', '\n')[0] + result = (module.__doc__ or '').splitlines()[0] del sys.modules['__temp__'] else: # text modules can be directly examined - line = file.readline() - while line[:1] == '#' or not strip(line): - line = file.readline() - if not line: break - line = strip(line) - if line[:4] == 'r"""': line = line[1:] - if line[:3] == '"""': - line = line[3:] - if line[-1:] == '\\': line = line[:-1] - while not strip(line): - line = file.readline() - if not line: break - result = strip(split(line, '"""')[0]) - else: result = None - file.close() + result = source_synopsis(file) + file.close() cache[filename] = (mtime, result) return result @@ -643,16 +653,8 @@ class HTMLDoc(Doc): if hasattr(object, '__path__'): modpkgs = [] - modnames = [] - for file in os.listdir(object.__path__[0]): - path = os.path.join(object.__path__[0], file) - modname = inspect.getmodulename(file) - if modname != '__init__': - if modname and modname not in modnames: - modpkgs.append((modname, name, 0, 0)) - modnames.append(modname) - elif ispackage(path): - modpkgs.append((file, name, 1, 0)) + for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): + modpkgs.append((modname, name, ispkg, 0)) modpkgs.sort() contents = self.multicolumn(modpkgs, self.modpkglink) result = result + self.bigsection( @@ -796,7 +798,10 @@ class HTMLDoc(Doc): tag += ':<br>\n' # Sort attrs by name. - attrs.sort(key=lambda t: t[0]) + try: + attrs.sort(key=lambda t: t[0]) + except TypeError: + attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat # Pump out the attrs, segregated by kind. attrs = spill('Methods %s' % tag, attrs, @@ -914,25 +919,9 @@ class HTMLDoc(Doc): """Generate an HTML index for a directory of modules.""" modpkgs = [] if shadowed is None: shadowed = {} - seen = {} - files = os.listdir(dir) - - def found(name, ispackage, - modpkgs=modpkgs, shadowed=shadowed, seen=seen): - if name not in seen: - modpkgs.append((name, '', ispackage, name in shadowed)) - seen[name] = 1 - shadowed[name] = 1 - - # Package spam/__init__.py takes precedence over module spam.py. - for file in files: - path = os.path.join(dir, file) - if ispackage(path): found(file, 1) - for file in files: - path = os.path.join(dir, file) - if os.path.isfile(path): - modname = inspect.getmodulename(file) - if modname: found(modname, 0) + for importer, name, ispkg in pkgutil.iter_modules([dir]): + modpkgs.append((name, '', ispkg, name in shadowed)) + shadowed[name] = 1 modpkgs.sort() contents = self.multicolumn(modpkgs, self.modpkglink) @@ -1059,14 +1048,12 @@ class TextDoc(Doc): if hasattr(object, '__path__'): modpkgs = [] - for file in os.listdir(object.__path__[0]): - path = os.path.join(object.__path__[0], file) - modname = inspect.getmodulename(file) - if modname != '__init__': - if modname and modname not in modpkgs: - modpkgs.append(modname) - elif ispackage(path): - modpkgs.append(file + ' (package)') + for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): + if ispkg: + modpkgs.append(modname + ' (package)') + else: + modpkgs.append(modname) + modpkgs.sort() result = result + self.section( 'PACKAGE CONTENTS', join(modpkgs, '\n')) @@ -1490,20 +1477,9 @@ def writedoc(thing, forceload=0): def writedocs(dir, pkgpath='', done=None): """Write out HTML documentation for all modules in a directory tree.""" if done is None: done = {} - for file in os.listdir(dir): - path = os.path.join(dir, file) - if ispackage(path): - writedocs(path, pkgpath + file + '.', done) - elif os.path.isfile(path): - modname = inspect.getmodulename(path) - if modname: - if modname == '__init__': - modname = pkgpath[:-1] # remove trailing period - else: - modname = pkgpath + modname - if modname not in done: - done[modname] = 1 - writedoc(modname) + for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): + writedoc(modname) + return def raw_input(prompt): sys.stdout.write(prompt) @@ -1835,30 +1811,9 @@ class Scanner: self.state.append((child, self.children(child))) return child -class ModuleScanner(Scanner): - """An interruptible scanner that searches module synopses.""" - def __init__(self): - roots = map(lambda dir: (dir, ''), pathdirs()) - Scanner.__init__(self, roots, self.submodules, self.isnewpackage) - self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots) - - def submodules(self, (dir, package)): - children = [] - for file in os.listdir(dir): - path = os.path.join(dir, file) - if ispackage(path): - children.append((path, package + (package and '.') + file)) - else: - children.append((path, package)) - children.sort() # so that spam.py comes before spam.pyc or spam.pyo - return children - def isnewpackage(self, (dir, package)): - inode = os.path.exists(dir) and os.stat(dir).st_ino - if not (os.path.islink(dir) and inode in self.inodes): - self.inodes.append(inode) # detect circular symbolic links - return ispackage(dir) - return False +class ModuleScanner: + """An interruptible scanner that searches module synopses.""" def run(self, callback, key=None, completer=None): if key: key = lower(key) @@ -1875,22 +1830,31 @@ class ModuleScanner(Scanner): if find(lower(modname + ' - ' + desc), key) >= 0: callback(None, modname, desc) - while not self.quit: - node = self.next() - if not node: break - path, package = node - modname = inspect.getmodulename(path) - if os.path.isfile(path) and modname: - modname = package + (package and '.') + modname - if not modname in seen: - seen[modname] = 1 # if we see spam.py, skip spam.pyc - if key is None: - callback(path, modname, '') + for importer, modname, ispkg in pkgutil.walk_packages(): + if self.quit: + break + if key is None: + callback(None, modname, '') + else: + loader = importer.find_module(modname) + if hasattr(loader,'get_source'): + import StringIO + desc = source_synopsis( + StringIO.StringIO(loader.get_source(modname)) + ) or '' + if hasattr(loader,'get_filename'): + path = loader.get_filename(modname) else: - desc = synopsis(path) or '' - if find(lower(modname + ' - ' + desc), key) >= 0: - callback(path, modname, desc) - if completer: completer() + path = None + else: + module = loader.load_module(modname) + desc = (module.__doc__ or '').splitlines()[0] + path = getattr(module,'__file__',None) + if find(lower(modname + ' - ' + desc), key) >= 0: + callback(path, modname, desc) + + if completer: + completer() def apropos(key): """Print all the one-line module summaries that contain a substring.""" @@ -1955,7 +1919,7 @@ def serve(port, callback=None, completer=None): 'Built-in Modules', '#ffffff', '#ee77aa', contents)] seen = {} - for dir in pathdirs(): + for dir in sys.path: indices.append(html.index(dir, seen)) contents = heading + join(indices) + '''<p align=right> <font color="#909090" face="helvetica, arial"><strong> diff --git a/Lib/random.py b/Lib/random.py index b4ad2b3..465f477 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -285,6 +285,15 @@ class Random(_random.Random): 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. @@ -304,7 +313,9 @@ class Random(_random.Random): setsize = 21 # size of a small set minus size of an empty list if k > 5: setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets - if n <= setsize: # is an n-length list smaller than a k-length set + if n <= setsize or hasattr(population, "keys"): + # An n-length list is smaller than a k-length set, or this is a + # mapping type so the other algorithm wouldn't work. pool = list(population) for i in xrange(k): # invariant: non-selected at [0,n-i) j = _int(random() * (n-i)) @@ -312,17 +323,18 @@ class Random(_random.Random): pool[j] = pool[n-i-1] # move non-selected item into vacancy else: try: - n > 0 and (population[0], population[n//2], population[n-1]) - except (TypeError, KeyError): # handle non-sequence iterables - population = tuple(population) - selected = set() - selected_add = selected.add - for i in xrange(k): - j = _int(random() * n) - while j in selected: + selected = set() + selected_add = selected.add + for i in xrange(k): j = _int(random() * n) - selected_add(j) - result[i] = population[j] + while j in selected: + j = _int(random() * n) + selected_add(j) + result[i] = population[j] + except (TypeError, KeyError): # handle (at least) sets + if isinstance(population, list): + raise + return self.sample(tuple(population), k) return result ## -------------------- real-valued distributions ------------------- diff --git a/Lib/reconvert.py b/Lib/reconvert.py deleted file mode 100755 index 64bab5b..0000000 --- a/Lib/reconvert.py +++ /dev/null @@ -1,192 +0,0 @@ -#! /usr/bin/env python - -r"""Convert old ("regex") regular expressions to new syntax ("re"). - -When imported as a module, there are two functions, with their own -strings: - - convert(s, syntax=None) -- convert a regex regular expression to re syntax - - quote(s) -- return a quoted string literal - -When used as a script, read a Python string literal (or any other -expression evaluating to a string) from stdin, and write the -translated expression to stdout as a string literal. Unless stdout is -a tty, no trailing \n is written to stdout. This is done so that it -can be used with Emacs C-U M-| (shell-command-on-region with argument -which filters the region through the shell command). - -No attempt has been made at coding for performance. - -Translation table... - - \( ( (unless RE_NO_BK_PARENS set) - \) ) (unless RE_NO_BK_PARENS set) - \| | (unless RE_NO_BK_VBAR set) - \< \b (not quite the same, but alla...) - \> \b (not quite the same, but alla...) - \` \A - \' \Z - -Not translated... - - . - ^ - $ - * - + (unless RE_BK_PLUS_QM set, then to \+) - ? (unless RE_BK_PLUS_QM set, then to \?) - \ - \b - \B - \w - \W - \1 ... \9 - -Special cases... - - Non-printable characters are always replaced by their 3-digit - escape code (except \t, \n, \r, which use mnemonic escapes) - - Newline is turned into | when RE_NEWLINE_OR is set - -XXX To be done... - - [...] (different treatment of backslashed items?) - [^...] (different treatment of backslashed items?) - ^ $ * + ? (in some error contexts these are probably treated differently) - \vDD \DD (in the regex docs but only works when RE_ANSI_HEX set) - -""" - - -import warnings -warnings.filterwarnings("ignore", ".* regex .*", DeprecationWarning, __name__, - append=1) - -import regex -from regex_syntax import * # RE_* - -__all__ = ["convert","quote"] - -# Default translation table -mastertable = { - r'\<': r'\b', - r'\>': r'\b', - r'\`': r'\A', - r'\'': r'\Z', - r'\(': '(', - r'\)': ')', - r'\|': '|', - '(': r'\(', - ')': r'\)', - '|': r'\|', - '\t': r'\t', - '\n': r'\n', - '\r': r'\r', -} - - -def convert(s, syntax=None): - """Convert a regex regular expression to re syntax. - - The first argument is the regular expression, as a string object, - just like it would be passed to regex.compile(). (I.e., pass the - actual string object -- string quotes must already have been - removed and the standard escape processing has already been done, - e.g. by eval().) - - The optional second argument is the regex syntax variant to be - used. This is an integer mask as passed to regex.set_syntax(); - the flag bits are defined in regex_syntax. When not specified, or - when None is given, the current regex syntax mask (as retrieved by - regex.get_syntax()) is used -- which is 0 by default. - - The return value is a regular expression, as a string object that - could be passed to re.compile(). (I.e., no string quotes have - been added -- use quote() below, or repr().) - - The conversion is not always guaranteed to be correct. More - syntactical analysis should be performed to detect borderline - cases and decide what to do with them. For example, 'x*?' is not - translated correctly. - - """ - table = mastertable.copy() - if syntax is None: - syntax = regex.get_syntax() - if syntax & RE_NO_BK_PARENS: - del table[r'\('], table[r'\)'] - del table['('], table[')'] - if syntax & RE_NO_BK_VBAR: - del table[r'\|'] - del table['|'] - if syntax & RE_BK_PLUS_QM: - table['+'] = r'\+' - table['?'] = r'\?' - table[r'\+'] = '+' - table[r'\?'] = '?' - if syntax & RE_NEWLINE_OR: - table['\n'] = '|' - res = "" - - i = 0 - end = len(s) - while i < end: - c = s[i] - i = i+1 - if c == '\\': - c = s[i] - i = i+1 - key = '\\' + c - key = table.get(key, key) - res = res + key - else: - c = table.get(c, c) - res = res + c - return res - - -def quote(s, quote=None): - """Convert a string object to a quoted string literal. - - This is similar to repr() but will return a "raw" string (r'...' - or r"...") when the string contains backslashes, instead of - doubling all backslashes. The resulting string does *not* always - evaluate to the same string as the original; however it will do - just the right thing when passed into re.compile(). - - The optional second argument forces the string quote; it must be - a single character which is a valid Python string quote. - - """ - if quote is None: - q = "'" - altq = "'" - if q in s and altq not in s: - q = altq - else: - assert quote in ('"', "'", '"""', "'''") - q = quote - res = q - for c in s: - if c == q: c = '\\' + c - elif c < ' ' or c > '~': c = "\\%03o" % ord(c) - res = res + c - res = res + q - if '\\' in res: - res = 'r' + res - return res - - -def main(): - """Main program -- called when run as a script.""" - import sys - s = eval(sys.stdin.read()) - sys.stdout.write(quote(convert(s))) - if sys.stdout.isatty(): - sys.stdout.write("\n") - - -if __name__ == '__main__': - main() diff --git a/Lib/regex_syntax.py b/Lib/regex_syntax.py deleted file mode 100644 index b0a0dbf..0000000 --- a/Lib/regex_syntax.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Constants for selecting regexp syntaxes for the obsolete regex module. - -This module is only for backward compatibility. "regex" has now -been replaced by the new regular expression module, "re". - -These bits are passed to regex.set_syntax() to choose among -alternative regexp syntaxes. -""" - -# 1 means plain parentheses serve as grouping, and backslash -# parentheses are needed for literal searching. -# 0 means backslash-parentheses are grouping, and plain parentheses -# are for literal searching. -RE_NO_BK_PARENS = 1 - -# 1 means plain | serves as the "or"-operator, and \| is a literal. -# 0 means \| serves as the "or"-operator, and | is a literal. -RE_NO_BK_VBAR = 2 - -# 0 means plain + or ? serves as an operator, and \+, \? are literals. -# 1 means \+, \? are operators and plain +, ? are literals. -RE_BK_PLUS_QM = 4 - -# 1 means | binds tighter than ^ or $. -# 0 means the contrary. -RE_TIGHT_VBAR = 8 - -# 1 means treat \n as an _OR operator -# 0 means treat it as a normal character -RE_NEWLINE_OR = 16 - -# 0 means that a special characters (such as *, ^, and $) always have -# their special meaning regardless of the surrounding context. -# 1 means that special characters may act as normal characters in some -# contexts. Specifically, this applies to: -# ^ - only special at the beginning, or after ( or | -# $ - only special at the end, or before ) or | -# *, +, ? - only special when not after the beginning, (, or | -RE_CONTEXT_INDEP_OPS = 32 - -# ANSI sequences (\n etc) and \xhh -RE_ANSI_HEX = 64 - -# No GNU extensions -RE_NO_GNU_EXTENSIONS = 128 - -# Now define combinations of bits for the standard possibilities. -RE_SYNTAX_AWK = (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) -RE_SYNTAX_EGREP = (RE_SYNTAX_AWK | RE_NEWLINE_OR) -RE_SYNTAX_GREP = (RE_BK_PLUS_QM | RE_NEWLINE_OR) -RE_SYNTAX_EMACS = 0 - -# (Python's obsolete "regexp" module used a syntax similar to awk.) diff --git a/Lib/regsub.py b/Lib/regsub.py deleted file mode 100644 index 0fc10a5..0000000 --- a/Lib/regsub.py +++ /dev/null @@ -1,198 +0,0 @@ -"""Regexp-based split and replace using the obsolete regex module. - -This module is only for backward compatibility. These operations -are now provided by the new regular expression module, "re". - -sub(pat, repl, str): replace first occurrence of pattern in string -gsub(pat, repl, str): replace all occurrences of pattern in string -split(str, pat, maxsplit): split string using pattern as delimiter -splitx(str, pat, maxsplit): split string using pattern as delimiter plus - return delimiters -""" - -import warnings -warnings.warn("the regsub module is deprecated; please use re.sub()", - DeprecationWarning) - -# Ignore further deprecation warnings about this module -warnings.filterwarnings("ignore", "", DeprecationWarning, __name__) - -import regex - -__all__ = ["sub","gsub","split","splitx","capwords"] - -# Replace first occurrence of pattern pat in string str by replacement -# repl. If the pattern isn't found, the string is returned unchanged. -# The replacement may contain references \digit to subpatterns and -# escaped backslashes. The pattern may be a string or an already -# compiled pattern. - -def sub(pat, repl, str): - prog = compile(pat) - if prog.search(str) >= 0: - regs = prog.regs - a, b = regs[0] - str = str[:a] + expand(repl, regs, str) + str[b:] - return str - - -# Replace all (non-overlapping) occurrences of pattern pat in string -# str by replacement repl. The same rules as for sub() apply. -# Empty matches for the pattern are replaced only when not adjacent to -# a previous match, so e.g. gsub('', '-', 'abc') returns '-a-b-c-'. - -def gsub(pat, repl, str): - prog = compile(pat) - new = '' - start = 0 - first = 1 - while prog.search(str, start) >= 0: - regs = prog.regs - a, b = regs[0] - if a == b == start and not first: - if start >= len(str) or prog.search(str, start+1) < 0: - break - regs = prog.regs - a, b = regs[0] - new = new + str[start:a] + expand(repl, regs, str) - start = b - first = 0 - new = new + str[start:] - return new - - -# Split string str in fields separated by delimiters matching pattern -# pat. Only non-empty matches for the pattern are considered, so e.g. -# split('abc', '') returns ['abc']. -# The optional 3rd argument sets the number of splits that are performed. - -def split(str, pat, maxsplit = 0): - return intsplit(str, pat, maxsplit, 0) - -# Split string str in fields separated by delimiters matching pattern -# pat. Only non-empty matches for the pattern are considered, so e.g. -# split('abc', '') returns ['abc']. The delimiters are also included -# in the list. -# The optional 3rd argument sets the number of splits that are performed. - - -def splitx(str, pat, maxsplit = 0): - return intsplit(str, pat, maxsplit, 1) - -# Internal function used to implement split() and splitx(). - -def intsplit(str, pat, maxsplit, retain): - prog = compile(pat) - res = [] - start = next = 0 - splitcount = 0 - while prog.search(str, next) >= 0: - regs = prog.regs - a, b = regs[0] - if a == b: - next = next + 1 - if next >= len(str): - break - else: - res.append(str[start:a]) - if retain: - res.append(str[a:b]) - start = next = b - splitcount = splitcount + 1 - if (maxsplit and (splitcount >= maxsplit)): - break - res.append(str[start:]) - return res - - -# Capitalize words split using a pattern - -def capwords(str, pat='[^a-zA-Z0-9_]+'): - words = splitx(str, pat) - for i in range(0, len(words), 2): - words[i] = words[i].capitalize() - return "".join(words) - - -# Internal subroutines: -# compile(pat): compile a pattern, caching already compiled patterns -# expand(repl, regs, str): expand \digit escapes in replacement string - - -# Manage a cache of compiled regular expressions. -# -# If the pattern is a string a compiled version of it is returned. If -# the pattern has been used before we return an already compiled -# version from the cache; otherwise we compile it now and save the -# compiled version in the cache, along with the syntax it was compiled -# with. Instead of a string, a compiled regular expression can also -# be passed. - -cache = {} - -def compile(pat): - if type(pat) != type(''): - return pat # Assume it is a compiled regex - key = (pat, regex.get_syntax()) - if key in cache: - prog = cache[key] # Get it from the cache - else: - prog = cache[key] = regex.compile(pat) - return prog - - -def clear_cache(): - global cache - cache = {} - - -# Expand \digit in the replacement. -# Each occurrence of \digit is replaced by the substring of str -# indicated by regs[digit]. To include a literal \ in the -# replacement, double it; other \ escapes are left unchanged (i.e. -# the \ and the following character are both copied). - -def expand(repl, regs, str): - if '\\' not in repl: - return repl - new = '' - i = 0 - ord0 = ord('0') - while i < len(repl): - c = repl[i]; i = i+1 - if c != '\\' or i >= len(repl): - new = new + c - else: - c = repl[i]; i = i+1 - if '0' <= c <= '9': - a, b = regs[ord(c)-ord0] - new = new + str[a:b] - elif c == '\\': - new = new + c - else: - new = new + '\\' + c - return new - - -# Test program, reads sequences "pat repl str" from stdin. -# Optional argument specifies pattern used to split lines. - -def test(): - import sys - if sys.argv[1:]: - delpat = sys.argv[1] - else: - delpat = '[ \t\n]+' - while 1: - if sys.stdin.isatty(): sys.stderr.write('--> ') - line = sys.stdin.readline() - if not line: break - if line[-1] == '\n': line = line[:-1] - fields = split(line, delpat) - if len(fields) != 3: - print 'Sorry, not three fields' - print 'split:', repr(fields) - continue - [pat, repl, str] = split(line, delpat) - print 'sub :', repr(sub(pat, repl, str)) - print 'gsub:', repr(gsub(pat, repl, str)) diff --git a/Lib/rexec.py b/Lib/rexec.py index ed01d24..10e4bc0 100644 --- a/Lib/rexec.py +++ b/Lib/rexec.py @@ -136,7 +136,7 @@ class RExec(ihooks._Verbose): ok_builtin_modules = ('audioop', 'array', 'binascii', 'cmath', 'errno', 'imageop', 'marshal', 'math', 'md5', 'operator', - 'parser', 'regex', 'select', + 'parser', 'select', 'sha', '_sre', 'strop', 'struct', 'time', '_weakref') diff --git a/Lib/runpy.py b/Lib/runpy.py index afb0098..8290dfe 100755 --- a/Lib/runpy.py +++ b/Lib/runpy.py @@ -11,349 +11,15 @@ importers when locating support scripts as well as when importing modules. import sys import imp +try: + from imp import get_loader +except ImportError: + from pkgutil import get_loader __all__ = [ "run_module", ] -try: - _get_loader = imp.get_loader -except AttributeError: - # get_loader() is not provided by the imp module, so emulate it - # as best we can using the PEP 302 import machinery exposed since - # Python 2.3. The emulation isn't perfect, but the differences - # in the way names are shadowed shouldn't matter in practice. - import os.path - import marshal # Handle compiled Python files - - # This helper is needed in order for the PEP 302 emulation to - # correctly handle compiled files - def _read_compiled_file(compiled_file): - magic = compiled_file.read(4) - if magic != imp.get_magic(): - return None - try: - compiled_file.read(4) # Skip timestamp - return marshal.load(compiled_file) - except Exception: - return None - - class _AbsoluteImporter(object): - """PEP 302 importer wrapper for top level import machinery""" - def find_module(self, mod_name, path=None): - if path is not None: - return None - try: - file, filename, mod_info = imp.find_module(mod_name) - except ImportError: - return None - suffix, mode, mod_type = mod_info - if mod_type == imp.PY_SOURCE: - loader = _SourceFileLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.PY_COMPILED: - loader = _CompiledFileLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.PKG_DIRECTORY: - loader = _PackageDirLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.C_EXTENSION: - loader = _FileSystemLoader(mod_name, file, - filename, mod_info) - else: - loader = _BasicLoader(mod_name, file, - filename, mod_info) - return loader - - - class _FileSystemImporter(object): - """PEP 302 importer wrapper for filesystem based imports""" - def __init__(self, path_item=None): - if path_item is not None: - if path_item != '' and not os.path.isdir(path_item): - raise ImportError("%s is not a directory" % path_item) - self.path_dir = path_item - else: - raise ImportError("Filesystem importer requires " - "a directory name") - - def find_module(self, mod_name, path=None): - if path is not None: - return None - path_dir = self.path_dir - if path_dir == '': - path_dir = os.getcwd() - sub_name = mod_name.rsplit(".", 1)[-1] - try: - file, filename, mod_info = imp.find_module(sub_name, - [path_dir]) - except ImportError: - return None - if not filename.startswith(path_dir): - return None - suffix, mode, mod_type = mod_info - if mod_type == imp.PY_SOURCE: - loader = _SourceFileLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.PY_COMPILED: - loader = _CompiledFileLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.PKG_DIRECTORY: - loader = _PackageDirLoader(mod_name, file, - filename, mod_info) - elif mod_type == imp.C_EXTENSION: - loader = _FileSystemLoader(mod_name, file, - filename, mod_info) - else: - loader = _BasicLoader(mod_name, file, - filename, mod_info) - return loader - - - class _BasicLoader(object): - """PEP 302 loader wrapper for top level import machinery""" - def __init__(self, mod_name, file, filename, mod_info): - self.mod_name = mod_name - self.file = file - self.filename = filename - self.mod_info = mod_info - - def _fix_name(self, mod_name): - if mod_name is None: - mod_name = self.mod_name - elif mod_name != self.mod_name: - raise ImportError("Loader for module %s cannot handle " - "module %s" % (self.mod_name, mod_name)) - return mod_name - - def load_module(self, mod_name=None): - mod_name = self._fix_name(mod_name) - mod = imp.load_module(mod_name, self.file, - self.filename, self.mod_info) - mod.__loader__ = self # for introspection - return mod - - def get_code(self, mod_name=None): - return None - - def get_source(self, mod_name=None): - return None - - def is_package(self, mod_name=None): - return False - - def close(self): - if self.file: - self.file.close() - - def __del__(self): - self.close() - - - class _FileSystemLoader(_BasicLoader): - """PEP 302 loader wrapper for filesystem based imports""" - def get_code(self, mod_name=None): - mod_name = self._fix_name(mod_name) - return self._get_code(mod_name) - - def get_data(self, pathname): - return open(pathname, "rb").read() - - def get_filename(self, mod_name=None): - mod_name = self._fix_name(mod_name) - return self._get_filename(mod_name) - - def get_source(self, mod_name=None): - mod_name = self._fix_name(mod_name) - return self._get_source(mod_name) - - def is_package(self, mod_name=None): - mod_name = self._fix_name(mod_name) - return self._is_package(mod_name) - - def _get_code(self, mod_name): - return None - - def _get_filename(self, mod_name): - return self.filename - - def _get_source(self, mod_name): - return None - - def _is_package(self, mod_name): - return False - - class _PackageDirLoader(_FileSystemLoader): - """PEP 302 loader wrapper for PKG_DIRECTORY directories""" - def _is_package(self, mod_name): - return True - - - class _SourceFileLoader(_FileSystemLoader): - """PEP 302 loader wrapper for PY_SOURCE modules""" - def _get_code(self, mod_name): - return compile(self._get_source(mod_name), - self.filename, 'exec') - - def _get_source(self, mod_name): - f = self.file - f.seek(0) - return f.read() - - - class _CompiledFileLoader(_FileSystemLoader): - """PEP 302 loader wrapper for PY_COMPILED modules""" - def _get_code(self, mod_name): - f = self.file - f.seek(0) - return _read_compiled_file(f) - - - def _get_importer(path_item): - """Retrieve a PEP 302 importer for the given path item - - 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 cache (or part of it) can be cleared manually if a - rescan of sys.path_hooks is necessary. - """ - try: - importer = sys.path_importer_cache[path_item] - except KeyError: - for path_hook in sys.path_hooks: - try: - importer = path_hook(path_item) - break - except ImportError: - pass - else: - importer = None - sys.path_importer_cache[path_item] = importer - if importer is None: - try: - importer = _FileSystemImporter(path_item) - except ImportError: - pass - return importer - - - def _get_path_loader(mod_name, path=None): - """Retrieve a PEP 302 loader using a path importer""" - if path is None: - path = sys.path - absolute_loader = _AbsoluteImporter().find_module(mod_name) - if isinstance(absolute_loader, _FileSystemLoader): - # Found in filesystem, so scan path hooks - # before accepting this one as the right one - loader = None - else: - # Not found in filesystem, so use top-level loader - loader = absolute_loader - else: - loader = absolute_loader = None - if loader is None: - for path_item in path: - importer = _get_importer(path_item) - if importer is not None: - loader = importer.find_module(mod_name) - if loader is not None: - # Found a loader for our module - break - else: - # No path hook found, so accept the top level loader - loader = absolute_loader - return loader - - def _get_package(pkg_name): - """Retrieve a named package""" - pkg = __import__(pkg_name) - sub_pkg_names = pkg_name.split(".") - for sub_pkg in sub_pkg_names[1:]: - pkg = getattr(pkg, sub_pkg) - return pkg - - def _get_loader(mod_name, path=None): - """Retrieve a PEP 302 loader for the given module or package - - If the module or package is accessible via the normal import - mechanism, a wrapper around the relevant part of that machinery - is returned. - - 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 - 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, - 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 - """ - try: - loader = sys.modules[mod_name].__loader__ - except (KeyError, AttributeError): - loader = None - if loader is None: - imp.acquire_lock() - try: - # Module not in sys.modules, or uses an unhooked loader - parts = mod_name.rsplit(".", 1) - if len(parts) == 2: - # Sub package, so use parent package's path - pkg_name, sub_name = parts - if pkg_name and pkg_name[0] != '.': - if path is not None: - raise ImportError("Path argument must be None " - "for a dotted module name") - pkg = _get_package(pkg_name) - try: - path = pkg.__path__ - except AttributeError: - raise ImportError(pkg_name + - " is not a package") - else: - raise ImportError("Relative import syntax is not " - "supported by _get_loader()") - else: - # Top level module, so stick with default path - sub_name = mod_name - - for importer in sys.meta_path: - loader = importer.find_module(mod_name, path) - if loader is not None: - # Found a metahook to handle the module - break - else: - # Handling via the standard path mechanism - loader = _get_path_loader(mod_name, path) - finally: - imp.release_lock() - return loader - - -# This helper is needed due to a missing component in the PEP 302 -# loader protocol (specifically, "get_filename" is non-standard) -def _get_filename(loader, mod_name): - try: - get_filename = loader.get_filename - except AttributeError: - return None - else: - return get_filename(mod_name) - -# ------------------------------------------------------------ -# Done with the import machinery emulation, on with the code! def _run_code(code, run_globals, init_globals, mod_name, mod_fname, mod_loader): @@ -379,21 +45,17 @@ def _run_module_code(code, init_globals=None, restore_module = mod_name in sys.modules if restore_module: saved_module = sys.modules[mod_name] - imp.acquire_lock() + sys.argv[0] = mod_fname + sys.modules[mod_name] = temp_module try: - sys.argv[0] = mod_fname - sys.modules[mod_name] = temp_module - try: - _run_code(code, mod_globals, init_globals, - mod_name, mod_fname, mod_loader) - finally: - sys.argv[0] = saved_argv0 - if restore_module: - sys.modules[mod_name] = saved_module - else: - del sys.modules[mod_name] + _run_code(code, mod_globals, init_globals, + mod_name, mod_fname, mod_loader) finally: - imp.release_lock() + sys.argv[0] = saved_argv0 + if restore_module: + sys.modules[mod_name] = saved_module + else: + del sys.modules[mod_name] # Copy the globals of the temporary module, as they # may be cleared when the temporary module goes away return mod_globals.copy() @@ -403,13 +65,24 @@ def _run_module_code(code, init_globals=None, mod_name, mod_fname, mod_loader) +# This helper is needed due to a missing component in the PEP 302 +# loader protocol (specifically, "get_filename" is non-standard) +def _get_filename(loader, mod_name): + try: + get_filename = loader.get_filename + except AttributeError: + return None + else: + return get_filename(mod_name) + + def run_module(mod_name, init_globals=None, run_name=None, alter_sys=False): """Execute a module's code without importing it Returns the resulting top level namespace dictionary """ - loader = _get_loader(mod_name) + loader = get_loader(mod_name) if loader is None: raise ImportError("No module named " + mod_name) code = loader.get_code(mod_name) diff --git a/Lib/setuptools.egg-info/PKG-INFO b/Lib/setuptools.egg-info/PKG-INFO new file mode 100644 index 0000000..ff5c1a1 --- /dev/null +++ b/Lib/setuptools.egg-info/PKG-INFO @@ -0,0 +1,89 @@ +Metadata-Version: 1.0 +Name: setuptools +Version: 0.7a1dev-r45536 +Summary: Download, build, install, upgrade, and uninstall Python packages -- easily! +Home-page: http://peak.telecommunity.com/DevCenter/setuptools +Author: Phillip J. Eby +Author-email: peak@eby-sarna.com +License: PSF or ZPL +Description: ``setuptools`` is a collection of enhancements to the Python ``distutils`` + (for Python 2.3.5 and up on most platforms; 64-bit platforms require a minimum + of Python 2.4) that allow you to more easily build and distribute Python + packages, especially ones that have dependencies on other packages. + + Packages built and distributed using ``setuptools`` look to the user like + ordinary Python packages based on the ``distutils``. Your users don't need to + install or even know about setuptools in order to use them, and you don't + have to include the entire setuptools package in your distributions. By + including just a single `bootstrap module`_ (an 8K .py file), your package will + automatically download and install ``setuptools`` if the user is building your + package from source and doesn't have a suitable version already installed. + + .. _bootstrap module: http://peak.telecommunity.com/dist/ez_setup.py + + Feature Highlights: + + * Automatically find/download/install/upgrade dependencies at build time using + the `EasyInstall tool <http://peak.telecommunity.com/DevCenter/EasyInstall>`_, + which supports downloading via HTTP, FTP, Subversion, and SourceForge, and + automatically scans web pages linked from PyPI to find download links. (It's + the closest thing to CPAN currently available for Python.) + + * Create `Python Eggs <http://peak.telecommunity.com/DevCenter/PythonEggs>`_ - + a single-file importable distribution format + + * Include data files inside your package directories, where your code can + actually use them. (Python 2.4 distutils also supports this feature, but + setuptools provides the feature for Python 2.3 packages also, and supports + accessing data files in zipped packages too.) + + * Automatically include all packages in your source tree, without listing them + individually in setup.py + + * Automatically include all relevant files in your source distributions, + without needing to create a ``MANIFEST.in`` file, and without having to force + regeneration of the ``MANIFEST`` file when your source tree changes. + + * Automatically generate wrapper scripts or Windows (console and GUI) .exe + files for any number of "main" functions in your project. (Note: this is not + a py2exe replacement; the .exe files rely on the local Python installation.) + + * Transparent Pyrex support, so that your setup.py can list ``.pyx`` files and + still work even when the end-user doesn't have Pyrex installed (as long as + you include the Pyrex-generated C in your source distribution) + + * Command aliases - create project-specific, per-user, or site-wide shortcut + names for commonly used commands and options + + * PyPI upload support - upload your source distributions and eggs to PyPI + + * Deploy your project in "development mode", such that it's available on + ``sys.path``, yet can still be edited directly from its source checkout. + + * Easily extend the distutils with new commands or ``setup()`` arguments, and + distribute/reuse your extensions for multiple projects, without copying code. + + * Create extensible applications and frameworks that automatically discover + extensions, using simple "entry points" declared in a project's setup script. + + In addition to the PyPI downloads, the development version of ``setuptools`` + is available from the `Python SVN sandbox`_, and in-development versions of the + `0.6 branch`_ are available as well. + + .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06 + + .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev + + +Keywords: CPAN PyPI distutils eggs package management +Platform: UNKNOWN +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: License :: OSI Approved :: Zope Public License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities diff --git a/Lib/setuptools.egg-info/entry_points.txt b/Lib/setuptools.egg-info/entry_points.txt new file mode 100755 index 0000000..0afe2cb --- /dev/null +++ b/Lib/setuptools.egg-info/entry_points.txt @@ -0,0 +1,51 @@ +[distutils.setup_keywords] +dependency_links = setuptools.dist:assert_string_list +entry_points = setuptools.dist:check_entry_points +extras_require = setuptools.dist:check_extras +package_data = setuptools.dist:check_package_data +install_requires = setuptools.dist:check_requirements +include_package_data = setuptools.dist:assert_bool +exclude_package_data = setuptools.dist:check_package_data +namespace_packages = setuptools.dist:check_nsp +test_suite = setuptools.dist:check_test_suite +eager_resources = setuptools.dist:assert_string_list +zip_safe = setuptools.dist:assert_bool +test_loader = setuptools.dist:check_importable +tests_require = setuptools.dist:check_requirements + +[setuptools.file_finders] +svn_cvs = setuptools.command.sdist:_default_revctrl + +[egg_info.writers] +dependency_links.txt = setuptools.command.egg_info:overwrite_arg +requires.txt = setuptools.command.egg_info:write_requirements +PKG-INFO = setuptools.command.egg_info:write_pkg_info +eager_resources.txt = setuptools.command.egg_info:overwrite_arg +top_level.txt = setuptools.command.egg_info:write_toplevel_names +namespace_packages.txt = setuptools.command.egg_info:overwrite_arg +entry_points.txt = setuptools.command.egg_info:write_entries +depends.txt = setuptools.command.egg_info:warn_depends_obsolete + +[console_scripts] +easy_install = setuptools.command.easy_install:main +easy_install-2.5 = setuptools.command.easy_install:main + +[distutils.commands] +bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm +rotate = setuptools.command.rotate:rotate +develop = setuptools.command.develop:develop +setopt = setuptools.command.setopt:setopt +build_py = setuptools.command.build_py:build_py +saveopts = setuptools.command.saveopts:saveopts +egg_info = setuptools.command.egg_info:egg_info +install_egg_info = setuptools.command.install_egg_info:install_egg_info +alias = setuptools.command.alias:alias +easy_install = setuptools.command.easy_install:easy_install +install_scripts = setuptools.command.install_scripts:install_scripts +bdist_egg = setuptools.command.bdist_egg:bdist_egg +install = setuptools.command.install:install +test = setuptools.command.test:test +install_lib = setuptools.command.install_lib:install_lib +build_ext = setuptools.command.build_ext:build_ext +sdist = setuptools.command.sdist:sdist + diff --git a/Lib/setuptools.egg-info/top_level.txt b/Lib/setuptools.egg-info/top_level.txt new file mode 100644 index 0000000..4577c6a --- /dev/null +++ b/Lib/setuptools.egg-info/top_level.txt @@ -0,0 +1,3 @@ +easy_install +pkg_resources +setuptools diff --git a/Lib/setuptools.egg-info/zip-safe b/Lib/setuptools.egg-info/zip-safe new file mode 100644 index 0000000..e69de29 diff --git a/Lib/setuptools/__init__.py b/Lib/setuptools/__init__.py new file mode 100644 index 0000000..3921ce2 --- /dev/null +++ b/Lib/setuptools/__init__.py @@ -0,0 +1,64 @@ +"""Extensions to the 'distutils' for large or complex distributions""" +from setuptools.extension import Extension, Library +from setuptools.dist import Distribution, Feature, _get_unpatched +import distutils.core, setuptools.command +from setuptools.depends import Require +from distutils.core import Command as _Command +from distutils.util import convert_path +import os.path + +__version__ = '0.7a1' +__all__ = [ + 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', + 'find_packages' +] + +bootstrap_install_from = None + +def find_packages(where='.', exclude=()): + """Return a list all Python packages found within directory 'where' + + 'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it + will be converted to the appropriate local path syntax. 'exclude' is a + sequence of package names to exclude; '*' can be used as a wildcard in the + names, such that 'foo.*' will exclude all subpackages of 'foo' (but not + 'foo' itself). + """ + out = [] + stack=[(convert_path(where), '')] + while stack: + where,prefix = stack.pop(0) + for name in os.listdir(where): + fn = os.path.join(where,name) + if (os.path.isdir(fn) and + os.path.isfile(os.path.join(fn,'__init__.py')) + ): + out.append(prefix+name); stack.append((fn,prefix+name+'.')) + for pat in exclude: + from fnmatch import fnmatchcase + out = [item for item in out if not fnmatchcase(item,pat)] + return out + +setup = distutils.core.setup + +_Command = _get_unpatched(_Command) + +class Command(_Command): + __doc__ = _Command.__doc__ + + command_consumes_arguments = False + + def __init__(self, dist, **kw): + # Add support for keyword arguments + _Command.__init__(self,dist) + for k,v in kw.items(): + setattr(self,k,v) + + def reinitialize_command(self, command, reinit_subcommands=0, **kw): + cmd = _Command.reinitialize_command(self, command, reinit_subcommands) + for k,v in kw.items(): + setattr(cmd,k,v) # update command with keywords + return cmd + +import distutils.core +distutils.core.Command = Command # we can't patch distutils.cmd, alas diff --git a/Lib/setuptools/archive_util.py b/Lib/setuptools/archive_util.py new file mode 100755 index 0000000..dd9c684 --- /dev/null +++ b/Lib/setuptools/archive_util.py @@ -0,0 +1,200 @@ +"""Utilities for extracting common archive formats""" + + +__all__ = [ + "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", + "UnrecognizedFormat", "extraction_drivers", "unpack_directory", +] + +import zipfile, tarfile, os, shutil +from pkg_resources import ensure_directory +from distutils.errors import DistutilsError + +class UnrecognizedFormat(DistutilsError): + """Couldn't recognize the archive type""" + +def default_filter(src,dst): + """The default progress/filter callback; returns True for all files""" + return dst + + + + + + + + + + + + + + + + + + + + + + + +def unpack_archive(filename, extract_dir, progress_filter=default_filter, + drivers=None +): + """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` + + `progress_filter` is a function taking two arguments: a source path + internal to the archive ('/'-separated), and a filesystem path where it + will be extracted. The callback must return the desired extract path + (which may be the same as the one passed in), or else ``None`` to skip + that file or directory. The callback can thus be used to report on the + progress of the extraction, as well as to filter the items extracted or + alter their extraction paths. + + `drivers`, if supplied, must be a non-empty sequence of functions with the + same signature as this function (minus the `drivers` argument), that raise + ``UnrecognizedFormat`` if they do not support extracting the designated + archive type. The `drivers` are tried in sequence until one is found that + does not raise an error, or until all are exhausted (in which case + ``UnrecognizedFormat`` is raised). If you do not supply a sequence of + drivers, the module's ``extraction_drivers`` constant will be used, which + means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that + order. + """ + for driver in drivers or extraction_drivers: + try: + driver(filename, extract_dir, progress_filter) + except UnrecognizedFormat: + continue + else: + return + else: + raise UnrecognizedFormat( + "Not a recognized archive type: %s" % filename + ) + + + + + + + +def unpack_directory(filename, extract_dir, progress_filter=default_filter): + """"Unpack" a directory, using the same interface as for archives + + Raises ``UnrecognizedFormat`` if `filename` is not a directory + """ + if not os.path.isdir(filename): + raise UnrecognizedFormat("%s is not a directory" % (filename,)) + + paths = {filename:('',extract_dir)} + for base, dirs, files in os.walk(filename): + src,dst = paths[base] + for d in dirs: + paths[os.path.join(base,d)] = src+d+'/', os.path.join(dst,d) + for f in files: + name = src+f + target = os.path.join(dst,f) + target = progress_filter(src+f, target) + if not target: + continue # skip non-files + ensure_directory(target) + f = os.path.join(base,f) + shutil.copyfile(f, target) + shutil.copystat(f, target) + + + + + + + + + + + + + + + + + + +def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): + """Unpack zip `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined + by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + + if not zipfile.is_zipfile(filename): + raise UnrecognizedFormat("%s is not a zip file" % (filename,)) + + z = zipfile.ZipFile(filename) + try: + for info in z.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + target = progress_filter(name, target) + if not target: + continue + if name.endswith('/'): + # directory + ensure_directory(target) + else: + # file + ensure_directory(target) + data = z.read(info.filename) + f = open(target,'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + z.close() + + +def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined + by ``tarfile.open()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise UnrecognizedFormat( + "%s is not a compressed or uncompressed tar file" % (filename,) + ) + + try: + tarobj.chown = lambda *args: None # don't do any chowning! + for member in tarobj: + if member.isfile() or member.isdir(): + name = member.name + # don't extract absolute paths or ones with .. in them + if not name.startswith('/') and '..' not in name: + dst = os.path.join(extract_dir, *name.split('/')) + dst = progress_filter(name, dst) + if dst: + if dst.endswith(os.sep): + dst = dst[:-1] + tarobj._extract_member(member,dst) # XXX Ugh + return True + finally: + tarobj.close() + + + + +extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/Lib/setuptools/cli.exe b/Lib/setuptools/cli.exe new file mode 100755 index 0000000..fc83339 Binary files /dev/null and b/Lib/setuptools/cli.exe differ diff --git a/Lib/setuptools/command/__init__.py b/Lib/setuptools/command/__init__.py new file mode 100644 index 0000000..bff53e7 --- /dev/null +++ b/Lib/setuptools/command/__init__.py @@ -0,0 +1,19 @@ +__all__ = [ + 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', + 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', + 'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts', +] + +import sys +if sys.version>='2.5': + # In Python 2.5 and above, distutils includes its own upload command + __all__.remove('upload') + + +from distutils.command.bdist import bdist + +if 'egg' not in bdist.format_commands: + bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") + bdist.format_commands.append('egg') + +del bdist, sys diff --git a/Lib/setuptools/command/alias.py b/Lib/setuptools/command/alias.py new file mode 100755 index 0000000..1df474a --- /dev/null +++ b/Lib/setuptools/command/alias.py @@ -0,0 +1,79 @@ +import distutils, os +from setuptools import Command +from distutils.util import convert_path +from distutils import log +from distutils.errors import * +from setuptools.command.setopt import edit_config, option_base, config_file + +def shquote(arg): + """Quote an argument for later parsing by shlex.split()""" + for c in '"', "'", "\\", "#": + if c in arg: return repr(arg) + if arg.split()<>[arg]: + return repr(arg) + return arg + + +class alias(option_base): + """Define a shortcut that invokes one or more commands""" + + description = "define a shortcut to invoke one or more commands" + command_consumes_arguments = True + + user_options = [ + ('remove', 'r', 'remove (unset) the alias'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.args = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.remove and len(self.args)<>1: + raise DistutilsOptionError( + "Must specify exactly one argument (the alias name) when " + "using --remove" + ) + + def run(self): + aliases = self.distribution.get_option_dict('aliases') + + if not self.args: + print "Command Aliases" + print "---------------" + for alias in aliases: + print "setup.py alias", format_alias(alias, aliases) + return + + elif len(self.args)==1: + alias, = self.args + if self.remove: + command = None + elif alias in aliases: + print "setup.py alias", format_alias(alias, aliases) + return + else: + print "No alias definition found for %r" % alias + return + else: + alias = self.args[0] + command = ' '.join(map(shquote,self.args[1:])) + + edit_config(self.filename, {'aliases': {alias:command}}, self.dry_run) + + +def format_alias(name, aliases): + source, command = aliases[name] + if source == config_file('global'): + source = '--global-config ' + elif source == config_file('user'): + source = '--user-config ' + elif source == config_file('local'): + source = '' + else: + source = '--filename=%r' % source + return source+name+' '+command diff --git a/Lib/setuptools/command/bdist_egg.py b/Lib/setuptools/command/bdist_egg.py new file mode 100644 index 0000000..617d88d --- /dev/null +++ b/Lib/setuptools/command/bdist_egg.py @@ -0,0 +1,449 @@ +"""setuptools.command.bdist_egg + +Build .egg distributions""" + +# This module should be kept compatible with Python 2.3 +import sys, os, marshal +from setuptools import Command +from distutils.dir_util import remove_tree, mkpath +from distutils.sysconfig import get_python_version, get_python_lib +from distutils import log +from pkg_resources import get_build_platform, Distribution +from types import CodeType +from setuptools.extension import Library + +def write_stub(resource, pyfile): + f = open(pyfile,'w') + f.write('\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __loader__, __file__", + " import sys, pkg_resources, imp", + " __file__ = pkg_resources.resource_filename(__name__,%r)" + % resource, + " del __bootstrap__, __loader__", + " imp.load_dynamic(__name__,__file__)", + "__bootstrap__()", + "" # terminal \n + ])) + f.close() + +# stub __init__.py for packages distributed without one +NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)' + + + + + + + + + + +class bdist_egg(Command): + + description = "create an \"egg\" distribution" + + user_options = [ + ('bdist-dir=', 'b', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_build_platform()), + ('exclude-source-files', None, + "remove all .py files from the generated egg"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ] + + boolean_options = [ + 'keep-temp', 'skip-build', 'exclude-source-files' + ] + + + + + + + + + + + + + + + + + + def initialize_options (self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = 0 + self.egg_output = None + self.exclude_source_files = None + + + def finalize_options(self): + ei_cmd = self.get_finalized_command("egg_info") + self.egg_info = ei_cmd.egg_info + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'egg') + + if self.plat_name is None: + self.plat_name = get_build_platform() + + self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) + + if self.egg_output is None: + + # Compute filename of the output egg + basename = Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version, + get_python_version(), + self.distribution.has_ext_modules() and self.plat_name + ).egg_name() + + self.egg_output = os.path.join(self.dist_dir, basename+'.egg') + + + + + + + + + def do_install_data(self): + # Hack for packages that install data to install's --install-lib + self.get_finalized_command('install').install_lib = self.bdist_dir + + site_packages = os.path.normcase(os.path.realpath(get_python_lib())) + old, self.distribution.data_files = self.distribution.data_files,[] + + for item in old: + if isinstance(item,tuple) and len(item)==2: + if os.path.isabs(item[0]): + realpath = os.path.realpath(item[0]) + normalized = os.path.normcase(realpath) + if normalized==site_packages or normalized.startswith( + site_packages+os.sep + ): + item = realpath[len(site_packages)+1:], item[1] + # XXX else: raise ??? + self.distribution.data_files.append(item) + + try: + log.info("installing package data to %s" % self.bdist_dir) + self.call_command('install_data', force=0, root=None) + finally: + self.distribution.data_files = old + + + def get_outputs(self): + return [self.egg_output] + + + def call_command(self,cmdname,**kw): + """Invoke reinitialized command `cmdname` with keyword args""" + for dirname in INSTALL_DIRECTORY_ATTRS: + kw.setdefault(dirname,self.bdist_dir) + kw.setdefault('skip_build',self.skip_build) + kw.setdefault('dry_run', self.dry_run) + cmd = self.reinitialize_command(cmdname, **kw) + self.run_command(cmdname) + return cmd + + + def run(self): + # Generate metadata first + self.run_command("egg_info") + + # We run install_lib before install_data, because some data hacks + # pull their data path from the install_lib command. + log.info("installing library code to %s" % self.bdist_dir) + instcmd = self.get_finalized_command('install') + old_root = instcmd.root; instcmd.root = None + cmd = self.call_command('install_lib', warn_dir=0) + instcmd.root = old_root + + all_outputs, ext_outputs = self.get_ext_outputs() + self.stubs = [] + to_compile = [] + for (p,ext_name) in enumerate(ext_outputs): + filename,ext = os.path.splitext(ext_name) + pyfile = os.path.join(self.bdist_dir, filename + '.py') + self.stubs.append(pyfile) + log.info("creating stub loader for %s" % ext_name) + if not self.dry_run: + write_stub(os.path.basename(ext_name), pyfile) + to_compile.append(pyfile) + ext_outputs[p] = ext_name.replace(os.sep,'/') + + to_compile.extend(self.make_init_files()) + if to_compile: + cmd.byte_compile(to_compile) + + if self.distribution.data_files: + self.do_install_data() + + # Make the EGG-INFO directory + archive_root = self.bdist_dir + egg_info = os.path.join(archive_root,'EGG-INFO') + self.mkpath(egg_info) + if self.distribution.scripts: + script_dir = os.path.join(egg_info, 'scripts') + log.info("installing scripts to %s" % script_dir) + self.call_command('install_scripts',install_dir=script_dir,no_ep=1) + + native_libs = os.path.join(self.egg_info,"native_libs.txt") + if all_outputs: + log.info("writing %s" % native_libs) + if not self.dry_run: + libs_file = open(native_libs, 'wt') + libs_file.write('\n'.join(all_outputs)) + libs_file.write('\n') + libs_file.close() + elif os.path.isfile(native_libs): + log.info("removing %s" % native_libs) + if not self.dry_run: + os.unlink(native_libs) + + for filename in os.listdir(self.egg_info): + path = os.path.join(self.egg_info,filename) + if os.path.isfile(path): + self.copy_file(path,os.path.join(egg_info,filename)) + + write_safety_flag( + os.path.join(archive_root,'EGG-INFO'), self.zip_safe() + ) + + if os.path.exists(os.path.join(self.egg_info,'depends.txt')): + log.warn( + "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + if self.exclude_source_files: + self.zap_pyfiles() + + # Make the archive + make_zipfile(self.egg_output, archive_root, verbose=self.verbose, + dry_run=self.dry_run) + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + # Add to 'Distribution.dist_files' so that the "upload" command works + getattr(self.distribution,'dist_files',[]).append( + ('bdist_egg',get_python_version(),self.egg_output)) + + def zap_pyfiles(self): + log.info("Removing .py files from temporary directory") + for base,dirs,files in walk_egg(self.bdist_dir): + for name in files: + if name.endswith('.py'): + path = os.path.join(base,name) + log.debug("Deleting %s", path) + os.unlink(path) + + def zip_safe(self): + safe = getattr(self.distribution,'zip_safe',None) + if safe is not None: + return safe + log.warn("zip_safe flag not set; analyzing archive contents...") + return analyze_egg(self.bdist_dir, self.stubs) + + def make_init_files(self): + """Create missing package __init__ files""" + init_files = [] + for base,dirs,files in walk_egg(self.bdist_dir): + if base==self.bdist_dir: + # don't put an __init__ in the root + continue + for name in files: + if name.endswith('.py'): + if '__init__.py' not in files: + pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.') + if self.distribution.has_contents_for(pkg): + log.warn("Creating missing __init__.py for %s",pkg) + filename = os.path.join(base,'__init__.py') + if not self.dry_run: + f = open(filename,'w'); f.write(NS_PKG_STUB) + f.close() + init_files.append(filename) + break + else: + # not a package, don't traverse to subdirectories + dirs[:] = [] + + return init_files + + def get_ext_outputs(self): + """Get a list of relative paths to C extensions in the output distro""" + + all_outputs = [] + ext_outputs = [] + + paths = {self.bdist_dir:''} + for base, dirs, files in os.walk(self.bdist_dir): + for filename in files: + if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: + all_outputs.append(paths[base]+filename) + for filename in dirs: + paths[os.path.join(base,filename)] = paths[base]+filename+'/' + + if self.distribution.has_ext_modules(): + build_cmd = self.get_finalized_command('build_ext') + for ext in build_cmd.extensions: + if isinstance(ext,Library): + continue + fullname = build_cmd.get_ext_fullname(ext.name) + filename = build_cmd.get_ext_filename(fullname) + if not os.path.basename(filename).startswith('dl-'): + if os.path.exists(os.path.join(self.bdist_dir,filename)): + ext_outputs.append(filename) + + return all_outputs, ext_outputs + + +NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) + + + + + + + + + + + + +def walk_egg(egg_dir): + """Walk an unpacked egg's contents, skipping the metadata directory""" + walker = os.walk(egg_dir) + base,dirs,files = walker.next() + if 'EGG-INFO' in dirs: + dirs.remove('EGG-INFO') + yield base,dirs,files + for bdf in walker: + yield bdf + +def analyze_egg(egg_dir, stubs): + # check for existing flag in EGG-INFO + for flag,fn in safety_flags.items(): + if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)): + return flag + + safe = True + for base, dirs, files in walk_egg(egg_dir): + for name in files: + if name.endswith('.py') or name.endswith('.pyw'): + continue + elif name.endswith('.pyc') or name.endswith('.pyo'): + # always scan, even if we already know we're not safe + safe = scan_module(egg_dir, base, name, stubs) and safe + return safe + +def write_safety_flag(egg_dir, safe): + # Write or remove zip safety flag file(s) + for flag,fn in safety_flags.items(): + fn = os.path.join(egg_dir, fn) + if os.path.exists(fn): + if safe is None or bool(safe)<>flag: + os.unlink(fn) + elif safe is not None and bool(safe)==flag: + open(fn,'w').close() + +safety_flags = { + True: 'zip-safe', + False: 'not-zip-safe', +} + +def scan_module(egg_dir, base, name, stubs): + """Check whether module possibly uses unsafe-for-zipfile stuff""" + + filename = os.path.join(base,name) + if filename[:-1] in stubs: + return True # Extension module + pkg = base[len(egg_dir)+1:].replace(os.sep,'.') + module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0] + f = open(filename,'rb'); f.read(8) # skip magic & date + code = marshal.load(f); f.close() + safe = True + symbols = dict.fromkeys(iter_symbols(code)) + for bad in ['__file__', '__path__']: + if bad in symbols: + log.warn("%s: module references %s", module, bad) + safe = False + if 'inspect' in symbols: + for bad in [ + 'getsource', 'getabsfile', 'getsourcefile', 'getfile' + 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', + 'getinnerframes', 'getouterframes', 'stack', 'trace' + ]: + if bad in symbols: + log.warn("%s: module MAY be using inspect.%s", module, bad) + safe = False + if '__name__' in symbols and '__main__' in symbols and '.' not in module: + if get_python_version()>="2.4": + log.warn("%s: top-level module may be 'python -m' script", module) + safe = False + return safe + +def iter_symbols(code): + """Yield names and strings used by `code` and its nested code objects""" + for name in code.co_names: yield name + for const in code.co_consts: + if isinstance(const,basestring): + yield const + elif isinstance(const,CodeType): + for name in iter_symbols(const): + yield name + +# Attribute names of options for commands that might need to be convinced to +# install to the egg build directory + +INSTALL_DIRECTORY_ATTRS = [ + 'install_lib', 'install_dir', 'install_data', 'install_base' +] + +def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None): + """Create a zip file from all the files under 'base_dir'. The output + zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" + Python module (if available) or the InfoZIP "zip" utility (if installed + and found on the default search path). If neither tool is available, + raises DistutilsExecError. Returns the name of the output zip file. + """ + import zipfile + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) + + def visit (z, dirname, names): + for name in names: + path = os.path.normpath(os.path.join(dirname, name)) + if os.path.isfile(path): + p = path[len(base_dir)+1:] + if not dry_run: + z.write(path, p) + log.debug("adding '%s'" % p) + + if compress is None: + compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits + + compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)] + if not dry_run: + z = zipfile.ZipFile(zip_filename, "w", compression=compression) + os.path.walk(base_dir, visit, z) + z.close() + else: + os.path.walk(base_dir, visit, None) + + return zip_filename diff --git a/Lib/setuptools/command/bdist_rpm.py b/Lib/setuptools/command/bdist_rpm.py new file mode 100755 index 0000000..00e07ac --- /dev/null +++ b/Lib/setuptools/command/bdist_rpm.py @@ -0,0 +1,37 @@ +# This is just a kludge so that bdist_rpm doesn't guess wrong about the +# distribution name and version, if the egg_info command is going to alter +# them, and another kludge to allow you to build old-style non-egg RPMs + +from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm + +class bdist_rpm(_bdist_rpm): + + def initialize_options(self): + _bdist_rpm.initialize_options(self) + self.no_egg = None + + def run(self): + self.run_command('egg_info') # ensure distro name is up-to-date + _bdist_rpm.run(self) + + def _make_spec_file(self): + version = self.distribution.get_version() + rpmversion = version.replace('-','_') + spec = _bdist_rpm._make_spec_file(self) + line23 = '%define version '+version + line24 = '%define version '+rpmversion + spec = [ + line.replace( + "Source0: %{name}-%{version}.tar", + "Source0: %{name}-%{unmangled_version}.tar" + ).replace( + "setup.py install ", + "setup.py install --single-version-externally-managed " + ).replace( + "%setup", + "%setup -n %{name}-%{unmangled_version}" + ).replace(line23,line24) + for line in spec + ] + spec.insert(spec.index(line24)+1, "%define unmangled_version "+version) + return spec diff --git a/Lib/setuptools/command/build_ext.py b/Lib/setuptools/command/build_ext.py new file mode 100644 index 0000000..f8551fb --- /dev/null +++ b/Lib/setuptools/command/build_ext.py @@ -0,0 +1,285 @@ +from distutils.command.build_ext import build_ext as _du_build_ext +try: + # Attempt to use Pyrex for building extensions, if available + from Pyrex.Distutils.build_ext import build_ext as _build_ext +except ImportError: + _build_ext = _du_build_ext + +import os, sys +from distutils.file_util import copy_file +from setuptools.extension import Library +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler, get_config_var +get_config_var("LDSHARED") # make sure _config_vars is initialized +from distutils.sysconfig import _config_vars +from distutils import log +from distutils.errors import * + +have_rtld = False +use_stubs = False +libtype = 'shared' + +if sys.platform == "darwin": + use_stubs = True +elif os.name != 'nt': + try: + from dl import RTLD_NOW + have_rtld = True + use_stubs = True + except ImportError: + pass + +def if_dl(s): + if have_rtld: + return s + return '' + + + + + + +class build_ext(_build_ext): + def run(self): + """Build extensions in build directory, then copy if --inplace""" + old_inplace, self.inplace = self.inplace, 0 + _build_ext.run(self) + self.inplace = old_inplace + if old_inplace: + self.copy_extensions_to_source() + + def copy_extensions_to_source(self): + build_py = self.get_finalized_command('build_py') + for ext in self.extensions: + fullname = self.get_ext_fullname(ext.name) + filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[:-1]) + package_dir = build_py.get_package_dir(package) + dest_filename = os.path.join(package_dir,os.path.basename(filename)) + src_filename = os.path.join(self.build_lib,filename) + + # Always copy, even if source is older than destination, to ensure + # that the right extensions for the current Python/platform are + # used. + copy_file( + src_filename, dest_filename, verbose=self.verbose, + dry_run=self.dry_run + ) + if ext._needs_stub: + self.write_stub(package_dir or os.curdir, ext, True) + + + if _build_ext is not _du_build_ext: + # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4 + def swig_sources(self, sources, *otherargs): + # first do any Pyrex processing + sources = _build_ext.swig_sources(self, sources) or sources + # Then do any actual SWIG stuff on the remainder + return _du_build_ext.swig_sources(self, sources, *otherargs) + + + + def get_ext_filename(self, fullname): + filename = _build_ext.get_ext_filename(self,fullname) + ext = self.ext_map[fullname] + if isinstance(ext,Library): + fn, ext = os.path.splitext(filename) + return self.shlib_compiler.library_filename(fn,libtype) + elif use_stubs and ext._links_to_dynamic: + d,fn = os.path.split(filename) + return os.path.join(d,'dl-'+fn) + else: + return filename + + def initialize_options(self): + _build_ext.initialize_options(self) + self.shlib_compiler = None + self.shlibs = [] + self.ext_map = {} + + def finalize_options(self): + _build_ext.finalize_options(self) + self.extensions = self.extensions or [] + self.check_extensions_list(self.extensions) + self.shlibs = [ext for ext in self.extensions + if isinstance(ext,Library)] + if self.shlibs: + self.setup_shlib_compiler() + for ext in self.extensions: + fullname = ext._full_name = self.get_ext_fullname(ext.name) + self.ext_map[fullname] = ext + ltd = ext._links_to_dynamic = \ + self.shlibs and self.links_to_dynamic(ext) or False + ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library) + filename = ext._file_name = self.get_ext_filename(fullname) + libdir = os.path.dirname(os.path.join(self.build_lib,filename)) + if ltd and libdir not in ext.library_dirs: + ext.library_dirs.append(libdir) + if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: + ext.runtime_library_dirs.append(os.curdir) + + + + def setup_shlib_compiler(self): + compiler = self.shlib_compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=self.force + ) + if sys.platform == "darwin": + tmp = _config_vars.copy() + try: + # XXX Help! I don't have any idea whether these are right... + _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" + _config_vars['CCSHARED'] = " -dynamiclib" + _config_vars['SO'] = ".dylib" + customize_compiler(compiler) + finally: + _config_vars.clear() + _config_vars.update(tmp) + else: + customize_compiler(compiler) + + if self.include_dirs is not None: + compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + compiler.undefine_macro(macro) + if self.libraries is not None: + compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + compiler.set_link_objects(self.link_objects) + + # hack so distutils' build_extension() builds a library instead + compiler.link_shared_object = link_shared_object.__get__(compiler) + + + + def get_export_symbols(self, ext): + if isinstance(ext,Library): + return ext.export_symbols + return _build_ext.get_export_symbols(self,ext) + + def build_extension(self, ext): + _compiler = self.compiler + try: + if isinstance(ext,Library): + self.compiler = self.shlib_compiler + _build_ext.build_extension(self,ext) + if ext._needs_stub: + self.write_stub( + self.get_finalized_command('build_py').build_lib, ext + ) + finally: + self.compiler = _compiler + + def links_to_dynamic(self, ext): + """Return true if 'ext' links to a dynamic lib in the same package""" + # XXX this should check to ensure the lib is actually being built + # XXX as dynamic, and not just using a locally-found version or a + # XXX static-compiled version + libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) + pkg = '.'.join(ext._full_name.split('.')[:-1]+['']) + for libname in ext.libraries: + if pkg+libname in libnames: return True + return False + + def get_outputs(self): + outputs = _build_ext.get_outputs(self) + optimize = self.get_finalized_command('build_py').optimize + for ext in self.extensions: + if ext._needs_stub: + base = os.path.join(self.build_lib, *ext._full_name.split('.')) + outputs.append(base+'.py') + outputs.append(base+'.pyc') + if optimize: + outputs.append(base+'.pyo') + return outputs + + def write_stub(self, output_dir, ext, compile=False): + log.info("writing stub loader for %s to %s",ext._full_name, output_dir) + stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py' + if compile and os.path.exists(stub_file): + raise DistutilsError(stub_file+" already exists! Please delete.") + if not self.dry_run: + f = open(stub_file,'w') + f.write('\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __file__, __loader__", + " import sys, os, pkg_resources, imp"+if_dl(", dl"), + " __file__ = pkg_resources.resource_filename(__name__,%r)" + % os.path.basename(ext._file_name), + " del __bootstrap__", + " if '__loader__' in globals():", + " del __loader__", + if_dl(" old_flags = sys.getdlopenflags()"), + " old_dir = os.getcwd()", + " try:", + " os.chdir(os.path.dirname(__file__))", + if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), + " imp.load_dynamic(__name__,__file__)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "" # terminal \n + ])) + f.close() + if compile: + from distutils.util import byte_compile + byte_compile([stub_file], optimize=0, + force=True, dry_run=self.dry_run) + optimize = self.get_finalized_command('install_lib').optimize + if optimize > 0: + byte_compile([stub_file], optimize=optimize, + force=True, dry_run=self.dry_run) + if os.path.exists(stub_file) and not self.dry_run: + os.unlink(stub_file) + + +if use_stubs or os.name=='nt': + # Build shared libraries + # + def link_shared_object(self, 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 + ): self.link( + self.SHARED_LIBRARY, objects, output_libname, + output_dir, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs, + build_temp, target_lang + ) +else: + # Build static libraries everywhere else + libtype = 'static' + + def link_shared_object(self, 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 + ): + # XXX we need to either disallow these attrs on Library instances, + # or warn/abort here if set, or something... + #libraries=None, library_dirs=None, runtime_library_dirs=None, + #export_symbols=None, extra_preargs=None, extra_postargs=None, + #build_temp=None + + assert output_dir is None # distutils build_ext doesn't pass this + output_dir,filename = os.path.split(output_libname) + basename, ext = os.path.splitext(filename) + if self.library_filename("x").startswith('lib'): + # strip 'lib' prefix; this is kludgy if some platform uses + # a different prefix + basename = basename[3:] + + self.create_static_lib( + objects, basename, output_dir, debug, target_lang + ) diff --git a/Lib/setuptools/command/build_py.py b/Lib/setuptools/command/build_py.py new file mode 100644 index 0000000..77a9b23 --- /dev/null +++ b/Lib/setuptools/command/build_py.py @@ -0,0 +1,192 @@ +import os.path, sys, fnmatch +from distutils.command.build_py import build_py as _build_py +from distutils.util import convert_path +from glob import glob + +class build_py(_build_py): + """Enhanced 'build_py' command that includes data files with packages + + The data files are specified via a 'package_data' argument to 'setup()'. + See 'setuptools.dist.Distribution' for more details. + + Also, this version of the 'build_py' command allows you to specify both + 'py_modules' and 'packages' in the same setup operation. + """ + def finalize_options(self): + _build_py.finalize_options(self) + self.package_data = self.distribution.package_data + self.exclude_package_data = self.distribution.exclude_package_data or {} + if 'data_files' in self.__dict__: del self.__dict__['data_files'] + + def run(self): + """Build modules, packages, and copy data files to build directory""" + if not self.py_modules and not self.packages: + return + + if self.py_modules: + self.build_modules() + + if self.packages: + self.build_packages() + self.build_package_data() + + # Only compile actual .py files, using our base class' idea of what our + # output files are. + self.byte_compile(_build_py.get_outputs(self, include_bytecode=0)) + + def __getattr__(self,attr): + if attr=='data_files': # lazily compute data files + self.data_files = files = self._get_data_files(); return files + return _build_py.__getattr__(self,attr) + + def _get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + self.analyze_manifest() + data = [] + for package in self.packages or (): + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Length of path to strip from found files + plen = len(src_dir)+1 + + # Strip directory from globbed filenames + filenames = [ + file[plen:] for file in self.find_data_files(package, src_dir) + ] + data.append( (package, src_dir, build_dir, filenames) ) + return data + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = self.manifest_files.get(package, [])[:] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + files.extend(glob(os.path.join(src_dir, convert_path(pattern)))) + return self.exclude_data_files(package, src_dir, files) + + def build_package_data(self): + """Copy data files into build directory""" + lastdir = None + 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) + + + def analyze_manifest(self): + self.manifest_files = mf = {} + if not self.distribution.include_package_data: + return + src_dirs = {} + for package in self.packages or (): + # Locate package source directory + src_dirs[assert_relative(self.get_package_dir(package))] = package + + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + for path in ei_cmd.filelist.files: + if path.endswith('.py'): + continue + d,f = os.path.split(assert_relative(path)) + prev = None + while d and d!=prev and d not in src_dirs: + prev = d + d, df = os.path.split(d) + f = os.path.join(df, f) + if d in src_dirs: + mf.setdefault(src_dirs[d],[]).append(path) + + + def get_data_files(self): pass # kludge 2.4 for lazy computation + + if sys.version<"2.4": # Python 2.4 already has this code + def get_outputs(self, include_bytecode=1): + """Return complete list of files copied to the build directory + + This includes both '.py' files and data files, as well as '.pyc' + and '.pyo' files if 'include_bytecode' is true. (This method is + needed for the 'install_lib' command to do its job properly, and to + generate a correct installation manifest.) + """ + return _build_py.get_outputs(self, include_bytecode) + [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir,filenames in self.data_files + for filename in filenames + ] + + def check_package(self, package, package_dir): + """Check namespace packages' __init__ for declare_namespace""" + try: + return self.packages_checked[package] + except KeyError: + pass + + init_py = _build_py.check_package(self, package, package_dir) + self.packages_checked[package] = init_py + + if not init_py or not self.distribution.namespace_packages: + return init_py + + for pkg in self.distribution.namespace_packages: + if pkg==package or pkg.startswith(package+'.'): + break + else: + return init_py + + f = open(init_py,'rU') + if 'declare_namespace' not in f.read(): + from distutils.errors import DistutilsError + raise DistutilsError( + "Namespace package problem: %s is a namespace package, but its\n" + "__init__.py does not call declare_namespace()! Please fix it.\n" + '(See the setuptools manual under "Namespace Packages" for ' + "details.)\n" % (package,) + ) + f.close() + return init_py + + def initialize_options(self): + self.packages_checked={} + _build_py.initialize_options(self) + + + + + + + + def exclude_data_files(self, package, src_dir, files): + """Filter filenames for package's data files in 'src_dir'""" + globs = (self.exclude_package_data.get('', []) + + self.exclude_package_data.get(package, [])) + bad = [] + for pattern in globs: + bad.extend( + fnmatch.filter( + files, os.path.join(src_dir, convert_path(pattern)) + ) + ) + bad = dict.fromkeys(bad) + return [f for f in files if f not in bad] + + +def assert_relative(path): + if not os.path.isabs(path): + return path + from distutils.errors import DistutilsSetupError + raise DistutilsSetupError( +"""Error: setup script specifies an absolute path: + + %s + +setup() arguments must *always* be /-separated paths relative to the +setup.py directory, *never* absolute paths. +""" % path + ) diff --git a/Lib/setuptools/command/develop.py b/Lib/setuptools/command/develop.py new file mode 100755 index 0000000..7ab5b23 --- /dev/null +++ b/Lib/setuptools/command/develop.py @@ -0,0 +1,116 @@ +from setuptools.command.easy_install import easy_install +from distutils.util import convert_path +from pkg_resources import Distribution, PathMetadata, normalize_path +from distutils import log +from distutils.errors import * +import sys, os + +class develop(easy_install): + """Set up package for development""" + + description = "install package in 'development mode'" + + user_options = easy_install.user_options + [ + ("uninstall", "u", "Uninstall this source package"), + ] + + boolean_options = easy_install.boolean_options + ['uninstall'] + + command_consumes_arguments = False # override base + + def run(self): + if self.uninstall: + self.multi_version = True + self.uninstall_link() + else: + self.install_for_development() + self.warn_deprecated_options() + + def initialize_options(self): + self.uninstall = None + easy_install.initialize_options(self) + + + + + + + + + + + def finalize_options(self): + ei = self.get_finalized_command("egg_info") + if ei.broken_egg_info: + raise DistutilsError( + "Please rename %r to %r before using 'develop'" + % (ei.egg_info, ei.broken_egg_info) + ) + self.args = [ei.egg_name] + easy_install.finalize_options(self) + self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link') + self.egg_base = ei.egg_base + self.egg_path = os.path.abspath(ei.egg_base) + + # Make a distribution for the package's source + self.dist = Distribution( + normalize_path(self.egg_path), + PathMetadata(self.egg_path, os.path.abspath(ei.egg_info)), + project_name = ei.egg_name + ) + + def install_for_development(self): + # Ensure metadata is up-to-date + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + self.install_site_py() # ensure that target dir is site-safe + + # create an .egg-link in the installation dir, pointing to our egg + log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) + if not self.dry_run: + f = open(self.egg_link,"w") + f.write(self.egg_path) + f.close() + + # postprocess the installed distro, fixing up .pth, installing scripts, + # and handling requirements + self.process_distribution(None, self.dist) + + def uninstall_link(self): + if os.path.exists(self.egg_link): + log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) + contents = [line.rstrip() for line in file(self.egg_link)] + if contents != [self.egg_path]: + log.warn("Link points to %s: uninstall aborted", contents) + return + if not self.dry_run: + os.unlink(self.egg_link) + if not self.dry_run: + self.update_pth(self.dist) # remove any .pth link to us + if self.distribution.scripts: + # XXX should also check for entry point scripts! + log.warn("Note: you must uninstall or replace scripts manually!") + + + def install_egg_scripts(self, dist): + if dist is not self.dist: + # Installing a dependency, so fall back to normal behavior + return easy_install.install_egg_scripts(self,dist) + + # create wrapper scripts in the script dir, pointing to dist.scripts + + # new-style... + self.install_wrapper_scripts(dist) + + # ...and old-style + for script_name in self.distribution.scripts or []: + script_path = os.path.abspath(convert_path(script_name)) + script_name = os.path.basename(script_path) + f = open(script_path,'rU') + script_text = f.read() + f.close() + self.install_script(dist, script_name, script_text, script_path) diff --git a/Lib/setuptools/command/easy_install.py b/Lib/setuptools/command/easy_install.py new file mode 100755 index 0000000..3ddcec4 --- /dev/null +++ b/Lib/setuptools/command/easy_install.py @@ -0,0 +1,1555 @@ +#!python +"""\ +Easy Install +------------ + +A tool for doing automatic download/extract/build of distutils-based Python +packages. For detailed documentation, see the accompanying EasyInstall.txt +file, or visit the `EasyInstall home page`__. + +__ http://peak.telecommunity.com/DevCenter/EasyInstall +""" +import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random +from glob import glob +from setuptools import Command +from setuptools.sandbox import run_setup +from distutils import log, dir_util +from distutils.sysconfig import get_python_lib +from distutils.errors import DistutilsArgError, DistutilsOptionError, \ + DistutilsError +from setuptools.archive_util import unpack_archive +from setuptools.package_index import PackageIndex, parse_bdist_wininst +from setuptools.package_index import URL_SCHEME +from setuptools.command import bdist_egg, egg_info +from pkg_resources import * +sys_executable = os.path.normpath(sys.executable) + +__all__ = [ + 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', + 'main', 'get_exe_prefixes', +] + +def samefile(p1,p2): + if hasattr(os.path,'samefile') and ( + os.path.exists(p1) and os.path.exists(p2) + ): + return os.path.samefile(p1,p2) + return ( + os.path.normpath(os.path.normcase(p1)) == + os.path.normpath(os.path.normcase(p2)) + ) + +class easy_install(Command): + """Manage a download/build/install process""" + description = "Find/get/install Python packages" + command_consumes_arguments = True + + user_options = [ + ('prefix=', None, "installation prefix"), + ("zip-ok", "z", "install package as a zipfile"), + ("multi-version", "m", "make apps have to require() a version"), + ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), + ("install-dir=", "d", "install package to DIR"), + ("script-dir=", "s", "install scripts to DIR"), + ("exclude-scripts", "x", "Don't install scripts"), + ("always-copy", "a", "Copy all needed packages to install dir"), + ("index-url=", "i", "base URL of Python Package Index"), + ("find-links=", "f", "additional URL(s) to search for packages"), + ("delete-conflicting", "D", "no longer needed; don't use this"), + ("ignore-conflicts-at-my-risk", None, + "no longer needed; don't use this"), + ("build-directory=", "b", + "download/extract/build in DIR; keep the results"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('record=', None, + "filename in which to record list of installed files"), + ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), + ('site-dirs=','S',"list of directories where .pth files work"), + ('editable', 'e', "Install specified packages in editable form"), + ('no-deps', 'N', "don't install dependencies"), + ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), + ] + boolean_options = [ + 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', + 'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable', + 'no-deps', + ] + negative_opt = {'always-unzip': 'zip-ok'} + create_index = PackageIndex + + + def initialize_options(self): + self.zip_ok = None + self.install_dir = self.script_dir = self.exclude_scripts = None + self.index_url = None + self.find_links = None + self.build_directory = None + self.args = None + self.optimize = self.record = None + self.upgrade = self.always_copy = self.multi_version = None + self.editable = self.no_deps = self.allow_hosts = None + self.root = self.prefix = self.no_report = None + + # Options not specifiable via command line + self.package_index = None + self.pth_file = None + self.delete_conflicting = None + self.ignore_conflicts_at_my_risk = None + self.site_dirs = None + self.installed_projects = {} + self.sitepy_installed = False + # Always read easy_install options, even if we are subclassed, or have + # an independent instance created. This ensures that defaults will + # always come from the standard configuration file(s)' "easy_install" + # section, even if this is a "develop" or "install" command, or some + # other embedding. + self._dry_run = None + self.verbose = self.distribution.verbose + self.distribution._set_command_options( + self, self.distribution.get_option_dict('easy_install') + ) + + def delete_blockers(self, blockers): + for filename in blockers: + if os.path.exists(filename) or os.path.islink(filename): + log.info("Deleting %s", filename) + if not self.dry_run: + if os.path.isdir(filename) and not os.path.islink(filename): + rmtree(filename) + else: + os.unlink(filename) + + def finalize_options(self): + self._expand('install_dir','script_dir','build_directory','site_dirs') + # If a non-default installation directory was specified, default the + # script directory to match it. + if self.script_dir is None: + self.script_dir = self.install_dir + + # Let install_dir get set by install_lib command, which in turn + # gets its info from the install command, and takes into account + # --prefix and --home and all that other crud. + self.set_undefined_options('install_lib', + ('install_dir','install_dir') + ) + # Likewise, set default script_dir from 'install_scripts.install_dir' + self.set_undefined_options('install_scripts', + ('install_dir', 'script_dir') + ) + # default --record from the install command + self.set_undefined_options('install', ('record', 'record')) + normpath = map(normalize_path, sys.path) + self.all_site_dirs = get_site_dirs() + if self.site_dirs is not None: + site_dirs = [ + os.path.expanduser(s.strip()) for s in self.site_dirs.split(',') + ] + for d in site_dirs: + if not os.path.isdir(d): + log.warn("%s (in --site-dirs) does not exist", d) + elif normalize_path(d) not in normpath: + raise DistutilsOptionError( + d+" (in --site-dirs) is not on sys.path" + ) + else: + self.all_site_dirs.append(normalize_path(d)) + self.check_site_dir() + self.index_url = self.index_url or "http://www.python.org/pypi" + self.shadow_path = self.all_site_dirs[:] + for path_item in self.install_dir, normalize_path(self.script_dir): + if path_item not in self.shadow_path: + self.shadow_path.insert(0, path_item) + + if self.allow_hosts is not None: + hosts = [s.strip() for s in self.allow_hosts.split(',')] + else: + hosts = ['*'] + + if self.package_index is None: + self.package_index = self.create_index( + self.index_url, search_path = self.shadow_path, hosts=hosts + ) + self.local_index = Environment(self.shadow_path) + + if self.find_links is not None: + if isinstance(self.find_links, basestring): + self.find_links = self.find_links.split() + else: + self.find_links = [] + + self.package_index.add_find_links(self.find_links) + self.set_undefined_options('install_lib', ('optimize','optimize')) + if not isinstance(self.optimize,int): + try: + self.optimize = int(self.optimize) + if not (0 <= self.optimize <= 2): raise ValueError + except ValueError: + raise DistutilsOptionError("--optimize must be 0, 1, or 2") + + if self.delete_conflicting and self.ignore_conflicts_at_my_risk: + raise DistutilsOptionError( + "Can't use both --delete-conflicting and " + "--ignore-conflicts-at-my-risk at the same time" + ) + if self.editable and not self.build_directory: + raise DistutilsArgError( + "Must specify a build directory (-b) when using --editable" + ) + if not self.args: + raise DistutilsArgError( + "No urls, filenames, or requirements specified (see --help)") + + self.outputs = [] + + def run(self): + if self.verbose<>self.distribution.verbose: + log.set_verbosity(self.verbose) + try: + for spec in self.args: + self.easy_install(spec, not self.no_deps) + if self.record: + outputs = self.outputs + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in xrange(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + from distutils import file_util + self.execute( + file_util.write_file, (self.record, outputs), + "writing list of installed files to '%s'" % + self.record + ) + self.warn_deprecated_options() + finally: + log.set_verbosity(self.distribution.verbose) + + def pseudo_tempname(self): + """Return a pseudo-tempname base in the install directory. + This code is intentionally naive; if a malicious party can write to + the target directory you're already in deep doodoo. + """ + try: + pid = os.getpid() + except: + pid = random.randint(0,sys.maxint) + return os.path.join(self.install_dir, "test-easy-install-%s" % pid) + + def warn_deprecated_options(self): + if self.delete_conflicting or self.ignore_conflicts_at_my_risk: + log.warn( + "Note: The -D, --delete-conflicting and" + " --ignore-conflicts-at-my-risk no longer have any purpose" + " and should not be used." + ) + + def check_site_dir(self): + """Verify that self.install_dir is .pth-capable dir, if needed""" + + instdir = normalize_path(self.install_dir) + pth_file = os.path.join(instdir,'easy-install.pth') + + # Is it a configured, PYTHONPATH, implicit, or explicit site dir? + is_site_dir = instdir in self.all_site_dirs + + if not is_site_dir: + # No? Then directly test whether it does .pth file processing + is_site_dir = self.check_pth_processing() + else: + # make sure we can write to target dir + testfile = self.pseudo_tempname()+'.write-test' + test_exists = os.path.exists(testfile) + try: + if test_exists: os.unlink(testfile) + open(testfile,'w').close() + os.unlink(testfile) + except (OSError,IOError): + self.cant_write_to_target() + + if not is_site_dir and not self.multi_version: + # Can't install non-multi to non-site dir + raise DistutilsError(self.no_default_version_msg()) + + if is_site_dir: + if self.pth_file is None: + self.pth_file = PthDistributions(pth_file) + else: + self.pth_file = None + + PYTHONPATH = os.environ.get('PYTHONPATH','').split(os.pathsep) + if instdir not in map(normalize_path, filter(None,PYTHONPATH)): + # only PYTHONPATH dirs need a site.py, so pretend it's there + self.sitepy_installed = True + + self.install_dir = instdir + + + def cant_write_to_target(self): + msg = """can't create or remove files in install directory + +The following error occurred while trying to add or remove files in the +installation directory: + + %s + +The installation directory you specified (via --install-dir, --prefix, or +the distutils default setting) was: + + %s +""" % (sys.exc_info()[1], self.install_dir,) + + if not os.path.exists(self.install_dir): + msg += """ +This directory does not currently exist. Please create it and try again, or +choose a different installation directory (using the -d or --install-dir +option). +""" + else: + msg += """ +Perhaps your account does not have write access to this directory? If the +installation directory is a system-owned directory, you may need to sign in +as the administrator or "root" account. If you do not have administrative +access to this machine, you may wish to choose a different installation +directory, preferably one that is listed in your PYTHONPATH environment +variable. + +For information on other options, you may wish to consult the +documentation at: + + http://peak.telecommunity.com/EasyInstall.html + +Please make the appropriate changes for your system and try again. +""" + raise DistutilsError(msg) + + + + + def check_pth_processing(self): + """Empirically verify whether .pth files are supported in inst. dir""" + instdir = self.install_dir + log.info("Checking .pth file support in %s", instdir) + pth_file = self.pseudo_tempname()+".pth" + ok_file = pth_file+'.ok' + ok_exists = os.path.exists(ok_file) + try: + if ok_exists: os.unlink(ok_file) + f = open(pth_file,'w') + except (OSError,IOError): + self.cant_write_to_target() + else: + try: + f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,)) + f.close(); f=None + executable = sys.executable + if os.name=='nt': + dirname,basename = os.path.split(executable) + alt = os.path.join(dirname,'pythonw.exe') + if basename.lower()=='python.exe' and os.path.exists(alt): + # use pythonw.exe to avoid opening a console window + executable = alt + if ' ' in executable: executable='"%s"' % executable + from distutils.spawn import spawn + spawn([executable,'-E','-c','pass'],0) + + if os.path.exists(ok_file): + log.info( + "TEST PASSED: %s appears to support .pth files", + instdir + ) + return True + finally: + if f: f.close() + if os.path.exists(ok_file): os.unlink(ok_file) + if os.path.exists(pth_file): os.unlink(pth_file) + if not self.multi_version: + log.warn("TEST FAILED: %s does NOT support .pth files", instdir) + return False + + def install_egg_scripts(self, dist): + """Write all the scripts for `dist`, unless scripts are excluded""" + + self.install_wrapper_scripts(dist) + if self.exclude_scripts or not dist.metadata_isdir('scripts'): + return + + for script_name in dist.metadata_listdir('scripts'): + self.install_script( + dist, script_name, + dist.get_metadata('scripts/'+script_name).replace('\r','\n') + ) + + def add_output(self, path): + if os.path.isdir(path): + for base, dirs, files in os.walk(path): + for filename in files: + self.outputs.append(os.path.join(base,filename)) + else: + self.outputs.append(path) + + def not_editable(self, spec): + if self.editable: + raise DistutilsArgError( + "Invalid argument %r: you can't use filenames or URLs " + "with --editable (except via the --find-links option)." + % (spec,) + ) + + def check_editable(self,spec): + if not self.editable: + return + + if os.path.exists(os.path.join(self.build_directory, spec.key)): + raise DistutilsArgError( + "%r already exists in %s; can't do a checkout there" % + (spec.key, self.build_directory) + ) + + + + def easy_install(self, spec, deps=False): + tmpdir = tempfile.mkdtemp(prefix="easy_install-") + download = None + self.install_site_py() + + try: + if not isinstance(spec,Requirement): + if URL_SCHEME(spec): + # It's a url, download it to tmpdir and process + self.not_editable(spec) + download = self.package_index.download(spec, tmpdir) + return self.install_item(None, download, tmpdir, deps, True) + + elif os.path.exists(spec): + # Existing file or directory, just process it directly + self.not_editable(spec) + return self.install_item(None, spec, tmpdir, deps, True) + else: + spec = parse_requirement_arg(spec) + + self.check_editable(spec) + dist = self.package_index.fetch_distribution( + spec, tmpdir, self.upgrade, self.editable, not self.always_copy + ) + + if dist is None: + msg = "Could not find suitable distribution for %r" % spec + if self.always_copy: + msg+=" (--always-copy skips system and development eggs)" + raise DistutilsError(msg) + elif dist.precedence==DEVELOP_DIST: + # .egg-info dists don't need installing, just process deps + self.process_distribution(spec, dist, deps, "Using") + return dist + else: + return self.install_item(spec, dist.location, tmpdir, deps) + + finally: + if os.path.exists(tmpdir): + rmtree(tmpdir) + + def install_item(self, spec, download, tmpdir, deps, install_needed=False): + + # Installation is also needed if file in tmpdir or is not an egg + install_needed = install_needed or os.path.dirname(download) == tmpdir + install_needed = install_needed or not download.endswith('.egg') + + log.info("Processing %s", os.path.basename(download)) + + if install_needed or self.always_copy: + dists = self.install_eggs(spec, download, tmpdir) + for dist in dists: + self.process_distribution(spec, dist, deps) + else: + dists = [self.check_conflicts(self.egg_distribution(download))] + self.process_distribution(spec, dists[0], deps, "Using") + + if spec is not None: + for dist in dists: + if dist in spec: + return dist + + + + + + + + + + + + + + + + + + + + + + def process_distribution(self, requirement, dist, deps=True, *info): + self.update_pth(dist) + self.package_index.add(dist) + self.local_index.add(dist) + self.install_egg_scripts(dist) + self.installed_projects[dist.key] = dist + log.warn(self.installation_report(requirement, dist, *info)) + if not deps and not self.always_copy: + return + elif requirement is not None and dist.key != requirement.key: + log.warn("Skipping dependencies for %s", dist) + return # XXX this is not the distribution we were looking for + elif requirement is None or dist not in requirement: + # if we wound up with a different version, resolve what we've got + distreq = dist.as_requirement() + requirement = requirement or distreq + requirement = Requirement( + distreq.project_name, distreq.specs, requirement.extras + ) + if dist.has_metadata('dependency_links.txt'): + self.package_index.add_find_links( + dist.get_metadata_lines('dependency_links.txt') + ) + log.info("Processing dependencies for %s", requirement) + try: + distros = WorkingSet([]).resolve( + [requirement], self.local_index, self.easy_install + ) + except DistributionNotFound, e: + raise DistutilsError( + "Could not find required distribution %s" % e.args + ) + except VersionConflict, e: + raise DistutilsError( + "Installed distribution %s conflicts with requirement %s" + % e.args + ) + if self.always_copy: + # Force all the relevant distros to be copied or activated + for dist in distros: + if dist.key not in self.installed_projects: + self.easy_install(dist.as_requirement()) + + def should_unzip(self, dist): + if self.zip_ok is not None: + return not self.zip_ok + if dist.has_metadata('not-zip-safe'): + return True + if not dist.has_metadata('zip-safe'): + return True + return False + + def maybe_move(self, spec, dist_filename, setup_base): + dst = os.path.join(self.build_directory, spec.key) + if os.path.exists(dst): + log.warn( + "%r already exists in %s; build directory %s will not be kept", + spec.key, self.build_directory, setup_base + ) + return setup_base + if os.path.isdir(dist_filename): + setup_base = dist_filename + else: + if os.path.dirname(dist_filename)==setup_base: + os.unlink(dist_filename) # get it out of the tmp dir + contents = os.listdir(setup_base) + if len(contents)==1: + dist_filename = os.path.join(setup_base,contents[0]) + if os.path.isdir(dist_filename): + # if the only thing there is a directory, move it instead + setup_base = dist_filename + ensure_directory(dst); shutil.move(setup_base, dst) + return dst + + def install_wrapper_scripts(self, dist): + if not self.exclude_scripts: + for args in get_script_args(dist): + self.write_script(*args) + + + + + + + def install_script(self, dist, script_name, script_text, dev_path=None): + """Generate a legacy script wrapper and install it""" + spec = str(dist.as_requirement()) + + if dev_path: + script_text = get_script_header(script_text) + ( + "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n" + "__requires__ = %(spec)r\n" + "from pkg_resources import require; require(%(spec)r)\n" + "del require\n" + "__file__ = %(dev_path)r\n" + "execfile(__file__)\n" + ) % locals() + else: + script_text = get_script_header(script_text) + ( + "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n" + "__requires__ = %(spec)r\n" + "import pkg_resources\n" + "pkg_resources.run_script(%(spec)r, %(script_name)r)\n" + ) % locals() + + self.write_script(script_name, script_text) + + def write_script(self, script_name, contents, mode="t", blockers=()): + """Write an executable file to the scripts directory""" + self.delete_blockers( # clean up old .py/.pyw w/o a script + [os.path.join(self.script_dir,x) for x in blockers]) + log.info("Installing %s script to %s", script_name, self.script_dir) + target = os.path.join(self.script_dir, script_name) + self.add_output(target) + + if not self.dry_run: + ensure_directory(target) + f = open(target,"w"+mode) + f.write(contents) + f.close() + try: + os.chmod(target,0755) + except (AttributeError, os.error): + pass + + def install_eggs(self, spec, dist_filename, tmpdir): + # .egg dirs or files are already built, so just return them + if dist_filename.lower().endswith('.egg'): + return [self.install_egg(dist_filename, tmpdir)] + elif dist_filename.lower().endswith('.exe'): + return [self.install_exe(dist_filename, tmpdir)] + + # Anything else, try to extract and build + setup_base = tmpdir + if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): + unpack_archive(dist_filename, tmpdir, self.unpack_progress) + elif os.path.isdir(dist_filename): + setup_base = os.path.abspath(dist_filename) + + if (setup_base.startswith(tmpdir) # something we downloaded + and self.build_directory and spec is not None + ): + setup_base = self.maybe_move(spec, dist_filename, setup_base) + + # Find the setup.py file + setup_script = os.path.join(setup_base, 'setup.py') + + if not os.path.exists(setup_script): + setups = glob(os.path.join(setup_base, '*', 'setup.py')) + if not setups: + raise DistutilsError( + "Couldn't find a setup script in %s" % dist_filename + ) + if len(setups)>1: + raise DistutilsError( + "Multiple setup scripts in %s" % dist_filename + ) + setup_script = setups[0] + + # Now run it, and return the result + if self.editable: + log.warn(self.report_editable(spec, setup_script)) + return [] + else: + return self.build_and_install(setup_script, setup_base) + + def egg_distribution(self, egg_path): + if os.path.isdir(egg_path): + metadata = PathMetadata(egg_path,os.path.join(egg_path,'EGG-INFO')) + else: + metadata = EggMetadata(zipimport.zipimporter(egg_path)) + return Distribution.from_filename(egg_path,metadata=metadata) + + def install_egg(self, egg_path, tmpdir): + destination = os.path.join(self.install_dir,os.path.basename(egg_path)) + destination = os.path.abspath(destination) + if not self.dry_run: + ensure_directory(destination) + + dist = self.egg_distribution(egg_path) + self.check_conflicts(dist) + if not samefile(egg_path, destination): + if os.path.isdir(destination) and not os.path.islink(destination): + dir_util.remove_tree(destination, dry_run=self.dry_run) + elif os.path.exists(destination): + self.execute(os.unlink,(destination,),"Removing "+destination) + uncache_zipdir(destination) + if os.path.isdir(egg_path): + if egg_path.startswith(tmpdir): + f,m = shutil.move, "Moving" + else: + f,m = shutil.copytree, "Copying" + elif self.should_unzip(dist): + self.mkpath(destination) + f,m = self.unpack_and_compile, "Extracting" + elif egg_path.startswith(tmpdir): + f,m = shutil.move, "Moving" + else: + f,m = shutil.copy2, "Copying" + + self.execute(f, (egg_path, destination), + (m+" %s to %s") % + (os.path.basename(egg_path),os.path.dirname(destination))) + + self.add_output(destination) + return self.egg_distribution(destination) + + def install_exe(self, dist_filename, tmpdir): + # See if it's valid, get data + cfg = extract_wininst_cfg(dist_filename) + if cfg is None: + raise DistutilsError( + "%s is not a valid distutils Windows .exe" % dist_filename + ) + # Create a dummy distribution object until we build the real distro + dist = Distribution(None, + project_name=cfg.get('metadata','name'), + version=cfg.get('metadata','version'), platform="win32" + ) + + # Convert the .exe to an unpacked egg + egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg') + egg_tmp = egg_path+'.tmp' + egg_info = os.path.join(egg_tmp, 'EGG-INFO') + pkg_inf = os.path.join(egg_info, 'PKG-INFO') + ensure_directory(pkg_inf) # make sure EGG-INFO dir exists + dist._provider = PathMetadata(egg_tmp, egg_info) # XXX + self.exe_to_egg(dist_filename, egg_tmp) + + # Write EGG-INFO/PKG-INFO + if not os.path.exists(pkg_inf): + f = open(pkg_inf,'w') + f.write('Metadata-Version: 1.0\n') + for k,v in cfg.items('metadata'): + if k<>'target_version': + f.write('%s: %s\n' % (k.replace('_','-').title(), v)) + f.close() + script_dir = os.path.join(egg_info,'scripts') + self.delete_blockers( # delete entry-point scripts to avoid duping + [os.path.join(script_dir,args[0]) for args in get_script_args(dist)] + ) + # Build .egg file from tmpdir + bdist_egg.make_zipfile( + egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run + ) + # install the .egg + return self.install_egg(egg_path, tmpdir) + + def exe_to_egg(self, dist_filename, egg_tmp): + """Extract a bdist_wininst to the directories an egg would use""" + # Check for .pth file and set up prefix translations + prefixes = get_exe_prefixes(dist_filename) + to_compile = [] + native_libs = [] + top_level = {} + + def process(src,dst): + for old,new in prefixes: + if src.startswith(old): + src = new+src[len(old):] + parts = src.split('/') + dst = os.path.join(egg_tmp, *parts) + dl = dst.lower() + if dl.endswith('.pyd') or dl.endswith('.dll'): + top_level[os.path.splitext(parts[0])[0]] = 1 + native_libs.append(src) + elif dl.endswith('.py') and old!='SCRIPTS/': + top_level[os.path.splitext(parts[0])[0]] = 1 + to_compile.append(dst) + return dst + if not src.endswith('.pth'): + log.warn("WARNING: can't process %s", src) + return None + + # extract, tracking .pyd/.dll->native_libs and .py -> to_compile + unpack_archive(dist_filename, egg_tmp, process) + stubs = [] + for res in native_libs: + if res.lower().endswith('.pyd'): # create stubs for .pyd's + parts = res.split('/') + resource, parts[-1] = parts[-1], parts[-1][:-1] + pyfile = os.path.join(egg_tmp, *parts) + to_compile.append(pyfile); stubs.append(pyfile) + bdist_egg.write_stub(resource, pyfile) + + self.byte_compile(to_compile) # compile .py's + bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'), + bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag + + for name in 'top_level','native_libs': + if locals()[name]: + txt = os.path.join(egg_tmp, 'EGG-INFO', name+'.txt') + if not os.path.exists(txt): + open(txt,'w').write('\n'.join(locals()[name])+'\n') + + def check_conflicts(self, dist): + """Verify that there are no conflicting "old-style" packages""" + + return dist # XXX temporarily disable until new strategy is stable + from imp import find_module, get_suffixes + from glob import glob + + blockers = [] + names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr + + exts = {'.pyc':1, '.pyo':1} # get_suffixes() might leave one out + for ext,mode,typ in get_suffixes(): + exts[ext] = 1 + + for path,files in expand_paths([self.install_dir]+self.all_site_dirs): + for filename in files: + base,ext = os.path.splitext(filename) + if base in names: + if not ext: + # no extension, check for package + try: + f, filename, descr = find_module(base, [path]) + except ImportError: + continue + else: + if f: f.close() + if filename not in blockers: + blockers.append(filename) + elif ext in exts and base!='site': # XXX ugh + blockers.append(os.path.join(path,filename)) + if blockers: + self.found_conflicts(dist, blockers) + + return dist + + def found_conflicts(self, dist, blockers): + if self.delete_conflicting: + log.warn("Attempting to delete conflicting packages:") + return self.delete_blockers(blockers) + + msg = """\ +------------------------------------------------------------------------- +CONFLICT WARNING: + +The following modules or packages have the same names as modules or +packages being installed, and will be *before* the installed packages in +Python's search path. You MUST remove all of the relevant files and +directories before you will be able to use the package(s) you are +installing: + + %s + +""" % '\n '.join(blockers) + + if self.ignore_conflicts_at_my_risk: + msg += """\ +(Note: you can run EasyInstall on '%s' with the +--delete-conflicting option to attempt deletion of the above files +and/or directories.) +""" % dist.project_name + else: + msg += """\ +Note: you can attempt this installation again with EasyInstall, and use +either the --delete-conflicting (-D) option or the +--ignore-conflicts-at-my-risk option, to either delete the above files +and directories, or to ignore the conflicts, respectively. Note that if +you ignore the conflicts, the installed package(s) may not work. +""" + msg += """\ +------------------------------------------------------------------------- +""" + sys.stderr.write(msg) + sys.stderr.flush() + if not self.ignore_conflicts_at_my_risk: + raise DistutilsError("Installation aborted due to conflicts") + + def installation_report(self, req, dist, what="Installed"): + """Helpful installation message for display to package users""" + msg = "\n%(what)s %(eggloc)s%(extras)s" + if self.multi_version and not self.no_report: + msg += """ + +Because this distribution was installed --multi-version or --install-dir, +before you can import modules from this package in an application, you +will need to 'import pkg_resources' and then use a 'require()' call +similar to one of these examples, in order to select the desired version: + + pkg_resources.require("%(name)s") # latest installed version + pkg_resources.require("%(name)s==%(version)s") # this exact version + pkg_resources.require("%(name)s>=%(version)s") # this version or higher +""" + if self.install_dir not in map(normalize_path,sys.path): + msg += """ + +Note also that the installation directory must be on sys.path at runtime for +this to work. (e.g. by being the application's script directory, by being on +PYTHONPATH, or by being added to sys.path by your code.) +""" + eggloc = dist.location + name = dist.project_name + version = dist.version + extras = '' # TODO: self.report_extras(req, dist) + return msg % locals() + + def report_editable(self, spec, setup_script): + dirname = os.path.dirname(setup_script) + python = sys.executable + return """\nExtracted editable version of %(spec)s to %(dirname)s + +If it uses setuptools in its setup script, you can activate it in +"development" mode by going to that directory and running:: + + %(python)s setup.py develop + +See the setuptools documentation for the "develop" command for more info. +""" % locals() + + def run_setup(self, setup_script, setup_base, args): + sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) + sys.modules.setdefault('distutils.command.egg_info', egg_info) + + args = list(args) + if self.verbose>2: + v = 'v' * (self.verbose - 1) + args.insert(0,'-'+v) + elif self.verbose<2: + args.insert(0,'-q') + if self.dry_run: + args.insert(0,'-n') + log.info( + "Running %s %s", setup_script[len(setup_base)+1:], ' '.join(args) + ) + try: + run_setup(setup_script, args) + except SystemExit, v: + raise DistutilsError("Setup script exited with %s" % (v.args[0],)) + + def build_and_install(self, setup_script, setup_base): + args = ['bdist_egg', '--dist-dir'] + dist_dir = tempfile.mkdtemp( + prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) + ) + try: + args.append(dist_dir) + self.run_setup(setup_script, setup_base, args) + all_eggs = Environment([dist_dir]) + eggs = [] + for key in all_eggs: + for dist in all_eggs[key]: + eggs.append(self.install_egg(dist.location, setup_base)) + if not eggs and not self.dry_run: + log.warn("No eggs found in %s (setup script problem?)", + dist_dir) + return eggs + finally: + rmtree(dist_dir) + log.set_verbosity(self.verbose) # restore our log verbosity + + def update_pth(self,dist): + if self.pth_file is None: + return + + for d in self.pth_file[dist.key]: # drop old entries + if self.multi_version or d.location != dist.location: + log.info("Removing %s from easy-install.pth file", d) + self.pth_file.remove(d) + if d.location in self.shadow_path: + self.shadow_path.remove(d.location) + + if not self.multi_version: + if dist.location in self.pth_file.paths: + log.info( + "%s is already the active version in easy-install.pth", + dist + ) + else: + log.info("Adding %s to easy-install.pth file", dist) + self.pth_file.add(dist) # add new entry + if dist.location not in self.shadow_path: + self.shadow_path.append(dist.location) + + if not self.dry_run: + + self.pth_file.save() + + if dist.key=='setuptools': + # Ensure that setuptools itself never becomes unavailable! + # XXX should this check for latest version? + filename = os.path.join(self.install_dir,'setuptools.pth') + if os.path.islink(filename): os.unlink(filename) + f = open(filename, 'wt') + f.write(self.pth_file.make_relative(dist.location)+'\n') + f.close() + + def unpack_progress(self, src, dst): + # Progress filter for unpacking + log.debug("Unpacking %s to %s", src, dst) + return dst # only unpack-and-compile skips files for dry run + + def unpack_and_compile(self, egg_path, destination): + to_compile = [] + + def pf(src,dst): + if dst.endswith('.py') and not src.startswith('EGG-INFO/'): + to_compile.append(dst) + self.unpack_progress(src,dst) + return not self.dry_run and dst or None + + unpack_archive(egg_path, destination, pf) + self.byte_compile(to_compile) + + + def byte_compile(self, to_compile): + from distutils.util import byte_compile + try: + # try to make the byte compile messages quieter + log.set_verbosity(self.verbose - 1) + + byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) + if self.optimize: + byte_compile( + to_compile, optimize=self.optimize, force=1, + dry_run=self.dry_run + ) + finally: + log.set_verbosity(self.verbose) # restore original verbosity + + + + + + + + + + + + + + + def no_default_version_msg(self): + return """bad install directory or PYTHONPATH + +You are attempting to install a package to a directory that is not +on PYTHONPATH and which Python does not read ".pth" files from. The +installation directory you specified (via --install-dir, --prefix, or +the distutils default setting) was: + + %s + +and your PYTHONPATH environment variable currently contains: + + %r + +Here are some of your options for correcting the problem: + +* You can choose a different installation directory, i.e., one that is + on PYTHONPATH or supports .pth files + +* You can add the installation directory to the PYTHONPATH environment + variable. (It must then also be on PYTHONPATH whenever you run + Python and want to use the package(s) you are installing.) + +* You can set up the installation directory to support ".pth" files by + using one of the approaches described here: + + http://peak.telecommunity.com/EasyInstall.html#custom-installation-locations + +Please make the appropriate changes for your system and try again.""" % ( + self.install_dir, os.environ.get('PYTHONPATH','') + ) + + + + + + + + + + + def install_site_py(self): + """Make sure there's a site.py in the target dir, if needed""" + + if self.sitepy_installed: + return # already did it, or don't need to + + sitepy = os.path.join(self.install_dir, "site.py") + source = resource_string("setuptools", "site-patch.py") + current = "" + + if os.path.exists(sitepy): + log.debug("Checking existing site.py in %s", self.install_dir) + current = open(sitepy,'rb').read() + if not current.startswith('def __boot():'): + raise DistutilsError( + "%s is not a setuptools-generated site.py; please" + " remove it." % sitepy + ) + + if current != source: + log.info("Creating %s", sitepy) + if not self.dry_run: + ensure_directory(sitepy) + f = open(sitepy,'wb') + f.write(source) + f.close() + self.byte_compile([sitepy]) + + self.sitepy_installed = True + + + + + + + + + + + + + INSTALL_SCHEMES = dict( + posix = dict( + install_dir = '$base/lib/python$py_version_short/site-packages', + script_dir = '$base/bin', + ), + ) + + DEFAULT_SCHEME = dict( + install_dir = '$base/Lib/site-packages', + script_dir = '$base/Scripts', + ) + + def _expand(self, *attrs): + config_vars = self.get_finalized_command('install').config_vars + + if self.prefix: + # Set default install_dir/scripts from --prefix + config_vars = config_vars.copy() + config_vars['base'] = self.prefix + scheme = self.INSTALL_SCHEMES.get(os.name,self.DEFAULT_SCHEME) + for attr,val in scheme.items(): + if getattr(self,attr,None) is None: + setattr(self,attr,val) + + from distutils.util import subst_vars + for attr in attrs: + val = getattr(self, attr) + if val is not None: + val = subst_vars(val, config_vars) + if os.name == 'posix': + val = os.path.expanduser(val) + setattr(self, attr, val) + + + + + + + + + +def get_site_dirs(): + # return a list of 'site' dirs + sitedirs = filter(None,os.environ.get('PYTHONPATH','').split(os.pathsep)) + prefixes = [sys.prefix] + if sys.exec_prefix != sys.prefix: + prefixes.append(sys.exec_prefix) + for prefix in prefixes: + if prefix: + if sys.platform in ('os2emx', 'riscos'): + sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) + elif os.sep == '/': + sitedirs.extend([os.path.join(prefix, + "lib", + "python" + sys.version[:3], + "site-packages"), + os.path.join(prefix, "lib", "site-python")]) + else: + sitedirs.extend( + [prefix, os.path.join(prefix, "lib", "site-packages")] + ) + if sys.platform == 'darwin': + # for framework builds *only* we add the standard Apple + # locations. Currently only per-user, but /Library and + # /Network/Library could be added too + if 'Python.framework' in prefix: + home = os.environ.get('HOME') + if home: + sitedirs.append( + os.path.join(home, + 'Library', + 'Python', + sys.version[:3], + 'site-packages')) + for plat_specific in (0,1): + site_lib = get_python_lib(plat_specific) + if site_lib not in sitedirs: sitedirs.append(site_lib) + + sitedirs = map(normalize_path, sitedirs) + return sitedirs + + +def expand_paths(inputs): + """Yield sys.path directories that might contain "old-style" packages""" + + seen = {} + + for dirname in inputs: + dirname = normalize_path(dirname) + if dirname in seen: + continue + + seen[dirname] = 1 + if not os.path.isdir(dirname): + continue + + files = os.listdir(dirname) + yield dirname, files + + for name in files: + if not name.endswith('.pth'): + # We only care about the .pth files + continue + if name in ('easy-install.pth','setuptools.pth'): + # Ignore .pth files that we control + continue + + # Read the .pth file + f = open(os.path.join(dirname,name)) + lines = list(yield_lines(f)) + f.close() + + # Yield existing non-dupe, non-import directory lines from it + for line in lines: + if not line.startswith("import"): + line = normalize_path(line.rstrip()) + if line not in seen: + seen[line] = 1 + if not os.path.isdir(line): + continue + yield line, os.listdir(line) + + +def extract_wininst_cfg(dist_filename): + """Extract configuration data from a bdist_wininst .exe + + Returns a ConfigParser.RawConfigParser, or None + """ + f = open(dist_filename,'rb') + try: + endrec = zipfile._EndRecData(f) + if endrec is None: + return None + + prepended = (endrec[9] - endrec[5]) - endrec[6] + if prepended < 12: # no wininst data here + return None + f.seek(prepended-12) + + import struct, StringIO, ConfigParser + tag, cfglen, bmlen = struct.unpack("<iii",f.read(12)) + if tag not in (0x1234567A, 0x1234567B): + return None # not a valid tag + + f.seek(prepended-(12+cfglen+bmlen)) + cfg = ConfigParser.RawConfigParser({'version':'','target_version':''}) + try: + cfg.readfp(StringIO.StringIO(f.read(cfglen).split(chr(0),1)[0])) + except ConfigParser.Error: + return None + if not cfg.has_section('metadata') or not cfg.has_section('Setup'): + return None + return cfg + + finally: + f.close() + + + + + + + + +def get_exe_prefixes(exe_filename): + """Get exe->egg path translations for a given .exe file""" + + prefixes = [ + ('PURELIB/', ''), + ('PLATLIB/', ''), + ('SCRIPTS/', 'EGG-INFO/scripts/') + ] + z = zipfile.ZipFile(exe_filename) + try: + for info in z.infolist(): + name = info.filename + parts = name.split('/') + if len(parts)==3 and parts[2]=='PKG-INFO': + if parts[1].endswith('.egg-info'): + prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/')) + break + if len(parts)<>2 or not name.endswith('.pth'): + continue + if name.endswith('-nspkg.pth'): + continue + if parts[0] in ('PURELIB','PLATLIB'): + for pth in yield_lines(z.read(name)): + pth = pth.strip().replace('\\','/') + if not pth.startswith('import'): + prefixes.append((('%s/%s/' % (parts[0],pth)), '')) + finally: + z.close() + + prefixes.sort(); prefixes.reverse() + return prefixes + + +def parse_requirement_arg(spec): + try: + return Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % (spec,) + ) + +class PthDistributions(Environment): + """A .pth file with Distribution paths in it""" + + dirty = False + + def __init__(self, filename): + self.filename = filename + self.basedir = normalize_path(os.path.dirname(self.filename)) + self._load(); Environment.__init__(self, [], None, None) + for path in yield_lines(self.paths): + map(self.add, find_distributions(path, True)) + + def _load(self): + self.paths = [] + saw_import = False + seen = {} + if os.path.isfile(self.filename): + for line in open(self.filename,'rt'): + if line.startswith('import'): + saw_import = True + continue + path = line.rstrip() + self.paths.append(path) + if not path.strip() or path.strip().startswith('#'): + continue + # skip non-existent paths, in case somebody deleted a package + # manually, and duplicate paths as well + path = self.paths[-1] = normalize_path( + os.path.join(self.basedir,path) + ) + if not os.path.exists(path) or path in seen: + self.paths.pop() # skip it + self.dirty = True # we cleaned up, so we're dirty now :) + continue + seen[path] = 1 + + if self.paths and not saw_import: + self.dirty = True # ensure anything we touch has import wrappers + while self.paths and not self.paths[-1].strip(): + self.paths.pop() + + def save(self): + """Write changed .pth file back to disk""" + if not self.dirty: + return + + data = '\n'.join(map(self.make_relative,self.paths)) + if data: + log.debug("Saving %s", self.filename) + data = ( + "import sys; sys.__plen = len(sys.path)\n" + "%s\n" + "import sys; new=sys.path[sys.__plen:];" + " del sys.path[sys.__plen:];" + " p=getattr(sys,'__egginsert',0); sys.path[p:p]=new;" + " sys.__egginsert = p+len(new)\n" + ) % data + + if os.path.islink(self.filename): + os.unlink(self.filename) + f = open(self.filename,'wb') + f.write(data); f.close() + + elif os.path.exists(self.filename): + log.debug("Deleting empty %s", self.filename) + os.unlink(self.filename) + + self.dirty = False + + def add(self,dist): + """Add `dist` to the distribution map""" + if dist.location not in self.paths: + self.paths.append(dist.location); self.dirty = True + Environment.add(self,dist) + + def remove(self,dist): + """Remove `dist` from the distribution map""" + while dist.location in self.paths: + self.paths.remove(dist.location); self.dirty = True + Environment.remove(self,dist) + + + def make_relative(self,path): + if normalize_path(os.path.dirname(path))==self.basedir: + return os.path.basename(path) + return path + + +def get_script_header(script_text, executable=sys_executable): + """Create a #! line, getting options (if any) from script_text""" + from distutils.command.build_scripts import first_line_re + first, rest = (script_text+'\n').split('\n',1) + match = first_line_re.match(first) + options = '' + if match: + script_text = rest + options = match.group(1) or '' + if options: + options = ' '+options + return "#!%(executable)s%(options)s\n" % locals() + + +def auto_chmod(func, arg, exc): + if func is os.remove and os.name=='nt': + os.chmod(arg, stat.S_IWRITE) + return func(arg) + exc = sys.exc_info() + raise exc[0], (exc[1][0], exc[1][1] + (" %s %s" % (func,arg))) + + +def uncache_zipdir(path): + """Ensure that the zip directory cache doesn't have stale info for path""" + from zipimport import _zip_directory_cache as zdc + if path in zdc: + del zdc[path] + else: + path = normalize_path(path) + for p in zdc: + if normalize_path(p)==path: + del zdc[p] + return + + +def get_script_args(dist, executable=sys_executable): + """Yield write_script() argument tuples for a distribution's entrypoints""" + spec = str(dist.as_requirement()) + header = get_script_header("", executable) + for group in 'console_scripts', 'gui_scripts': + for name,ep in dist.get_entry_map(group).items(): + script_text = ( + "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n" + "__requires__ = %(spec)r\n" + "import sys\n" + "from pkg_resources import load_entry_point\n" + "\n" + "sys.exit(\n" + " load_entry_point(%(spec)r, %(group)r, %(name)r)()\n" + ")\n" + ) % locals() + if sys.platform=='win32': + # On Windows, add a .py extension and an .exe launcher + if group=='gui_scripts': + ext, launcher = '-script.pyw', 'gui.exe' + old = ['.pyw'] + new_header = re.sub('(?i)python.exe','pythonw.exe',header) + else: + ext, launcher = '-script.py', 'cli.exe' + old = ['.py','.pyc','.pyo'] + new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header) + + if os.path.exists(new_header[2:-1]): + hdr = new_header + else: + hdr = header + yield (name+ext, hdr+script_text, 't', [name+x for x in old]) + yield ( + name+'.exe', resource_string('setuptools', launcher), + 'b' # write in binary mode + ) + else: + # On other platforms, we assume the right thing to do is to + # just write the stub with no extension. + yield (name, header+script_text) + +def rmtree(path, ignore_errors=False, onerror=auto_chmod): + """Recursively delete a directory tree. + + This code is taken from the Python 2.4 version of 'shutil', because + the 2.3 version doesn't really work right. + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + names = [] + try: + names = os.listdir(path) + except os.error, err: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error, err: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + + + + + + +def main(argv=None, **kw): + from setuptools import setup + from setuptools.dist import Distribution + import distutils.core + + USAGE = """\ +usage: %(script)s [options] requirement_or_url ... + or: %(script)s --help +""" + + def gen_usage (script_name): + script = os.path.basename(script_name) + return USAGE % vars() + + def with_ei_usage(f): + old_gen_usage = distutils.core.gen_usage + try: + distutils.core.gen_usage = gen_usage + return f() + finally: + distutils.core.gen_usage = old_gen_usage + + class DistributionWithoutHelpCommands(Distribution): + def _show_help(self,*args,**kw): + with_ei_usage(lambda: Distribution._show_help(self,*args,**kw)) + + if argv is None: + argv = sys.argv[1:] + + with_ei_usage(lambda: + setup( + script_args = ['-q','easy_install', '-v']+argv, + script_name = sys.argv[0] or 'easy_install', + distclass=DistributionWithoutHelpCommands, **kw + ) + ) diff --git a/Lib/setuptools/command/egg_info.py b/Lib/setuptools/command/egg_info.py new file mode 100755 index 0000000..b68fb39 --- /dev/null +++ b/Lib/setuptools/command/egg_info.py @@ -0,0 +1,365 @@ +"""setuptools.command.egg_info + +Create a distribution's .egg-info directory and contents""" + +# This module should be kept compatible with Python 2.3 +import os, re +from setuptools import Command +from distutils.errors import * +from distutils import log +from setuptools.command.sdist import sdist +from distutils import file_util +from distutils.util import convert_path +from distutils.filelist import FileList +from pkg_resources import parse_requirements, safe_name, parse_version, \ + safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename +from sdist import walk_revctrl + +class egg_info(Command): + description = "create a distribution's .egg-info directory" + + user_options = [ + ('egg-base=', 'e', "directory containing .egg-info directories" + " (default: top of the source tree)"), + ('tag-svn-revision', 'r', + "Add subversion revision ID to version number"), + ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), + ('tag-build=', 'b', "Specify explicit tag to add to version number"), + ] + + boolean_options = ['tag-date','tag-svn-revision'] + + def initialize_options (self): + self.egg_name = None + self.egg_version = None + self.egg_base = None + self.egg_info = None + self.tag_build = None + self.tag_svn_revision = 0 + self.tag_date = 0 + self.broken_egg_info = False + + def finalize_options (self): + self.egg_name = safe_name(self.distribution.get_name()) + self.egg_version = self.tagged_version() + + try: + list( + parse_requirements('%s==%s' % (self.egg_name,self.egg_version)) + ) + except ValueError: + raise DistutilsOptionError( + "Invalid distribution name or version syntax: %s-%s" % + (self.egg_name,self.egg_version) + ) + + if self.egg_base is None: + dirs = self.distribution.package_dir + self.egg_base = (dirs or {}).get('',os.curdir) + + self.ensure_dirname('egg_base') + self.egg_info = to_filename(self.egg_name)+'.egg-info' + if self.egg_base != os.curdir: + self.egg_info = os.path.join(self.egg_base, self.egg_info) + if '-' in self.egg_name: self.check_broken_egg_info() + + # Set package version for the benefit of dumber commands + # (e.g. sdist, bdist_wininst, etc.) + # + self.distribution.metadata.version = self.egg_version + + # If we bootstrapped around the lack of a PKG-INFO, as might be the + # case in a fresh checkout, make sure that any special tags get added + # to the version info + # + pd = self.distribution._patched_dist + if pd is not None and pd.key==self.egg_name.lower(): + pd._version = self.egg_version + pd._parsed_version = parse_version(self.egg_version) + self.distribution._patched_dist = None + + + + def write_or_delete_file(self, what, filename, data, force=False): + """Write `data` to `filename` or delete if empty + + If `data` is non-empty, this routine is the same as ``write_file()``. + If `data` is empty but not ``None``, this is the same as calling + ``delete_file(filename)`. If `data` is ``None``, then this is a no-op + unless `filename` exists, in which case a warning is issued about the + orphaned file (if `force` is false), or deleted (if `force` is true). + """ + if data: + self.write_file(what, filename, data) + elif os.path.exists(filename): + if data is None and not force: + log.warn( + "%s not set in setup(), but %s exists", what, filename + ) + return + else: + self.delete_file(filename) + + def write_file(self, what, filename, data): + """Write `data` to `filename` (if not a dry run) after announcing it + + `what` is used in a log message to identify what is being written + to the file. + """ + log.info("writing %s to %s", what, filename) + if not self.dry_run: + f = open(filename, 'wb') + f.write(data) + f.close() + + def delete_file(self, filename): + """Delete `filename` (if not a dry run) after announcing it""" + log.info("deleting %s", filename) + if not self.dry_run: + os.unlink(filename) + + + + + def run(self): + self.mkpath(self.egg_info) + installer = self.distribution.fetch_build_egg + for ep in iter_entry_points('egg_info.writers'): + writer = ep.load(installer=installer) + writer(self, ep.name, os.path.join(self.egg_info,ep.name)) + self.find_sources() + + def tagged_version(self): + version = self.distribution.get_version() + if self.tag_build: + version+=self.tag_build + if self.tag_svn_revision and ( + os.path.exists('.svn') or os.path.exists('PKG-INFO') + ): version += '-r%s' % self.get_svn_revision() + if self.tag_date: + import time; version += time.strftime("-%Y%m%d") + return safe_version(version) + + def get_svn_revision(self): + revision = 0 + urlre = re.compile('url="([^"]+)"') + revre = re.compile('committed-rev="(\d+)"') + for base,dirs,files in os.walk(os.curdir): + if '.svn' not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove('.svn') + f = open(os.path.join(base,'.svn','entries')) + data = f.read() + f.close() + dirurl = urlre.search(data).group(1) # get repository URL + if base==os.curdir: + base_url = dirurl+'/' # save the root url + elif not dirurl.startswith(base_url): + dirs[:] = [] + continue # not part of the same svn tree, skip it + for match in revre.finditer(data): + revision = max(revision, int(match.group(1))) + return str(revision or get_pkg_info_revision()) + + def find_sources(self): + """Generate SOURCES.txt manifest file""" + manifest_filename = os.path.join(self.egg_info,"SOURCES.txt") + mm = manifest_maker(self.distribution) + mm.manifest = manifest_filename + mm.run() + self.filelist = mm.filelist + + def check_broken_egg_info(self): + bei = self.egg_name+'.egg-info' + if self.egg_base != os.curdir: + bei = os.path.join(self.egg_base, bei) + if os.path.exists(bei): + log.warn( + "-"*78+'\n' + "Note: Your current .egg-info directory has a '-' in its name;" + '\nthis will not work correctly with "setup.py develop".\n\n' + 'Please rename %s to %s to correct this problem.\n'+'-'*78, + bei, self.egg_info + ) + self.broken_egg_info = self.egg_info + self.egg_info = bei # make it work for now + +class FileList(FileList): + """File list that accepts only existing, platform-independent paths""" + + def append(self, item): + path = convert_path(item) + if os.path.exists(path): + self.files.append(path) + + + + + + + + + + + +class manifest_maker(sdist): + + template = "MANIFEST.in" + + def initialize_options (self): + self.use_defaults = 1 + self.prune = 1 + self.manifest_only = 1 + self.force_manifest = 1 + + def finalize_options(self): + pass + + def run(self): + self.filelist = FileList() + if not os.path.exists(self.manifest): + self.write_manifest() # it must exist so it'll get in the list + self.filelist.findall() + self.add_defaults() + if os.path.exists(self.template): + self.read_template() + self.prune_file_list() + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def write_manifest (self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + files = self.filelist.files + if os.sep!='/': + files = [f.replace(os.sep,'/') for f in files] + self.execute(file_util.write_file, (self.manifest, files), + "writing manifest file '%s'" % self.manifest) + + + + + + def add_defaults(self): + sdist.add_defaults(self) + self.filelist.append(self.template) + self.filelist.append(self.manifest) + rcfiles = list(walk_revctrl()) + if rcfiles: + self.filelist.extend(rcfiles) + elif os.path.exists(self.manifest): + self.read_manifest() + ei_cmd = self.get_finalized_command('egg_info') + self.filelist.include_pattern("*", prefix=ei_cmd.egg_info) + + def prune_file_list (self): + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + sep = re.escape(os.sep) + self.filelist.exclude_pattern(sep+r'(RCS|CVS|\.svn)'+sep, is_regex=1) + + + + + + + + + + + + + + + + + + + + + + +def write_pkg_info(cmd, basename, filename): + log.info("writing %s", filename) + if not cmd.dry_run: + metadata = cmd.distribution.metadata + metadata.version, oldver = cmd.egg_version, metadata.version + metadata.name, oldname = cmd.egg_name, metadata.name + try: + # write unescaped data to PKG-INFO, so older pkg_resources + # can still parse it + metadata.write_pkg_info(cmd.egg_info) + finally: + metadata.name, metadata.version = oldname, oldver + + safe = getattr(cmd.distribution,'zip_safe',None) + import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe) + +def warn_depends_obsolete(cmd, basename, filename): + if os.path.exists(filename): + log.warn( + "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + +def write_requirements(cmd, basename, filename): + dist = cmd.distribution + data = ['\n'.join(yield_lines(dist.install_requires or ()))] + for extra,reqs in (dist.extras_require or {}).items(): + data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs)))) + cmd.write_or_delete_file("requirements", filename, ''.join(data)) + +def write_toplevel_names(cmd, basename, filename): + pkgs = dict.fromkeys( + [k.split('.',1)[0] + for k in cmd.distribution.iter_distribution_names() + ] + ) + cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n') + + + +def overwrite_arg(cmd, basename, filename): + write_arg(cmd, basename, filename, True) + +def write_arg(cmd, basename, filename, force=False): + argname = os.path.splitext(basename)[0] + value = getattr(cmd.distribution, argname, None) + if value is not None: + value = '\n'.join(value)+'\n' + cmd.write_or_delete_file(argname, filename, value, force) + +def write_entries(cmd, basename, filename): + ep = cmd.distribution.entry_points + + if isinstance(ep,basestring) or ep is None: + data = ep + elif ep is not None: + data = [] + for section, contents in ep.items(): + if not isinstance(contents,basestring): + contents = EntryPoint.parse_group(section, contents) + contents = '\n'.join(map(str,contents.values())) + data.append('[%s]\n%s\n\n' % (section,contents)) + data = ''.join(data) + + cmd.write_or_delete_file('entry points', filename, data, True) + +def get_pkg_info_revision(): + # See if we can get a -r### off of PKG-INFO, in case this is an sdist of + # a subversion revision + # + if os.path.exists('PKG-INFO'): + f = open('PKG-INFO','rU') + for line in f: + match = re.match(r"Version:.*-r(\d+)\s*$", line) + if match: + return int(match.group(1)) + return 0 diff --git a/Lib/setuptools/command/install.py b/Lib/setuptools/command/install.py new file mode 100644 index 0000000..bfb9af5 --- /dev/null +++ b/Lib/setuptools/command/install.py @@ -0,0 +1,101 @@ +import setuptools, sys +from distutils.command.install import install as _install +from distutils.errors import DistutilsArgError + +class install(_install): + """Use easy_install to install the package, w/dependencies""" + + user_options = _install.user_options + [ + ('old-and-unmanageable', None, "Try not to use this!"), + ('single-version-externally-managed', None, + "used by system package builders to create 'flat' eggs"), + ] + boolean_options = _install.boolean_options + [ + 'old-and-unmanageable', 'single-version-externally-managed', + ] + new_commands = [ + ('install_egg_info', lambda self: True), + ('install_scripts', lambda self: True), + ] + _nc = dict(new_commands) + sub_commands = [ + cmd for cmd in _install.sub_commands if cmd[0] not in _nc + ] + new_commands + + def initialize_options(self): + _install.initialize_options(self) + self.old_and_unmanageable = None + self.single_version_externally_managed = None + self.no_compile = None # make DISTUTILS_DEBUG work right! + + def finalize_options(self): + _install.finalize_options(self) + if self.root: + self.single_version_externally_managed = True + elif self.single_version_externally_managed: + if not self.root and not self.record: + raise DistutilsArgError( + "You must specify --record or --root when building system" + " packages" + ) + + def handle_extra_path(self): + # We always ignore extra_path, because we install as .egg or .egg-info + self.path_file = None + self.extra_dirs = '' + + def run(self): + # Explicit request for old-style install? Just do it + if self.old_and_unmanageable or self.single_version_externally_managed: + return _install.run(self) + + # Attempt to detect whether we were called from setup() or by another + # command. If we were called by setup(), our caller will be the + # 'run_command' method in 'distutils.dist', and *its* caller will be + # the 'run_commands' method. If we were called any other way, our + # immediate caller *might* be 'run_command', but it won't have been + # called by 'run_commands'. This is slightly kludgy, but seems to + # work. + # + caller = sys._getframe(2) + caller_module = caller.f_globals.get('__name__','') + caller_name = caller.f_code.co_name + + if caller_module != 'distutils.dist' or caller_name!='run_commands': + # We weren't called from the command line or setup(), so we + # should run in backward-compatibility mode to support bdist_* + # commands. + _install.run(self) + else: + self.do_egg_install() + + + + + + + + + + + + + def do_egg_install(self): + + from setuptools.command.easy_install import easy_install + + cmd = easy_install( + self.distribution, args="x", root=self.root, record=self.record, + ) + cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + + self.run_command('bdist_egg') + args = [self.distribution.get_command_obj('bdist_egg').egg_output] + + if setuptools.bootstrap_install_from: + # Bootstrap self-installation of setuptools + args.insert(0, setuptools.bootstrap_install_from) + + cmd.args = args + cmd.run() + setuptools.bootstrap_install_from = None diff --git a/Lib/setuptools/command/install_egg_info.py b/Lib/setuptools/command/install_egg_info.py new file mode 100755 index 0000000..193e91a --- /dev/null +++ b/Lib/setuptools/command/install_egg_info.py @@ -0,0 +1,81 @@ +from setuptools import Command +from setuptools.archive_util import unpack_archive +from distutils import log, dir_util +import os, shutil, pkg_resources + +class install_egg_info(Command): + """Install an .egg-info directory for the package""" + + description = "Install an .egg-info directory for the package" + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + ei_cmd = self.get_finalized_command("egg_info") + basename = pkg_resources.Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version + ).egg_name()+'.egg-info' + self.source = ei_cmd.egg_info + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + self.run_command('egg_info') + target = self.target + if os.path.isdir(self.target) and not os.path.islink(self.target): + dir_util.remove_tree(self.target, dry_run=self.dry_run) + elif os.path.exists(self.target): + self.execute(os.unlink,(self.target,),"Removing "+self.target) + if not self.dry_run: + pkg_resources.ensure_directory(self.target) + self.execute(self.copytree, (), + "Copying %s to %s" % (self.source, self.target) + ) + self.install_namespaces() + + def get_outputs(self): + return self.outputs + + def copytree(self): + # Copy the .egg-info tree to site-packages + def skimmer(src,dst): + # filter out source-control directories; note that 'src' is always + # a '/'-separated path, regardless of platform. 'dst' is a + # platform-specific path. + for skip in '.svn/','CVS/': + if src.startswith(skip) or '/'+skip in src: + return None + self.outputs.append(dst) + log.debug("Copying %s to %s", src, dst) + return dst + unpack_archive(self.source, self.target, skimmer) + + def install_namespaces(self): + nsp = (self.distribution.namespace_packages or [])[:] + if not nsp: return + nsp.sort() # set up shorter names first + filename,ext = os.path.splitext(self.target) + filename += '-nspkg.pth'; self.outputs.append(filename) + log.info("Installing %s",filename) + if not self.dry_run: + f = open(filename,'wb') + for pkg in nsp: + pth = tuple(pkg.split('.')) + f.write( + "import sys,new,os; " + "p = os.path.join(sys._getframe(1).f_locals['sitedir'], " + "*%(pth)r); " + "ie = os.path.exists(os.path.join(p,'__init__.py')); " + "m = not ie and " + "sys.modules.setdefault(%(pkg)r,new.module(%(pkg)r)); " + "mp = (m or []) and m.__dict__.setdefault('__path__',[]); " + "(p not in mp) and mp.append(p)\n" + % locals() + ) + f.close() diff --git a/Lib/setuptools/command/install_lib.py b/Lib/setuptools/command/install_lib.py new file mode 100644 index 0000000..96c8dfe --- /dev/null +++ b/Lib/setuptools/command/install_lib.py @@ -0,0 +1,76 @@ +from distutils.command.install_lib import install_lib as _install_lib +import os + +class install_lib(_install_lib): + """Don't add compiled flags to filenames of non-Python files""" + + def _bytecode_filenames (self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + if not py_file.endswith('.py'): + continue + if self.compile: + bytecode_files.append(py_file + "c") + if self.optimize > 0: + bytecode_files.append(py_file + "o") + + return bytecode_files + + def run(self): + self.build() + outfiles = self.install() + if outfiles is not None: + # always compile, in case we have any extension stubs to deal with + self.byte_compile(outfiles) + + def get_exclusions(self): + exclude = {} + nsp = self.distribution.namespace_packages + + if (nsp and self.get_finalized_command('install') + .single_version_externally_managed + ): + for pkg in nsp: + parts = pkg.split('.') + while parts: + pkgdir = os.path.join(self.install_dir, *parts) + for f in '__init__.py', '__init__.pyc', '__init__.pyo': + exclude[os.path.join(pkgdir,f)] = 1 + parts.pop() + return exclude + + def copy_tree( + self, infile, outfile, + preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 + ): + assert preserve_mode and preserve_times and not preserve_symlinks + exclude = self.get_exclusions() + + if not exclude: + return _install_lib.copy_tree(self, infile, outfile) + + # Exclude namespace package __init__.py* files from the output + + from setuptools.archive_util import unpack_directory + from distutils import log + + outfiles = [] + + def pf(src, dst): + if dst in exclude: + log.warn("Skipping installation of %s (namespace package)",dst) + return False + + log.info("copying %s -> %s", src, os.path.dirname(dst)) + outfiles.append(dst) + return dst + + unpack_directory(infile, outfile, pf) + return outfiles + + def get_outputs(self): + outputs = _install_lib.get_outputs(self) + exclude = self.get_exclusions() + if exclude: + return [f for f in outputs if f not in exclude] + return outputs diff --git a/Lib/setuptools/command/install_scripts.py b/Lib/setuptools/command/install_scripts.py new file mode 100755 index 0000000..69558bf --- /dev/null +++ b/Lib/setuptools/command/install_scripts.py @@ -0,0 +1,56 @@ +from distutils.command.install_scripts import install_scripts \ + as _install_scripts +from easy_install import get_script_args, sys_executable +from pkg_resources import Distribution, PathMetadata, ensure_directory +import os +from distutils import log + +class install_scripts(_install_scripts): + """Do normal script install, plus any egg_info wrapper scripts""" + + def initialize_options(self): + _install_scripts.initialize_options(self) + self.no_ep = False + + def run(self): + self.run_command("egg_info") + if self.distribution.scripts: + _install_scripts.run(self) # run first to set up self.outfiles + else: + self.outfiles = [] + if self.no_ep: + # don't install entry point scripts into .egg file! + return + + ei_cmd = self.get_finalized_command("egg_info") + dist = Distribution( + ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info), + ei_cmd.egg_name, ei_cmd.egg_version, + ) + bs_cmd = self.get_finalized_command('build_scripts') + executable = getattr(bs_cmd,'executable',sys_executable) + + for args in get_script_args(dist, executable): + self.write_script(*args) + + + + + + + + def write_script(self, script_name, contents, mode="t", *ignored): + """Write an executable file to the scripts directory""" + log.info("Installing %s script to %s", script_name, self.install_dir) + target = os.path.join(self.install_dir, script_name) + self.outfiles.append(target) + + if not self.dry_run: + ensure_directory(target) + f = open(target,"w"+mode) + f.write(contents) + f.close() + try: + os.chmod(target,0755) + except (AttributeError, os.error): + pass diff --git a/Lib/setuptools/command/rotate.py b/Lib/setuptools/command/rotate.py new file mode 100755 index 0000000..8aab312 --- /dev/null +++ b/Lib/setuptools/command/rotate.py @@ -0,0 +1,57 @@ +import distutils, os +from setuptools import Command +from distutils.util import convert_path +from distutils import log +from distutils.errors import * + +class rotate(Command): + """Delete older distributions""" + + description = "delete older distributions, keeping N newest files" + user_options = [ + ('match=', 'm', "patterns to match (required)"), + ('dist-dir=', 'd', "directory where the distributions are"), + ('keep=', 'k', "number of matching distributions to keep"), + ] + + boolean_options = [] + + def initialize_options(self): + self.match = None + self.dist_dir = None + self.keep = None + + def finalize_options(self): + if self.match is None: + raise DistutilsOptionError( + "Must specify one or more (comma-separated) match patterns " + "(e.g. '.zip' or '.egg')" + ) + if self.keep is None: + raise DistutilsOptionError("Must specify number of files to keep") + try: + self.keep = int(self.keep) + except ValueError: + raise DistutilsOptionError("--keep must be an integer") + if isinstance(self.match, basestring): + self.match = [ + convert_path(p.strip()) for p in self.match.split(',') + ] + self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) + + def run(self): + self.run_command("egg_info") + from glob import glob + for pattern in self.match: + pattern = self.distribution.get_name()+'*'+pattern + files = glob(os.path.join(self.dist_dir,pattern)) + files = [(os.path.getmtime(f),f) for f in files] + files.sort() + files.reverse() + + log.info("%d file(s) matching %s", len(files), pattern) + files = files[self.keep:] + for (t,f) in files: + log.info("Deleting %s", f) + if not self.dry_run: + os.unlink(f) diff --git a/Lib/setuptools/command/saveopts.py b/Lib/setuptools/command/saveopts.py new file mode 100755 index 0000000..9c58d72 --- /dev/null +++ b/Lib/setuptools/command/saveopts.py @@ -0,0 +1,24 @@ +import distutils, os +from setuptools import Command +from setuptools.command.setopt import edit_config, option_base + +class saveopts(option_base): + """Save command-line options to a file""" + + description = "save supplied options to setup.cfg or other config file" + + def run(self): + dist = self.distribution + commands = dist.command_options.keys() + settings = {} + + for cmd in commands: + + if cmd=='saveopts': + continue # don't save our own options! + + for opt,(src,val) in dist.get_option_dict(cmd).items(): + if src=="command line": + settings.setdefault(cmd,{})[opt] = val + + edit_config(self.filename, settings, self.dry_run) diff --git a/Lib/setuptools/command/sdist.py b/Lib/setuptools/command/sdist.py new file mode 100755 index 0000000..829cd3c --- /dev/null +++ b/Lib/setuptools/command/sdist.py @@ -0,0 +1,163 @@ +from distutils.command.sdist import sdist as _sdist +from distutils.util import convert_path +import os, re, sys, pkg_resources + +entities = [ + ("<","<"), (">", ">"), (""", '"'), ("'", "'"), + ("&", "&") +] + +def unescape(data): + for old,new in entities: + data = data.replace(old,new) + return data + +def re_finder(pattern, postproc=None): + def find(dirname, filename): + f = open(filename,'rU') + data = f.read() + f.close() + for match in pattern.finditer(data): + path = match.group(1) + if postproc: + path = postproc(path) + yield joinpath(dirname,path) + return find + +def joinpath(prefix,suffix): + if not prefix: + return suffix + return os.path.join(prefix,suffix) + + + + + + + + + + + +def walk_revctrl(dirname=''): + """Find all files under revision control""" + for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): + for item in ep.load()(dirname): + yield item + +def _default_revctrl(dirname=''): + for path, finder in finders: + path = joinpath(dirname,path) + if os.path.isfile(path): + for path in finder(dirname,path): + if os.path.isfile(path): + yield path + elif os.path.isdir(path): + for item in _default_revctrl(path): + yield item + +def externals_finder(dirname, filename): + """Find any 'svn:externals' directories""" + found = False + f = open(filename,'rb') + for line in iter(f.readline, ''): # can't use direct iter! + parts = line.split() + if len(parts)==2: + kind,length = parts + data = f.read(int(length)) + if kind=='K' and data=='svn:externals': + found = True + elif kind=='V' and found: + f.close() + break + else: + f.close() + return + + for line in data.splitlines(): + parts = line.split() + if parts: + yield joinpath(dirname, parts[0]) + + +finders = [ + (convert_path('CVS/Entries'), + re_finder(re.compile(r"^\w?/([^/]+)/", re.M))), + (convert_path('.svn/entries'), + re_finder( + re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I), + unescape + ) + ), + (convert_path('.svn/dir-props'), externals_finder), +] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +class sdist(_sdist): + """Smart sdist that finds anything supported by revision control""" + + user_options = [ + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ] + + negative_opt = {} + + def run(self): + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + self.filelist = ei_cmd.filelist + self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt')) + + self.check_metadata() + self.make_distribution() + + dist_files = getattr(self.distribution,'dist_files',[]) + for file in self.archive_files: + data = ('sdist', '', file) + if data not in dist_files: + dist_files.append(data) + + def read_template(self): + try: + _sdist.read_template(self) + except: + # grody hack to close the template file (MANIFEST.in) + # this prevents easy_install's attempt at deleting the file from + # dying and thus masking the real error + sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close() + raise diff --git a/Lib/setuptools/command/setopt.py b/Lib/setuptools/command/setopt.py new file mode 100755 index 0000000..e0c1058 --- /dev/null +++ b/Lib/setuptools/command/setopt.py @@ -0,0 +1,158 @@ +import distutils, os +from setuptools import Command +from distutils.util import convert_path +from distutils import log +from distutils.errors import * + +__all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] + + +def config_file(kind="local"): + """Get the filename of the distutils, local, global, or per-user config + + `kind` must be one of "local", "global", or "user" + """ + if kind=='local': + return 'setup.cfg' + if kind=='global': + return os.path.join( + os.path.dirname(distutils.__file__),'distutils.cfg' + ) + if kind=='user': + dot = os.name=='posix' and '.' or '' + return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot)) + raise ValueError( + "config_file() type must be 'local', 'global', or 'user'", kind + ) + + + + + + + + + + + + + + + +def edit_config(filename, settings, dry_run=False): + """Edit a configuration file to include `settings` + + `settings` is a dictionary of dictionaries or ``None`` values, keyed by + command/section name. A ``None`` value means to delete the entire section, + while a dictionary lists settings to be changed or deleted in that section. + A setting of ``None`` means to delete that setting. + """ + from ConfigParser import RawConfigParser + log.debug("Reading configuration from %s", filename) + opts = RawConfigParser() + opts.read([filename]) + for section, options in settings.items(): + if options is None: + log.info("Deleting section [%s] from %s", section, filename) + opts.remove_section(section) + else: + if not opts.has_section(section): + log.debug("Adding new section [%s] to %s", section, filename) + opts.add_section(section) + for option,value in options.items(): + if value is None: + log.debug("Deleting %s.%s from %s", + section, option, filename + ) + opts.remove_option(section,option) + if not opts.options(section): + log.info("Deleting empty [%s] section from %s", + section, filename) + opts.remove_section(section) + else: + log.debug( + "Setting %s.%s to %r in %s", + section, option, value, filename + ) + opts.set(section,option,value) + + log.info("Writing %s", filename) + if not dry_run: + f = open(filename,'w'); opts.write(f); f.close() + +class option_base(Command): + """Abstract base class for commands that mess with config files""" + + user_options = [ + ('global-config', 'g', + "save options to the site-wide distutils.cfg file"), + ('user-config', 'u', + "save options to the current user's pydistutils.cfg file"), + ('filename=', 'f', + "configuration file to use (default=setup.cfg)"), + ] + + boolean_options = [ + 'global-config', 'user-config', + ] + + def initialize_options(self): + self.global_config = None + self.user_config = None + self.filename = None + + def finalize_options(self): + filenames = [] + if self.global_config: + filenames.append(config_file('global')) + if self.user_config: + filenames.append(config_file('user')) + if self.filename is not None: + filenames.append(self.filename) + if not filenames: + filenames.append(config_file('local')) + if len(filenames)>1: + raise DistutilsOptionError( + "Must specify only one configuration file option", + filenames + ) + self.filename, = filenames + + + + +class setopt(option_base): + """Save command-line options to a file""" + + description = "set an option in setup.cfg or another config file" + + user_options = [ + ('command=', 'c', 'command to set an option for'), + ('option=', 'o', 'option to set'), + ('set-value=', 's', 'value of the option'), + ('remove', 'r', 'remove (unset) the value'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.command = None + self.option = None + self.set_value = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.command is None or self.option is None: + raise DistutilsOptionError("Must specify --command *and* --option") + if self.set_value is None and not self.remove: + raise DistutilsOptionError("Must specify --set-value or --remove") + + def run(self): + edit_config( + self.filename, { + self.command: {self.option.replace('-','_'):self.set_value} + }, + self.dry_run + ) diff --git a/Lib/setuptools/command/test.py b/Lib/setuptools/command/test.py new file mode 100644 index 0000000..01fca35 --- /dev/null +++ b/Lib/setuptools/command/test.py @@ -0,0 +1,119 @@ +from setuptools import Command +from distutils.errors import DistutilsOptionError +import sys +from pkg_resources import * +from unittest import TestLoader, main + +class ScanningLoader(TestLoader): + + def loadTestsFromModule(self, module): + """Return a suite of all tests cases contained in the given module + + If the module is a package, load tests from all the modules in it. + If the module has an ``additional_tests`` function, call it and add + the return value to the tests. + """ + tests = [] + if module.__name__!='setuptools.tests.doctest': # ugh + tests.append(TestLoader.loadTestsFromModule(self,module)) + + if hasattr(module, "additional_tests"): + tests.append(module.additional_tests()) + + if hasattr(module, '__path__'): + for file in resource_listdir(module.__name__, ''): + if file.endswith('.py') and file!='__init__.py': + submodule = module.__name__+'.'+file[:-3] + else: + if resource_exists( + module.__name__, file+'/__init__.py' + ): + submodule = module.__name__+'.'+file + else: + continue + tests.append(self.loadTestsFromName(submodule)) + + if len(tests)!=1: + return self.suiteClass(tests) + else: + return tests[0] # don't create a nested suite for only one return + + +class test(Command): + + """Command to run unit tests after in-place build""" + + description = "run unit tests after in-place build" + + user_options = [ + ('test-module=','m', "Run 'test_suite' in specified module"), + ('test-suite=','s', + "Test suite to run (e.g. 'some_module.test_suite')"), + ] + + def initialize_options(self): + self.test_suite = None + self.test_module = None + self.test_loader = None + + + def finalize_options(self): + + if self.test_suite is None: + if self.test_module is None: + self.test_suite = self.distribution.test_suite + else: + self.test_suite = self.test_module+".test_suite" + elif self.test_module: + raise DistutilsOptionError( + "You may specify a module or a suite, but not both" + ) + + self.test_args = [self.test_suite] + + if self.verbose: + self.test_args.insert(0,'--verbose') + if self.test_loader is None: + self.test_loader = getattr(self.distribution,'test_loader',None) + if self.test_loader is None: + self.test_loader = "setuptools.command.test:ScanningLoader" + + + + def run(self): + # Ensure metadata is up-to-date + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + if self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + + if self.test_suite: + cmd = ' '.join(self.test_args) + if self.dry_run: + self.announce('skipping "unittest %s" (dry run)' % cmd) + else: + self.announce('running "unittest %s"' % cmd) + self.run_tests() + + + def run_tests(self): + import unittest + old_path = sys.path[:] + ei_cmd = self.get_finalized_command("egg_info") + path_item = normalize_path(ei_cmd.egg_base) + metadata = PathMetadata( + path_item, normalize_path(ei_cmd.egg_info) + ) + dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name) + working_set.add(dist) + require(str(dist.as_requirement())) + loader_ep = EntryPoint.parse("x="+self.test_loader) + loader_class = loader_ep.load(require=False) + unittest.main( + None, None, [unittest.__file__]+self.test_args, + testLoader = loader_class() + ) diff --git a/Lib/setuptools/command/upload.py b/Lib/setuptools/command/upload.py new file mode 100755 index 0000000..644c400 --- /dev/null +++ b/Lib/setuptools/command/upload.py @@ -0,0 +1,178 @@ +"""distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to PyPI).""" + +from distutils.errors import * +from distutils.core import Command +from distutils.spawn import spawn +from distutils import log +from md5 import md5 +import os +import socket +import platform +import ConfigParser +import httplib +import base64 +import urlparse +import cStringIO as StringIO + +class upload(Command): + + description = "upload binary package to PyPI" + + DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ('sign', 's', + 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), + ] + boolean_options = ['show-response', 'sign'] + + def initialize_options(self): + self.username = '' + self.password = '' + self.repository = '' + self.show_response = 0 + self.sign = False + self.identity = None + + def finalize_options(self): + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) + if os.environ.has_key('HOME'): + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + config = ConfigParser.ConfigParser({ + 'username':'', + 'password':'', + 'repository':''}) + config.read(rc) + if not self.repository: + self.repository = config.get('server-login', 'repository') + if not self.username: + self.username = config.get('server-login', 'username') + if not self.password: + self.password = config.get('server-login', 'password') + if not self.repository: + self.repository = self.DEFAULT_REPOSITORY + + def run(self): + if not self.distribution.dist_files: + raise DistutilsOptionError("No dist file created in earlier command") + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data + content = open(filename,'rb').read() + basename = os.path.basename(filename) + comment = '' + if command=='bdist_egg' and self.distribution.has_ext_modules(): + comment = "built on %s" % platform.platform(terse=1) + data = { + ':action':'file_upload', + 'protcol_version':'1', + 'name':self.distribution.get_name(), + 'version':self.distribution.get_version(), + 'content':(basename,content), + 'filetype':command, + 'pyversion':pyversion, + 'md5_digest':md5(content).hexdigest(), + } + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc").read()) + + # set up the authentication + auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): + value = [value] + for value in value: + if type(value) is tuple: + fn = ';filename="%s"' % value[0] + value = value[1] + else: + fn = "" + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write(fn) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + http = httplib.HTTPConnection(netloc) + elif schema == 'https': + http = httplib.HTTPSConnection(netloc) + else: + raise AssertionError, "unsupported schema "+schema + + data = '' + loglevel = log.INFO + try: + http.connect() + http.putrequest("POST", url) + http.putheader('Content-type', + 'multipart/form-data; boundary=%s'%boundary) + http.putheader('Content-length', str(len(body))) + http.putheader('Authorization', auth) + http.endheaders() + http.send(body) + except socket.error, e: + self.announce(e.msg, log.ERROR) + return + + r = http.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.ERROR) + if self.show_response: + print '-'*75, r.read(), '-'*75 diff --git a/Lib/setuptools/depends.py b/Lib/setuptools/depends.py new file mode 100644 index 0000000..68d8194 --- /dev/null +++ b/Lib/setuptools/depends.py @@ -0,0 +1,239 @@ +from __future__ import generators +import sys, imp, marshal +from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN +from distutils.version import StrictVersion, LooseVersion + +__all__ = [ + 'Require', 'find_module', 'get_module_constant', 'extract_constant' +] + +class Require: + """A prerequisite to building or installing a distribution""" + + def __init__(self,name,requested_version,module,homepage='', + attribute=None,format=None + ): + + if format is None and requested_version is not None: + format = StrictVersion + + if format is not None: + requested_version = format(requested_version) + if attribute is None: + attribute = '__version__' + + self.__dict__.update(locals()) + del self.self + + + def full_name(self): + """Return full package/distribution name, w/version""" + if self.requested_version is not None: + return '%s-%s' % (self.name,self.requested_version) + return self.name + + + def version_ok(self,version): + """Is 'version' sufficiently up-to-date?""" + return self.attribute is None or self.format is None or \ + str(version)<>"unknown" and version >= self.requested_version + + + def get_version(self, paths=None, default="unknown"): + + """Get version number of installed module, 'None', or 'default' + + Search 'paths' for module. If not found, return 'None'. If found, + return the extracted version attribute, or 'default' if no version + attribute was specified, or the value cannot be determined without + importing the module. The version is formatted according to the + requirement's version format (if any), unless it is 'None' or the + supplied 'default'. + """ + + if self.attribute is None: + try: + f,p,i = find_module(self.module,paths) + if f: f.close() + return default + except ImportError: + return None + + v = get_module_constant(self.module,self.attribute,default,paths) + + if v is not None and v is not default and self.format is not None: + return self.format(v) + + return v + + + def is_present(self,paths=None): + """Return true if dependency is present on 'paths'""" + return self.get_version(paths) is not None + + + def is_current(self,paths=None): + """Return true if dependency is present and up-to-date on 'paths'""" + version = self.get_version(paths) + if version is None: + return False + return self.version_ok(version) + + +def _iter_code(code): + + """Yield '(op,arg)' pair for each operation in code object 'code'""" + + from array import array + from dis import HAVE_ARGUMENT, EXTENDED_ARG + + bytes = array('b',code.co_code) + eof = len(code.co_code) + + ptr = 0 + extended_arg = 0 + + while ptr<eof: + + op = bytes[ptr] + + if op>=HAVE_ARGUMENT: + + arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg + ptr += 3 + + if op==EXTENDED_ARG: + extended_arg = arg * 65536L + continue + + else: + arg = None + ptr += 1 + + yield op,arg + + + + + + + + + + +def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + + parts = module.split('.') + + while parts: + part = parts.pop(0) + f, path, (suffix,mode,kind) = info = imp.find_module(part, paths) + + if kind==PKG_DIRECTORY: + parts = parts or ['__init__'] + paths = [path] + + elif parts: + raise ImportError("Can't find %r in %s" % (parts,module)) + + return info + + + + + + + + + + + + + + + + + + + + + + + + +def get_module_constant(module, symbol, default=-1, paths=None): + + """Find 'module' by searching 'paths', and extract 'symbol' + + Return 'None' if 'module' does not exist on 'paths', or it does not define + 'symbol'. If the module defines 'symbol' as a constant, return the + constant. Otherwise, return 'default'.""" + + try: + f, path, (suffix,mode,kind) = find_module(module,paths) + except ImportError: + # Module doesn't exist + return None + + try: + if kind==PY_COMPILED: + f.read(8) # skip magic & date + code = marshal.load(f) + elif kind==PY_FROZEN: + code = imp.get_frozen_object(module) + elif kind==PY_SOURCE: + code = compile(f.read(), path, 'exec') + else: + # Not something we can parse; we'll have to import it. :( + if module not in sys.modules: + imp.load_module(module,f,path,(suffix,mode,kind)) + return getattr(sys.modules[module],symbol,None) + + finally: + if f: + f.close() + + return extract_constant(code,symbol,default) + + + + + + + + +def extract_constant(code,symbol,default=-1): + + """Extract the constant value of 'symbol' from 'code' + + If the name 'symbol' is bound to a constant value by the Python code + object 'code', return that value. If 'symbol' is bound to an expression, + return 'default'. Otherwise, return 'None'. + + Return value is based on the first assignment to 'symbol'. 'symbol' must + be a global, or at least a non-"fast" local in the code block. That is, + only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' + must be present in 'code.co_names'. + """ + + if symbol not in code.co_names: + # name's not there, can't possibly be an assigment + return None + + name_idx = list(code.co_names).index(symbol) + + STORE_NAME = 90 + STORE_GLOBAL = 97 + LOAD_CONST = 100 + + const = default + + for op, arg in _iter_code(code): + + if op==LOAD_CONST: + const = code.co_consts[arg] + elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL): + return const + else: + const = default diff --git a/Lib/setuptools/dist.py b/Lib/setuptools/dist.py new file mode 100644 index 0000000..f0417c1 --- /dev/null +++ b/Lib/setuptools/dist.py @@ -0,0 +1,798 @@ +__all__ = ['Distribution'] + +from distutils.core import Distribution as _Distribution +from setuptools.depends import Require +from setuptools.command.install import install +from setuptools.command.sdist import sdist +from setuptools.command.install_lib import install_lib +from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils.errors import DistutilsSetupError +import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd +import os + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls + +_Distribution = _get_unpatched(_Distribution) + +sequence = tuple, list + +def check_importable(dist, attr, value): + try: + ep = pkg_resources.EntryPoint.parse('x='+value) + assert not ep.extras + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be importable 'module:attrs' string (got %r)" + % (attr,value) + ) + + +def assert_string_list(dist, attr, value): + """Verify that value is a string list or None""" + try: + assert ''.join(value)!=value + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be a list of strings (got %r)" % (attr,value) + ) + +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + assert_string_list(dist,attr,value) + + for nsp in value: + if not dist.has_contents_for(nsp): + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + for k,v in value.items(): + list(pkg_resources.parse_requirements(v)) + except (TypeError,ValueError,AttributeError): + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) + +def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + raise DistutilsSetupError( + "%r must be a boolean value (got %r)" % (attr,value) + ) + + + +def check_requirements(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + except (TypeError,ValueError): + raise DistutilsSetupError( + "%r must be a string or list of strings " + "containing valid project/version requirement specifiers" % (attr,) + ) + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + pkg_resources.EntryPoint.parse_map(value) + except ValueError, e: + raise DistutilsSetupError(e) + + +def check_test_suite(dist, attr, value): + if not isinstance(value,basestring): + raise DistutilsSetupError("test_suite must be a string") + + +def check_package_data(dist, attr, value): + """Verify that value is a dictionary of package names to glob lists""" + if isinstance(value,dict): + for k,v in value.items(): + if not isinstance(k,str): break + try: iter(v) + except TypeError: + break + else: + return + raise DistutilsSetupError( + attr+" must be a dictionary mapping package names to lists of " + "wildcard patterns" + ) + + + + +class Distribution(_Distribution): + """Distribution with support for features, tests, and package data + + This is an enhanced version of 'distutils.dist.Distribution' that + effectively adds the following new optional keyword arguments to 'setup()': + + 'install_requires' -- a string or sequence of strings specifying project + versions that the distribution requires when installed, in the format + used by 'pkg_resources.require()'. They will be installed + automatically when the package is installed. If you wish to use + packages that are not available in PyPI, or want to give your users an + alternate download location, you can add a 'find_links' option to the + '[easy_install]' section of your project's 'setup.cfg' file, and then + setuptools will scan the listed web pages for links that satisfy the + requirements. + + 'extras_require' -- a dictionary mapping names of optional "extras" to the + additional requirement(s) that using those extras incurs. For example, + this:: + + extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) + + indicates that the distribution can optionally provide an extra + capability called "reST", but it can only be used if docutils and + reSTedit are installed. If the user installs your package using + EasyInstall and requests one of your extras, the corresponding + additional requirements will be installed if needed. + + 'features' -- a dictionary mapping option names to 'setuptools.Feature' + objects. Features are a portion of the distribution that can be + included or excluded based on user options, inter-feature dependencies, + and availability on the current system. Excluded features are omitted + from all setup commands, including source and binary distributions, so + you can create multiple distributions from the same source tree. + Feature names should be valid Python identifiers, except that they may + contain the '-' (minus) sign. Features can be included or excluded + via the command line options '--with-X' and '--without-X', where 'X' is + the name of the feature. Whether a feature is included by default, and + whether you are allowed to control this from the command line, is + determined by the Feature object. See the 'Feature' class for more + information. + + 'test_suite' -- the name of a test suite to run for the 'test' command. + If the user runs 'python setup.py test', the package will be installed, + and the named test suite will be run. The format is the same as + would be used on a 'unittest.py' command line. That is, it is the + dotted name of an object to import and call to generate a test suite. + + 'package_data' -- a dictionary mapping package names to lists of filenames + or globs to use to find data files contained in the named packages. + If the dictionary has filenames or globs listed under '""' (the empty + string), those names will be searched for in every package, in addition + to any names for the specific package. Data files found using these + names/globs will be installed along with the package, in the same + location as the package. Note that globs are allowed to reference + the contents of non-package subdirectories, as long as you use '/' as + a path separator. (Globs are automatically converted to + platform-specific paths at runtime.) + + In addition to these new keywords, this class also has several new methods + for manipulating the distribution's contents. For example, the 'include()' + and 'exclude()' methods can be thought of as in-place add and subtract + commands that add or remove packages, modules, extensions, and so on from + the distribution. They are used by the feature subsystem to configure the + distribution for the included and excluded features. + """ + + _patched_dist = None + + def patch_missing_pkg_info(self, attrs): + # Fake up a replacement for the data that would normally come from + # PKG-INFO, but which might not yet be built if this is a fresh + # checkout. + # + if not attrs or 'name' not in attrs or 'version' not in attrs: + return + key = pkg_resources.safe_name(str(attrs['name'])).lower() + dist = pkg_resources.working_set.by_key.get(key) + if dist is not None and not dist.has_metadata('PKG-INFO'): + dist._version = pkg_resources.safe_version(str(attrs['version'])) + self._patched_dist = dist + + def __init__ (self, attrs=None): + have_package_data = hasattr(self, "package_data") + if not have_package_data: + self.package_data = {} + self.require_features = [] + self.features = {} + self.dist_files = [] + self.patch_missing_pkg_info(attrs) + # Make sure we have any eggs needed to interpret 'attrs' + if attrs and 'dependency_links' in attrs: + self.dependency_links = attrs.pop('dependency_links') + assert_string_list(self,'dependency_links',self.dependency_links) + if attrs and 'setup_requires' in attrs: + self.fetch_build_eggs(attrs.pop('setup_requires')) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + if not hasattr(self,ep.name): + setattr(self,ep.name,None) + _Distribution.__init__(self,attrs) + if isinstance(self.metadata.version, (int,long,float)): + # Some people apparently take "version number" too literally :) + self.metadata.version = str(self.metadata.version) + + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result + + def _feature_attrname(self,name): + """Convert feature name to corresponding option attribute name""" + return 'with_'+name.replace('-','_') + + def fetch_build_eggs(self, requires): + """Resolve pre-setup requirements""" + from pkg_resources import working_set, parse_requirements + for dist in working_set.resolve( + parse_requirements(requires), installer=self.fetch_build_egg + ): + working_set.add(dist) + + def finalize_options(self): + _Distribution.finalize_options(self) + if self.features: + self._set_global_opts_from_features() + + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + value = getattr(self,ep.name,None) + if value is not None: + ep.require(installer=self.fetch_build_egg) + ep.load()(self, ep.name, value) + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" + try: + cmd = self._egg_fetcher + except AttributeError: + from setuptools.command.easy_install import easy_install + dist = self.__class__({'script_args':['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', + 'site_dirs', 'allow_hosts' + ) + for key in opts.keys(): + if key not in keep: + del opts[key] # don't use any other settings + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + cmd = easy_install( + dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False, multi_version=True, no_report = True + ) + cmd.ensure_finalized() + self._egg_fetcher = cmd + return cmd.easy_install(req) + + def _set_global_opts_from_features(self): + """Add --with-X/--without-X options based on optional features""" + + go = [] + no = self.negative_opt.copy() + + for name,feature in self.features.items(): + self._set_feature(name,None) + feature.validate(self) + + if feature.optional: + descr = feature.description + incdef = ' (default)' + excdef='' + if not feature.include_by_default(): + excdef, incdef = incdef, excdef + + go.append(('with-'+name, None, 'include '+descr+incdef)) + go.append(('without-'+name, None, 'exclude '+descr+excdef)) + no['without-'+name] = 'with-'+name + + self.global_options = self.feature_options = go + self.global_options + self.negative_opt = self.feature_negopt = no + + + + + + + + + + + + + + + + + + + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + + + def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + + for ep in pkg_resources.iter_entry_points('distutils.commands',command): + ep.require(installer=self.fetch_build_egg) + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + + def print_commands(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load(False) # don't require extras, we're not running + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) + + + + + + def _set_feature(self,name,status): + """Set feature's inclusion status""" + setattr(self,self._feature_attrname(name),status) + + def feature_is_included(self,name): + """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" + return getattr(self,self._feature_attrname(name)) + + def include_feature(self,name): + """Request inclusion of feature named 'name'""" + + if self.feature_is_included(name)==0: + descr = self.features[name].description + raise DistutilsOptionError( + descr + " is required, but was excluded or is not available" + ) + self.features[name].include_in(self) + self._set_feature(name,1) + + def include(self,**attrs): + """Add items to distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would add 'x' to + the distribution's 'py_modules' attribute, if it was not already + there. + + Currently, this method only supports inclusion for attributes that are + lists or tuples. If you need to add support for adding to other + attributes in this or a subclass, you can add an '_include_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' + will try to call 'dist._include_foo({"bar":"baz"})', which can then + handle whatever special inclusion logic is needed. + """ + for k,v in attrs.items(): + include = getattr(self, '_include_'+k, None) + if include: + include(v) + else: + self._include_misc(k,v) + + def exclude_package(self,package): + """Remove packages, modules, and extensions in named package""" + + pfx = package+'.' + if self.packages: + self.packages = [ + p for p in self.packages + if p<>package and not p.startswith(pfx) + ] + + if self.py_modules: + self.py_modules = [ + p for p in self.py_modules + if p<>package and not p.startswith(pfx) + ] + + if self.ext_modules: + self.ext_modules = [ + p for p in self.ext_modules + if p.name<>package and not p.name.startswith(pfx) + ] + + + def has_contents_for(self,package): + """Return true if 'exclude_package(package)' would do something""" + + pfx = package+'.' + + for p in self.iter_distribution_names(): + if p==package or p.startswith(pfx): + return True + + + + + + + + + + + def _exclude_misc(self,name,value): + """Handle 'exclude()' for list/tuple attrs without a special handler""" + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list or tuple (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is not None and not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + elif old: + setattr(self,name,[item for item in old if item not in value]) + + def _include_misc(self,name,value): + """Handle 'include()' for list/tuple attrs without a special handler""" + + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is None: + setattr(self,name,value) + elif not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + else: + setattr(self,name,old+[item for item in value if item not in old]) + + def exclude(self,**attrs): + """Remove items from distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from + the distribution's 'py_modules' attribute. Excluding packages uses + the 'exclude_package()' method, so all of the package's contained + packages, modules, and extensions are also excluded. + + Currently, this method only supports exclusion from attributes that are + lists or tuples. If you need to add support for excluding from other + attributes in this or a subclass, you can add an '_exclude_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' + will try to call 'dist._exclude_foo({"bar":"baz"})', which can then + handle whatever special exclusion logic is needed. + """ + for k,v in attrs.items(): + exclude = getattr(self, '_exclude_'+k, None) + if exclude: + exclude(v) + else: + self._exclude_misc(k,v) + + def _exclude_packages(self,packages): + if not isinstance(packages,sequence): + raise DistutilsSetupError( + "packages: setting must be a list or tuple (%r)" % (packages,) + ) + map(self.exclude_package, packages) + + + + + + + + + + + + + def _parse_command_opts(self, parser, args): + # Remove --with-X/--without-X options when processing command args + self.global_options = self.__class__.global_options + self.negative_opt = self.__class__.negative_opt + + # First, expand any aliases + command = args[0] + aliases = self.get_option_dict('aliases') + while command in aliases: + src,alias = aliases[command] + del aliases[command] # ensure each alias can expand only once! + import shlex + args[:1] = shlex.split(alias,True) + command = args[0] + + nargs = _Distribution._parse_command_opts(self, parser, args) + + # Handle commands that want to consume all remaining arguments + cmd_class = self.get_command_class(command) + if getattr(cmd_class,'command_consumes_arguments',None): + self.get_option_dict(command)['args'] = ("command line", nargs) + if nargs is not None: + return [] + + return nargs + + + + + + + + + + + + + + + + + def get_cmdline_options(self): + """Return a '{cmd: {opt:val}}' map of all command-line options + + Option names are all long, but do not include the leading '--', and + contain dashes rather than underscores. If the option doesn't take + an argument (e.g. '--quiet'), the 'val' is 'None'. + + Note that options provided by config files are intentionally excluded. + """ + + d = {} + + for cmd,opts in self.command_options.items(): + + for opt,(src,val) in opts.items(): + + if src != "command line": + continue + + opt = opt.replace('_','-') + + if val==0: + cmdobj = self.get_command_obj(cmd) + neg_opt = self.negative_opt.copy() + neg_opt.update(getattr(cmdobj,'negative_opt',{})) + for neg,pos in neg_opt.items(): + if pos==opt: + opt=neg + val=None + break + else: + raise AssertionError("Shouldn't be able to get here") + + elif val==1: + val = None + + d.setdefault(cmd,{})[opt] = val + + return d + + + def iter_distribution_names(self): + """Yield all packages, modules, and extension names in distribution""" + + for pkg in self.packages or (): + yield pkg + + for module in self.py_modules or (): + yield module + + for ext in self.ext_modules or (): + if isinstance(ext,tuple): + name,buildinfo = ext + yield name + else: + yield ext.name + +# Install it throughout the distutils +for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = Distribution + + + + + + + + + + + + + + + + + + + + + + +class Feature: + """A subset of the distribution that can be excluded if unneeded/wanted + + Features are created using these keyword arguments: + + 'description' -- a short, human readable description of the feature, to + be used in error messages, and option help messages. + + 'standard' -- if true, the feature is included by default if it is + available on the current system. Otherwise, the feature is only + included if requested via a command line '--with-X' option, or if + another included feature requires it. The default setting is 'False'. + + 'available' -- if true, the feature is available for installation on the + current system. The default setting is 'True'. + + 'optional' -- if true, the feature's inclusion can be controlled from the + command line, using the '--with-X' or '--without-X' options. If + false, the feature's inclusion status is determined automatically, + based on 'availabile', 'standard', and whether any other feature + requires it. The default setting is 'True'. + + 'require_features' -- a string or sequence of strings naming features + that should also be included if this feature is included. Defaults to + empty list. May also contain 'Require' objects that should be + added/removed from the distribution. + + 'remove' -- a string or list of strings naming packages to be removed + from the distribution if this feature is *not* included. If the + feature *is* included, this argument is ignored. This argument exists + to support removing features that "crosscut" a distribution, such as + defining a 'tests' feature that removes all the 'tests' subpackages + provided by other features. The default for this argument is an empty + list. (Note: the named package(s) or modules must exist in the base + distribution when the 'setup()' function is initially called.) + + other keywords -- any other keyword arguments are saved, and passed to + the distribution's 'include()' and 'exclude()' methods when the + feature is included or excluded, respectively. So, for example, you + could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be + added or removed from the distribution as appropriate. + + A feature must include at least one 'requires', 'remove', or other + keyword argument. Otherwise, it can't affect the distribution in any way. + Note also that you can subclass 'Feature' to create your own specialized + feature types that modify the distribution in other ways when included or + excluded. See the docstrings for the various methods here for more detail. + Aside from the methods, the only feature attributes that distributions look + at are 'description' and 'optional'. + """ + def __init__(self, description, standard=False, available=True, + optional=True, require_features=(), remove=(), **extras + ): + + self.description = description + self.standard = standard + self.available = available + self.optional = optional + if isinstance(require_features,(str,Require)): + require_features = require_features, + + self.require_features = [ + r for r in require_features if isinstance(r,str) + ] + er = [r for r in require_features if not isinstance(r,str)] + if er: extras['require_features'] = er + + if isinstance(remove,str): + remove = remove, + self.remove = remove + self.extras = extras + + if not remove and not require_features and not extras: + raise DistutilsSetupError( + "Feature %s: must define 'require_features', 'remove', or at least one" + " of 'packages', 'py_modules', etc." + ) + + def include_by_default(self): + """Should this feature be included by default?""" + return self.available and self.standard + + def include_in(self,dist): + + """Ensure feature and its requirements are included in distribution + + You may override this in a subclass to perform additional operations on + the distribution. Note that this method may be called more than once + per feature, and so should be idempotent. + + """ + + if not self.available: + raise DistutilsPlatformError( + self.description+" is required," + "but is not available on this platform" + ) + + dist.include(**self.extras) + + for f in self.require_features: + dist.include_feature(f) + + + + def exclude_from(self,dist): + + """Ensure feature is excluded from distribution + + You may override this in a subclass to perform additional operations on + the distribution. This method will be called at most once per + feature, and only after all included features have been asked to + include themselves. + """ + + dist.exclude(**self.extras) + + if self.remove: + for item in self.remove: + dist.exclude_package(item) + + + + def validate(self,dist): + + """Verify that feature makes sense in context of distribution + + This method is called by the distribution just before it parses its + command line. It checks to ensure that the 'remove' attribute, if any, + contains only valid package/module names that are present in the base + distribution when 'setup()' is called. You may override it in a + subclass to perform any other required validation of the feature + against a target distribution. + """ + + for item in self.remove: + if not dist.has_contents_for(item): + raise DistutilsSetupError( + "%s wants to be able to remove %s, but the distribution" + " doesn't contain any packages or modules under %s" + % (self.description, item, item) + ) diff --git a/Lib/setuptools/extension.py b/Lib/setuptools/extension.py new file mode 100644 index 0000000..cfcf55b --- /dev/null +++ b/Lib/setuptools/extension.py @@ -0,0 +1,35 @@ +from distutils.core import Extension as _Extension +from dist import _get_unpatched +_Extension = _get_unpatched(_Extension) + +try: + from Pyrex.Distutils.build_ext import build_ext +except ImportError: + have_pyrex = False +else: + have_pyrex = True + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + if not have_pyrex: + # convert .pyx extensions to .c + def __init__(self,*args,**kw): + _Extension.__init__(self,*args,**kw) + sources = [] + for s in self.sources: + if s.endswith('.pyx'): + sources.append(s[:-3]+'c') + else: + sources.append(s) + self.sources = sources + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" + +import sys, distutils.core, distutils.extension +distutils.core.Extension = Extension +distutils.extension.Extension = Extension +if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = Extension diff --git a/Lib/setuptools/gui.exe b/Lib/setuptools/gui.exe new file mode 100755 index 0000000..63ff35f Binary files /dev/null and b/Lib/setuptools/gui.exe differ diff --git a/Lib/setuptools/package_index.py b/Lib/setuptools/package_index.py new file mode 100755 index 0000000..107e222 --- /dev/null +++ b/Lib/setuptools/package_index.py @@ -0,0 +1,674 @@ +"""PyPI and direct package downloading""" + +import sys, os.path, re, urlparse, urllib2, shutil, random, socket +from pkg_resources import * +from distutils import log +from distutils.errors import DistutilsError +from md5 import md5 +from fnmatch import translate + +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( + '<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a href="[^?]+\?:action=show_md5' + '&digest=([0-9a-f]{32})">md5</a>\\)' +) + +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match +EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() + +__all__ = [ + 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst', + 'interpret_distro_name', +] + + +def parse_bdist_wininst(name): + """Return (base,pyversion) or (None,None) for possible .exe name""" + + 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) + dists = distros_for_location(url, base, metadata) + if fragment and not dists: + match = EGG_FRAGMENT.match(fragment) + if match: + return interpret_distro_name( + url, match.group(1), metadata, precedence = CHECKOUT_DIST + ) + return dists + +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'): # 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('-') + 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 + ) + + + + + +class PackageIndex(Environment): + """A distribution index that scans web pages for download URLs""" + + def __init__(self,index_url="http://www.python.org/pypi",hosts=('*',),*args,**kw): + 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 process_url(self, url, retrieve=False): + """Evaluate a URL as a possible download, and maybe retrieve it""" + url = fix_sf_url(url) + 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) + self.fetched_urls[url] = self.fetched_urls[f.url] = True + + + if 'html' not in f.headers['content-type'].lower(): + f.close() # not html, we can't process it + return + + base = f.url # handle redirects + page = f.read() + f.close() + if url.startswith(self.index_url): + page = self.process_index(url, page) + + for match in HREF.finditer(page): + link = urlparse.urljoin(base, match.group(1)) + self.process_url(link) + + def process_filename(self, fn, nested=False): + # process filenames or directories + if not os.path.exists(fn): + self.warn("Not found: %s", url) + 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): + if 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) + + + + 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: + # 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 + + if url==self.index_url or 'Index of Packages' in page: + # process an index page into the package-page index + for match in HREF.finditer(page): + scan( urlparse.urljoin(url, match.group(1)) ) + else: + pkg,ver = scan(url) # ensure this page is in the page index + # process individual package page + for tag in ("Home Page", "Download URL"): + pos = page.find(tag) + if pos!=-1: + match = HREF.search(page,pos) + if match: + # Process the found URL + new_url = urlparse.urljoin(url, match.group(1)) + base, frag = egg_info_for_url(new_url) + if base.endswith('.py') and not frag: + if pkg and 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 + ) + + 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.warn( + "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.warn( + "Couldn't find index page for %r (maybe misspelled?)", + requirement.unsafe_name + ) + self.scan_all() + + for url in 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 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); #import pdb; pdb.set_trace() + 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 files without an '#egg=name-version'" + " suffix to enable automatic setup script generation." + ) + + dl_blocksize = 8192 + def _download_to(self, url, filename): + self.url_ok(url,True) # raises error if not allowed + 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 retry_sf_download(self, url, filename): + try: + return self._download_to(url, filename) + except: + scheme, server, path, param, query, frag = urlparse.urlparse(url) + if server!='dl.sourceforge.net': + raise + + mirror = get_sf_ip() + + while _sf_mirrors: + self.warn("Download failed: %s", sys.exc_info()[1]) + url = urlparse.urlunparse((scheme, mirror, path, param, '', frag)) + try: + return self._download_to(url, filename) + except: + _sf_mirrors.remove(mirror) # don't retry the same mirror + mirror = get_sf_ip() + + raise # fail if no mirror works + + + + + + + + + + + + + + + + + + + + + + def open_url(self, url): + try: + return urllib2.urlopen(url) + except urllib2.HTTPError, v: + return v + except urllib2.URLError, v: + raise DistutilsError("Download error: %s" % v.reason) + + + 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) + else: + headers = self.retry_sf_download(url, filename) + if 'html' in headers['content-type'].lower(): + return self._download_html(url, headers, filename, tmpdir) + else: + return filename + + def scan_url(self, url): + self.process_url(url, True) + + + def _download_html(self, url, headers, filename, tmpdir): + 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): + log.debug(msg, *args) + + def info(self, msg, *args): + log.info(msg, *args) + + def warn(self, msg, *args): + log.warn(msg, *args) + + + + + + + + + + + + +def fix_sf_url(url): + scheme, server, path, param, query, frag = urlparse.urlparse(url) + if server!='prdownloads.sourceforge.net': + return url + return urlparse.urlunparse( + (scheme, 'dl.sourceforge.net', 'sourceforge'+path, param, '', frag) + ) + +_sf_mirrors = [] + +def get_sf_ip(): + if not _sf_mirrors: + try: + _sf_mirrors[:] = socket.gethostbyname_ex('dl.sourceforge.net')[-1] + except socket.error: + # DNS-bl0ck1n9 f1r3w4llz sUx0rs! + _sf_mirrors[:] = ['dl.sourceforge.net'] + return random.choice(_sf_mirrors) diff --git a/Lib/setuptools/sandbox.py b/Lib/setuptools/sandbox.py new file mode 100755 index 0000000..606944b --- /dev/null +++ b/Lib/setuptools/sandbox.py @@ -0,0 +1,203 @@ +import os, sys, __builtin__, tempfile +_os = sys.modules[os.name] +_open = open +from distutils.errors import DistutilsError +__all__ = [ + "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", +] + +def run_setup(setup_script, args): + """Run a distutils setup script, sandboxed in its directory""" + + old_dir = os.getcwd() + save_argv = sys.argv[:] + save_path = sys.path[:] + setup_dir = os.path.abspath(os.path.dirname(setup_script)) + temp_dir = os.path.join(setup_dir,'temp') + if not os.path.isdir(temp_dir): os.makedirs(temp_dir) + save_tmp = tempfile.tempdir + + try: + tempfile.tempdir = temp_dir + os.chdir(setup_dir) + try: + sys.argv[:] = [setup_script]+list(args) + sys.path.insert(0, setup_dir) + DirectorySandbox(setup_dir).run( + lambda: execfile( + "setup.py", + {'__file__':setup_script, '__name__':'__main__'} + ) + ) + except SystemExit, v: + if v.args and v.args[0]: + raise + # Normal exit, just return + finally: + os.chdir(old_dir) + sys.path[:] = save_path + sys.argv[:] = save_argv + tempfile.tempdir = save_tmp + +class AbstractSandbox: + """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" + + _active = False + + def __init__(self): + self._attrs = [ + name for name in dir(_os) + if not name.startswith('_') and hasattr(self,name) + ] + + def _copy(self, source): + for name in self._attrs: + setattr(os, name, getattr(source,name)) + + def run(self, func): + """Run 'func' under os sandboxing""" + try: + self._copy(self) + __builtin__.open = __builtin__.file = self._open + self._active = True + return func() + finally: + self._active = False + __builtin__.open = __builtin__.file = _open + self._copy(_os) + + + def _mk_dual_path_wrapper(name): + original = getattr(_os,name) + def wrap(self,src,dst,*args,**kw): + if self._active: + src,dst = self._remap_pair(name,src,dst,*args,**kw) + return original(src,dst,*args,**kw) + return wrap + + + for name in ["rename", "link", "symlink"]: + if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name) + + + def _mk_single_path_wrapper(name, original=None): + original = original or getattr(_os,name) + def wrap(self,path,*args,**kw): + if self._active: + path = self._remap_input(name,path,*args,**kw) + return original(path,*args,**kw) + return wrap + + _open = _mk_single_path_wrapper('file', _open) + for name in [ + "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", + "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", + "startfile", "mkfifo", "mknod", "pathconf", "access" + ]: + if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name) + + + def _mk_single_with_return(name): + original = getattr(_os,name) + def wrap(self,path,*args,**kw): + if self._active: + path = self._remap_input(name,path,*args,**kw) + return self._remap_output(name, original(path,*args,**kw)) + return original(path,*args,**kw) + return wrap + + for name in ['readlink', 'tempnam']: + if hasattr(_os,name): locals()[name] = _mk_single_with_return(name) + + def _mk_query(name): + original = getattr(_os,name) + def wrap(self,*args,**kw): + retval = original(*args,**kw) + if self._active: + return self._remap_output(name, retval) + return retval + return wrap + + for name in ['getcwd', 'tmpnam']: + if hasattr(_os,name): locals()[name] = _mk_query(name) + + def _validate_path(self,path): + """Called to remap or validate any path, whether input or output""" + return path + + def _remap_input(self,operation,path,*args,**kw): + """Called for path inputs""" + return self._validate_path(path) + + def _remap_output(self,operation,path): + """Called for path outputs""" + return self._validate_path(path) + + def _remap_pair(self,operation,src,dst,*args,**kw): + """Called for path pairs like rename, link, and symlink operations""" + return ( + self._remap_input(operation+'-from',src,*args,**kw), + self._remap_input(operation+'-to',dst,*args,**kw) + ) + + +class DirectorySandbox(AbstractSandbox): + """Restrict operations to a single subdirectory - pseudo-chroot""" + + write_ops = dict.fromkeys([ + "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", + "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", + ]) + + def __init__(self,sandbox): + self._sandbox = os.path.normcase(os.path.realpath(sandbox)) + self._prefix = os.path.join(self._sandbox,'') + AbstractSandbox.__init__(self) + + def _violation(self, operation, *args, **kw): + raise SandboxViolation(operation, args, kw) + + def _open(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rt', 'rb', 'rU') and not self._ok(path): + self._violation("open", path, mode, *args, **kw) + return _open(path,mode,*args,**kw) + + def tmpnam(self): + self._violation("tmpnam") + + def _ok(self,path): + active = self._active + try: + self._active = False + realpath = os.path.normcase(os.path.realpath(path)) + if realpath==self._sandbox or realpath.startswith(self._prefix): + return True + finally: + self._active = active + + def _remap_input(self,operation,path,*args,**kw): + """Called for path inputs""" + if operation in self.write_ops and not self._ok(path): + self._violation(operation, os.path.realpath(path), *args, **kw) + return path + + def _remap_pair(self,operation,src,dst,*args,**kw): + """Called for path pairs like rename, link, and symlink operations""" + if not self._ok(src) or not self._ok(dst): + self._violation(operation, src, dst, *args, **kw) + return (src,dst) + + +class SandboxViolation(DistutilsError): + """A setup script attempted to modify the filesystem outside the sandbox""" + + def __str__(self): + return """SandboxViolation: %s%r %s + +The package setup script has attempted to modify files on your system +that are not within the EasyInstall build area, and has been aborted. + +This package cannot be safely installed by EasyInstall, and may not +support alternate installation locations even if you run its setup +script by hand. Please inform the package's author and the EasyInstall +maintainers to find out if a fix or workaround is available.""" % self.args diff --git a/Lib/setuptools/site-patch.py b/Lib/setuptools/site-patch.py new file mode 100755 index 0000000..b1b27b9 --- /dev/null +++ b/Lib/setuptools/site-patch.py @@ -0,0 +1,74 @@ +def __boot(): + import sys, imp, os, os.path + PYTHONPATH = os.environ.get('PYTHONPATH') + if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH): + PYTHONPATH = [] + else: + PYTHONPATH = PYTHONPATH.split(os.pathsep) + + pic = getattr(sys,'path_importer_cache',{}) + stdpath = sys.path[len(PYTHONPATH):] + mydir = os.path.dirname(__file__) + #print "searching",stdpath,sys.path + + for item in stdpath: + if item==mydir or not item: + continue # skip if current dir. on Windows, or my own directory + importer = pic.get(item) + if importer is not None: + loader = importer.find_module('site') + if loader is not None: + # This should actually reload the current module + loader.load_module('site') + break + else: + try: + stream, path, descr = imp.find_module('site',[item]) + except ImportError: + continue + if stream is None: + continue + try: + # This should actually reload the current module + imp.load_module('site',stream,path,descr) + finally: + stream.close() + break + else: + raise ImportError("Couldn't find the real 'site' module") + + #print "loaded", __file__ + + known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp + + oldpos = getattr(sys,'__egginsert',0) # save old insertion position + sys.__egginsert = 0 # and reset the current one + + for item in PYTHONPATH: + addsitedir(item) + + sys.__egginsert += oldpos # restore effective old position + + d,nd = makepath(stdpath[0]) + insert_at = None + new_path = [] + + for item in sys.path: + p,np = makepath(item) + + if np==nd and insert_at is None: + # We've hit the first 'system' path entry, so added entries go here + insert_at = len(new_path) + + if np in known_paths or insert_at is None: + new_path.append(item) + else: + # new path after the insert point, back-insert it + new_path.insert(insert_at, item) + insert_at += 1 + + sys.path[:] = new_path + +if __name__=='site': + __boot() + del __boot diff --git a/Lib/setuptools/tests/__init__.py b/Lib/setuptools/tests/__init__.py new file mode 100644 index 0000000..8a767dc --- /dev/null +++ b/Lib/setuptools/tests/__init__.py @@ -0,0 +1,364 @@ +"""Tests for the 'setuptools' package""" + +from unittest import TestSuite, TestCase, makeSuite, defaultTestLoader +import distutils.core, distutils.cmd +from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils.errors import DistutilsSetupError +import setuptools, setuptools.dist +from setuptools import Feature +from distutils.core import Extension +from setuptools.depends import extract_constant, get_module_constant +from setuptools.depends import find_module, Require +from distutils.version import StrictVersion, LooseVersion +from distutils.util import convert_path +import sys, os.path + +def additional_tests(): + import doctest + return doctest.DocFileSuite( + 'api_tests.txt', optionflags=doctest.ELLIPSIS, package=__name__, + ) + + +def makeSetup(**args): + """Return distribution from 'setup(**args)', without executing commands""" + + distutils.core._setup_stop_after = "commandline" + + # Don't let system command line leak into tests! + args.setdefault('script_args',['install']) + + try: + return setuptools.setup(**args) + finally: + distutils.core_setup_stop_after = None + + + + + + + +class DependsTests(TestCase): + + def testExtractConst(self): + + from setuptools.depends import extract_constant + + def f1(): + global x,y,z + x = "test" + y = z + + # unrecognized name + self.assertEqual(extract_constant(f1.func_code,'q', -1), None) + + # constant assigned + self.assertEqual(extract_constant(f1.func_code,'x', -1), "test") + + # expression assigned + self.assertEqual(extract_constant(f1.func_code,'y', -1), -1) + + # recognized name, not assigned + self.assertEqual(extract_constant(f1.func_code,'z', -1), None) + + + def testFindModule(self): + self.assertRaises(ImportError, find_module, 'no-such.-thing') + self.assertRaises(ImportError, find_module, 'setuptools.non-existent') + f,p,i = find_module('setuptools.tests'); f.close() + + def testModuleExtract(self): + from distutils import __version__ + self.assertEqual( + get_module_constant('distutils','__version__'), __version__ + ) + self.assertEqual( + get_module_constant('sys','version'), sys.version + ) + self.assertEqual( + get_module_constant('setuptools.tests','__doc__'),__doc__ + ) + + def testRequire(self): + + req = Require('Distutils','1.0.3','distutils') + + self.assertEqual(req.name, 'Distutils') + self.assertEqual(req.module, 'distutils') + self.assertEqual(req.requested_version, '1.0.3') + self.assertEqual(req.attribute, '__version__') + self.assertEqual(req.full_name(), 'Distutils-1.0.3') + + from distutils import __version__ + self.assertEqual(req.get_version(), __version__) + self.failUnless(req.version_ok('1.0.9')) + self.failIf(req.version_ok('0.9.1')) + self.failIf(req.version_ok('unknown')) + + self.failUnless(req.is_present()) + self.failUnless(req.is_current()) + + req = Require('Distutils 3000','03000','distutils',format=LooseVersion) + self.failUnless(req.is_present()) + self.failIf(req.is_current()) + self.failIf(req.version_ok('unknown')) + + req = Require('Do-what-I-mean','1.0','d-w-i-m') + self.failIf(req.is_present()) + self.failIf(req.is_current()) + + req = Require('Tests', None, 'tests', homepage="http://example.com") + self.assertEqual(req.format, None) + self.assertEqual(req.attribute, None) + self.assertEqual(req.requested_version, None) + self.assertEqual(req.full_name(), 'Tests') + self.assertEqual(req.homepage, 'http://example.com') + + paths = [os.path.dirname(p) for p in __path__] + self.failUnless(req.is_present(paths)) + self.failUnless(req.is_current(paths)) + + + +class DistroTests(TestCase): + + def setUp(self): + self.e1 = Extension('bar.ext',['bar.c']) + self.e2 = Extension('c.y', ['y.c']) + + self.dist = makeSetup( + packages=['a', 'a.b', 'a.b.c', 'b', 'c'], + py_modules=['b.d','x'], + ext_modules = (self.e1, self.e2), + package_dir = {}, + ) + + + def testDistroType(self): + self.failUnless(isinstance(self.dist,setuptools.dist.Distribution)) + + + def testExcludePackage(self): + self.dist.exclude_package('a') + self.assertEqual(self.dist.packages, ['b','c']) + + self.dist.exclude_package('b') + self.assertEqual(self.dist.packages, ['c']) + self.assertEqual(self.dist.py_modules, ['x']) + self.assertEqual(self.dist.ext_modules, [self.e1, self.e2]) + + self.dist.exclude_package('c') + self.assertEqual(self.dist.packages, []) + self.assertEqual(self.dist.py_modules, ['x']) + self.assertEqual(self.dist.ext_modules, [self.e1]) + + # test removals from unspecified options + makeSetup().exclude_package('x') + + + + + + + + def testIncludeExclude(self): + # remove an extension + self.dist.exclude(ext_modules=[self.e1]) + self.assertEqual(self.dist.ext_modules, [self.e2]) + + # add it back in + self.dist.include(ext_modules=[self.e1]) + self.assertEqual(self.dist.ext_modules, [self.e2, self.e1]) + + # should not add duplicate + self.dist.include(ext_modules=[self.e1]) + self.assertEqual(self.dist.ext_modules, [self.e2, self.e1]) + + def testExcludePackages(self): + self.dist.exclude(packages=['c','b','a']) + self.assertEqual(self.dist.packages, []) + self.assertEqual(self.dist.py_modules, ['x']) + self.assertEqual(self.dist.ext_modules, [self.e1]) + + def testEmpty(self): + dist = makeSetup() + dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + dist = makeSetup() + dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + + def testContents(self): + self.failUnless(self.dist.has_contents_for('a')) + self.dist.exclude_package('a') + self.failIf(self.dist.has_contents_for('a')) + + self.failUnless(self.dist.has_contents_for('b')) + self.dist.exclude_package('b') + self.failIf(self.dist.has_contents_for('b')) + + self.failUnless(self.dist.has_contents_for('c')) + self.dist.exclude_package('c') + self.failIf(self.dist.has_contents_for('c')) + + + + + def testInvalidIncludeExclude(self): + self.assertRaises(DistutilsSetupError, + self.dist.include, nonexistent_option='x' + ) + self.assertRaises(DistutilsSetupError, + self.dist.exclude, nonexistent_option='x' + ) + self.assertRaises(DistutilsSetupError, + self.dist.include, packages={'x':'y'} + ) + self.assertRaises(DistutilsSetupError, + self.dist.exclude, packages={'x':'y'} + ) + self.assertRaises(DistutilsSetupError, + self.dist.include, ext_modules={'x':'y'} + ) + self.assertRaises(DistutilsSetupError, + self.dist.exclude, ext_modules={'x':'y'} + ) + + self.assertRaises(DistutilsSetupError, + self.dist.include, package_dir=['q'] + ) + self.assertRaises(DistutilsSetupError, + self.dist.exclude, package_dir=['q'] + ) + + + + + + + + + + + + + + + +class FeatureTests(TestCase): + + def setUp(self): + self.req = Require('Distutils','1.0.3','distutils') + self.dist = makeSetup( + features={ + 'foo': Feature("foo",standard=True,require_features=['baz',self.req]), + 'bar': Feature("bar", standard=True, packages=['pkg.bar'], + py_modules=['bar_et'], remove=['bar.ext'], + ), + 'baz': Feature( + "baz", optional=False, packages=['pkg.baz'], + scripts = ['scripts/baz_it'], + libraries=[('libfoo','foo/foofoo.c')] + ), + 'dwim': Feature("DWIM", available=False, remove='bazish'), + }, + script_args=['--without-bar', 'install'], + packages = ['pkg.bar', 'pkg.foo'], + py_modules = ['bar_et', 'bazish'], + ext_modules = [Extension('bar.ext',['bar.c'])] + ) + + def testDefaults(self): + self.failIf( + Feature( + "test",standard=True,remove='x',available=False + ).include_by_default() + ) + self.failUnless( + Feature("test",standard=True,remove='x').include_by_default() + ) + # Feature must have either kwargs, removes, or require_features + self.assertRaises(DistutilsSetupError, Feature, "test") + + def testAvailability(self): + self.assertRaises( + DistutilsPlatformError, + self.dist.features['dwim'].include_in, self.dist + ) + + def testFeatureOptions(self): + dist = self.dist + self.failUnless( + ('with-dwim',None,'include DWIM') in dist.feature_options + ) + self.failUnless( + ('without-dwim',None,'exclude DWIM (default)') in dist.feature_options + ) + self.failUnless( + ('with-bar',None,'include bar (default)') in dist.feature_options + ) + self.failUnless( + ('without-bar',None,'exclude bar') in dist.feature_options + ) + self.assertEqual(dist.feature_negopt['without-foo'],'with-foo') + self.assertEqual(dist.feature_negopt['without-bar'],'with-bar') + self.assertEqual(dist.feature_negopt['without-dwim'],'with-dwim') + self.failIf('without-baz' in dist.feature_negopt) + + def testUseFeatures(self): + dist = self.dist + self.assertEqual(dist.with_foo,1) + self.assertEqual(dist.with_bar,0) + self.assertEqual(dist.with_baz,1) + self.failIf('bar_et' in dist.py_modules) + self.failIf('pkg.bar' in dist.packages) + self.failUnless('pkg.baz' in dist.packages) + self.failUnless('scripts/baz_it' in dist.scripts) + self.failUnless(('libfoo','foo/foofoo.c') in dist.libraries) + self.assertEqual(dist.ext_modules,[]) + self.assertEqual(dist.require_features, [self.req]) + + # If we ask for bar, it should fail because we explicitly disabled + # it on the command line + self.assertRaises(DistutilsOptionError, dist.include_feature, 'bar') + + def testFeatureWithInvalidRemove(self): + self.assertRaises( + SystemExit, makeSetup, features = {'x':Feature('x', remove='y')} + ) + +class TestCommandTests(TestCase): + + def testTestIsCommand(self): + test_cmd = makeSetup().get_command_obj('test') + self.failUnless(isinstance(test_cmd, distutils.cmd.Command)) + + def testLongOptSuiteWNoDefault(self): + ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite']) + ts1 = ts1.get_command_obj('test') + ts1.ensure_finalized() + self.assertEqual(ts1.test_suite, 'foo.tests.suite') + + def testDefaultSuite(self): + ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test') + ts2.ensure_finalized() + self.assertEqual(ts2.test_suite, 'bar.tests.suite') + + def testDefaultWModuleOnCmdLine(self): + ts3 = makeSetup( + test_suite='bar.tests', + script_args=['test','-m','foo.tests'] + ).get_command_obj('test') + ts3.ensure_finalized() + self.assertEqual(ts3.test_module, 'foo.tests') + self.assertEqual(ts3.test_suite, 'foo.tests.test_suite') + + def testConflictingOptions(self): + ts4 = makeSetup( + script_args=['test','-m','bar.tests', '-s','foo.tests.suite'] + ).get_command_obj('test') + self.assertRaises(DistutilsOptionError, ts4.ensure_finalized) + + def testNoSuite(self): + ts5 = makeSetup().get_command_obj('test') + ts5.ensure_finalized() + self.assertEqual(ts5.test_suite, None) diff --git a/Lib/setuptools/tests/api_tests.txt b/Lib/setuptools/tests/api_tests.txt new file mode 100755 index 0000000..735ad8d --- /dev/null +++ b/Lib/setuptools/tests/api_tests.txt @@ -0,0 +1,330 @@ +Pluggable Distributions of Python Software +========================================== + +Distributions +------------- + +A "Distribution" is a collection of files that represent a "Release" of a +"Project" as of a particular point in time, denoted by a +"Version":: + + >>> import sys, pkg_resources + >>> from pkg_resources import Distribution + >>> Distribution(project_name="Foo", version="1.2") + Foo 1.2 + +Distributions have a location, which can be a filename, URL, or really anything +else you care to use:: + + >>> dist = Distribution( + ... location="http://example.com/something", + ... project_name="Bar", version="0.9" + ... ) + + >>> dist + Bar 0.9 (http://example.com/something) + + +Distributions have various introspectable attributes:: + + >>> dist.location + 'http://example.com/something' + + >>> dist.project_name + 'Bar' + + >>> dist.version + '0.9' + + >>> dist.py_version == sys.version[:3] + True + + >>> print dist.platform + None + +Including various computed attributes:: + + >>> from pkg_resources import parse_version + >>> dist.parsed_version == parse_version(dist.version) + True + + >>> dist.key # case-insensitive form of the project name + 'bar' + +Distributions are compared (and hashed) by version first:: + + >>> Distribution(version='1.0') == Distribution(version='1.0') + True + >>> Distribution(version='1.0') == Distribution(version='1.1') + False + >>> Distribution(version='1.0') < Distribution(version='1.1') + True + +but also by project name (case-insensitive), platform, Python version, +location, etc.:: + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="Foo",version="1.0") + True + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="foo",version="1.0") + True + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="Foo",version="1.1") + False + + >>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \ + ... Distribution(project_name="Foo",py_version="2.4",version="1.0") + False + + >>> Distribution(location="spam",version="1.0") == \ + ... Distribution(location="spam",version="1.0") + True + + >>> Distribution(location="spam",version="1.0") == \ + ... Distribution(location="baz",version="1.0") + False + + + +Hash and compare distribution by prio/plat + +Get version from metadata +provider capabilities +egg_name() +as_requirement() +from_location, from_filename (w/path normalization) + +Releases may have zero or more "Requirements", which indicate +what releases of another project the release requires in order to +function. A Requirement names the other project, expresses some criteria +as to what releases of that project are acceptable, and lists any "Extras" +that the requiring release may need from that project. (An Extra is an +optional feature of a Release, that can only be used if its additional +Requirements are satisfied.) + + + +The Working Set +--------------- + +A collection of active distributions is called a Working Set. Note that a +Working Set can contain any importable distribution, not just pluggable ones. +For example, the Python standard library is an importable distribution that +will usually be part of the Working Set, even though it is not pluggable. +Similarly, when you are doing development work on a project, the files you are +editing are also a Distribution. (And, with a little attention to the +directory names used, and including some additional metadata, such a +"development distribution" can be made pluggable as well.) + + >>> from pkg_resources import WorkingSet + +A working set's entries are the sys.path entries that correspond to the active +distributions. By default, the working set's entries are the items on +``sys.path``:: + + >>> ws = WorkingSet() + >>> ws.entries == sys.path + True + +But you can also create an empty working set explicitly, and add distributions +to it:: + + >>> ws = WorkingSet([]) + >>> ws.add(dist) + >>> ws.entries + ['http://example.com/something'] + >>> dist in ws + True + >>> Distribution('foo',version="") in ws + False + +And you can iterate over its distributions:: + + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +Adding the same distribution more than once is a no-op:: + + >>> ws.add(dist) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +For that matter, adding multiple distributions for the same project also does +nothing, because a working set can only hold one active distribution per +project -- the first one added to it:: + + >>> ws.add( + ... Distribution( + ... 'http://example.com/something', project_name="Bar", + ... version="7.2" + ... ) + ... ) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +You can append a path entry to a working set using ``add_entry()``:: + + >>> ws.entries + ['http://example.com/something'] + >>> ws.add_entry(pkg_resources.__file__) + >>> ws.entries + ['http://example.com/something', '...pkg_resources.py...'] + +Multiple additions result in multiple entries, even if the entry is already in +the working set (because ``sys.path`` can contain the same entry more than +once):: + + >>> ws.add_entry(pkg_resources.__file__) + >>> ws.entries + ['...example.com...', '...pkg_resources...', '...pkg_resources...'] + +And you can specify the path entry a distribution was found under, using the +optional second parameter to ``add()``:: + + >>> ws = WorkingSet([]) + >>> ws.add(dist,"foo") + >>> ws.entries + ['foo'] + +But even if a distribution is found under multiple path entries, it still only +shows up once when iterating the working set: + + >>> ws.add_entry(ws.entries[0]) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +You can ask a WorkingSet to ``find()`` a distribution matching a requirement:: + + >>> from pkg_resources import Requirement + >>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None + None + + >>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution + Bar 0.9 (http://example.com/something) + +Note that asking for a conflicting version of a distribution already in a +working set triggers a ``pkg_resources.VersionConflict`` error: + + >>> ws.find(Requirement.parse("Bar==1.0")) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + VersionConflict: (Bar 0.9 (http://example.com/something), + Requirement.parse('Bar==1.0')) + +You can subscribe a callback function to receive notifications whenever a new +distribution is added to a working set. The callback is immediately invoked +once for each existing distribution in the working set, and then is called +again for new distributions added thereafter:: + + >>> def added(dist): print "Added", dist + >>> ws.subscribe(added) + Added Bar 0.9 + >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12") + >>> ws.add(foo12) + Added Foo 1.2 + +Note, however, that only the first distribution added for a given project name +will trigger a callback, even during the initial ``subscribe()`` callback:: + + >>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14") + >>> ws.add(foo14) # no callback, because Foo 1.2 is already active + + >>> ws = WorkingSet([]) + >>> ws.add(foo12) + >>> ws.add(foo14) + >>> ws.subscribe(added) + Added Foo 1.2 + +And adding a callback more than once has no effect, either:: + + >>> ws.subscribe(added) # no callbacks + + # and no double-callbacks on subsequent additions, either + >>> just_a_test = Distribution(project_name="JustATest", version="0.99") + >>> ws.add(just_a_test) + Added JustATest 0.99 + + +Finding Plugins +--------------- + +``WorkingSet`` objects can be used to figure out what plugins in an +``Environment`` can be loaded without any resolution errors:: + + >>> from pkg_resources import Environment + + >>> plugins = Environment([]) # normally, a list of plugin directories + >>> plugins.add(foo12) + >>> plugins.add(foo14) + >>> plugins.add(just_a_test) + +In the simplest case, we just get the newest version of each distribution in +the plugin environment:: + + >>> ws = WorkingSet([]) + >>> ws.find_plugins(plugins) + ([JustATest 0.99, Foo 1.4 (f14)], {}) + +But if there's a problem with a version conflict or missing requirements, the +method falls back to older versions, and the error info dict will contain an +exception instance for each unloadable plugin:: + + >>> ws.add(foo12) # this will conflict with Foo 1.4 + >>> ws.find_plugins(plugins) + ([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)}) + +But if you disallow fallbacks, the failed plugin will be skipped instead of +trying older versions:: + + >>> ws.find_plugins(plugins, fallback=False) + ([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)}) + + + +Platform Compatibility Rules +---------------------------- + +On the Mac, there are potential compatibility issues for modules compiled +on newer versions of Mac OS X than what the user is running. Additionally, +Mac OS X will soon have two platforms to contend with: Intel and PowerPC. + +Basic equality works as on other platforms:: + + >>> from pkg_resources import compatible_platforms as cp + >>> reqd = 'macosx-10.4-ppc' + >>> cp(reqd, reqd) + True + >>> cp("win32", reqd) + False + +Distributions made on other machine types are not compatible:: + + >>> cp("macosx-10.4-i386", reqd) + False + +Distributions made on earlier versions of the OS are compatible, as +long as they are from the same top-level version. The patchlevel version +number does not matter:: + + >>> cp("macosx-10.4-ppc", reqd) + True + >>> cp("macosx-10.3-ppc", reqd) + True + >>> cp("macosx-10.5-ppc", reqd) + False + >>> cp("macosx-9.5-ppc", reqd) + False + +Backwards compatibility for packages made via earlier versions of +setuptools is provided as well:: + + >>> cp("darwin-8.2.0-Power_Macintosh", reqd) + True + >>> cp("darwin-7.2.0-Power_Macintosh", reqd) + True + >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc") + False + diff --git a/Lib/setuptools/tests/test_resources.py b/Lib/setuptools/tests/test_resources.py new file mode 100644 index 0000000..f32c72e --- /dev/null +++ b/Lib/setuptools/tests/test_resources.py @@ -0,0 +1,483 @@ +from unittest import TestCase, makeSuite +from pkg_resources import * +import pkg_resources, sys +from sets import ImmutableSet + +class Metadata(EmptyProvider): + """Mock object to return metadata as if from an on-disk distribution""" + + def __init__(self,*pairs): + self.metadata = dict(pairs) + + def has_metadata(self,name): + return name in self.metadata + + def get_metadata(self,name): + return self.metadata[name] + + def get_metadata_lines(self,name): + return yield_lines(self.get_metadata(name)) + + +class DistroTests(TestCase): + + def testCollection(self): + # empty path should produce no distributions + ad = Environment([], platform=None, python=None) + self.assertEqual(list(ad), []) + self.assertEqual(ad['FooPkg'],[]) + + ad.add(Distribution.from_filename("FooPkg-1.3_1.egg")) + ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")) + ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg")) + + # Name is in there now + self.failUnless(ad['FooPkg']) + + # But only 1 package + self.assertEqual(list(ad), ['foopkg']) + + + + # Distributions sort by version + self.assertEqual( + [dist.version for dist in ad['FooPkg']], ['1.4','1.3-1','1.2'] + ) + # Removing a distribution leaves sequence alone + ad.remove(ad['FooPkg'][1]) + self.assertEqual( + [dist.version for dist in ad['FooPkg']], ['1.4','1.2'] + ) + # And inserting adds them in order + ad.add(Distribution.from_filename("FooPkg-1.9.egg")) + self.assertEqual( + [dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2'] + ) + + ws = WorkingSet([]) + foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg") + foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg") + req, = parse_requirements("FooPkg>=1.3") + + # Nominal case: no distros on path, should yield all applicable + self.assertEqual(ad.best_match(req,ws).version, '1.9') + # If a matching distro is already installed, should return only that + ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4') + + # If the first matching distro is unsuitable, it's a version conflict + ws = WorkingSet([]); ws.add(foo12); ws.add(foo14) + self.assertRaises(VersionConflict, ad.best_match, req, ws) + + # If more than one match on the path, the first one takes precedence + ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14); + self.assertEqual(ad.best_match(req,ws).version, '1.4') + + def checkFooPkg(self,d): + self.assertEqual(d.project_name, "FooPkg") + self.assertEqual(d.key, "foopkg") + self.assertEqual(d.version, "1.3-1") + self.assertEqual(d.py_version, "2.4") + self.assertEqual(d.platform, "win32") + self.assertEqual(d.parsed_version, parse_version("1.3-1")) + + def testDistroBasics(self): + d = Distribution( + "/some/path", + project_name="FooPkg",version="1.3-1",py_version="2.4",platform="win32" + ) + self.checkFooPkg(d) + + d = Distribution("/some/path") + self.assertEqual(d.py_version, sys.version[:3]) + self.assertEqual(d.platform, None) + + def testDistroParse(self): + d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg") + self.checkFooPkg(d) + d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info") + self.checkFooPkg(d) + + def testDistroMetadata(self): + d = Distribution( + "/some/path", project_name="FooPkg", py_version="2.4", platform="win32", + metadata = Metadata( + ('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n") + ) + ) + self.checkFooPkg(d) + + + def distRequires(self, txt): + return Distribution("/foo", metadata=Metadata(('depends.txt', txt))) + + def checkRequires(self, dist, txt, extras=()): + self.assertEqual( + list(dist.requires(extras)), + list(parse_requirements(txt)) + ) + + def testDistroDependsSimple(self): + for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0": + self.checkRequires(self.distRequires(v), v) + + + def testResolve(self): + ad = Environment([]); ws = WorkingSet([]) + # Resolving no requirements -> nothing to install + self.assertEqual( list(ws.resolve([],ad)), [] ) + # Request something not in the collection -> DistributionNotFound + self.assertRaises( + DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad + ) + Foo = Distribution.from_filename( + "/foo_dir/Foo-1.2.egg", + metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0")) + ) + ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg")) + + # Request thing(s) that are available -> list to activate + for i in range(3): + targets = list(ws.resolve(parse_requirements("Foo"), ad)) + self.assertEqual(targets, [Foo]) + map(ws.add,targets) + self.assertRaises(VersionConflict, ws.resolve, + parse_requirements("Foo==0.9"), ad) + ws = WorkingSet([]) # reset + + # Request an extra that causes an unresolved dependency for "Baz" + self.assertRaises( + DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad + ) + Baz = Distribution.from_filename( + "/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo")) + ) + ad.add(Baz) + + # Activation list now includes resolved dependency + self.assertEqual( + list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz] + ) + # Requests for conflicting versions produce VersionConflict + self.assertRaises( VersionConflict, + ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad + ) + + def testDistroDependsOptions(self): + d = self.distRequires(""" + Twisted>=1.5 + [docgen] + ZConfig>=2.0 + docutils>=0.3 + [fastcgi] + fcgiapp>=0.1""") + self.checkRequires(d,"Twisted>=1.5") + self.checkRequires( + d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"] + ) + self.checkRequires( + d,"Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"] + ) + self.checkRequires( + d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(), + ["docgen","fastcgi"] + ) + self.checkRequires( + d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(), + ["fastcgi", "docgen"] + ) + self.assertRaises(UnknownExtra, d.requires, ["foo"]) + + + + + + + + + + + + + + + + + +class EntryPointTests(TestCase): + + def assertfields(self, ep): + self.assertEqual(ep.name,"foo") + self.assertEqual(ep.module_name,"setuptools.tests.test_resources") + self.assertEqual(ep.attrs, ("EntryPointTests",)) + self.assertEqual(ep.extras, ("x",)) + self.failUnless(ep.load() is EntryPointTests) + self.assertEqual( + str(ep), + "foo = setuptools.tests.test_resources:EntryPointTests [x]" + ) + + def setUp(self): + self.dist = Distribution.from_filename( + "FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]'))) + + def testBasics(self): + ep = EntryPoint( + "foo", "setuptools.tests.test_resources", ["EntryPointTests"], + ["x"], self.dist + ) + self.assertfields(ep) + + def testParse(self): + s = "foo = setuptools.tests.test_resources:EntryPointTests [x]" + ep = EntryPoint.parse(s, self.dist) + self.assertfields(ep) + + ep = EntryPoint.parse("bar baz= spammity[PING]") + self.assertEqual(ep.name,"bar baz") + self.assertEqual(ep.module_name,"spammity") + self.assertEqual(ep.attrs, ()) + self.assertEqual(ep.extras, ("ping",)) + + ep = EntryPoint.parse(" fizzly = wocka:foo") + self.assertEqual(ep.name,"fizzly") + self.assertEqual(ep.module_name,"wocka") + self.assertEqual(ep.attrs, ("foo",)) + self.assertEqual(ep.extras, ()) + + def testRejects(self): + for ep in [ + "foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2", + ]: + try: EntryPoint.parse(ep) + except ValueError: pass + else: raise AssertionError("Should've been bad", ep) + + def checkSubMap(self, m): + self.assertEqual(str(m), + "{" + "'feature2': EntryPoint.parse(" + "'feature2 = another.module:SomeClass [extra1,extra2]'), " + "'feature1': EntryPoint.parse(" + "'feature1 = somemodule:somefunction')" + "}" + ) + + submap_str = """ + # define features for blah blah + feature1 = somemodule:somefunction + feature2 = another.module:SomeClass [extra1,extra2] + """ + + def testParseList(self): + self.checkSubMap(EntryPoint.parse_group("xyz", self.submap_str)) + self.assertRaises(ValueError, EntryPoint.parse_group, "x a", "foo=bar") + self.assertRaises(ValueError, EntryPoint.parse_group, "x", + ["foo=baz", "foo=bar"]) + + def testParseMap(self): + m = EntryPoint.parse_map({'xyz':self.submap_str}) + self.checkSubMap(m['xyz']) + self.assertEqual(m.keys(),['xyz']) + m = EntryPoint.parse_map("[xyz]\n"+self.submap_str) + self.checkSubMap(m['xyz']) + self.assertEqual(m.keys(),['xyz']) + self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"]) + self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str) + + +class RequirementsTests(TestCase): + + def testBasics(self): + r = Requirement.parse("Twisted>=1.2") + self.assertEqual(str(r),"Twisted>=1.2") + self.assertEqual(repr(r),"Requirement.parse('Twisted>=1.2')") + self.assertEqual(r, Requirement("Twisted", [('>=','1.2')], ())) + self.assertEqual(r, Requirement("twisTed", [('>=','1.2')], ())) + self.assertNotEqual(r, Requirement("Twisted", [('>=','2.0')], ())) + self.assertNotEqual(r, Requirement("Zope", [('>=','1.2')], ())) + self.assertNotEqual(r, Requirement("Zope", [('>=','3.0')], ())) + self.assertNotEqual(r, Requirement.parse("Twisted[extras]>=1.2")) + + def testOrdering(self): + r1 = Requirement("Twisted", [('==','1.2c1'),('>=','1.2')], ()) + r2 = Requirement("Twisted", [('>=','1.2'),('==','1.2c1')], ()) + self.assertEqual(r1,r2) + self.assertEqual(str(r1),str(r2)) + self.assertEqual(str(r2),"Twisted==1.2c1,>=1.2") + + def testBasicContains(self): + r = Requirement("Twisted", [('>=','1.2')], ()) + foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg") + twist11 = Distribution.from_filename("Twisted-1.1.egg") + twist12 = Distribution.from_filename("Twisted-1.2.egg") + self.failUnless(parse_version('1.2') in r) + self.failUnless(parse_version('1.1') not in r) + self.failUnless('1.2' in r) + self.failUnless('1.1' not in r) + self.failUnless(foo_dist not in r) + self.failUnless(twist11 not in r) + self.failUnless(twist12 in r) + + def testAdvancedContains(self): + r, = parse_requirements("Foo>=1.2,<=1.3,==1.9,>2.0,!=2.5,<3.0,==4.5") + for v in ('1.2','1.2.2','1.3','1.9','2.0.1','2.3','2.6','3.0c1','4.5'): + self.failUnless(v in r, (v,r)) + for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'): + self.failUnless(v not in r, (v,r)) + + + def testOptionsAndHashing(self): + r1 = Requirement.parse("Twisted[foo,bar]>=1.2") + r2 = Requirement.parse("Twisted[bar,FOO]>=1.2") + r3 = Requirement.parse("Twisted[BAR,FOO]>=1.2.0") + self.assertEqual(r1,r2) + self.assertEqual(r1,r3) + self.assertEqual(r1.extras, ("foo","bar")) + self.assertEqual(r2.extras, ("bar","foo")) # extras are normalized + self.assertEqual(hash(r1), hash(r2)) + self.assertEqual( + hash(r1), hash(("twisted", ((">=",parse_version("1.2")),), + ImmutableSet(["foo","bar"]))) + ) + + def testVersionEquality(self): + r1 = Requirement.parse("setuptools==0.3a2") + r2 = Requirement.parse("setuptools!=0.3a4") + d = Distribution.from_filename + + self.failIf(d("setuptools-0.3a4.egg") in r1) + self.failIf(d("setuptools-0.3a1.egg") in r1) + self.failIf(d("setuptools-0.3a4.egg") in r2) + + self.failUnless(d("setuptools-0.3a2.egg") in r1) + self.failUnless(d("setuptools-0.3a2.egg") in r2) + self.failUnless(d("setuptools-0.3a3.egg") in r2) + self.failUnless(d("setuptools-0.3a5.egg") in r2) + + + + + + + + + + + + + + +class ParseTests(TestCase): + + def testEmptyParse(self): + self.assertEqual(list(parse_requirements('')), []) + + def testYielding(self): + for inp,out in [ + ([], []), ('x',['x']), ([[]],[]), (' x\n y', ['x','y']), + (['x\n\n','y'], ['x','y']), + ]: + self.assertEqual(list(pkg_resources.yield_lines(inp)),out) + + def testSplitting(self): + self.assertEqual( + list( + pkg_resources.split_sections(""" + x + [Y] + z + + a + [b ] + # foo + c + [ d] + [q] + v + """ + ) + ), + [(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])] + ) + self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo")) + + def testSafeName(self): + self.assertEqual(safe_name("adns-python"), "adns-python") + self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils") + self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils") + self.assertEqual(safe_name("Money$$$Maker"), "Money-Maker") + self.assertNotEqual(safe_name("peak.web"), "peak-web") + + def testSafeVersion(self): + self.assertEqual(safe_version("1.2-1"), "1.2-1") + self.assertEqual(safe_version("1.2 alpha"), "1.2.alpha") + self.assertEqual(safe_version("2.3.4 20050521"), "2.3.4.20050521") + self.assertEqual(safe_version("Money$$$Maker"), "Money-Maker") + self.assertEqual(safe_version("peak.web"), "peak.web") + + def testSimpleRequirements(self): + self.assertEqual( + list(parse_requirements('Twis-Ted>=1.2-1')), + [Requirement('Twis-Ted',[('>=','1.2-1')], ())] + ) + self.assertEqual( + list(parse_requirements('Twisted >=1.2, \ # more\n<2.0')), + [Requirement('Twisted',[('>=','1.2'),('<','2.0')], ())] + ) + self.assertEqual( + Requirement.parse("FooBar==1.99a3"), + Requirement("FooBar", [('==','1.99a3')], ()) + ) + self.assertRaises(ValueError,Requirement.parse,">=2.3") + self.assertRaises(ValueError,Requirement.parse,"x\\") + self.assertRaises(ValueError,Requirement.parse,"x==2 q") + self.assertRaises(ValueError,Requirement.parse,"X==1\nY==2") + self.assertRaises(ValueError,Requirement.parse,"#") + + def testVersionEquality(self): + def c(s1,s2): + p1, p2 = parse_version(s1),parse_version(s2) + self.assertEqual(p1,p2, (s1,s2,p1,p2)) + + c('1.2-rc1', '1.2rc1') + c('0.4', '0.4.0') + c('0.4.0.0', '0.4.0') + c('0.4.0-0', '0.4-0') + c('0pl1', '0.0pl1') + c('0pre1', '0.0c1') + c('0.0.0preview1', '0c1') + c('0.0c1', '0-rc1') + c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a') + + def testVersionOrdering(self): + def c(s1,s2): + p1, p2 = parse_version(s1),parse_version(s2) + self.failUnless(p1<p2, (s1,s2,p1,p2)) + + c('2.1','2.1.1') + c('2a1','2b0') + c('2a1','2.1') + c('2.3a1', '2.3') + c('2.1-1', '2.1-2') + c('2.1-1', '2.1.1') + c('2.1', '2.1pl4') + c('2.1a0-20040501', '2.1') + c('1.1', '02.1') + c('A56','B27') + c('3.2', '3.2.pl0') + c('3.2-1', '3.2pl1') + c('3.2pl1', '3.2pl1-1') + c('0.4', '4.0') + c('0.0.4', '0.4.0') + c('0pl1', '0.4pl1') + c('2.1.0-rc1','2.1.0') + + torture =""" + 0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1 + 0.79.9999+0.80.0pre2-3 0.79.9999+0.80.0pre2-2 + 0.77.2-1 0.77.1-1 0.77.0-1 + """.split() + + for p,v1 in enumerate(torture): + for v2 in torture[p+1:]: + c(v2,v1) diff --git a/Lib/sgmllib.py b/Lib/sgmllib.py index 08e365b..3e85a91 100644 --- a/Lib/sgmllib.py +++ b/Lib/sgmllib.py @@ -269,9 +269,37 @@ class SGMLParser(markupbase.ParserBase): attrname, rest, attrvalue = match.group(1, 2, 3) if not rest: attrvalue = attrname - elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ - attrvalue[:1] == '"' == attrvalue[-1:]: - attrvalue = attrvalue[1:-1] + else: + if (attrvalue[:1] == "'" == attrvalue[-1:] or + attrvalue[:1] == '"' == attrvalue[-1:]): + # strip quotes + attrvalue = attrvalue[1:-1] + l = 0 + new_attrvalue = '' + while l < len(attrvalue): + av_match = entityref.match(attrvalue, l) + if (av_match and av_match.group(1) in self.entitydefs and + attrvalue[av_match.end(1)] == ';'): + # only substitute entityrefs ending in ';' since + # otherwise we may break <a href='?p=x&q=y'> + # which is very common + new_attrvalue += self.entitydefs[av_match.group(1)] + l = av_match.end(0) + continue + ch_match = charref.match(attrvalue, l) + if ch_match: + try: + char = chr(int(ch_match.group(1))) + new_attrvalue += char + l = ch_match.end(0) + continue + except ValueError: + # invalid character reference, don't substitute + pass + # all other cases + new_attrvalue += attrvalue[l] + l += 1 + attrvalue = new_attrvalue attrs.append((attrname.lower(), attrvalue)) k = match.end(0) if rawdata[j] == '>': diff --git a/Lib/site.py b/Lib/site.py index 5e7ff7b..47eda24 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -69,6 +69,8 @@ def makepath(*paths): def abs__file__(): """Set all module' __file__ attribute to an absolute path""" for m in sys.modules.values(): + if hasattr(m, '__loader__'): + continue # don't mess with a PEP 302-supplied __file__ try: m.__file__ = os.path.abspath(m.__file__) except AttributeError: diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 71d25fd..07916cc 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -255,7 +255,11 @@ class SMTP: self.local_hostname = fqdn else: # We can't find an fqdn hostname, so use a domain literal - addr = socket.gethostbyname(socket.gethostname()) + addr = '127.0.0.1' + try: + addr = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + pass self.local_hostname = '[%s]' % addr def set_debuglevel(self, debuglevel): diff --git a/Lib/socket.py b/Lib/socket.py index ee2457f..32a92b4 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -121,14 +121,6 @@ def getfqdn(name=''): return name -# -# These classes are used by the socket() defined on Windows and BeOS -# platforms to provide a best-effort implementation of the cleanup -# semantics needed when sockets can't be dup()ed. -# -# These are not actually used on other platforms. -# - _socketmethods = ( 'bind', 'connect', 'connect_ex', 'fileno', 'listen', 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', @@ -183,6 +175,10 @@ class _socketobject(object): and bufsize arguments are as for the built-in open() function.""" return _fileobject(self._sock, mode, bufsize) + family = property(lambda self: self._sock.family, doc="the socket family") + type = property(lambda self: self._sock.type, doc="the socket type") + proto = property(lambda self: self._sock.proto, doc="the socket protocol") + _s = ("def %s(self, *args): return self._sock.%s(*args)\n\n" "%s.__doc__ = _realsocket.%s.__doc__\n") for _m in _socketmethods: diff --git a/Lib/sqlite3/__init__.py b/Lib/sqlite3/__init__.py new file mode 100644 index 0000000..41ef2b7 --- /dev/null +++ b/Lib/sqlite3/__init__.py @@ -0,0 +1,24 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/__init__.py: the pysqlite2 package. +# +# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +from dbapi2 import * diff --git a/Lib/sqlite3/dbapi2.py b/Lib/sqlite3/dbapi2.py new file mode 100644 index 0000000..e0c8a84 --- /dev/null +++ b/Lib/sqlite3/dbapi2.py @@ -0,0 +1,84 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/dbapi2.py: the DB-API 2.0 interface +# +# Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import datetime + +paramstyle = "qmark" + +threadsafety = 1 + +apilevel = "2.0" + +from _sqlite3 import * + +import datetime, time + +Date = datetime.date + +Time = datetime.time + +Timestamp = datetime.datetime + +def DateFromTicks(ticks): + return apply(Date,time.localtime(ticks)[:3]) + +def TimeFromTicks(ticks): + return apply(Time,time.localtime(ticks)[3:6]) + +def TimestampFromTicks(ticks): + return apply(Timestamp,time.localtime(ticks)[:6]) + +_major, _minor, _micro = version.split(".") +version_info = (int(_major), int(_minor), _micro) +_major, _minor, _micro = sqlite_version.split(".") +sqlite_version_info = (int(_major), int(_minor), _micro) + +Binary = buffer + +def adapt_date(val): + return val.isoformat() + +def adapt_datetime(val): + return val.isoformat(" ") + +def convert_date(val): + return datetime.date(*map(int, val.split("-"))) + +def convert_timestamp(val): + datepart, timepart = val.split(" ") + year, month, day = map(int, datepart.split("-")) + timepart_full = timepart.split(".") + hours, minutes, seconds = map(int, timepart_full[0].split(":")) + if len(timepart_full) == 2: + microseconds = int(float("0." + timepart_full[1]) * 1000000) + else: + microseconds = 0 + + val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds) + return val + + +register_adapter(datetime.date, adapt_date) +register_adapter(datetime.datetime, adapt_datetime) +register_converter("date", convert_date) +register_converter("timestamp", convert_timestamp) diff --git a/Lib/sqlite3/test/__init__.py b/Lib/sqlite3/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py new file mode 100644 index 0000000..b08da9c --- /dev/null +++ b/Lib/sqlite3/test/dbapi.py @@ -0,0 +1,732 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/dbapi.py: tests for DB-API compliance +# +# Copyright (C) 2004-2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import unittest +import threading +import sqlite3 as sqlite + +class ModuleTests(unittest.TestCase): + def CheckAPILevel(self): + self.assertEqual(sqlite.apilevel, "2.0", + "apilevel is %s, should be 2.0" % sqlite.apilevel) + + def CheckThreadSafety(self): + self.assertEqual(sqlite.threadsafety, 1, + "threadsafety is %d, should be 1" % sqlite.threadsafety) + + def CheckParamStyle(self): + self.assertEqual(sqlite.paramstyle, "qmark", + "paramstyle is '%s', should be 'qmark'" % + sqlite.paramstyle) + + def CheckWarning(self): + self.assert_(issubclass(sqlite.Warning, StandardError), + "Warning is not a subclass of StandardError") + + def CheckError(self): + self.failUnless(issubclass(sqlite.Error, StandardError), + "Error is not a subclass of StandardError") + + def CheckInterfaceError(self): + self.failUnless(issubclass(sqlite.InterfaceError, sqlite.Error), + "InterfaceError is not a subclass of Error") + + def CheckDatabaseError(self): + self.failUnless(issubclass(sqlite.DatabaseError, sqlite.Error), + "DatabaseError is not a subclass of Error") + + def CheckDataError(self): + self.failUnless(issubclass(sqlite.DataError, sqlite.DatabaseError), + "DataError is not a subclass of DatabaseError") + + def CheckOperationalError(self): + self.failUnless(issubclass(sqlite.OperationalError, sqlite.DatabaseError), + "OperationalError is not a subclass of DatabaseError") + + def CheckIntegrityError(self): + self.failUnless(issubclass(sqlite.IntegrityError, sqlite.DatabaseError), + "IntegrityError is not a subclass of DatabaseError") + + def CheckInternalError(self): + self.failUnless(issubclass(sqlite.InternalError, sqlite.DatabaseError), + "InternalError is not a subclass of DatabaseError") + + def CheckProgrammingError(self): + self.failUnless(issubclass(sqlite.ProgrammingError, sqlite.DatabaseError), + "ProgrammingError is not a subclass of DatabaseError") + + def CheckNotSupportedError(self): + self.failUnless(issubclass(sqlite.NotSupportedError, + sqlite.DatabaseError), + "NotSupportedError is not a subclass of DatabaseError") + +class ConnectionTests(unittest.TestCase): + def setUp(self): + self.cx = sqlite.connect(":memory:") + cu = self.cx.cursor() + cu.execute("create table test(id integer primary key, name text)") + cu.execute("insert into test(name) values (?)", ("foo",)) + + def tearDown(self): + self.cx.close() + + def CheckCommit(self): + self.cx.commit() + + def CheckCommitAfterNoChanges(self): + """ + A commit should also work when no changes were made to the database. + """ + self.cx.commit() + self.cx.commit() + + def CheckRollback(self): + self.cx.rollback() + + def CheckRollbackAfterNoChanges(self): + """ + A rollback should also work when no changes were made to the database. + """ + self.cx.rollback() + self.cx.rollback() + + def CheckCursor(self): + cu = self.cx.cursor() + + def CheckFailedOpen(self): + YOU_CANNOT_OPEN_THIS = "/foo/bar/bla/23534/mydb.db" + try: + con = sqlite.connect(YOU_CANNOT_OPEN_THIS) + except sqlite.OperationalError: + return + self.fail("should have raised an OperationalError") + + def CheckClose(self): + self.cx.close() + + def CheckExceptions(self): + # Optional DB-API extension. + self.failUnlessEqual(self.cx.Warning, sqlite.Warning) + self.failUnlessEqual(self.cx.Error, sqlite.Error) + self.failUnlessEqual(self.cx.InterfaceError, sqlite.InterfaceError) + self.failUnlessEqual(self.cx.DatabaseError, sqlite.DatabaseError) + self.failUnlessEqual(self.cx.DataError, sqlite.DataError) + self.failUnlessEqual(self.cx.OperationalError, sqlite.OperationalError) + self.failUnlessEqual(self.cx.IntegrityError, sqlite.IntegrityError) + self.failUnlessEqual(self.cx.InternalError, sqlite.InternalError) + self.failUnlessEqual(self.cx.ProgrammingError, sqlite.ProgrammingError) + self.failUnlessEqual(self.cx.NotSupportedError, sqlite.NotSupportedError) + +class CursorTests(unittest.TestCase): + def setUp(self): + self.cx = sqlite.connect(":memory:") + self.cu = self.cx.cursor() + self.cu.execute("create table test(id integer primary key, name text, income number)") + self.cu.execute("insert into test(name) values (?)", ("foo",)) + + def tearDown(self): + self.cu.close() + self.cx.close() + + def CheckExecuteNoArgs(self): + self.cu.execute("delete from test") + + def CheckExecuteIllegalSql(self): + try: + self.cu.execute("select asdf") + self.fail("should have raised an OperationalError") + except sqlite.OperationalError: + return + except: + self.fail("raised wrong exception") + + def CheckExecuteTooMuchSql(self): + try: + self.cu.execute("select 5+4; select 4+5") + self.fail("should have raised a Warning") + except sqlite.Warning: + return + except: + self.fail("raised wrong exception") + + def CheckExecuteTooMuchSql2(self): + self.cu.execute("select 5+4; -- foo bar") + + def CheckExecuteTooMuchSql3(self): + self.cu.execute(""" + select 5+4; + + /* + foo + */ + """) + + def CheckExecuteWrongSqlArg(self): + try: + self.cu.execute(42) + self.fail("should have raised a ValueError") + except ValueError: + return + except: + self.fail("raised wrong exception.") + + def CheckExecuteArgInt(self): + self.cu.execute("insert into test(id) values (?)", (42,)) + + def CheckExecuteArgFloat(self): + self.cu.execute("insert into test(income) values (?)", (2500.32,)) + + def CheckExecuteArgString(self): + self.cu.execute("insert into test(name) values (?)", ("Hugo",)) + + def CheckExecuteWrongNoOfArgs1(self): + # too many parameters + try: + self.cu.execute("insert into test(id) values (?)", (17, "Egon")) + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckExecuteWrongNoOfArgs2(self): + # too little parameters + try: + self.cu.execute("insert into test(id) values (?)") + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckExecuteWrongNoOfArgs3(self): + # no parameters, parameters are needed + try: + self.cu.execute("insert into test(id) values (?)") + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckExecuteDictMapping(self): + self.cu.execute("insert into test(name) values ('foo')") + self.cu.execute("select name from test where name=:name", {"name": "foo"}) + row = self.cu.fetchone() + self.failUnlessEqual(row[0], "foo") + + def CheckExecuteDictMappingTooLittleArgs(self): + self.cu.execute("insert into test(name) values ('foo')") + try: + self.cu.execute("select name from test where name=:name and id=:id", {"name": "foo"}) + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckExecuteDictMappingNoArgs(self): + self.cu.execute("insert into test(name) values ('foo')") + try: + self.cu.execute("select name from test where name=:name") + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckExecuteDictMappingUnnamed(self): + self.cu.execute("insert into test(name) values ('foo')") + try: + self.cu.execute("select name from test where name=?", {"name": "foo"}) + self.fail("should have raised ProgrammingError") + except sqlite.ProgrammingError: + pass + + def CheckClose(self): + self.cu.close() + + def CheckRowcountExecute(self): + self.cu.execute("delete from test") + self.cu.execute("insert into test(name) values ('foo')") + self.cu.execute("insert into test(name) values ('foo')") + self.cu.execute("update test set name='bar'") + self.failUnlessEqual(self.cu.rowcount, 2) + + def CheckRowcountExecutemany(self): + self.cu.execute("delete from test") + self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)]) + self.failUnlessEqual(self.cu.rowcount, 3) + + def CheckTotalChanges(self): + self.cu.execute("insert into test(name) values ('foo')") + self.cu.execute("insert into test(name) values ('foo')") + if self.cx.total_changes < 2: + self.fail("total changes reported wrong value") + + # Checks for executemany: + # Sequences are required by the DB-API, iterators + # enhancements in pysqlite. + + def CheckExecuteManySequence(self): + self.cu.executemany("insert into test(income) values (?)", [(x,) for x in range(100, 110)]) + + def CheckExecuteManyIterator(self): + class MyIter: + def __init__(self): + self.value = 5 + + def next(self): + if self.value == 10: + raise StopIteration + else: + self.value += 1 + return (self.value,) + + self.cu.executemany("insert into test(income) values (?)", MyIter()) + + def CheckExecuteManyGenerator(self): + def mygen(): + for i in range(5): + yield (i,) + + self.cu.executemany("insert into test(income) values (?)", mygen()) + + def CheckExecuteManyWrongSqlArg(self): + try: + self.cu.executemany(42, [(3,)]) + self.fail("should have raised a ValueError") + except ValueError: + return + except: + self.fail("raised wrong exception.") + + def CheckExecuteManySelect(self): + try: + self.cu.executemany("select ?", [(3,)]) + self.fail("should have raised a ProgrammingError") + except sqlite.ProgrammingError: + return + except: + self.fail("raised wrong exception.") + + def CheckExecuteManyNotIterable(self): + try: + self.cu.executemany("insert into test(income) values (?)", 42) + self.fail("should have raised a TypeError") + except TypeError: + return + except Exception, e: + print "raised", e.__class__ + self.fail("raised wrong exception.") + + def CheckFetchIter(self): + # Optional DB-API extension. + self.cu.execute("delete from test") + self.cu.execute("insert into test(id) values (?)", (5,)) + self.cu.execute("insert into test(id) values (?)", (6,)) + self.cu.execute("select id from test order by id") + lst = [] + for row in self.cu: + lst.append(row[0]) + self.failUnlessEqual(lst[0], 5) + self.failUnlessEqual(lst[1], 6) + + def CheckFetchone(self): + self.cu.execute("select name from test") + row = self.cu.fetchone() + self.failUnlessEqual(row[0], "foo") + row = self.cu.fetchone() + self.failUnlessEqual(row, None) + + def CheckFetchoneNoStatement(self): + cur = self.cx.cursor() + row = cur.fetchone() + self.failUnlessEqual(row, None) + + def CheckArraySize(self): + # must default ot 1 + self.failUnlessEqual(self.cu.arraysize, 1) + + # now set to 2 + self.cu.arraysize = 2 + + # now make the query return 3 rows + self.cu.execute("delete from test") + self.cu.execute("insert into test(name) values ('A')") + self.cu.execute("insert into test(name) values ('B')") + self.cu.execute("insert into test(name) values ('C')") + self.cu.execute("select name from test") + res = self.cu.fetchmany() + + self.failUnlessEqual(len(res), 2) + + def CheckFetchmany(self): + self.cu.execute("select name from test") + res = self.cu.fetchmany(100) + self.failUnlessEqual(len(res), 1) + res = self.cu.fetchmany(100) + self.failUnlessEqual(res, []) + + def CheckFetchall(self): + self.cu.execute("select name from test") + res = self.cu.fetchall() + self.failUnlessEqual(len(res), 1) + res = self.cu.fetchall() + self.failUnlessEqual(res, []) + + def CheckSetinputsizes(self): + self.cu.setinputsizes([3, 4, 5]) + + def CheckSetoutputsize(self): + self.cu.setoutputsize(5, 0) + + def CheckSetoutputsizeNoColumn(self): + self.cu.setoutputsize(42) + + def CheckCursorConnection(self): + # Optional DB-API extension. + self.failUnlessEqual(self.cu.connection, self.cx) + + def CheckWrongCursorCallable(self): + try: + def f(): pass + cur = self.cx.cursor(f) + self.fail("should have raised a TypeError") + except TypeError: + return + self.fail("should have raised a ValueError") + + def CheckCursorWrongClass(self): + class Foo: pass + foo = Foo() + try: + cur = sqlite.Cursor(foo) + self.fail("should have raised a ValueError") + except TypeError: + pass + +class ThreadTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + self.cur = self.con.cursor() + self.cur.execute("create table test(id integer primary key, name text, bin binary, ratio number, ts timestamp)") + + def tearDown(self): + self.cur.close() + self.con.close() + + def CheckConCursor(self): + def run(con, errors): + try: + cur = con.cursor() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckConCommit(self): + def run(con, errors): + try: + con.commit() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckConRollback(self): + def run(con, errors): + try: + con.rollback() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckConClose(self): + def run(con, errors): + try: + con.close() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"con": self.con, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckCurImplicitBegin(self): + def run(cur, errors): + try: + cur.execute("insert into test(name) values ('a')") + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckCurClose(self): + def run(cur, errors): + try: + cur.close() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckCurExecute(self): + def run(cur, errors): + try: + cur.execute("select name from test") + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + self.cur.execute("insert into test(name) values ('a')") + t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + + def CheckCurIterNext(self): + def run(cur, errors): + try: + row = cur.fetchone() + errors.append("did not raise ProgrammingError") + return + except sqlite.ProgrammingError: + return + except: + errors.append("raised wrong exception") + + errors = [] + self.cur.execute("insert into test(name) values ('a')") + self.cur.execute("select name from test") + t = threading.Thread(target=run, kwargs={"cur": self.cur, "errors": errors}) + t.start() + t.join() + if len(errors) > 0: + self.fail("\n".join(errors)) + +class ConstructorTests(unittest.TestCase): + def CheckDate(self): + d = sqlite.Date(2004, 10, 28) + + def CheckTime(self): + t = sqlite.Time(12, 39, 35) + + def CheckTimestamp(self): + ts = sqlite.Timestamp(2004, 10, 28, 12, 39, 35) + + def CheckDateFromTicks(self): + d = sqlite.DateFromTicks(42) + + def CheckTimeFromTicks(self): + t = sqlite.TimeFromTicks(42) + + def CheckTimestampFromTicks(self): + ts = sqlite.TimestampFromTicks(42) + + def CheckBinary(self): + b = sqlite.Binary(chr(0) + "'") + +class ExtensionTests(unittest.TestCase): + def CheckScriptStringSql(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + cur.executescript(""" + -- bla bla + /* a stupid comment */ + create table a(i); + insert into a(i) values (5); + """) + cur.execute("select i from a") + res = cur.fetchone()[0] + self.failUnlessEqual(res, 5) + + def CheckScriptStringUnicode(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + cur.executescript(u""" + create table a(i); + insert into a(i) values (5); + select i from a; + delete from a; + insert into a(i) values (6); + """) + cur.execute("select i from a") + res = cur.fetchone()[0] + self.failUnlessEqual(res, 6) + + def CheckScriptErrorIncomplete(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + raised = False + try: + cur.executescript("create table test(sadfsadfdsa") + except sqlite.ProgrammingError: + raised = True + self.failUnlessEqual(raised, True, "should have raised an exception") + + def CheckScriptErrorNormal(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + raised = False + try: + cur.executescript("create table test(sadfsadfdsa); select foo from hurz;") + except sqlite.OperationalError: + raised = True + self.failUnlessEqual(raised, True, "should have raised an exception") + + def CheckConnectionExecute(self): + con = sqlite.connect(":memory:") + result = con.execute("select 5").fetchone()[0] + self.failUnlessEqual(result, 5, "Basic test of Connection.execute") + + def CheckConnectionExecutemany(self): + con = sqlite.connect(":memory:") + con.execute("create table test(foo)") + con.executemany("insert into test(foo) values (?)", [(3,), (4,)]) + result = con.execute("select foo from test order by foo").fetchall() + self.failUnlessEqual(result[0][0], 3, "Basic test of Connection.executemany") + self.failUnlessEqual(result[1][0], 4, "Basic test of Connection.executemany") + + def CheckConnectionExecutescript(self): + con = sqlite.connect(":memory:") + con.executescript("create table test(foo); insert into test(foo) values (5);") + result = con.execute("select foo from test").fetchone()[0] + self.failUnlessEqual(result, 5, "Basic test of Connection.executescript") + +class ClosedTests(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def CheckClosedConCursor(self): + con = sqlite.connect(":memory:") + con.close() + try: + cur = con.cursor() + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedConCommit(self): + con = sqlite.connect(":memory:") + con.close() + try: + con.commit() + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedConRollback(self): + con = sqlite.connect(":memory:") + con.close() + try: + con.rollback() + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + + def CheckClosedCurExecute(self): + con = sqlite.connect(":memory:") + cur = con.cursor() + con.close() + try: + cur.execute("select 4") + self.fail("Should have raised a ProgrammingError") + except sqlite.ProgrammingError: + pass + except: + self.fail("Should have raised a ProgrammingError") + +def suite(): + module_suite = unittest.makeSuite(ModuleTests, "Check") + connection_suite = unittest.makeSuite(ConnectionTests, "Check") + cursor_suite = unittest.makeSuite(CursorTests, "Check") + thread_suite = unittest.makeSuite(ThreadTests, "Check") + constructor_suite = unittest.makeSuite(ConstructorTests, "Check") + ext_suite = unittest.makeSuite(ExtensionTests, "Check") + closed_suite = unittest.makeSuite(ClosedTests, "Check") + return unittest.TestSuite((module_suite, connection_suite, cursor_suite, thread_suite, constructor_suite, ext_suite, closed_suite)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py new file mode 100644 index 0000000..8778056 --- /dev/null +++ b/Lib/sqlite3/test/factory.py @@ -0,0 +1,164 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/factory.py: tests for the various factories in pysqlite +# +# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import unittest +import sqlite3 as sqlite + +class MyConnection(sqlite.Connection): + def __init__(self, *args, **kwargs): + sqlite.Connection.__init__(self, *args, **kwargs) + +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + +class MyCursor(sqlite.Cursor): + def __init__(self, *args, **kwargs): + sqlite.Cursor.__init__(self, *args, **kwargs) + self.row_factory = dict_factory + +class ConnectionFactoryTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:", factory=MyConnection) + + def tearDown(self): + self.con.close() + + def CheckIsInstance(self): + self.failUnless(isinstance(self.con, + MyConnection), + "connection is not instance of MyConnection") + +class CursorFactoryTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def tearDown(self): + self.con.close() + + def CheckIsInstance(self): + cur = self.con.cursor(factory=MyCursor) + self.failUnless(isinstance(cur, + MyCursor), + "cursor is not instance of MyCursor") + +class RowFactoryTestsBackwardsCompat(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def CheckIsProducedByFactory(self): + cur = self.con.cursor(factory=MyCursor) + cur.execute("select 4+5 as foo") + row = cur.fetchone() + self.failUnless(isinstance(row, + dict), + "row is not instance of dict") + cur.close() + + def tearDown(self): + self.con.close() + +class RowFactoryTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def CheckCustomFactory(self): + self.con.row_factory = lambda cur, row: list(row) + row = self.con.execute("select 1, 2").fetchone() + self.failUnless(isinstance(row, + list), + "row is not instance of list") + + def CheckSqliteRow(self): + self.con.row_factory = sqlite.Row + row = self.con.execute("select 1 as a, 2 as b").fetchone() + self.failUnless(isinstance(row, + sqlite.Row), + "row is not instance of sqlite.Row") + + col1, col2 = row["a"], row["b"] + self.failUnless(col1 == 1, "by name: wrong result for column 'a'") + self.failUnless(col2 == 2, "by name: wrong result for column 'a'") + + col1, col2 = row["A"], row["B"] + self.failUnless(col1 == 1, "by name: wrong result for column 'A'") + self.failUnless(col2 == 2, "by name: wrong result for column 'B'") + + col1, col2 = row[0], row[1] + self.failUnless(col1 == 1, "by index: wrong result for column 0") + self.failUnless(col2 == 2, "by index: wrong result for column 1") + + def tearDown(self): + self.con.close() + +class TextFactoryTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def CheckUnicode(self): + austria = unicode("Österreich", "latin1") + row = self.con.execute("select ?", (austria,)).fetchone() + self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode") + + def CheckString(self): + self.con.text_factory = str + austria = unicode("Österreich", "latin1") + row = self.con.execute("select ?", (austria,)).fetchone() + self.failUnless(type(row[0]) == str, "type of row[0] must be str") + self.failUnless(row[0] == austria.encode("utf-8"), "column must equal original data in UTF-8") + + def CheckCustom(self): + self.con.text_factory = lambda x: unicode(x, "utf-8", "ignore") + austria = unicode("Österreich", "latin1") + row = self.con.execute("select ?", (austria.encode("latin1"),)).fetchone() + self.failUnless(type(row[0]) == unicode, "type of row[0] must be unicode") + self.failUnless(row[0].endswith(u"reich"), "column must contain original data") + + def CheckOptimizedUnicode(self): + self.con.text_factory = sqlite.OptimizedUnicode + austria = unicode("Österreich", "latin1") + germany = unicode("Deutchland") + a_row = self.con.execute("select ?", (austria,)).fetchone() + d_row = self.con.execute("select ?", (germany,)).fetchone() + self.failUnless(type(a_row[0]) == unicode, "type of non-ASCII row must be unicode") + self.failUnless(type(d_row[0]) == str, "type of ASCII-only row must be str") + + def tearDown(self): + self.con.close() + +def suite(): + connection_suite = unittest.makeSuite(ConnectionFactoryTests, "Check") + cursor_suite = unittest.makeSuite(CursorFactoryTests, "Check") + row_suite_compat = unittest.makeSuite(RowFactoryTestsBackwardsCompat, "Check") + row_suite = unittest.makeSuite(RowFactoryTests, "Check") + text_suite = unittest.makeSuite(TextFactoryTests, "Check") + return unittest.TestSuite((connection_suite, cursor_suite, row_suite_compat, row_suite, text_suite)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py new file mode 100644 index 0000000..21f7b88 --- /dev/null +++ b/Lib/sqlite3/test/hooks.py @@ -0,0 +1,115 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/hooks.py: tests for various SQLite-specific hooks +# +# Copyright (C) 2006 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import os, unittest +import pysqlite2.dbapi2 as sqlite + +class CollationTests(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def CheckCreateCollationNotCallable(self): + con = sqlite.connect(":memory:") + try: + con.create_collation("X", 42) + self.fail("should have raised a TypeError") + except TypeError, e: + self.failUnlessEqual(e.args[0], "parameter must be callable") + + def CheckCreateCollationNotAscii(self): + con = sqlite.connect(":memory:") + try: + con.create_collation("collä", cmp) + self.fail("should have raised a ProgrammingError") + except sqlite.ProgrammingError, e: + pass + + def CheckCollationIsUsed(self): + def mycoll(x, y): + # reverse order + return -cmp(x, y) + + con = sqlite.connect(":memory:") + con.create_collation("mycoll", mycoll) + sql = """ + select x from ( + select 'a' as x + union + select 'b' as x + union + select 'c' as x + ) order by x collate mycoll + """ + result = con.execute(sql).fetchall() + if result[0][0] != "c" or result[1][0] != "b" or result[2][0] != "a": + self.fail("the expected order was not returned") + + con.create_collation("mycoll", None) + try: + result = con.execute(sql).fetchall() + self.fail("should have raised an OperationalError") + except sqlite.OperationalError, e: + self.failUnlessEqual(e.args[0], "no such collation sequence: mycoll") + + def CheckCollationRegisterTwice(self): + """ + Register two different collation functions under the same name. + Verify that the last one is actually used. + """ + con = sqlite.connect(":memory:") + con.create_collation("mycoll", cmp) + con.create_collation("mycoll", lambda x, y: -cmp(x, y)) + result = con.execute(""" + select x from (select 'a' as x union select 'b' as x) order by x collate mycoll + """).fetchall() + if result[0][0] != 'b' or result[1][0] != 'a': + self.fail("wrong collation function is used") + + def CheckDeregisterCollation(self): + """ + Register a collation, then deregister it. Make sure an error is raised if we try + to use it. + """ + con = sqlite.connect(":memory:") + con.create_collation("mycoll", cmp) + con.create_collation("mycoll", None) + try: + con.execute("select 'a' as x union select 'b' as x order by x collate mycoll") + self.fail("should have raised an OperationalError") + except sqlite.OperationalError, e: + if not e.args[0].startswith("no such collation sequence"): + self.fail("wrong OperationalError raised") + +def suite(): + collation_suite = unittest.makeSuite(CollationTests, "Check") + return unittest.TestSuite((collation_suite,)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py new file mode 100644 index 0000000..648ada5 --- /dev/null +++ b/Lib/sqlite3/test/regression.py @@ -0,0 +1,48 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/regression.py: pysqlite regression tests +# +# Copyright (C) 2006 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import unittest +import pysqlite2.dbapi2 as sqlite + +class RegressionTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def tearDown(self): + self.con.close() + + def CheckPragmaUserVersion(self): + # This used to crash pysqlite because this pragma command returns NULL for the column name + cur = self.con.cursor() + cur.execute("pragma user_version") + +def suite(): + regression_suite = unittest.makeSuite(RegressionTests, "Check") + return unittest.TestSuite((regression_suite,)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py new file mode 100644 index 0000000..1f0b19a --- /dev/null +++ b/Lib/sqlite3/test/transactions.py @@ -0,0 +1,156 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/transactions.py: tests transactions +# +# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import os, unittest +import sqlite3 as sqlite + +def get_db_path(): + return "sqlite_testdb" + +class TransactionTests(unittest.TestCase): + def setUp(self): + try: + os.remove(get_db_path()) + except: + pass + + self.con1 = sqlite.connect(get_db_path(), timeout=0.1) + self.cur1 = self.con1.cursor() + + self.con2 = sqlite.connect(get_db_path(), timeout=0.1) + self.cur2 = self.con2.cursor() + + def tearDown(self): + self.cur1.close() + self.con1.close() + + self.cur2.close() + self.con2.close() + + os.unlink(get_db_path()) + + def CheckDMLdoesAutoCommitBefore(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.cur1.execute("create table test2(j)") + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 1) + + def CheckInsertStartsTransaction(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 0) + + def CheckUpdateStartsTransaction(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.con1.commit() + self.cur1.execute("update test set i=6") + self.cur2.execute("select i from test") + res = self.cur2.fetchone()[0] + self.failUnlessEqual(res, 5) + + def CheckDeleteStartsTransaction(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.con1.commit() + self.cur1.execute("delete from test") + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 1) + + def CheckReplaceStartsTransaction(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.con1.commit() + self.cur1.execute("replace into test(i) values (6)") + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 1) + self.failUnlessEqual(res[0][0], 5) + + def CheckToggleAutoCommit(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + self.con1.isolation_level = None + self.failUnlessEqual(self.con1.isolation_level, None) + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 1) + + self.con1.isolation_level = "DEFERRED" + self.failUnlessEqual(self.con1.isolation_level , "DEFERRED") + self.cur1.execute("insert into test(i) values (5)") + self.cur2.execute("select i from test") + res = self.cur2.fetchall() + self.failUnlessEqual(len(res), 1) + + def CheckRaiseTimeout(self): + self.cur1.execute("create table test(i)") + self.cur1.execute("insert into test(i) values (5)") + try: + self.cur2.execute("insert into test(i) values (5)") + self.fail("should have raised an OperationalError") + except sqlite.OperationalError: + pass + except: + self.fail("should have raised an OperationalError") + +class SpecialCommandTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + self.cur = self.con.cursor() + + def CheckVacuum(self): + self.cur.execute("create table test(i)") + self.cur.execute("insert into test(i) values (5)") + self.cur.execute("vacuum") + + def CheckDropTable(self): + self.cur.execute("create table test(i)") + self.cur.execute("insert into test(i) values (5)") + self.cur.execute("drop table test") + + def CheckPragma(self): + self.cur.execute("create table test(i)") + self.cur.execute("insert into test(i) values (5)") + self.cur.execute("pragma count_changes=1") + + def tearDown(self): + self.cur.close() + self.con.close() + +def suite(): + default_suite = unittest.makeSuite(TransactionTests, "Check") + special_command_suite = unittest.makeSuite(SpecialCommandTests, "Check") + return unittest.TestSuite((default_suite, special_command_suite)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py new file mode 100644 index 0000000..e49f7dd --- /dev/null +++ b/Lib/sqlite3/test/types.py @@ -0,0 +1,339 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/types.py: tests for type conversion and detection +# +# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import datetime +import unittest +import sqlite3 as sqlite + +class SqliteTypeTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + self.cur = self.con.cursor() + self.cur.execute("create table test(i integer, s varchar, f number, b blob)") + + def tearDown(self): + self.cur.close() + self.con.close() + + def CheckString(self): + self.cur.execute("insert into test(s) values (?)", (u"Österreich",)) + self.cur.execute("select s from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], u"Österreich") + + def CheckSmallInt(self): + self.cur.execute("insert into test(i) values (?)", (42,)) + self.cur.execute("select i from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], 42) + + def CheckLargeInt(self): + num = 2**40 + self.cur.execute("insert into test(i) values (?)", (num,)) + self.cur.execute("select i from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], num) + + def CheckFloat(self): + val = 3.14 + self.cur.execute("insert into test(f) values (?)", (val,)) + self.cur.execute("select f from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], val) + + def CheckBlob(self): + val = buffer("Guglhupf") + self.cur.execute("insert into test(b) values (?)", (val,)) + self.cur.execute("select b from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], val) + + def CheckUnicodeExecute(self): + self.cur.execute(u"select 'Österreich'") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], u"Österreich") + +class DeclTypesTests(unittest.TestCase): + class Foo: + def __init__(self, _val): + self.val = _val + + def __cmp__(self, other): + if not isinstance(other, DeclTypesTests.Foo): + raise ValueError + if self.val == other.val: + return 0 + else: + return 1 + + def __conform__(self, protocol): + if protocol is sqlite.PrepareProtocol: + return self.val + else: + return None + + def __str__(self): + return "<%s>" % self.val + + def setUp(self): + self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) + self.cur = self.con.cursor() + self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob)") + + # override float, make them always return the same number + sqlite.converters["float"] = lambda x: 47.2 + + # and implement two custom ones + sqlite.converters["bool"] = lambda x: bool(int(x)) + sqlite.converters["foo"] = DeclTypesTests.Foo + + def tearDown(self): + del sqlite.converters["float"] + del sqlite.converters["bool"] + del sqlite.converters["foo"] + self.cur.close() + self.con.close() + + def CheckString(self): + # default + self.cur.execute("insert into test(s) values (?)", ("foo",)) + self.cur.execute("select s from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], "foo") + + def CheckSmallInt(self): + # default + self.cur.execute("insert into test(i) values (?)", (42,)) + self.cur.execute("select i from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], 42) + + def CheckLargeInt(self): + # default + num = 2**40 + self.cur.execute("insert into test(i) values (?)", (num,)) + self.cur.execute("select i from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], num) + + def CheckFloat(self): + # custom + val = 3.14 + self.cur.execute("insert into test(f) values (?)", (val,)) + self.cur.execute("select f from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], 47.2) + + def CheckBool(self): + # custom + self.cur.execute("insert into test(b) values (?)", (False,)) + self.cur.execute("select b from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], False) + + self.cur.execute("delete from test") + self.cur.execute("insert into test(b) values (?)", (True,)) + self.cur.execute("select b from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], True) + + def CheckUnicode(self): + # default + val = u"\xd6sterreich" + self.cur.execute("insert into test(u) values (?)", (val,)) + self.cur.execute("select u from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], val) + + def CheckFoo(self): + val = DeclTypesTests.Foo("bla") + self.cur.execute("insert into test(foo) values (?)", (val,)) + self.cur.execute("select foo from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], val) + + def CheckUnsupportedSeq(self): + class Bar: pass + val = Bar() + try: + self.cur.execute("insert into test(f) values (?)", (val,)) + self.fail("should have raised an InterfaceError") + except sqlite.InterfaceError: + pass + except: + self.fail("should have raised an InterfaceError") + + def CheckUnsupportedDict(self): + class Bar: pass + val = Bar() + try: + self.cur.execute("insert into test(f) values (:val)", {"val": val}) + self.fail("should have raised an InterfaceError") + except sqlite.InterfaceError: + pass + except: + self.fail("should have raised an InterfaceError") + + def CheckBlob(self): + # default + val = buffer("Guglhupf") + self.cur.execute("insert into test(bin) values (?)", (val,)) + self.cur.execute("select bin from test") + row = self.cur.fetchone() + self.failUnlessEqual(row[0], val) + +class ColNamesTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES|sqlite.PARSE_DECLTYPES) + self.cur = self.con.cursor() + self.cur.execute("create table test(x foo)") + + sqlite.converters["foo"] = lambda x: "[%s]" % x + sqlite.converters["bar"] = lambda x: "<%s>" % x + sqlite.converters["exc"] = lambda x: 5/0 + + def tearDown(self): + del sqlite.converters["foo"] + del sqlite.converters["bar"] + del sqlite.converters["exc"] + self.cur.close() + self.con.close() + + def CheckDeclType(self): + self.cur.execute("insert into test(x) values (?)", ("xxx",)) + self.cur.execute("select x from test") + val = self.cur.fetchone()[0] + self.failUnlessEqual(val, "[xxx]") + + def CheckNone(self): + self.cur.execute("insert into test(x) values (?)", (None,)) + self.cur.execute("select x from test") + val = self.cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckExc(self): + # Exceptions in type converters result in returned Nones + self.cur.execute('select 5 as "x [exc]"') + val = self.cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckColName(self): + self.cur.execute("insert into test(x) values (?)", ("xxx",)) + self.cur.execute('select x as "x [bar]" from test') + val = self.cur.fetchone()[0] + self.failUnlessEqual(val, "<xxx>") + + # Check if the stripping of colnames works. Everything after the first + # whitespace should be stripped. + self.failUnlessEqual(self.cur.description[0][0], "x") + + def CheckCursorDescriptionNoRow(self): + """ + cursor.description should at least provide the column name(s), even if + no row returned. + """ + self.cur.execute("select * from test where 0 = 1") + self.assert_(self.cur.description[0][0] == "x") + +class ObjectAdaptationTests(unittest.TestCase): + def cast(obj): + return float(obj) + cast = staticmethod(cast) + + def setUp(self): + self.con = sqlite.connect(":memory:") + try: + del sqlite.adapters[int] + except: + pass + sqlite.register_adapter(int, ObjectAdaptationTests.cast) + self.cur = self.con.cursor() + + def tearDown(self): + del sqlite.adapters[(int, sqlite.PrepareProtocol)] + self.cur.close() + self.con.close() + + def CheckCasterIsUsed(self): + self.cur.execute("select ?", (4,)) + val = self.cur.fetchone()[0] + self.failUnlessEqual(type(val), float) + +class DateTimeTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) + self.cur = self.con.cursor() + self.cur.execute("create table test(d date, ts timestamp)") + + def tearDown(self): + self.cur.close() + self.con.close() + + def CheckSqliteDate(self): + d = sqlite.Date(2004, 2, 14) + self.cur.execute("insert into test(d) values (?)", (d,)) + self.cur.execute("select d from test") + d2 = self.cur.fetchone()[0] + self.failUnlessEqual(d, d2) + + def CheckSqliteTimestamp(self): + ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0) + self.cur.execute("insert into test(ts) values (?)", (ts,)) + self.cur.execute("select ts from test") + ts2 = self.cur.fetchone()[0] + self.failUnlessEqual(ts, ts2) + + def CheckSqlTimestamp(self): + # The date functions are only available in SQLite version 3.1 or later + if sqlite.sqlite_version_info < (3, 1): + return + + # SQLite's current_timestamp uses UTC time, while datetime.datetime.now() uses local time. + now = datetime.datetime.now() + self.cur.execute("insert into test(ts) values (current_timestamp)") + self.cur.execute("select ts from test") + ts = self.cur.fetchone()[0] + self.failUnlessEqual(type(ts), datetime.datetime) + self.failUnlessEqual(ts.year, now.year) + + def CheckDateTimeSubSeconds(self): + ts = sqlite.Timestamp(2004, 2, 14, 7, 15, 0, 500000) + self.cur.execute("insert into test(ts) values (?)", (ts,)) + self.cur.execute("select ts from test") + ts2 = self.cur.fetchone()[0] + self.failUnlessEqual(ts, ts2) + +def suite(): + sqlite_type_suite = unittest.makeSuite(SqliteTypeTests, "Check") + decltypes_type_suite = unittest.makeSuite(DeclTypesTests, "Check") + colnames_type_suite = unittest.makeSuite(ColNamesTests, "Check") + adaptation_suite = unittest.makeSuite(ObjectAdaptationTests, "Check") + date_suite = unittest.makeSuite(DateTimeTests, "Check") + return unittest.TestSuite((sqlite_type_suite, decltypes_type_suite, colnames_type_suite, adaptation_suite, date_suite)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py new file mode 100644 index 0000000..ff7db9c --- /dev/null +++ b/Lib/sqlite3/test/userfunctions.py @@ -0,0 +1,330 @@ +#-*- coding: ISO-8859-1 -*- +# pysqlite2/test/userfunctions.py: tests for user-defined functions and +# aggregates. +# +# Copyright (C) 2005 Gerhard Häring <gh@ghaering.de> +# +# This file is part of pysqlite. +# +# 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. + +import unittest +import sqlite3 as sqlite + +def func_returntext(): + return "foo" +def func_returnunicode(): + return u"bar" +def func_returnint(): + return 42 +def func_returnfloat(): + return 3.14 +def func_returnnull(): + return None +def func_returnblob(): + return buffer("blob") +def func_raiseexception(): + 5/0 + +def func_isstring(v): + return type(v) is unicode +def func_isint(v): + return type(v) is int +def func_isfloat(v): + return type(v) is float +def func_isnone(v): + return type(v) is type(None) +def func_isblob(v): + return type(v) is buffer + +class AggrNoStep: + def __init__(self): + pass + +class AggrNoFinalize: + def __init__(self): + pass + + def step(self, x): + pass + +class AggrExceptionInInit: + def __init__(self): + 5/0 + + def step(self, x): + pass + + def finalize(self): + pass + +class AggrExceptionInStep: + def __init__(self): + pass + + def step(self, x): + 5/0 + + def finalize(self): + return 42 + +class AggrExceptionInFinalize: + def __init__(self): + pass + + def step(self, x): + pass + + def finalize(self): + 5/0 + +class AggrCheckType: + def __init__(self): + self.val = None + + def step(self, whichType, val): + theType = {"str": unicode, "int": int, "float": float, "None": type(None), "blob": buffer} + self.val = int(theType[whichType] is type(val)) + + def finalize(self): + return self.val + +class AggrSum: + def __init__(self): + self.val = 0.0 + + def step(self, val): + self.val += val + + def finalize(self): + return self.val + +class FunctionTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + self.con.create_function("returntext", 0, func_returntext) + self.con.create_function("returnunicode", 0, func_returnunicode) + self.con.create_function("returnint", 0, func_returnint) + self.con.create_function("returnfloat", 0, func_returnfloat) + self.con.create_function("returnnull", 0, func_returnnull) + self.con.create_function("returnblob", 0, func_returnblob) + self.con.create_function("raiseexception", 0, func_raiseexception) + + self.con.create_function("isstring", 1, func_isstring) + self.con.create_function("isint", 1, func_isint) + self.con.create_function("isfloat", 1, func_isfloat) + self.con.create_function("isnone", 1, func_isnone) + self.con.create_function("isblob", 1, func_isblob) + + def tearDown(self): + self.con.close() + + def CheckFuncRefCount(self): + def getfunc(): + def f(): + return val + return f + self.con.create_function("reftest", 0, getfunc()) + cur = self.con.cursor() + cur.execute("select reftest()") + + def CheckFuncReturnText(self): + cur = self.con.cursor() + cur.execute("select returntext()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), unicode) + self.failUnlessEqual(val, "foo") + + def CheckFuncReturnUnicode(self): + cur = self.con.cursor() + cur.execute("select returnunicode()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), unicode) + self.failUnlessEqual(val, u"bar") + + def CheckFuncReturnInt(self): + cur = self.con.cursor() + cur.execute("select returnint()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), int) + self.failUnlessEqual(val, 42) + + def CheckFuncReturnFloat(self): + cur = self.con.cursor() + cur.execute("select returnfloat()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), float) + if val < 3.139 or val > 3.141: + self.fail("wrong value") + + def CheckFuncReturnNull(self): + cur = self.con.cursor() + cur.execute("select returnnull()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), type(None)) + self.failUnlessEqual(val, None) + + def CheckFuncReturnBlob(self): + cur = self.con.cursor() + cur.execute("select returnblob()") + val = cur.fetchone()[0] + self.failUnlessEqual(type(val), buffer) + self.failUnlessEqual(val, buffer("blob")) + + def CheckFuncException(self): + cur = self.con.cursor() + cur.execute("select raiseexception()") + val = cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckParamString(self): + cur = self.con.cursor() + cur.execute("select isstring(?)", ("foo",)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckParamInt(self): + cur = self.con.cursor() + cur.execute("select isint(?)", (42,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckParamFloat(self): + cur = self.con.cursor() + cur.execute("select isfloat(?)", (3.14,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckParamNone(self): + cur = self.con.cursor() + cur.execute("select isnone(?)", (None,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckParamBlob(self): + cur = self.con.cursor() + cur.execute("select isblob(?)", (buffer("blob"),)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + +class AggregateTests(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + cur = self.con.cursor() + cur.execute(""" + create table test( + t text, + i integer, + f float, + n, + b blob + ) + """) + cur.execute("insert into test(t, i, f, n, b) values (?, ?, ?, ?, ?)", + ("foo", 5, 3.14, None, buffer("blob"),)) + + self.con.create_aggregate("nostep", 1, AggrNoStep) + self.con.create_aggregate("nofinalize", 1, AggrNoFinalize) + self.con.create_aggregate("excInit", 1, AggrExceptionInInit) + self.con.create_aggregate("excStep", 1, AggrExceptionInStep) + self.con.create_aggregate("excFinalize", 1, AggrExceptionInFinalize) + self.con.create_aggregate("checkType", 2, AggrCheckType) + self.con.create_aggregate("mysum", 1, AggrSum) + + def tearDown(self): + #self.cur.close() + #self.con.close() + pass + + def CheckAggrNoStep(self): + cur = self.con.cursor() + cur.execute("select nostep(t) from test") + + def CheckAggrNoFinalize(self): + cur = self.con.cursor() + cur.execute("select nofinalize(t) from test") + val = cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckAggrExceptionInInit(self): + cur = self.con.cursor() + cur.execute("select excInit(t) from test") + val = cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckAggrExceptionInStep(self): + cur = self.con.cursor() + cur.execute("select excStep(t) from test") + val = cur.fetchone()[0] + self.failUnlessEqual(val, 42) + + def CheckAggrExceptionInFinalize(self): + cur = self.con.cursor() + cur.execute("select excFinalize(t) from test") + val = cur.fetchone()[0] + self.failUnlessEqual(val, None) + + def CheckAggrCheckParamStr(self): + cur = self.con.cursor() + cur.execute("select checkType('str', ?)", ("foo",)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckAggrCheckParamInt(self): + cur = self.con.cursor() + cur.execute("select checkType('int', ?)", (42,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckAggrCheckParamFloat(self): + cur = self.con.cursor() + cur.execute("select checkType('float', ?)", (3.14,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckAggrCheckParamNone(self): + cur = self.con.cursor() + cur.execute("select checkType('None', ?)", (None,)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckAggrCheckParamBlob(self): + cur = self.con.cursor() + cur.execute("select checkType('blob', ?)", (buffer("blob"),)) + val = cur.fetchone()[0] + self.failUnlessEqual(val, 1) + + def CheckAggrCheckAggrSum(self): + cur = self.con.cursor() + cur.execute("delete from test") + cur.executemany("insert into test(i) values (?)", [(10,), (20,), (30,)]) + cur.execute("select mysum(i) from test") + val = cur.fetchone()[0] + self.failUnlessEqual(val, 60) + +def suite(): + function_suite = unittest.makeSuite(FunctionTests, "Check") + aggregate_suite = unittest.makeSuite(AggregateTests, "Check") + return unittest.TestSuite((function_suite, aggregate_suite)) + +def test(): + runner = unittest.TextTestRunner() + runner.run(suite()) + +if __name__ == "__main__": + test() diff --git a/Lib/sre.py b/Lib/sre.py new file mode 100644 index 0000000..390094a --- /dev/null +++ b/Lib/sre.py @@ -0,0 +1,10 @@ +"""This file is only retained for backwards compatibility. +It will be removed in the future. sre was moved to re in version 2.5. +""" + +import warnings +warnings.warn("The sre module is deprecated, please import re.", + DeprecationWarning, 2) + +from re import * +from re import __all__ diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6827244..3cd0df5 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -414,7 +414,13 @@ _active = [] def _cleanup(): for inst in _active[:]: - inst.poll() + if inst.poll(_deadstate=sys.maxint) >= 0: + try: + _active.remove(inst) + except ValueError: + # This can happen if two threads create a new Popen instance. + # It's harmless that it was already removed, so ignore. + pass PIPE = -1 STDOUT = -2 @@ -527,6 +533,7 @@ class Popen(object): """Create new Popen instance.""" _cleanup() + self._child_created = False if not isinstance(bufsize, (int, long)): raise TypeError("bufsize must be an integer") @@ -592,14 +599,24 @@ class Popen(object): else: self.stderr = os.fdopen(errread, 'rb', bufsize) - _active.append(self) - def _translate_newlines(self, data): data = data.replace("\r\n", "\n") data = data.replace("\r", "\n") return data + + def __del__(self): + if not self._child_created: + # We didn't get to successfully create a child process. + return + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.returncode is None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def communicate(self, input=None): """Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for @@ -777,6 +794,7 @@ class Popen(object): raise WindowsError(*e.args) # Retain the process handle, but close the thread handle + self._child_created = True self._handle = hp self.pid = pid ht.Close() @@ -795,13 +813,12 @@ class Popen(object): errwrite.Close() - def poll(self): + def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: self.returncode = GetExitCodeProcess(self._handle) - _active.remove(self) return self.returncode @@ -811,7 +828,6 @@ class Popen(object): if self.returncode is None: obj = WaitForSingleObject(self._handle, INFINITE) self.returncode = GetExitCodeProcess(self._handle) - _active.remove(self) return self.returncode @@ -958,6 +974,7 @@ class Popen(object): self._set_cloexec_flag(errpipe_write) self.pid = os.fork() + self._child_created = True if self.pid == 0: # Child try: @@ -1042,10 +1059,8 @@ class Popen(object): # Should never happen raise RuntimeError("Unknown child exit status!") - _active.remove(self) - - def poll(self): + def poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: @@ -1054,7 +1069,8 @@ class Popen(object): if pid == self.pid: self._handle_exitstatus(sts) except os.error: - pass + if _deadstate is not None: + self.returncode = _deadstate return self.returncode diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index 8a2513b..3523037 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -438,7 +438,7 @@ class Telnet: else: self.iacseq += c elif len(self.iacseq) == 1: - 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]' + # 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]' if c in (DO, DONT, WILL, WONT): self.iacseq += c continue diff --git a/Lib/test/check_soundcard.vbs b/Lib/test/check_soundcard.vbs new file mode 100644 index 0000000..8c21852 --- /dev/null +++ b/Lib/test/check_soundcard.vbs @@ -0,0 +1,13 @@ +rem Check for a working sound-card - exit with 0 if OK, 1 otherwise. +set wmi = GetObject("winmgmts:") +set scs = wmi.InstancesOf("win32_sounddevice") +for each sc in scs + set status = sc.Properties_("Status") + wscript.Echo(sc.Properties_("Name") + "/" + status) + if status = "OK" then + wscript.Quit 0 rem normal exit + end if +next +rem No sound card found - exit with status code of 1 +wscript.Quit 1 + diff --git a/Lib/test/crashers/README b/Lib/test/crashers/README index 9369282..070c3f1 100644 --- a/Lib/test/crashers/README +++ b/Lib/test/crashers/README @@ -13,3 +13,8 @@ Each test should have a link to the bug report: Put as much info into a docstring or comments to help determine the cause of the failure. Particularly note if the cause is system or environment dependent and what the variables are. + +Once the crash is fixed, the test case should be moved into an appropriate +test (even if it was originally from the test suite). This ensures the +regression doesn't happen again. And if it does, it should be easier +to track down. diff --git a/Lib/test/crashers/dictresize_attack.py b/Lib/test/crashers/dictresize_attack.py new file mode 100644 index 0000000..1895791 --- /dev/null +++ b/Lib/test/crashers/dictresize_attack.py @@ -0,0 +1,32 @@ +# http://www.python.org/sf/1456209 + +# A dictresize() attack. If oldtable == mp->ma_smalltable then pure +# Python code can mangle with mp->ma_smalltable while it is being walked +# over. + +class X(object): + + def __hash__(self): + return 5 + + def __eq__(self, other): + if resizing: + d.clear() + return False + + +d = {} + +resizing = False + +d[X()] = 1 +d[X()] = 2 +d[X()] = 3 +d[X()] = 4 +d[X()] = 5 + +# now trigger a resize +resizing = True +d[9] = 6 + +# ^^^ I get Segmentation fault or Illegal instruction here. diff --git a/Lib/test/crashers/nasty_eq_vs_dict.py b/Lib/test/crashers/nasty_eq_vs_dict.py new file mode 100644 index 0000000..3f3083d --- /dev/null +++ b/Lib/test/crashers/nasty_eq_vs_dict.py @@ -0,0 +1,47 @@ +# from http://mail.python.org/pipermail/python-dev/2001-June/015239.html + +# if you keep changing a dictionary while looking up a key, you can +# provoke an infinite recursion in C + +# At the time neither Tim nor Michael could be bothered to think of a +# way to fix it. + +class Yuck: + def __init__(self): + self.i = 0 + + def make_dangerous(self): + self.i = 1 + + def __hash__(self): + # direct to slot 4 in table of size 8; slot 12 when size 16 + return 4 + 8 + + def __eq__(self, other): + if self.i == 0: + # leave dict alone + pass + elif self.i == 1: + # fiddle to 16 slots + self.__fill_dict(6) + self.i = 2 + else: + # fiddle to 8 slots + self.__fill_dict(4) + self.i = 1 + + return 1 + + def __fill_dict(self, n): + self.i = 0 + dict.clear() + for i in range(n): + dict[i] = i + dict[self] = "OK!" + +y = Yuck() +dict = {y: "OK!"} + +z = Yuck() +y.make_dangerous() +print dict[z] diff --git a/Lib/test/empty.vbs b/Lib/test/empty.vbs new file mode 100644 index 0000000..f35f076 --- /dev/null +++ b/Lib/test/empty.vbs @@ -0,0 +1 @@ +'Empty VBS file, does nothing. Helper for Lib\test\test_startfile.py. \ No newline at end of file diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py new file mode 100644 index 0000000..5600bdb --- /dev/null +++ b/Lib/test/fork_wait.py @@ -0,0 +1,71 @@ +"""This test case provides support for checking forking and wait behavior. + +To test different wait behavior, overrise the wait_impl method. + +We want fork1() semantics -- only the forking thread survives in the +child after a fork(). + +On some systems (e.g. Solaris without posix threads) we find that all +active threads survive in the child after a fork(); this is an error. + +While BeOS doesn't officially support fork and native threading in +the same application, the present example should work just fine. DC +""" + +import os, sys, time, thread, unittest +from test.test_support import TestSkipped + +LONGSLEEP = 2 +SHORTSLEEP = 0.5 +NUM_THREADS = 4 + +class ForkWait(unittest.TestCase): + + def setUp(self): + self.alive = {} + self.stop = 0 + + def f(self, id): + while not self.stop: + self.alive[id] = os.getpid() + try: + time.sleep(SHORTSLEEP) + except IOError: + pass + + def wait_impl(self, cpid): + spid, status = os.waitpid(cpid, 0) + self.assertEquals(spid, cpid) + self.assertEquals(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) + + def test_wait(self): + for i in range(NUM_THREADS): + thread.start_new(self.f, (i,)) + + time.sleep(LONGSLEEP) + + a = self.alive.keys() + a.sort() + self.assertEquals(a, range(NUM_THREADS)) + + prefork_lives = self.alive.copy() + + if sys.platform in ['unixware7']: + cpid = os.fork1() + else: + cpid = os.fork() + + if cpid == 0: + # Child + time.sleep(LONGSLEEP) + n = 0 + for key in self.alive: + if self.alive[key] != prefork_lives[key]: + n += 1 + os._exit(n) + else: + # Parent + self.wait_impl(cpid) + # Tell threads to die + self.stop = 1 + time.sleep(2*SHORTSLEEP) # Wait for threads to die diff --git a/Lib/test/leakers/README.txt b/Lib/test/leakers/README.txt index 69ee35a..beeee0e 100644 --- a/Lib/test/leakers/README.txt +++ b/Lib/test/leakers/README.txt @@ -5,6 +5,15 @@ the interpreter was built in debug mode. If the total ref count doesn't increase, the bug has been fixed and the file should be removed from the repository. +Note: be careful to check for cyclic garbage. Sometimes it may be helpful +to define the leak function like: + +def leak(): + def inner_leak(): + # this is the function that leaks, but also creates cycles + inner_leak() + gc.collect() ; gc.collect() ; gc.collect() + Here's an example interpreter session for test_gestalt which still leaks: >>> from test.leakers.test_gestalt import leak @@ -17,3 +26,7 @@ Here's an example interpreter session for test_gestalt which still leaks: [28940 refs] >>> +Once the leak is fixed, the test case should be moved into an appropriate +test (even if it was originally from the test suite). This ensures the +regression doesn't happen again. And if it does, it should be easier +to track down. diff --git a/Lib/test/leakers/test_ctypes.py b/Lib/test/leakers/test_ctypes.py new file mode 100644 index 0000000..0f9a2cd --- /dev/null +++ b/Lib/test/leakers/test_ctypes.py @@ -0,0 +1,16 @@ + +# Taken from Lib/ctypes/test/test_keeprefs.py, PointerToStructure.test(). +# When this leak is fixed, remember to remove from Misc/build.sh LEAKY_TESTS. + +from ctypes import Structure, c_int, POINTER +import gc + +def leak_inner(): + class POINT(Structure): + _fields_ = [("x", c_int)] + class RECT(Structure): + _fields_ = [("a", POINTER(POINT))] + +def leak(): + leak_inner() + gc.collect() diff --git a/Lib/test/leakers/test_selftype.py b/Lib/test/leakers/test_selftype.py new file mode 100644 index 0000000..4207c32 --- /dev/null +++ b/Lib/test/leakers/test_selftype.py @@ -0,0 +1,13 @@ +# Reference cycles involving only the ob_type field are rather uncommon +# but possible. Inspired by SF bug 1469629. + +import gc + +def leak(): + class T(type): + pass + class U(type): + __metaclass__ = T + U.__class__ = U + del U + gc.collect(); gc.collect(); gc.collect() diff --git a/Lib/test/leakers/test_tee.py b/Lib/test/leakers/test_tee.py deleted file mode 100644 index 4ce24ca..0000000 --- a/Lib/test/leakers/test_tee.py +++ /dev/null @@ -1,19 +0,0 @@ - -# Test case taken from test_itertools -# See http://mail.python.org/pipermail/python-dev/2005-November/058339.html - -from itertools import tee - -def leak(): - def fib(): - def yield_identity_forever(g): - while 1: - yield g - def _fib(): - for i in yield_identity_forever(head): - yield i - head, tail, result = tee(_fib(), 3) - return result - - x = fib() - x.next() diff --git a/Lib/test/output/test_augassign b/Lib/test/output/test_augassign deleted file mode 100644 index b66b7e5..0000000 --- a/Lib/test/output/test_augassign +++ /dev/null @@ -1,54 +0,0 @@ -test_augassign -6.0 -6 -[6.0] -6 -6.0 -6 -[1, 2, 3, 4, 1, 2, 3, 4] -[1, 2, 1, 2, 3] -True -True -True -11 -True -12 -True -True -13 -__add__ called -__radd__ called -__iadd__ called -__sub__ called -__rsub__ called -__isub__ called -__mul__ called -__rmul__ called -__imul__ called -__truediv__ called -__rtruediv__ called -__itruediv__ called -__floordiv__ called -__rfloordiv__ called -__ifloordiv__ called -__mod__ called -__rmod__ called -__imod__ called -__pow__ called -__rpow__ called -__ipow__ called -__or__ called -__ror__ called -__ior__ called -__and__ called -__rand__ called -__iand__ called -__xor__ called -__rxor__ called -__ixor__ called -__rshift__ called -__rrshift__ called -__irshift__ called -__lshift__ called -__rlshift__ called -__ilshift__ called diff --git a/Lib/test/output/test_coercion b/Lib/test/output/test_coercion deleted file mode 100644 index ad35b60..0000000 --- a/Lib/test/output/test_coercion +++ /dev/null @@ -1,1054 +0,0 @@ -test_coercion -2 + 2 = 4 -2 += 2 => 4 -2 - 2 = 0 -2 -= 2 => 0 -2 * 2 = 4 -2 *= 2 => 4 -2 / 2 = 1 -2 /= 2 => 1 -2 ** 2 = 4 -2 **= 2 => 4 -2 % 2 = 0 -2 %= 2 => 0 -2 + 4.0 = 6.0 -2 += 4.0 => 6.0 -2 - 4.0 = -2.0 -2 -= 4.0 => -2.0 -2 * 4.0 = 8.0 -2 *= 4.0 => 8.0 -2 / 4.0 = 0.5 -2 /= 4.0 => 0.5 -2 ** 4.0 = 16.0 -2 **= 4.0 => 16.0 -2 % 4.0 = 2.0 -2 %= 4.0 => 2.0 -2 + 2 = 4 -2 += 2 => 4 -2 - 2 = 0 -2 -= 2 => 0 -2 * 2 = 4 -2 *= 2 => 4 -2 / 2 = 1 -2 /= 2 => 1 -2 ** 2 = 4 -2 **= 2 => 4 -2 % 2 = 0 -2 %= 2 => 0 -2 + (2+0j) = (4.0 + 0.0j) -2 += (2+0j) => (4.0 + 0.0j) -2 - (2+0j) = (0.0 + 0.0j) -2 -= (2+0j) => (0.0 + 0.0j) -2 * (2+0j) = (4.0 + 0.0j) -2 *= (2+0j) => (4.0 + 0.0j) -2 / (2+0j) = (1.0 + 0.0j) -2 /= (2+0j) => (1.0 + 0.0j) -2 ** (2+0j) = (4.0 + 0.0j) -2 **= (2+0j) => (4.0 + 0.0j) -2 % (2+0j) = (0.0 + 0.0j) -2 %= (2+0j) => (0.0 + 0.0j) -2 + [1] ... exceptions.TypeError -2 += [1] ... exceptions.TypeError -2 - [1] ... exceptions.TypeError -2 -= [1] ... exceptions.TypeError -2 * [1] = [1, 1] -2 *= [1] => [1, 1] -2 / [1] ... exceptions.TypeError -2 /= [1] ... exceptions.TypeError -2 ** [1] ... exceptions.TypeError -2 **= [1] ... exceptions.TypeError -2 % [1] ... exceptions.TypeError -2 %= [1] ... exceptions.TypeError -2 + (2,) ... exceptions.TypeError -2 += (2,) ... exceptions.TypeError -2 - (2,) ... exceptions.TypeError -2 -= (2,) ... exceptions.TypeError -2 * (2,) = (2, 2) -2 *= (2,) => (2, 2) -2 / (2,) ... exceptions.TypeError -2 /= (2,) ... exceptions.TypeError -2 ** (2,) ... exceptions.TypeError -2 **= (2,) ... exceptions.TypeError -2 % (2,) ... exceptions.TypeError -2 %= (2,) ... exceptions.TypeError -2 + None ... exceptions.TypeError -2 += None ... exceptions.TypeError -2 - None ... exceptions.TypeError -2 -= None ... exceptions.TypeError -2 * None ... exceptions.TypeError -2 *= None ... exceptions.TypeError -2 / None ... exceptions.TypeError -2 /= None ... exceptions.TypeError -2 ** None ... exceptions.TypeError -2 **= None ... exceptions.TypeError -2 % None ... exceptions.TypeError -2 %= None ... exceptions.TypeError -2 + <MethodNumber 2> = 4 -2 += <MethodNumber 2> => 4 -2 - <MethodNumber 2> = 0 -2 -= <MethodNumber 2> => 0 -2 * <MethodNumber 2> = 4 -2 *= <MethodNumber 2> => 4 -2 / <MethodNumber 2> = 1 -2 /= <MethodNumber 2> => 1 -2 ** <MethodNumber 2> = 4 -2 **= <MethodNumber 2> => 4 -2 % <MethodNumber 2> = 0 -2 %= <MethodNumber 2> => 0 -2 + <CoerceNumber 2> = 4 -2 += <CoerceNumber 2> => 4 -2 - <CoerceNumber 2> = 0 -2 -= <CoerceNumber 2> => 0 -2 * <CoerceNumber 2> = 4 -2 *= <CoerceNumber 2> => 4 -2 / <CoerceNumber 2> = 1 -2 /= <CoerceNumber 2> => 1 -2 ** <CoerceNumber 2> = 4 -2 **= <CoerceNumber 2> => 4 -2 % <CoerceNumber 2> = 0 -2 %= <CoerceNumber 2> => 0 -4.0 + 2 = 6.0 -4.0 += 2 => 6.0 -4.0 - 2 = 2.0 -4.0 -= 2 => 2.0 -4.0 * 2 = 8.0 -4.0 *= 2 => 8.0 -4.0 / 2 = 2.0 -4.0 /= 2 => 2.0 -4.0 ** 2 = 16.0 -4.0 **= 2 => 16.0 -4.0 % 2 = 0.0 -4.0 %= 2 => 0.0 -4.0 + 4.0 = 8.0 -4.0 += 4.0 => 8.0 -4.0 - 4.0 = 0.0 -4.0 -= 4.0 => 0.0 -4.0 * 4.0 = 16.0 -4.0 *= 4.0 => 16.0 -4.0 / 4.0 = 1.0 -4.0 /= 4.0 => 1.0 -4.0 ** 4.0 = 256.0 -4.0 **= 4.0 => 256.0 -4.0 % 4.0 = 0.0 -4.0 %= 4.0 => 0.0 -4.0 + 2 = 6.0 -4.0 += 2 => 6.0 -4.0 - 2 = 2.0 -4.0 -= 2 => 2.0 -4.0 * 2 = 8.0 -4.0 *= 2 => 8.0 -4.0 / 2 = 2.0 -4.0 /= 2 => 2.0 -4.0 ** 2 = 16.0 -4.0 **= 2 => 16.0 -4.0 % 2 = 0.0 -4.0 %= 2 => 0.0 -4.0 + (2+0j) = (6.0 + 0.0j) -4.0 += (2+0j) => (6.0 + 0.0j) -4.0 - (2+0j) = (2.0 + 0.0j) -4.0 -= (2+0j) => (2.0 + 0.0j) -4.0 * (2+0j) = (8.0 + 0.0j) -4.0 *= (2+0j) => (8.0 + 0.0j) -4.0 / (2+0j) = (2.0 + 0.0j) -4.0 /= (2+0j) => (2.0 + 0.0j) -4.0 ** (2+0j) = (16.0 + 0.0j) -4.0 **= (2+0j) => (16.0 + 0.0j) -4.0 % (2+0j) = (0.0 + 0.0j) -4.0 %= (2+0j) => (0.0 + 0.0j) -4.0 + [1] ... exceptions.TypeError -4.0 += [1] ... exceptions.TypeError -4.0 - [1] ... exceptions.TypeError -4.0 -= [1] ... exceptions.TypeError -4.0 * [1] ... exceptions.TypeError -4.0 *= [1] ... exceptions.TypeError -4.0 / [1] ... exceptions.TypeError -4.0 /= [1] ... exceptions.TypeError -4.0 ** [1] ... exceptions.TypeError -4.0 **= [1] ... exceptions.TypeError -4.0 % [1] ... exceptions.TypeError -4.0 %= [1] ... exceptions.TypeError -4.0 + (2,) ... exceptions.TypeError -4.0 += (2,) ... exceptions.TypeError -4.0 - (2,) ... exceptions.TypeError -4.0 -= (2,) ... exceptions.TypeError -4.0 * (2,) ... exceptions.TypeError -4.0 *= (2,) ... exceptions.TypeError -4.0 / (2,) ... exceptions.TypeError -4.0 /= (2,) ... exceptions.TypeError -4.0 ** (2,) ... exceptions.TypeError -4.0 **= (2,) ... exceptions.TypeError -4.0 % (2,) ... exceptions.TypeError -4.0 %= (2,) ... exceptions.TypeError -4.0 + None ... exceptions.TypeError -4.0 += None ... exceptions.TypeError -4.0 - None ... exceptions.TypeError -4.0 -= None ... exceptions.TypeError -4.0 * None ... exceptions.TypeError -4.0 *= None ... exceptions.TypeError -4.0 / None ... exceptions.TypeError -4.0 /= None ... exceptions.TypeError -4.0 ** None ... exceptions.TypeError -4.0 **= None ... exceptions.TypeError -4.0 % None ... exceptions.TypeError -4.0 %= None ... exceptions.TypeError -4.0 + <MethodNumber 2> = 6.0 -4.0 += <MethodNumber 2> => 6.0 -4.0 - <MethodNumber 2> = 2.0 -4.0 -= <MethodNumber 2> => 2.0 -4.0 * <MethodNumber 2> = 8.0 -4.0 *= <MethodNumber 2> => 8.0 -4.0 / <MethodNumber 2> = 2.0 -4.0 /= <MethodNumber 2> => 2.0 -4.0 ** <MethodNumber 2> = 16.0 -4.0 **= <MethodNumber 2> => 16.0 -4.0 % <MethodNumber 2> = 0.0 -4.0 %= <MethodNumber 2> => 0.0 -4.0 + <CoerceNumber 2> = 6.0 -4.0 += <CoerceNumber 2> => 6.0 -4.0 - <CoerceNumber 2> = 2.0 -4.0 -= <CoerceNumber 2> => 2.0 -4.0 * <CoerceNumber 2> = 8.0 -4.0 *= <CoerceNumber 2> => 8.0 -4.0 / <CoerceNumber 2> = 2.0 -4.0 /= <CoerceNumber 2> => 2.0 -4.0 ** <CoerceNumber 2> = 16.0 -4.0 **= <CoerceNumber 2> => 16.0 -4.0 % <CoerceNumber 2> = 0.0 -4.0 %= <CoerceNumber 2> => 0.0 -2 + 2 = 4 -2 += 2 => 4 -2 - 2 = 0 -2 -= 2 => 0 -2 * 2 = 4 -2 *= 2 => 4 -2 / 2 = 1 -2 /= 2 => 1 -2 ** 2 = 4 -2 **= 2 => 4 -2 % 2 = 0 -2 %= 2 => 0 -2 + 4.0 = 6.0 -2 += 4.0 => 6.0 -2 - 4.0 = -2.0 -2 -= 4.0 => -2.0 -2 * 4.0 = 8.0 -2 *= 4.0 => 8.0 -2 / 4.0 = 0.5 -2 /= 4.0 => 0.5 -2 ** 4.0 = 16.0 -2 **= 4.0 => 16.0 -2 % 4.0 = 2.0 -2 %= 4.0 => 2.0 -2 + 2 = 4 -2 += 2 => 4 -2 - 2 = 0 -2 -= 2 => 0 -2 * 2 = 4 -2 *= 2 => 4 -2 / 2 = 1 -2 /= 2 => 1 -2 ** 2 = 4 -2 **= 2 => 4 -2 % 2 = 0 -2 %= 2 => 0 -2 + (2+0j) = (4.0 + 0.0j) -2 += (2+0j) => (4.0 + 0.0j) -2 - (2+0j) = (0.0 + 0.0j) -2 -= (2+0j) => (0.0 + 0.0j) -2 * (2+0j) = (4.0 + 0.0j) -2 *= (2+0j) => (4.0 + 0.0j) -2 / (2+0j) = (1.0 + 0.0j) -2 /= (2+0j) => (1.0 + 0.0j) -2 ** (2+0j) = (4.0 + 0.0j) -2 **= (2+0j) => (4.0 + 0.0j) -2 % (2+0j) = (0.0 + 0.0j) -2 %= (2+0j) => (0.0 + 0.0j) -2 + [1] ... exceptions.TypeError -2 += [1] ... exceptions.TypeError -2 - [1] ... exceptions.TypeError -2 -= [1] ... exceptions.TypeError -2 * [1] = [1, 1] -2 *= [1] => [1, 1] -2 / [1] ... exceptions.TypeError -2 /= [1] ... exceptions.TypeError -2 ** [1] ... exceptions.TypeError -2 **= [1] ... exceptions.TypeError -2 % [1] ... exceptions.TypeError -2 %= [1] ... exceptions.TypeError -2 + (2,) ... exceptions.TypeError -2 += (2,) ... exceptions.TypeError -2 - (2,) ... exceptions.TypeError -2 -= (2,) ... exceptions.TypeError -2 * (2,) = (2, 2) -2 *= (2,) => (2, 2) -2 / (2,) ... exceptions.TypeError -2 /= (2,) ... exceptions.TypeError -2 ** (2,) ... exceptions.TypeError -2 **= (2,) ... exceptions.TypeError -2 % (2,) ... exceptions.TypeError -2 %= (2,) ... exceptions.TypeError -2 + None ... exceptions.TypeError -2 += None ... exceptions.TypeError -2 - None ... exceptions.TypeError -2 -= None ... exceptions.TypeError -2 * None ... exceptions.TypeError -2 *= None ... exceptions.TypeError -2 / None ... exceptions.TypeError -2 /= None ... exceptions.TypeError -2 ** None ... exceptions.TypeError -2 **= None ... exceptions.TypeError -2 % None ... exceptions.TypeError -2 %= None ... exceptions.TypeError -2 + <MethodNumber 2> = 4 -2 += <MethodNumber 2> => 4 -2 - <MethodNumber 2> = 0 -2 -= <MethodNumber 2> => 0 -2 * <MethodNumber 2> = 4 -2 *= <MethodNumber 2> => 4 -2 / <MethodNumber 2> = 1 -2 /= <MethodNumber 2> => 1 -2 ** <MethodNumber 2> = 4 -2 **= <MethodNumber 2> => 4 -2 % <MethodNumber 2> = 0 -2 %= <MethodNumber 2> => 0 -2 + <CoerceNumber 2> = 4 -2 += <CoerceNumber 2> => 4 -2 - <CoerceNumber 2> = 0 -2 -= <CoerceNumber 2> => 0 -2 * <CoerceNumber 2> = 4 -2 *= <CoerceNumber 2> => 4 -2 / <CoerceNumber 2> = 1 -2 /= <CoerceNumber 2> => 1 -2 ** <CoerceNumber 2> = 4 -2 **= <CoerceNumber 2> => 4 -2 % <CoerceNumber 2> = 0 -2 %= <CoerceNumber 2> => 0 -(2+0j) + 2 = (4.0 + 0.0j) -(2+0j) += 2 => (4.0 + 0.0j) -(2+0j) - 2 = (0.0 + 0.0j) -(2+0j) -= 2 => (0.0 + 0.0j) -(2+0j) * 2 = (4.0 + 0.0j) -(2+0j) *= 2 => (4.0 + 0.0j) -(2+0j) / 2 = (1.0 + 0.0j) -(2+0j) /= 2 => (1.0 + 0.0j) -(2+0j) ** 2 = (4.0 + 0.0j) -(2+0j) **= 2 => (4.0 + 0.0j) -(2+0j) % 2 = (0.0 + 0.0j) -(2+0j) %= 2 => (0.0 + 0.0j) -(2+0j) + 4.0 = (6.0 + 0.0j) -(2+0j) += 4.0 => (6.0 + 0.0j) -(2+0j) - 4.0 = (-2.0 + 0.0j) -(2+0j) -= 4.0 => (-2.0 + 0.0j) -(2+0j) * 4.0 = (8.0 + 0.0j) -(2+0j) *= 4.0 => (8.0 + 0.0j) -(2+0j) / 4.0 = (0.5 + 0.0j) -(2+0j) /= 4.0 => (0.5 + 0.0j) -(2+0j) ** 4.0 = (16.0 + 0.0j) -(2+0j) **= 4.0 => (16.0 + 0.0j) -(2+0j) % 4.0 = (2.0 + 0.0j) -(2+0j) %= 4.0 => (2.0 + 0.0j) -(2+0j) + 2 = (4.0 + 0.0j) -(2+0j) += 2 => (4.0 + 0.0j) -(2+0j) - 2 = (0.0 + 0.0j) -(2+0j) -= 2 => (0.0 + 0.0j) -(2+0j) * 2 = (4.0 + 0.0j) -(2+0j) *= 2 => (4.0 + 0.0j) -(2+0j) / 2 = (1.0 + 0.0j) -(2+0j) /= 2 => (1.0 + 0.0j) -(2+0j) ** 2 = (4.0 + 0.0j) -(2+0j) **= 2 => (4.0 + 0.0j) -(2+0j) % 2 = (0.0 + 0.0j) -(2+0j) %= 2 => (0.0 + 0.0j) -(2+0j) + (2+0j) = (4.0 + 0.0j) -(2+0j) += (2+0j) => (4.0 + 0.0j) -(2+0j) - (2+0j) = (0.0 + 0.0j) -(2+0j) -= (2+0j) => (0.0 + 0.0j) -(2+0j) * (2+0j) = (4.0 + 0.0j) -(2+0j) *= (2+0j) => (4.0 + 0.0j) -(2+0j) / (2+0j) = (1.0 + 0.0j) -(2+0j) /= (2+0j) => (1.0 + 0.0j) -(2+0j) ** (2+0j) = (4.0 + 0.0j) -(2+0j) **= (2+0j) => (4.0 + 0.0j) -(2+0j) % (2+0j) = (0.0 + 0.0j) -(2+0j) %= (2+0j) => (0.0 + 0.0j) -(2+0j) + [1] ... exceptions.TypeError -(2+0j) += [1] ... exceptions.TypeError -(2+0j) - [1] ... exceptions.TypeError -(2+0j) -= [1] ... exceptions.TypeError -(2+0j) * [1] ... exceptions.TypeError -(2+0j) *= [1] ... exceptions.TypeError -(2+0j) / [1] ... exceptions.TypeError -(2+0j) /= [1] ... exceptions.TypeError -(2+0j) ** [1] ... exceptions.TypeError -(2+0j) **= [1] ... exceptions.TypeError -(2+0j) % [1] ... exceptions.TypeError -(2+0j) %= [1] ... exceptions.TypeError -(2+0j) + (2,) ... exceptions.TypeError -(2+0j) += (2,) ... exceptions.TypeError -(2+0j) - (2,) ... exceptions.TypeError -(2+0j) -= (2,) ... exceptions.TypeError -(2+0j) * (2,) ... exceptions.TypeError -(2+0j) *= (2,) ... exceptions.TypeError -(2+0j) / (2,) ... exceptions.TypeError -(2+0j) /= (2,) ... exceptions.TypeError -(2+0j) ** (2,) ... exceptions.TypeError -(2+0j) **= (2,) ... exceptions.TypeError -(2+0j) % (2,) ... exceptions.TypeError -(2+0j) %= (2,) ... exceptions.TypeError -(2+0j) + None ... exceptions.TypeError -(2+0j) += None ... exceptions.TypeError -(2+0j) - None ... exceptions.TypeError -(2+0j) -= None ... exceptions.TypeError -(2+0j) * None ... exceptions.TypeError -(2+0j) *= None ... exceptions.TypeError -(2+0j) / None ... exceptions.TypeError -(2+0j) /= None ... exceptions.TypeError -(2+0j) ** None ... exceptions.TypeError -(2+0j) **= None ... exceptions.TypeError -(2+0j) % None ... exceptions.TypeError -(2+0j) %= None ... exceptions.TypeError -(2+0j) + <MethodNumber 2> = (4.0 + 0.0j) -(2+0j) += <MethodNumber 2> => (4.0 + 0.0j) -(2+0j) - <MethodNumber 2> = (0.0 + 0.0j) -(2+0j) -= <MethodNumber 2> => (0.0 + 0.0j) -(2+0j) * <MethodNumber 2> = (4.0 + 0.0j) -(2+0j) *= <MethodNumber 2> => (4.0 + 0.0j) -(2+0j) / <MethodNumber 2> = (1.0 + 0.0j) -(2+0j) /= <MethodNumber 2> => (1.0 + 0.0j) -(2+0j) ** <MethodNumber 2> = (4.0 + 0.0j) -(2+0j) **= <MethodNumber 2> => (4.0 + 0.0j) -(2+0j) % <MethodNumber 2> = (0.0 + 0.0j) -(2+0j) %= <MethodNumber 2> => (0.0 + 0.0j) -(2+0j) + <CoerceNumber 2> = (4.0 + 0.0j) -(2+0j) += <CoerceNumber 2> => (4.0 + 0.0j) -(2+0j) - <CoerceNumber 2> = (0.0 + 0.0j) -(2+0j) -= <CoerceNumber 2> => (0.0 + 0.0j) -(2+0j) * <CoerceNumber 2> = (4.0 + 0.0j) -(2+0j) *= <CoerceNumber 2> => (4.0 + 0.0j) -(2+0j) / <CoerceNumber 2> = (1.0 + 0.0j) -(2+0j) /= <CoerceNumber 2> => (1.0 + 0.0j) -(2+0j) ** <CoerceNumber 2> = (4.0 + 0.0j) -(2+0j) **= <CoerceNumber 2> => (4.0 + 0.0j) -(2+0j) % <CoerceNumber 2> = (0.0 + 0.0j) -(2+0j) %= <CoerceNumber 2> => (0.0 + 0.0j) -[1] + 2 ... exceptions.TypeError -[1] += 2 ... exceptions.TypeError -[1] - 2 ... exceptions.TypeError -[1] -= 2 ... exceptions.TypeError -[1] * 2 = [1, 1] -[1] *= 2 => [1, 1] -[1] / 2 ... exceptions.TypeError -[1] /= 2 ... exceptions.TypeError -[1] ** 2 ... exceptions.TypeError -[1] **= 2 ... exceptions.TypeError -[1] % 2 ... exceptions.TypeError -[1] %= 2 ... exceptions.TypeError -[1] + 4.0 ... exceptions.TypeError -[1] += 4.0 ... exceptions.TypeError -[1] - 4.0 ... exceptions.TypeError -[1] -= 4.0 ... exceptions.TypeError -[1] * 4.0 ... exceptions.TypeError -[1] *= 4.0 ... exceptions.TypeError -[1] / 4.0 ... exceptions.TypeError -[1] /= 4.0 ... exceptions.TypeError -[1] ** 4.0 ... exceptions.TypeError -[1] **= 4.0 ... exceptions.TypeError -[1] % 4.0 ... exceptions.TypeError -[1] %= 4.0 ... exceptions.TypeError -[1] + 2 ... exceptions.TypeError -[1] += 2 ... exceptions.TypeError -[1] - 2 ... exceptions.TypeError -[1] -= 2 ... exceptions.TypeError -[1] * 2 = [1, 1] -[1] *= 2 => [1, 1] -[1] / 2 ... exceptions.TypeError -[1] /= 2 ... exceptions.TypeError -[1] ** 2 ... exceptions.TypeError -[1] **= 2 ... exceptions.TypeError -[1] % 2 ... exceptions.TypeError -[1] %= 2 ... exceptions.TypeError -[1] + (2+0j) ... exceptions.TypeError -[1] += (2+0j) ... exceptions.TypeError -[1] - (2+0j) ... exceptions.TypeError -[1] -= (2+0j) ... exceptions.TypeError -[1] * (2+0j) ... exceptions.TypeError -[1] *= (2+0j) ... exceptions.TypeError -[1] / (2+0j) ... exceptions.TypeError -[1] /= (2+0j) ... exceptions.TypeError -[1] ** (2+0j) ... exceptions.TypeError -[1] **= (2+0j) ... exceptions.TypeError -[1] % (2+0j) ... exceptions.TypeError -[1] %= (2+0j) ... exceptions.TypeError -[1] + [1] = [1, 1] -[1] += [1] => [1, 1] -[1] - [1] ... exceptions.TypeError -[1] -= [1] ... exceptions.TypeError -[1] * [1] ... exceptions.TypeError -[1] *= [1] ... exceptions.TypeError -[1] / [1] ... exceptions.TypeError -[1] /= [1] ... exceptions.TypeError -[1] ** [1] ... exceptions.TypeError -[1] **= [1] ... exceptions.TypeError -[1] % [1] ... exceptions.TypeError -[1] %= [1] ... exceptions.TypeError -[1] + (2,) ... exceptions.TypeError -[1] += (2,) => [1, 2] -[1] - (2,) ... exceptions.TypeError -[1] -= (2,) ... exceptions.TypeError -[1] * (2,) ... exceptions.TypeError -[1] *= (2,) ... exceptions.TypeError -[1] / (2,) ... exceptions.TypeError -[1] /= (2,) ... exceptions.TypeError -[1] ** (2,) ... exceptions.TypeError -[1] **= (2,) ... exceptions.TypeError -[1] % (2,) ... exceptions.TypeError -[1] %= (2,) ... exceptions.TypeError -[1] + None ... exceptions.TypeError -[1] += None ... exceptions.TypeError -[1] - None ... exceptions.TypeError -[1] -= None ... exceptions.TypeError -[1] * None ... exceptions.TypeError -[1] *= None ... exceptions.TypeError -[1] / None ... exceptions.TypeError -[1] /= None ... exceptions.TypeError -[1] ** None ... exceptions.TypeError -[1] **= None ... exceptions.TypeError -[1] % None ... exceptions.TypeError -[1] %= None ... exceptions.TypeError -[1] + <MethodNumber 2> ... exceptions.TypeError -[1] += <MethodNumber 2> ... exceptions.TypeError -[1] - <MethodNumber 2> ... exceptions.TypeError -[1] -= <MethodNumber 2> ... exceptions.TypeError -[1] * <MethodNumber 2> = [1, 1] -[1] *= <MethodNumber 2> => [1, 1] -[1] / <MethodNumber 2> ... exceptions.TypeError -[1] /= <MethodNumber 2> ... exceptions.TypeError -[1] ** <MethodNumber 2> ... exceptions.TypeError -[1] **= <MethodNumber 2> ... exceptions.TypeError -[1] % <MethodNumber 2> ... exceptions.TypeError -[1] %= <MethodNumber 2> ... exceptions.TypeError -[1] + <CoerceNumber 2> ... exceptions.TypeError -[1] += <CoerceNumber 2> ... exceptions.TypeError -[1] - <CoerceNumber 2> ... exceptions.TypeError -[1] -= <CoerceNumber 2> ... exceptions.TypeError -[1] * <CoerceNumber 2> = [1, 1] -[1] *= <CoerceNumber 2> => [1, 1] -[1] / <CoerceNumber 2> ... exceptions.TypeError -[1] /= <CoerceNumber 2> ... exceptions.TypeError -[1] ** <CoerceNumber 2> ... exceptions.TypeError -[1] **= <CoerceNumber 2> ... exceptions.TypeError -[1] % <CoerceNumber 2> ... exceptions.TypeError -[1] %= <CoerceNumber 2> ... exceptions.TypeError -(2,) + 2 ... exceptions.TypeError -(2,) += 2 ... exceptions.TypeError -(2,) - 2 ... exceptions.TypeError -(2,) -= 2 ... exceptions.TypeError -(2,) * 2 = (2, 2) -(2,) *= 2 => (2, 2) -(2,) / 2 ... exceptions.TypeError -(2,) /= 2 ... exceptions.TypeError -(2,) ** 2 ... exceptions.TypeError -(2,) **= 2 ... exceptions.TypeError -(2,) % 2 ... exceptions.TypeError -(2,) %= 2 ... exceptions.TypeError -(2,) + 4.0 ... exceptions.TypeError -(2,) += 4.0 ... exceptions.TypeError -(2,) - 4.0 ... exceptions.TypeError -(2,) -= 4.0 ... exceptions.TypeError -(2,) * 4.0 ... exceptions.TypeError -(2,) *= 4.0 ... exceptions.TypeError -(2,) / 4.0 ... exceptions.TypeError -(2,) /= 4.0 ... exceptions.TypeError -(2,) ** 4.0 ... exceptions.TypeError -(2,) **= 4.0 ... exceptions.TypeError -(2,) % 4.0 ... exceptions.TypeError -(2,) %= 4.0 ... exceptions.TypeError -(2,) + 2 ... exceptions.TypeError -(2,) += 2 ... exceptions.TypeError -(2,) - 2 ... exceptions.TypeError -(2,) -= 2 ... exceptions.TypeError -(2,) * 2 = (2, 2) -(2,) *= 2 => (2, 2) -(2,) / 2 ... exceptions.TypeError -(2,) /= 2 ... exceptions.TypeError -(2,) ** 2 ... exceptions.TypeError -(2,) **= 2 ... exceptions.TypeError -(2,) % 2 ... exceptions.TypeError -(2,) %= 2 ... exceptions.TypeError -(2,) + (2+0j) ... exceptions.TypeError -(2,) += (2+0j) ... exceptions.TypeError -(2,) - (2+0j) ... exceptions.TypeError -(2,) -= (2+0j) ... exceptions.TypeError -(2,) * (2+0j) ... exceptions.TypeError -(2,) *= (2+0j) ... exceptions.TypeError -(2,) / (2+0j) ... exceptions.TypeError -(2,) /= (2+0j) ... exceptions.TypeError -(2,) ** (2+0j) ... exceptions.TypeError -(2,) **= (2+0j) ... exceptions.TypeError -(2,) % (2+0j) ... exceptions.TypeError -(2,) %= (2+0j) ... exceptions.TypeError -(2,) + [1] ... exceptions.TypeError -(2,) += [1] ... exceptions.TypeError -(2,) - [1] ... exceptions.TypeError -(2,) -= [1] ... exceptions.TypeError -(2,) * [1] ... exceptions.TypeError -(2,) *= [1] ... exceptions.TypeError -(2,) / [1] ... exceptions.TypeError -(2,) /= [1] ... exceptions.TypeError -(2,) ** [1] ... exceptions.TypeError -(2,) **= [1] ... exceptions.TypeError -(2,) % [1] ... exceptions.TypeError -(2,) %= [1] ... exceptions.TypeError -(2,) + (2,) = (2, 2) -(2,) += (2,) => (2, 2) -(2,) - (2,) ... exceptions.TypeError -(2,) -= (2,) ... exceptions.TypeError -(2,) * (2,) ... exceptions.TypeError -(2,) *= (2,) ... exceptions.TypeError -(2,) / (2,) ... exceptions.TypeError -(2,) /= (2,) ... exceptions.TypeError -(2,) ** (2,) ... exceptions.TypeError -(2,) **= (2,) ... exceptions.TypeError -(2,) % (2,) ... exceptions.TypeError -(2,) %= (2,) ... exceptions.TypeError -(2,) + None ... exceptions.TypeError -(2,) += None ... exceptions.TypeError -(2,) - None ... exceptions.TypeError -(2,) -= None ... exceptions.TypeError -(2,) * None ... exceptions.TypeError -(2,) *= None ... exceptions.TypeError -(2,) / None ... exceptions.TypeError -(2,) /= None ... exceptions.TypeError -(2,) ** None ... exceptions.TypeError -(2,) **= None ... exceptions.TypeError -(2,) % None ... exceptions.TypeError -(2,) %= None ... exceptions.TypeError -(2,) + <MethodNumber 2> ... exceptions.TypeError -(2,) += <MethodNumber 2> ... exceptions.TypeError -(2,) - <MethodNumber 2> ... exceptions.TypeError -(2,) -= <MethodNumber 2> ... exceptions.TypeError -(2,) * <MethodNumber 2> = (2, 2) -(2,) *= <MethodNumber 2> => (2, 2) -(2,) / <MethodNumber 2> ... exceptions.TypeError -(2,) /= <MethodNumber 2> ... exceptions.TypeError -(2,) ** <MethodNumber 2> ... exceptions.TypeError -(2,) **= <MethodNumber 2> ... exceptions.TypeError -(2,) % <MethodNumber 2> ... exceptions.TypeError -(2,) %= <MethodNumber 2> ... exceptions.TypeError -(2,) + <CoerceNumber 2> ... exceptions.TypeError -(2,) += <CoerceNumber 2> ... exceptions.TypeError -(2,) - <CoerceNumber 2> ... exceptions.TypeError -(2,) -= <CoerceNumber 2> ... exceptions.TypeError -(2,) * <CoerceNumber 2> = (2, 2) -(2,) *= <CoerceNumber 2> => (2, 2) -(2,) / <CoerceNumber 2> ... exceptions.TypeError -(2,) /= <CoerceNumber 2> ... exceptions.TypeError -(2,) ** <CoerceNumber 2> ... exceptions.TypeError -(2,) **= <CoerceNumber 2> ... exceptions.TypeError -(2,) % <CoerceNumber 2> ... exceptions.TypeError -(2,) %= <CoerceNumber 2> ... exceptions.TypeError -None + 2 ... exceptions.TypeError -None += 2 ... exceptions.TypeError -None - 2 ... exceptions.TypeError -None -= 2 ... exceptions.TypeError -None * 2 ... exceptions.TypeError -None *= 2 ... exceptions.TypeError -None / 2 ... exceptions.TypeError -None /= 2 ... exceptions.TypeError -None ** 2 ... exceptions.TypeError -None **= 2 ... exceptions.TypeError -None % 2 ... exceptions.TypeError -None %= 2 ... exceptions.TypeError -None + 4.0 ... exceptions.TypeError -None += 4.0 ... exceptions.TypeError -None - 4.0 ... exceptions.TypeError -None -= 4.0 ... exceptions.TypeError -None * 4.0 ... exceptions.TypeError -None *= 4.0 ... exceptions.TypeError -None / 4.0 ... exceptions.TypeError -None /= 4.0 ... exceptions.TypeError -None ** 4.0 ... exceptions.TypeError -None **= 4.0 ... exceptions.TypeError -None % 4.0 ... exceptions.TypeError -None %= 4.0 ... exceptions.TypeError -None + 2 ... exceptions.TypeError -None += 2 ... exceptions.TypeError -None - 2 ... exceptions.TypeError -None -= 2 ... exceptions.TypeError -None * 2 ... exceptions.TypeError -None *= 2 ... exceptions.TypeError -None / 2 ... exceptions.TypeError -None /= 2 ... exceptions.TypeError -None ** 2 ... exceptions.TypeError -None **= 2 ... exceptions.TypeError -None % 2 ... exceptions.TypeError -None %= 2 ... exceptions.TypeError -None + (2+0j) ... exceptions.TypeError -None += (2+0j) ... exceptions.TypeError -None - (2+0j) ... exceptions.TypeError -None -= (2+0j) ... exceptions.TypeError -None * (2+0j) ... exceptions.TypeError -None *= (2+0j) ... exceptions.TypeError -None / (2+0j) ... exceptions.TypeError -None /= (2+0j) ... exceptions.TypeError -None ** (2+0j) ... exceptions.TypeError -None **= (2+0j) ... exceptions.TypeError -None % (2+0j) ... exceptions.TypeError -None %= (2+0j) ... exceptions.TypeError -None + [1] ... exceptions.TypeError -None += [1] ... exceptions.TypeError -None - [1] ... exceptions.TypeError -None -= [1] ... exceptions.TypeError -None * [1] ... exceptions.TypeError -None *= [1] ... exceptions.TypeError -None / [1] ... exceptions.TypeError -None /= [1] ... exceptions.TypeError -None ** [1] ... exceptions.TypeError -None **= [1] ... exceptions.TypeError -None % [1] ... exceptions.TypeError -None %= [1] ... exceptions.TypeError -None + (2,) ... exceptions.TypeError -None += (2,) ... exceptions.TypeError -None - (2,) ... exceptions.TypeError -None -= (2,) ... exceptions.TypeError -None * (2,) ... exceptions.TypeError -None *= (2,) ... exceptions.TypeError -None / (2,) ... exceptions.TypeError -None /= (2,) ... exceptions.TypeError -None ** (2,) ... exceptions.TypeError -None **= (2,) ... exceptions.TypeError -None % (2,) ... exceptions.TypeError -None %= (2,) ... exceptions.TypeError -None + None ... exceptions.TypeError -None += None ... exceptions.TypeError -None - None ... exceptions.TypeError -None -= None ... exceptions.TypeError -None * None ... exceptions.TypeError -None *= None ... exceptions.TypeError -None / None ... exceptions.TypeError -None /= None ... exceptions.TypeError -None ** None ... exceptions.TypeError -None **= None ... exceptions.TypeError -None % None ... exceptions.TypeError -None %= None ... exceptions.TypeError -None + <MethodNumber 2> ... exceptions.TypeError -None += <MethodNumber 2> ... exceptions.TypeError -None - <MethodNumber 2> ... exceptions.TypeError -None -= <MethodNumber 2> ... exceptions.TypeError -None * <MethodNumber 2> ... exceptions.TypeError -None *= <MethodNumber 2> ... exceptions.TypeError -None / <MethodNumber 2> ... exceptions.TypeError -None /= <MethodNumber 2> ... exceptions.TypeError -None ** <MethodNumber 2> ... exceptions.TypeError -None **= <MethodNumber 2> ... exceptions.TypeError -None % <MethodNumber 2> ... exceptions.TypeError -None %= <MethodNumber 2> ... exceptions.TypeError -None + <CoerceNumber 2> ... exceptions.TypeError -None += <CoerceNumber 2> ... exceptions.TypeError -None - <CoerceNumber 2> ... exceptions.TypeError -None -= <CoerceNumber 2> ... exceptions.TypeError -None * <CoerceNumber 2> ... exceptions.TypeError -None *= <CoerceNumber 2> ... exceptions.TypeError -None / <CoerceNumber 2> ... exceptions.TypeError -None /= <CoerceNumber 2> ... exceptions.TypeError -None ** <CoerceNumber 2> ... exceptions.TypeError -None **= <CoerceNumber 2> ... exceptions.TypeError -None % <CoerceNumber 2> ... exceptions.TypeError -None %= <CoerceNumber 2> ... exceptions.TypeError -<MethodNumber 2> + 2 = 4 -<MethodNumber 2> += 2 => 4 -<MethodNumber 2> - 2 = 0 -<MethodNumber 2> -= 2 => 0 -<MethodNumber 2> * 2 = 4 -<MethodNumber 2> *= 2 => 4 -<MethodNumber 2> / 2 = 1 -<MethodNumber 2> /= 2 => 1 -<MethodNumber 2> ** 2 = 4 -<MethodNumber 2> **= 2 => 4 -<MethodNumber 2> % 2 = 0 -<MethodNumber 2> %= 2 => 0 -<MethodNumber 2> + 4.0 = 6.0 -<MethodNumber 2> += 4.0 => 6.0 -<MethodNumber 2> - 4.0 = -2.0 -<MethodNumber 2> -= 4.0 => -2.0 -<MethodNumber 2> * 4.0 = 8.0 -<MethodNumber 2> *= 4.0 => 8.0 -<MethodNumber 2> / 4.0 = 0.5 -<MethodNumber 2> /= 4.0 => 0.5 -<MethodNumber 2> ** 4.0 = 16.0 -<MethodNumber 2> **= 4.0 => 16.0 -<MethodNumber 2> % 4.0 = 2.0 -<MethodNumber 2> %= 4.0 => 2.0 -<MethodNumber 2> + 2 = 4 -<MethodNumber 2> += 2 => 4 -<MethodNumber 2> - 2 = 0 -<MethodNumber 2> -= 2 => 0 -<MethodNumber 2> * 2 = 4 -<MethodNumber 2> *= 2 => 4 -<MethodNumber 2> / 2 = 1 -<MethodNumber 2> /= 2 => 1 -<MethodNumber 2> ** 2 = 4 -<MethodNumber 2> **= 2 => 4 -<MethodNumber 2> % 2 = 0 -<MethodNumber 2> %= 2 => 0 -<MethodNumber 2> + (2+0j) = (4.0 + 0.0j) -<MethodNumber 2> += (2+0j) => (4.0 + 0.0j) -<MethodNumber 2> - (2+0j) = (0.0 + 0.0j) -<MethodNumber 2> -= (2+0j) => (0.0 + 0.0j) -<MethodNumber 2> * (2+0j) = (4.0 + 0.0j) -<MethodNumber 2> *= (2+0j) => (4.0 + 0.0j) -<MethodNumber 2> / (2+0j) = (1.0 + 0.0j) -<MethodNumber 2> /= (2+0j) => (1.0 + 0.0j) -<MethodNumber 2> ** (2+0j) = (4.0 + 0.0j) -<MethodNumber 2> **= (2+0j) => (4.0 + 0.0j) -<MethodNumber 2> % (2+0j) = (0.0 + 0.0j) -<MethodNumber 2> %= (2+0j) => (0.0 + 0.0j) -<MethodNumber 2> + [1] ... exceptions.TypeError -<MethodNumber 2> += [1] ... exceptions.TypeError -<MethodNumber 2> - [1] ... exceptions.TypeError -<MethodNumber 2> -= [1] ... exceptions.TypeError -<MethodNumber 2> * [1] = [1, 1] -<MethodNumber 2> *= [1] => [1, 1] -<MethodNumber 2> / [1] ... exceptions.TypeError -<MethodNumber 2> /= [1] ... exceptions.TypeError -<MethodNumber 2> ** [1] ... exceptions.TypeError -<MethodNumber 2> **= [1] ... exceptions.TypeError -<MethodNumber 2> % [1] ... exceptions.TypeError -<MethodNumber 2> %= [1] ... exceptions.TypeError -<MethodNumber 2> + (2,) ... exceptions.TypeError -<MethodNumber 2> += (2,) ... exceptions.TypeError -<MethodNumber 2> - (2,) ... exceptions.TypeError -<MethodNumber 2> -= (2,) ... exceptions.TypeError -<MethodNumber 2> * (2,) = (2, 2) -<MethodNumber 2> *= (2,) => (2, 2) -<MethodNumber 2> / (2,) ... exceptions.TypeError -<MethodNumber 2> /= (2,) ... exceptions.TypeError -<MethodNumber 2> ** (2,) ... exceptions.TypeError -<MethodNumber 2> **= (2,) ... exceptions.TypeError -<MethodNumber 2> % (2,) ... exceptions.TypeError -<MethodNumber 2> %= (2,) ... exceptions.TypeError -<MethodNumber 2> + None ... exceptions.TypeError -<MethodNumber 2> += None ... exceptions.TypeError -<MethodNumber 2> - None ... exceptions.TypeError -<MethodNumber 2> -= None ... exceptions.TypeError -<MethodNumber 2> * None ... exceptions.TypeError -<MethodNumber 2> *= None ... exceptions.TypeError -<MethodNumber 2> / None ... exceptions.TypeError -<MethodNumber 2> /= None ... exceptions.TypeError -<MethodNumber 2> ** None ... exceptions.TypeError -<MethodNumber 2> **= None ... exceptions.TypeError -<MethodNumber 2> % None ... exceptions.TypeError -<MethodNumber 2> %= None ... exceptions.TypeError -<MethodNumber 2> + <MethodNumber 2> = 4 -<MethodNumber 2> += <MethodNumber 2> => 4 -<MethodNumber 2> - <MethodNumber 2> = 0 -<MethodNumber 2> -= <MethodNumber 2> => 0 -<MethodNumber 2> * <MethodNumber 2> = 4 -<MethodNumber 2> *= <MethodNumber 2> => 4 -<MethodNumber 2> / <MethodNumber 2> = 1 -<MethodNumber 2> /= <MethodNumber 2> => 1 -<MethodNumber 2> ** <MethodNumber 2> = 4 -<MethodNumber 2> **= <MethodNumber 2> => 4 -<MethodNumber 2> % <MethodNumber 2> = 0 -<MethodNumber 2> %= <MethodNumber 2> => 0 -<MethodNumber 2> + <CoerceNumber 2> = 4 -<MethodNumber 2> += <CoerceNumber 2> => 4 -<MethodNumber 2> - <CoerceNumber 2> = 0 -<MethodNumber 2> -= <CoerceNumber 2> => 0 -<MethodNumber 2> * <CoerceNumber 2> = 4 -<MethodNumber 2> *= <CoerceNumber 2> => 4 -<MethodNumber 2> / <CoerceNumber 2> = 1 -<MethodNumber 2> /= <CoerceNumber 2> => 1 -<MethodNumber 2> ** <CoerceNumber 2> = 4 -<MethodNumber 2> **= <CoerceNumber 2> => 4 -<MethodNumber 2> % <CoerceNumber 2> = 0 -<MethodNumber 2> %= <CoerceNumber 2> => 0 -<CoerceNumber 2> + 2 = 4 -<CoerceNumber 2> += 2 => 4 -<CoerceNumber 2> - 2 = 0 -<CoerceNumber 2> -= 2 => 0 -<CoerceNumber 2> * 2 = 4 -<CoerceNumber 2> *= 2 => 4 -<CoerceNumber 2> / 2 = 1 -<CoerceNumber 2> /= 2 => 1 -<CoerceNumber 2> ** 2 = 4 -<CoerceNumber 2> **= 2 => 4 -<CoerceNumber 2> % 2 = 0 -<CoerceNumber 2> %= 2 => 0 -<CoerceNumber 2> + 4.0 = 6.0 -<CoerceNumber 2> += 4.0 => 6.0 -<CoerceNumber 2> - 4.0 = -2.0 -<CoerceNumber 2> -= 4.0 => -2.0 -<CoerceNumber 2> * 4.0 = 8.0 -<CoerceNumber 2> *= 4.0 => 8.0 -<CoerceNumber 2> / 4.0 = 0.5 -<CoerceNumber 2> /= 4.0 => 0.5 -<CoerceNumber 2> ** 4.0 = 16.0 -<CoerceNumber 2> **= 4.0 => 16.0 -<CoerceNumber 2> % 4.0 = 2.0 -<CoerceNumber 2> %= 4.0 => 2.0 -<CoerceNumber 2> + 2 = 4 -<CoerceNumber 2> += 2 => 4 -<CoerceNumber 2> - 2 = 0 -<CoerceNumber 2> -= 2 => 0 -<CoerceNumber 2> * 2 = 4 -<CoerceNumber 2> *= 2 => 4 -<CoerceNumber 2> / 2 = 1 -<CoerceNumber 2> /= 2 => 1 -<CoerceNumber 2> ** 2 = 4 -<CoerceNumber 2> **= 2 => 4 -<CoerceNumber 2> % 2 = 0 -<CoerceNumber 2> %= 2 => 0 -<CoerceNumber 2> + (2+0j) = (4.0 + 0.0j) -<CoerceNumber 2> += (2+0j) => (4.0 + 0.0j) -<CoerceNumber 2> - (2+0j) = (0.0 + 0.0j) -<CoerceNumber 2> -= (2+0j) => (0.0 + 0.0j) -<CoerceNumber 2> * (2+0j) = (4.0 + 0.0j) -<CoerceNumber 2> *= (2+0j) => (4.0 + 0.0j) -<CoerceNumber 2> / (2+0j) = (1.0 + 0.0j) -<CoerceNumber 2> /= (2+0j) => (1.0 + 0.0j) -<CoerceNumber 2> ** (2+0j) = (4.0 + 0.0j) -<CoerceNumber 2> **= (2+0j) => (4.0 + 0.0j) -<CoerceNumber 2> % (2+0j) = (0.0 + 0.0j) -<CoerceNumber 2> %= (2+0j) => (0.0 + 0.0j) -<CoerceNumber 2> + [1] ... exceptions.TypeError -<CoerceNumber 2> += [1] ... exceptions.TypeError -<CoerceNumber 2> - [1] ... exceptions.TypeError -<CoerceNumber 2> -= [1] ... exceptions.TypeError -<CoerceNumber 2> * [1] = [1, 1] -<CoerceNumber 2> *= [1] => [1, 1] -<CoerceNumber 2> / [1] ... exceptions.TypeError -<CoerceNumber 2> /= [1] ... exceptions.TypeError -<CoerceNumber 2> ** [1] ... exceptions.TypeError -<CoerceNumber 2> **= [1] ... exceptions.TypeError -<CoerceNumber 2> % [1] ... exceptions.TypeError -<CoerceNumber 2> %= [1] ... exceptions.TypeError -<CoerceNumber 2> + (2,) ... exceptions.TypeError -<CoerceNumber 2> += (2,) ... exceptions.TypeError -<CoerceNumber 2> - (2,) ... exceptions.TypeError -<CoerceNumber 2> -= (2,) ... exceptions.TypeError -<CoerceNumber 2> * (2,) = (2, 2) -<CoerceNumber 2> *= (2,) => (2, 2) -<CoerceNumber 2> / (2,) ... exceptions.TypeError -<CoerceNumber 2> /= (2,) ... exceptions.TypeError -<CoerceNumber 2> ** (2,) ... exceptions.TypeError -<CoerceNumber 2> **= (2,) ... exceptions.TypeError -<CoerceNumber 2> % (2,) ... exceptions.TypeError -<CoerceNumber 2> %= (2,) ... exceptions.TypeError -<CoerceNumber 2> + None ... exceptions.TypeError -<CoerceNumber 2> += None ... exceptions.TypeError -<CoerceNumber 2> - None ... exceptions.TypeError -<CoerceNumber 2> -= None ... exceptions.TypeError -<CoerceNumber 2> * None ... exceptions.TypeError -<CoerceNumber 2> *= None ... exceptions.TypeError -<CoerceNumber 2> / None ... exceptions.TypeError -<CoerceNumber 2> /= None ... exceptions.TypeError -<CoerceNumber 2> ** None ... exceptions.TypeError -<CoerceNumber 2> **= None ... exceptions.TypeError -<CoerceNumber 2> % None ... exceptions.TypeError -<CoerceNumber 2> %= None ... exceptions.TypeError -<CoerceNumber 2> + <MethodNumber 2> = 4 -<CoerceNumber 2> += <MethodNumber 2> => 4 -<CoerceNumber 2> - <MethodNumber 2> = 0 -<CoerceNumber 2> -= <MethodNumber 2> => 0 -<CoerceNumber 2> * <MethodNumber 2> = 4 -<CoerceNumber 2> *= <MethodNumber 2> => 4 -<CoerceNumber 2> / <MethodNumber 2> = 1 -<CoerceNumber 2> /= <MethodNumber 2> => 1 -<CoerceNumber 2> ** <MethodNumber 2> = 4 -<CoerceNumber 2> **= <MethodNumber 2> => 4 -<CoerceNumber 2> % <MethodNumber 2> = 0 -<CoerceNumber 2> %= <MethodNumber 2> => 0 -<CoerceNumber 2> + <CoerceNumber 2> = 4 -<CoerceNumber 2> += <CoerceNumber 2> => 4 -<CoerceNumber 2> - <CoerceNumber 2> = 0 -<CoerceNumber 2> -= <CoerceNumber 2> => 0 -<CoerceNumber 2> * <CoerceNumber 2> = 4 -<CoerceNumber 2> *= <CoerceNumber 2> => 4 -<CoerceNumber 2> / <CoerceNumber 2> = 1 -<CoerceNumber 2> /= <CoerceNumber 2> => 1 -<CoerceNumber 2> ** <CoerceNumber 2> = 4 -<CoerceNumber 2> **= <CoerceNumber 2> => 4 -<CoerceNumber 2> % <CoerceNumber 2> = 0 -<CoerceNumber 2> %= <CoerceNumber 2> => 0 -divmod(2, 2) = (1, 0) -divmod(2, 4.0) = (0.0, 2.0) -divmod(2, 2) = (1L, 0L) -divmod(2, (2+0j)) = ((1+0j), 0j) -divmod(2, [1]) ... exceptions.TypeError -divmod(2, (2,)) ... exceptions.TypeError -divmod(2, None) ... exceptions.TypeError -divmod(2, <MethodNumber 2>) ... exceptions.TypeError -divmod(2, <CoerceNumber 2>) = (1, 0) -divmod(4.0, 2) = (2.0, 0.0) -divmod(4.0, 4.0) = (1.0, 0.0) -divmod(4.0, 2) = (2.0, 0.0) -divmod(4.0, (2+0j)) = ((2+0j), 0j) -divmod(4.0, [1]) ... exceptions.TypeError -divmod(4.0, (2,)) ... exceptions.TypeError -divmod(4.0, None) ... exceptions.TypeError -divmod(4.0, <MethodNumber 2>) ... exceptions.TypeError -divmod(4.0, <CoerceNumber 2>) = (2.0, 0.0) -divmod(2, 2) = (1L, 0L) -divmod(2, 4.0) = (0.0, 2.0) -divmod(2, 2) = (1L, 0L) -divmod(2, (2+0j)) = ((1+0j), 0j) -divmod(2, [1]) ... exceptions.TypeError -divmod(2, (2,)) ... exceptions.TypeError -divmod(2, None) ... exceptions.TypeError -divmod(2, <MethodNumber 2>) ... exceptions.TypeError -divmod(2, <CoerceNumber 2>) = (1L, 0L) -divmod((2+0j), 2) = ((1+0j), 0j) -divmod((2+0j), 4.0) = (0j, (2+0j)) -divmod((2+0j), 2) = ((1+0j), 0j) -divmod((2+0j), (2+0j)) = ((1+0j), 0j) -divmod((2+0j), [1]) ... exceptions.TypeError -divmod((2+0j), (2,)) ... exceptions.TypeError -divmod((2+0j), None) ... exceptions.TypeError -divmod((2+0j), <MethodNumber 2>) ... exceptions.TypeError -divmod((2+0j), <CoerceNumber 2>) = ((1+0j), 0j) -divmod([1], 2) ... exceptions.TypeError -divmod([1], 4.0) ... exceptions.TypeError -divmod([1], 2) ... exceptions.TypeError -divmod([1], (2+0j)) ... exceptions.TypeError -divmod([1], [1]) ... exceptions.TypeError -divmod([1], (2,)) ... exceptions.TypeError -divmod([1], None) ... exceptions.TypeError -divmod([1], <MethodNumber 2>) ... exceptions.TypeError -divmod([1], <CoerceNumber 2>) ... exceptions.TypeError -divmod((2,), 2) ... exceptions.TypeError -divmod((2,), 4.0) ... exceptions.TypeError -divmod((2,), 2) ... exceptions.TypeError -divmod((2,), (2+0j)) ... exceptions.TypeError -divmod((2,), [1]) ... exceptions.TypeError -divmod((2,), (2,)) ... exceptions.TypeError -divmod((2,), None) ... exceptions.TypeError -divmod((2,), <MethodNumber 2>) ... exceptions.TypeError -divmod((2,), <CoerceNumber 2>) ... exceptions.TypeError -divmod(None, 2) ... exceptions.TypeError -divmod(None, 4.0) ... exceptions.TypeError -divmod(None, 2) ... exceptions.TypeError -divmod(None, (2+0j)) ... exceptions.TypeError -divmod(None, [1]) ... exceptions.TypeError -divmod(None, (2,)) ... exceptions.TypeError -divmod(None, None) ... exceptions.TypeError -divmod(None, <MethodNumber 2>) ... exceptions.TypeError -divmod(None, <CoerceNumber 2>) ... exceptions.TypeError -divmod(<MethodNumber 2>, 2) ... exceptions.TypeError -divmod(<MethodNumber 2>, 4.0) ... exceptions.TypeError -divmod(<MethodNumber 2>, 2) ... exceptions.TypeError -divmod(<MethodNumber 2>, (2+0j)) ... exceptions.TypeError -divmod(<MethodNumber 2>, [1]) ... exceptions.TypeError -divmod(<MethodNumber 2>, (2,)) ... exceptions.TypeError -divmod(<MethodNumber 2>, None) ... exceptions.TypeError -divmod(<MethodNumber 2>, <MethodNumber 2>) ... exceptions.TypeError -divmod(<MethodNumber 2>, <CoerceNumber 2>) ... exceptions.TypeError -divmod(<CoerceNumber 2>, 2) = (1, 0) -divmod(<CoerceNumber 2>, 4.0) = (0.0, 2.0) -divmod(<CoerceNumber 2>, 2) = (1L, 0L) -divmod(<CoerceNumber 2>, (2+0j)) = ((1+0j), 0j) -divmod(<CoerceNumber 2>, [1]) ... exceptions.TypeError -divmod(<CoerceNumber 2>, (2,)) ... exceptions.TypeError -divmod(<CoerceNumber 2>, None) ... exceptions.TypeError -divmod(<CoerceNumber 2>, <MethodNumber 2>) ... exceptions.TypeError -divmod(<CoerceNumber 2>, <CoerceNumber 2>) = (1, 0) diff --git a/Lib/test/output/test_compare b/Lib/test/output/test_compare deleted file mode 100644 index 210bd97..0000000 --- a/Lib/test/output/test_compare +++ /dev/null @@ -1,101 +0,0 @@ -test_compare -2 == 2 -2 == 2.0 -2 == 2 -2 == (2+0j) -2 != [1] -2 != (3,) -2 != None -2 != <Empty> -2 == <Coerce 2> -2 == <Cmp 2.0> -2.0 == 2 -2.0 == 2.0 -2.0 == 2 -2.0 == (2+0j) -2.0 != [1] -2.0 != (3,) -2.0 != None -2.0 != <Empty> -2.0 == <Coerce 2> -2.0 == <Cmp 2.0> -2 == 2 -2 == 2.0 -2 == 2 -2 == (2+0j) -2 != [1] -2 != (3,) -2 != None -2 != <Empty> -2 == <Coerce 2> -2 == <Cmp 2.0> -(2+0j) == 2 -(2+0j) == 2.0 -(2+0j) == 2 -(2+0j) == (2+0j) -(2+0j) != [1] -(2+0j) != (3,) -(2+0j) != None -(2+0j) != <Empty> -(2+0j) == <Coerce 2> -(2+0j) == <Cmp 2.0> -[1] != 2 -[1] != 2.0 -[1] != 2 -[1] != (2+0j) -[1] == [1] -[1] != (3,) -[1] != None -[1] != <Empty> -[1] != <Coerce 2> -[1] != <Cmp 2.0> -(3,) != 2 -(3,) != 2.0 -(3,) != 2 -(3,) != (2+0j) -(3,) != [1] -(3,) == (3,) -(3,) != None -(3,) != <Empty> -(3,) != <Coerce 2> -(3,) != <Cmp 2.0> -None != 2 -None != 2.0 -None != 2 -None != (2+0j) -None != [1] -None != (3,) -None == None -None != <Empty> -None != <Coerce 2> -None != <Cmp 2.0> -<Empty> != 2 -<Empty> != 2.0 -<Empty> != 2 -<Empty> != (2+0j) -<Empty> != [1] -<Empty> != (3,) -<Empty> != None -<Empty> == <Empty> -<Empty> != <Coerce 2> -<Empty> != <Cmp 2.0> -<Coerce 2> == 2 -<Coerce 2> == 2.0 -<Coerce 2> == 2 -<Coerce 2> == (2+0j) -<Coerce 2> != [1] -<Coerce 2> != (3,) -<Coerce 2> != None -<Coerce 2> != <Empty> -<Coerce 2> == <Coerce 2> -<Coerce 2> == <Cmp 2.0> -<Cmp 2.0> == 2 -<Cmp 2.0> == 2.0 -<Cmp 2.0> == 2 -<Cmp 2.0> == (2+0j) -<Cmp 2.0> != [1] -<Cmp 2.0> != (3,) -<Cmp 2.0> != None -<Cmp 2.0> != <Empty> -<Cmp 2.0> == <Coerce 2> -<Cmp 2.0> == <Cmp 2.0> diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 85f57a6..566e54b 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -520,7 +520,7 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): import gc def cleanup(): import _strptime, linecache, warnings, dircache - import urlparse, urllib, urllib2 + import urlparse, urllib, urllib2, mimetypes, doctest from distutils.dir_util import _path_created _path_created.clear() warnings.filters[:] = fs @@ -536,6 +536,8 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): sys.path_importer_cache.update(pic) dircache.reset() linecache.clearcache() + mimetypes._default_mime_types() + doctest.master = None if indirect_test: def run_the_test(): indirect_test() @@ -547,6 +549,7 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): print >> sys.stderr, "beginning", repcount, "repetitions" print >> sys.stderr, \ ("1234567890"*(repcount//10 + 1))[:repcount] + cleanup() for i in range(repcount): rc = sys.gettotalrefcount() run_the_test() @@ -744,6 +747,8 @@ _expectations = { test_sunaudiodev test_threadsignals test_timing + test_wait3 + test_wait4 """, 'linux2': """ @@ -761,6 +766,8 @@ _expectations = { test_nis test_ntpath test_ossaudiodev + test_sqlite + test_startfile test_sunaudiodev """, 'mac': @@ -800,6 +807,8 @@ _expectations = { test_pwd test_resource test_signal + test_sqlite + test_startfile test_sunaudiodev test_sundry test_tarfile @@ -824,6 +833,8 @@ _expectations = { test_openpty test_pyexpat test_sax + test_startfile + test_sqlite test_sunaudiodev test_sundry """, @@ -846,6 +857,8 @@ _expectations = { test_openpty test_pyexpat test_sax + test_sqlite + test_startfile test_sunaudiodev test_sundry """, @@ -873,6 +886,8 @@ _expectations = { test_pyexpat test_queue test_sax + test_sqlite + test_startfile test_sunaudiodev test_sundry test_thread @@ -913,6 +928,8 @@ _expectations = { test_pty test_pwd test_strop + test_sqlite + test_startfile test_sunaudiodev test_sundry test_thread @@ -930,7 +947,6 @@ _expectations = { test_cd test_cl test_curses - test_dl test_gdbm test_gl test_imgfile @@ -942,6 +958,8 @@ _expectations = { test_ntpath test_ossaudiodev test_poll + test_sqlite + test_startfile test_sunaudiodev """, 'sunos5': @@ -960,6 +978,8 @@ _expectations = { test_imgfile test_linuxaudiodev test_openpty + test_sqlite + test_startfile test_zipfile test_zlib """, @@ -986,6 +1006,8 @@ _expectations = { test_openpty test_pyexpat test_sax + test_sqlite + test_startfile test_sunaudiodev test_zipfile test_zlib @@ -1011,6 +1033,8 @@ _expectations = { test_poll test_popen2 test_resource + test_sqlite + test_startfile test_sunaudiodev """, 'cygwin': @@ -1032,6 +1056,7 @@ _expectations = { test_nis test_ossaudiodev test_socketserver + test_sqlite test_sunaudiodev """, 'os2emx': @@ -1058,6 +1083,8 @@ _expectations = { test_pty test_resource test_signal + test_sqlite + test_startfile test_sunaudiodev """, 'freebsd4': @@ -1084,6 +1111,8 @@ _expectations = { test_scriptpackages test_socket_ssl test_socketserver + test_sqlite + test_startfile test_sunaudiodev test_tcl test_timeout @@ -1113,6 +1142,8 @@ _expectations = { test_macostools test_nis test_ossaudiodev + test_sqlite + test_startfile test_sunaudiodev test_tcl test_winreg @@ -1120,6 +1151,38 @@ _expectations = { test_zipimport test_zlib """, + 'openbsd3': + """ + test_aepack + test_al + test_applesingle + test_bsddb + test_bsddb3 + test_cd + test_cl + test_ctypes + test_dl + test_gdbm + test_gl + test_imgfile + test_linuxaudiodev + test_locale + test_macfs + test_macostools + test_nis + test_normalization + test_ossaudiodev + test_pep277 + test_plistlib + test_scriptpackages + test_tcl + test_sqlite + test_startfile + test_sunaudiodev + test_unicode_file + test_winreg + test_winsound + """, } _expectations['freebsd5'] = _expectations['freebsd4'] _expectations['freebsd6'] = _expectations['freebsd4'] diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 0b2e7da..c45e139 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -5,8 +5,6 @@ from test.test_support import verify, verbose import sys import warnings -warnings.filterwarnings("ignore", ".* regsub .*", DeprecationWarning, - r'^regsub$') warnings.filterwarnings("ignore", "the gopherlib module is deprecated", DeprecationWarning, @@ -128,8 +126,6 @@ class AllTest(unittest.TestCase): self.check_all("quopri") self.check_all("random") self.check_all("re") - self.check_all("reconvert") - self.check_all("regsub") self.check_all("repr") self.check_all("rexec") self.check_all("rfc822") diff --git a/Lib/test/test_applesingle.py b/Lib/test/test_applesingle.py index 2a2d60a..d533f1a 100644 --- a/Lib/test/test_applesingle.py +++ b/Lib/test/test_applesingle.py @@ -15,8 +15,8 @@ AS_VERSION=0x00020000 dataforkdata = 'hello\r\0world\n' resourceforkdata = 'goodbye\ncruel\0world\r' -applesingledata = struct.pack("ll16sh", AS_MAGIC, AS_VERSION, "foo", 2) + \ - struct.pack("llllll", 1, 50, len(dataforkdata), +applesingledata = struct.pack(">ll16sh", AS_MAGIC, AS_VERSION, "foo", 2) + \ + struct.pack(">llllll", 1, 50, len(dataforkdata), 2, 50+len(dataforkdata), len(resourceforkdata)) + \ dataforkdata + \ resourceforkdata diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 87d395d..62361fc 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -61,7 +61,7 @@ class BaseTest(unittest.TestCase): bi = a.buffer_info() self.assert_(isinstance(bi, tuple)) self.assertEqual(len(bi), 2) - self.assert_(isinstance(bi[0], int)) + self.assert_(isinstance(bi[0], (int, long))) self.assert_(isinstance(bi[1], int)) self.assertEqual(bi[1], len(a)) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index b42caa3..c64ad28 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -119,7 +119,8 @@ eval_tests = [ # excepthandler, arguments, keywords, alias if __name__=='__main__' and sys.argv[1:] == ['-g']: - for statements, kind in ((exec_tests, "exec"), (single_tests, "single"), (eval_tests, "eval")): + for statements, kind in ((exec_tests, "exec"), (single_tests, "single"), + (eval_tests, "eval")): print kind+"_results = [" for s in statements: print repr(to_tuple(compile(s, "?", kind, 0x400)))+"," @@ -131,7 +132,7 @@ def test_order(ast_node, parent_pos): if not isinstance(ast_node, _ast.AST) or ast_node._fields == None: return - if isinstance(ast_node, (_ast.expr, _ast.stmt)): + if isinstance(ast_node, (_ast.expr, _ast.stmt, _ast.excepthandler)): node_pos = (ast_node.lineno, ast_node.col_offset) assert node_pos >= parent_pos, (node_pos, parent_pos) parent_pos = (ast_node.lineno, ast_node.col_offset) @@ -145,8 +146,8 @@ def test_order(ast_node, parent_pos): def run_tests(): for input, output, kind in ((exec_tests, exec_results, "exec"), - (single_tests, single_results, "single"), - (eval_tests, eval_results, "eval")): + (single_tests, single_results, "single"), + (eval_tests, eval_results, "eval")): for i, o in itertools.izip(input, output): ast_tree = compile(i, "?", kind, 0x400) assert to_tuple(ast_tree) == o @@ -165,7 +166,7 @@ exec_results = [ ('Module', [('While', (1, 0), ('Name', (1, 6), 'v', ('Load',)), [('Pass', (1, 8))], [])]), ('Module', [('If', (1, 0), ('Name', (1, 3), 'v', ('Load',)), [('Pass', (1, 5))], [])]), ('Module', [('Raise', (1, 0), ('Name', (1, 6), 'Exception', ('Load',)), ('Str', (1, 17), 'string'), None)]), -('Module', [('TryExcept', (1, 0), [('Pass', (2, 2))], [('excepthandler', ('Name', (3, 7), 'Exception', ('Load',)), None, [('Pass', (4, 2))])], [])]), +('Module', [('TryExcept', (1, 0), [('Pass', (2, 2))], [('excepthandler', (3, 0), ('Name', (3, 7), 'Exception', ('Load',)), None, [('Pass', (4, 2))], 3, 0)], [])]), ('Module', [('TryFinally', (1, 0), [('Pass', (2, 2))], [('Pass', (4, 2))])]), ('Module', [('Assert', (1, 0), ('Name', (1, 7), 'v', ('Load',)), None)]), ('Module', [('Import', (1, 0), [('alias', 'sys', None)])]), diff --git a/Lib/test/test_audioop.py b/Lib/test/test_audioop.py index 440adab..f585733 100644 --- a/Lib/test/test_audioop.py +++ b/Lib/test/test_audioop.py @@ -136,12 +136,30 @@ def testlin2adpcm(data): return 0 return 1 +def testlin2alaw(data): + if verbose: + print 'lin2alaw' + if audioop.lin2alaw(data[0], 1) != '\xd5\xc5\xf5' or \ + audioop.lin2alaw(data[1], 2) != '\xd5\xd5\xd5' or \ + audioop.lin2alaw(data[2], 4) != '\xd5\xd5\xd5': + return 0 + return 1 + +def testalaw2lin(data): + if verbose: + print 'alaw2lin' + # Cursory + d = audioop.lin2alaw(data[0], 1) + if audioop.alaw2lin(d, 1) != data[0]: + return 0 + return 1 + def testlin2ulaw(data): if verbose: print 'lin2ulaw' - if audioop.lin2ulaw(data[0], 1) != '\377\347\333' or \ - audioop.lin2ulaw(data[1], 2) != '\377\377\377' or \ - audioop.lin2ulaw(data[2], 4) != '\377\377\377': + if audioop.lin2ulaw(data[0], 1) != '\xff\xe7\xdb' or \ + audioop.lin2ulaw(data[1], 2) != '\xff\xff\xff' or \ + audioop.lin2ulaw(data[2], 4) != '\xff\xff\xff': return 0 return 1 diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 22cca44..0309d6e 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -1,258 +1,312 @@ # Augmented assignment test. -x = 2 -x += 1 -x *= 2 -x **= 2 -x -= 8 -x %= 12 -x >>= 1 -x &= 2 -x |= 5 -x ^= 1 -x <<= 2 -x /= 2 -x //= 2 - -print x -print int(x) - -x = [2] -x[0] += 1 -x[0] *= 2 -x[0] **= 2 -x[0] -= 8 -x[0] %= 12 -x[0] >>= 1 -x[0] &= 2 -x[0] |= 5 -x[0] ^= 1 -x[0] <<= 2 -x[0] /= 2 -x[0] //= 2 - -print x -print int(x[0]) - -x = {0: 2} -x[0] += 1 -x[0] *= 2 -x[0] **= 2 -x[0] -= 8 -x[0] %= 12 -x[0] >>= 1 -x[0] &= 2 -x[0] |= 5 -x[0] ^= 1 -x[0] <<= 2 -x[0] /= 2 -x[0] //= 2 - -print x[0] -print int(x[0]) - -x = [1,2] -x += [3,4] -x *= 2 - -print x - -x = [1, 2, 3] -y = x -x[1:2] *= 2 -y[1:2] += [1] - -print x -print x is y - -class aug_test: - def __init__(self, value): - self.val = value - def __radd__(self, val): - return self.val + val - def __add__(self, val): - return aug_test(self.val + val) - - -class aug_test2(aug_test): - def __iadd__(self, val): - self.val = self.val + val - return self - -class aug_test3(aug_test): - def __iadd__(self, val): - return aug_test3(self.val + val) - -x = aug_test(1) -y = x -x += 10 - -print isinstance(x, aug_test) -print y is not x -print x.val - -x = aug_test2(2) -y = x -x += 10 - -print y is x -print x.val - -x = aug_test3(3) -y = x -x += 10 - -print isinstance(x, aug_test3) -print y is not x -print x.val - -class testall: - - def __add__(self, val): - print "__add__ called" - def __radd__(self, val): - print "__radd__ called" - def __iadd__(self, val): - print "__iadd__ called" - return self - - def __sub__(self, val): - print "__sub__ called" - def __rsub__(self, val): - print "__rsub__ called" - def __isub__(self, val): - print "__isub__ called" - return self - - def __mul__(self, val): - print "__mul__ called" - def __rmul__(self, val): - print "__rmul__ called" - def __imul__(self, val): - print "__imul__ called" - return self - - def __floordiv__(self, val): - print "__floordiv__ called" - return self - def __ifloordiv__(self, val): - print "__ifloordiv__ called" - return self - def __rfloordiv__(self, val): - print "__rfloordiv__ called" - return self - - def __truediv__(self, val): - print "__truediv__ called" - return self - def __itruediv__(self, val): - print "__itruediv__ called" - return self - def __rtruediv__(self, val): - print "__rtruediv__ called" - return self - - def __mod__(self, val): - print "__mod__ called" - def __rmod__(self, val): - print "__rmod__ called" - def __imod__(self, val): - print "__imod__ called" - return self - - def __pow__(self, val): - print "__pow__ called" - def __rpow__(self, val): - print "__rpow__ called" - def __ipow__(self, val): - print "__ipow__ called" - return self - - def __or__(self, val): - print "__or__ called" - def __ror__(self, val): - print "__ror__ called" - def __ior__(self, val): - print "__ior__ called" - return self - - def __and__(self, val): - print "__and__ called" - def __rand__(self, val): - print "__rand__ called" - def __iand__(self, val): - print "__iand__ called" - return self - - def __xor__(self, val): - print "__xor__ called" - def __rxor__(self, val): - print "__rxor__ called" - def __ixor__(self, val): - print "__ixor__ called" - return self - - def __rshift__(self, val): - print "__rshift__ called" - def __rrshift__(self, val): - print "__rrshift__ called" - def __irshift__(self, val): - print "__irshift__ called" - return self - - def __lshift__(self, val): - print "__lshift__ called" - def __rlshift__(self, val): - print "__rlshift__ called" - def __ilshift__(self, val): - print "__ilshift__ called" - return self - -x = testall() -x + 1 -1 + x -x += 1 - -x - 1 -1 - x -x -= 1 - -x * 1 -1 * x -x *= 1 - -x / 1 -1 / x -x /= 1 - -x // 1 -1 // x -x //= 1 - -x % 1 -1 % x -x %= 1 - -x ** 1 -1 ** x -x **= 1 - -x | 1 -1 | x -x |= 1 - -x & 1 -1 & x -x &= 1 - -x ^ 1 -1 ^ x -x ^= 1 - -x >> 1 -1 >> x -x >>= 1 - -x << 1 -1 << x -x <<= 1 +from test.test_support import run_unittest +import unittest + + +class AugAssignTest(unittest.TestCase): + def testBasic(self): + x = 2 + x += 1 + x *= 2 + x **= 2 + x -= 8 + x //= 5 + x %= 3 + x &= 2 + x |= 5 + x ^= 1 + x /= 2 + self.assertEquals(x, 3.0) + + def testInList(self): + x = [2] + x[0] += 1 + x[0] *= 2 + x[0] **= 2 + x[0] -= 8 + x[0] //= 5 + x[0] %= 3 + x[0] &= 2 + x[0] |= 5 + x[0] ^= 1 + x[0] /= 2 + self.assertEquals(x[0], 3.0) + + def testInDict(self): + x = {0: 2} + x[0] += 1 + x[0] *= 2 + x[0] **= 2 + x[0] -= 8 + x[0] //= 5 + x[0] %= 3 + x[0] &= 2 + x[0] |= 5 + x[0] ^= 1 + x[0] /= 2 + self.assertEquals(x[0], 3.0) + + def testSequences(self): + x = [1,2] + x += [3,4] + x *= 2 + + self.assertEquals(x, [1, 2, 3, 4, 1, 2, 3, 4]) + + x = [1, 2, 3] + y = x + x[1:2] *= 2 + y[1:2] += [1] + + self.assertEquals(x, [1, 2, 1, 2, 3]) + self.assert_(x is y) + + def testCustomMethods1(self): + + class aug_test: + def __init__(self, value): + self.val = value + def __radd__(self, val): + return self.val + val + def __add__(self, val): + return aug_test(self.val + val) + + class aug_test2(aug_test): + def __iadd__(self, val): + self.val = self.val + val + return self + + class aug_test3(aug_test): + def __iadd__(self, val): + return aug_test3(self.val + val) + + x = aug_test(1) + y = x + x += 10 + + self.assert_(isinstance(x, aug_test)) + self.assert_(y is not x) + self.assertEquals(x.val, 11) + + x = aug_test2(2) + y = x + x += 10 + + self.assert_(y is x) + self.assertEquals(x.val, 12) + + x = aug_test3(3) + y = x + x += 10 + + self.assert_(isinstance(x, aug_test3)) + self.assert_(y is not x) + self.assertEquals(x.val, 13) + + + def testCustomMethods2(test_self): + output = [] + + class testall: + def __add__(self, val): + output.append("__add__ called") + def __radd__(self, val): + output.append("__radd__ called") + def __iadd__(self, val): + output.append("__iadd__ called") + return self + + def __sub__(self, val): + output.append("__sub__ called") + def __rsub__(self, val): + output.append("__rsub__ called") + def __isub__(self, val): + output.append("__isub__ called") + return self + + def __mul__(self, val): + output.append("__mul__ called") + def __rmul__(self, val): + output.append("__rmul__ called") + def __imul__(self, val): + output.append("__imul__ called") + return self + + def __div__(self, val): + output.append("__div__ called") + def __rdiv__(self, val): + output.append("__rdiv__ called") + def __idiv__(self, val): + output.append("__idiv__ called") + return self + + def __floordiv__(self, val): + output.append("__floordiv__ called") + return self + def __ifloordiv__(self, val): + output.append("__ifloordiv__ called") + return self + def __rfloordiv__(self, val): + output.append("__rfloordiv__ called") + return self + + def __truediv__(self, val): + output.append("__truediv__ called") + return self + def __rtruediv__(self, val): + output.append("__rtruediv__ called") + return self + def __itruediv__(self, val): + output.append("__itruediv__ called") + return self + + def __mod__(self, val): + output.append("__mod__ called") + def __rmod__(self, val): + output.append("__rmod__ called") + def __imod__(self, val): + output.append("__imod__ called") + return self + + def __pow__(self, val): + output.append("__pow__ called") + def __rpow__(self, val): + output.append("__rpow__ called") + def __ipow__(self, val): + output.append("__ipow__ called") + return self + + def __or__(self, val): + output.append("__or__ called") + def __ror__(self, val): + output.append("__ror__ called") + def __ior__(self, val): + output.append("__ior__ called") + return self + + def __and__(self, val): + output.append("__and__ called") + def __rand__(self, val): + output.append("__rand__ called") + def __iand__(self, val): + output.append("__iand__ called") + return self + + def __xor__(self, val): + output.append("__xor__ called") + def __rxor__(self, val): + output.append("__rxor__ called") + def __ixor__(self, val): + output.append("__ixor__ called") + return self + + def __rshift__(self, val): + output.append("__rshift__ called") + def __rrshift__(self, val): + output.append("__rrshift__ called") + def __irshift__(self, val): + output.append("__irshift__ called") + return self + + def __lshift__(self, val): + output.append("__lshift__ called") + def __rlshift__(self, val): + output.append("__rlshift__ called") + def __ilshift__(self, val): + output.append("__ilshift__ called") + return self + + x = testall() + x + 1 + 1 + x + x += 1 + + x - 1 + 1 - x + x -= 1 + + x * 1 + 1 * x + x *= 1 + + x / 1 + 1 / x + x /= 1 + + x // 1 + 1 // x + x //= 1 + + x % 1 + 1 % x + x %= 1 + + x ** 1 + 1 ** x + x **= 1 + + x | 1 + 1 | x + x |= 1 + + x & 1 + 1 & x + x &= 1 + + x ^ 1 + 1 ^ x + x ^= 1 + + x >> 1 + 1 >> x + x >>= 1 + + x << 1 + 1 << x + x <<= 1 + + test_self.assertEquals(output, '''\ +__add__ called +__radd__ called +__iadd__ called +__sub__ called +__rsub__ called +__isub__ called +__mul__ called +__rmul__ called +__imul__ called +__truediv__ called +__rtruediv__ called +__itruediv__ called +__floordiv__ called +__rfloordiv__ called +__ifloordiv__ called +__mod__ called +__rmod__ called +__imod__ called +__pow__ called +__rpow__ called +__ipow__ called +__or__ called +__ror__ called +__ior__ called +__and__ called +__rand__ called +__iand__ called +__xor__ called +__rxor__ called +__ixor__ called +__rshift__ called +__rrshift__ called +__irshift__ called +__lshift__ called +__rlshift__ called +__ilshift__ called +'''.splitlines()) + +def test_main(): + run_unittest(AugAssignTest) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_bsddb.py b/Lib/test/test_bsddb.py index 1ec4801..513e541 100755 --- a/Lib/test/test_bsddb.py +++ b/Lib/test/test_bsddb.py @@ -11,9 +11,10 @@ from test import test_support from sets import Set class TestBSDDB(unittest.TestCase): + openflag = 'c' def setUp(self): - self.f = self.openmethod[0](self.fname, 'c') + self.f = self.openmethod[0](self.fname, self.openflag, cachesize=32768) self.d = dict(q='Guido', w='van', e='Rossum', r='invented', t='Python', y='') for k, v in self.d.iteritems(): self.f[k] = v @@ -267,6 +268,11 @@ class TestBTree_InMemory(TestBSDDB): fname = None openmethod = [bsddb.btopen] +class TestBTree_InMemory_Truncate(TestBSDDB): + fname = None + openflag = 'n' + openmethod = [bsddb.btopen] + class TestHashTable(TestBSDDB): fname = test_support.TESTFN openmethod = [bsddb.hashopen] @@ -285,6 +291,7 @@ def test_main(verbose=None): TestHashTable, TestBTree_InMemory, TestHashTable_InMemory, + TestBTree_InMemory_Truncate, ) if __name__ == "__main__": diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 6f11fdd..ef4f407 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -108,6 +108,7 @@ class BuiltinTest(unittest.TestCase): __import__('string') self.assertRaises(ImportError, __import__, 'spamspam') self.assertRaises(TypeError, __import__, 1, 2, 3, 4) + self.assertRaises(ValueError, __import__, '') def test_abs(self): # int @@ -1317,6 +1318,9 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(round(-8.0, -1), -10.0) + # test new kwargs + self.assertEqual(round(number=-8.0, ndigits=-1), -10.0) + self.assertRaises(TypeError, round) def test_setattr(self): diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 34d365b..e414324 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -4,6 +4,202 @@ import unittest from test import test_support +result_2004_text = """ + 2004 + + January February March +Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 3 4 1 1 2 3 4 5 6 7 + 5 6 7 8 9 10 11 2 3 4 5 6 7 8 8 9 10 11 12 13 14 +12 13 14 15 16 17 18 9 10 11 12 13 14 15 15 16 17 18 19 20 21 +19 20 21 22 23 24 25 16 17 18 19 20 21 22 22 23 24 25 26 27 28 +26 27 28 29 30 31 23 24 25 26 27 28 29 29 30 31 + + April May June +Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 3 4 1 2 1 2 3 4 5 6 + 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13 +12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20 +19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27 +26 27 28 29 30 24 25 26 27 28 29 30 28 29 30 + 31 + + July August September +Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 3 4 1 1 2 3 4 5 + 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12 +12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19 +19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26 +26 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 + 30 31 + + October November December +Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su + 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 + 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12 +11 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19 +18 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26 +25 26 27 28 29 30 31 29 30 27 28 29 30 31 +""" + +result_2004_html = """ +<?xml version="1.0" encoding="ascii"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ascii" /> +<link rel="stylesheet" type="text/css" href="calendar.css" /> +<title>Calendar for 2004 + + +
2004
+ + + + + + + +
January
MonTueWedThuFriSatSun
   1234
567891011
12131415161718
19202122232425
262728293031 
+
+ + + + + + + +
February
MonTueWedThuFriSatSun
      1
2345678
9101112131415
16171819202122
23242526272829
+
+ + + + + + + +
March
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
293031    
+
+ + + + + + + +
April
MonTueWedThuFriSatSun
   1234
567891011
12131415161718
19202122232425
2627282930  
+
+ + + + + + + + +
May
MonTueWedThuFriSatSun
     12
3456789
10111213141516
17181920212223
24252627282930
31      
+
+ + + + + + + +
June
MonTueWedThuFriSatSun
 123456
78910111213
14151617181920
21222324252627
282930    
+
+ + + + + + + +
July
MonTueWedThuFriSatSun
   1234
567891011
12131415161718
19202122232425
262728293031 
+
+ + + + + + + + +
August
MonTueWedThuFriSatSun
      1
2345678
9101112131415
16171819202122
23242526272829
3031     
+
+ + + + + + + +
September
MonTueWedThuFriSatSun
  12345
6789101112
13141516171819
20212223242526
27282930   
+
+ + + + + + + +
October
MonTueWedThuFriSatSun
    123
45678910
11121314151617
18192021222324
25262728293031
+
+ + + + + + + +
November
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
2930     
+
+ + + + + + + +
December
MonTueWedThuFriSatSun
  12345
6789101112
13141516171819
20212223242526
2728293031  
+
+ +""" + + +class OutputTestCase(unittest.TestCase): + def normalize_calendar(self, s): + # Filters out locale dependant strings + def neitherspacenordigit(c): + return not c.isspace() and not c.isdigit() + + lines = [] + for line in s.splitlines(False): + # Drop texts, as they are locale dependent + if line and not filter(neitherspacenordigit, line): + lines.append(line) + return lines + + def test_output(self): + self.assertEqual( + self.normalize_calendar(calendar.calendar(2004)), + self.normalize_calendar(result_2004_text) + ) + + def test_output_textcalendar(self): + self.assertEqual( + calendar.TextCalendar().formatyear(2004).strip(), + result_2004_text.strip() + ) + + def test_output_htmlcalendar(self): + self.assertEqual( + calendar.HTMLCalendar().formatyearpage(2004).strip(), + result_2004_html.strip() + ) + + class CalendarTestCase(unittest.TestCase): def test_isleap(self): # Make sure that the return is right for a few years, and @@ -72,57 +268,57 @@ class MondayTestCase(MonthCalendarTestCase): firstweekday = calendar.MONDAY def test_february(self): - # A 28-day february starting of monday (7+7+7+7 days) + # A 28-day february starting on monday (7+7+7+7 days) self.check_weeks(1999, 2, (7, 7, 7, 7)) - # A 28-day february starting of tuesday (6+7+7+7+1 days) + # A 28-day february starting on tuesday (6+7+7+7+1 days) self.check_weeks(2005, 2, (6, 7, 7, 7, 1)) - # A 28-day february starting of sunday (1+7+7+7+6 days) + # A 28-day february starting on sunday (1+7+7+7+6 days) self.check_weeks(1987, 2, (1, 7, 7, 7, 6)) - # A 29-day february starting of monday (7+7+7+7+1 days) + # A 29-day february starting on monday (7+7+7+7+1 days) self.check_weeks(1988, 2, (7, 7, 7, 7, 1)) - # A 29-day february starting of tuesday (6+7+7+7+2 days) + # A 29-day february starting on tuesday (6+7+7+7+2 days) self.check_weeks(1972, 2, (6, 7, 7, 7, 2)) - # A 29-day february starting of sunday (1+7+7+7+7 days) + # A 29-day february starting on sunday (1+7+7+7+7 days) self.check_weeks(2004, 2, (1, 7, 7, 7, 7)) def test_april(self): - # A 30-day april starting of monday (7+7+7+7+2 days) + # A 30-day april starting on monday (7+7+7+7+2 days) self.check_weeks(1935, 4, (7, 7, 7, 7, 2)) - # A 30-day april starting of tuesday (6+7+7+7+3 days) + # A 30-day april starting on tuesday (6+7+7+7+3 days) self.check_weeks(1975, 4, (6, 7, 7, 7, 3)) - # A 30-day april starting of sunday (1+7+7+7+7+1 days) + # A 30-day april starting on sunday (1+7+7+7+7+1 days) self.check_weeks(1945, 4, (1, 7, 7, 7, 7, 1)) - # A 30-day april starting of saturday (2+7+7+7+7 days) + # A 30-day april starting on saturday (2+7+7+7+7 days) self.check_weeks(1995, 4, (2, 7, 7, 7, 7)) - # A 30-day april starting of friday (3+7+7+7+6 days) + # A 30-day april starting on friday (3+7+7+7+6 days) self.check_weeks(1994, 4, (3, 7, 7, 7, 6)) def test_december(self): - # A 31-day december starting of monday (7+7+7+7+3 days) + # A 31-day december starting on monday (7+7+7+7+3 days) self.check_weeks(1980, 12, (7, 7, 7, 7, 3)) - # A 31-day december starting of tuesday (6+7+7+7+4 days) + # A 31-day december starting on tuesday (6+7+7+7+4 days) self.check_weeks(1987, 12, (6, 7, 7, 7, 4)) - # A 31-day december starting of sunday (1+7+7+7+7+2 days) + # A 31-day december starting on sunday (1+7+7+7+7+2 days) self.check_weeks(1968, 12, (1, 7, 7, 7, 7, 2)) - # A 31-day december starting of thursday (4+7+7+7+6 days) + # A 31-day december starting on thursday (4+7+7+7+6 days) self.check_weeks(1988, 12, (4, 7, 7, 7, 6)) - # A 31-day december starting of friday (3+7+7+7+7 days) + # A 31-day december starting on friday (3+7+7+7+7 days) self.check_weeks(2017, 12, (3, 7, 7, 7, 7)) - # A 31-day december starting of saturday (2+7+7+7+7+1 days) + # A 31-day december starting on saturday (2+7+7+7+7+1 days) self.check_weeks(2068, 12, (2, 7, 7, 7, 7, 1)) @@ -130,62 +326,63 @@ class SundayTestCase(MonthCalendarTestCase): firstweekday = calendar.SUNDAY def test_february(self): - # A 28-day february starting of sunday (7+7+7+7 days) + # A 28-day february starting on sunday (7+7+7+7 days) self.check_weeks(2009, 2, (7, 7, 7, 7)) - # A 28-day february starting of monday (6+7+7+7+1 days) + # A 28-day february starting on monday (6+7+7+7+1 days) self.check_weeks(1999, 2, (6, 7, 7, 7, 1)) - # A 28-day february starting of saturday (1+7+7+7+6 days) + # A 28-day february starting on saturday (1+7+7+7+6 days) self.check_weeks(1997, 2, (1, 7, 7, 7, 6)) - # A 29-day february starting of sunday (7+7+7+7+1 days) + # A 29-day february starting on sunday (7+7+7+7+1 days) self.check_weeks(2004, 2, (7, 7, 7, 7, 1)) - # A 29-day february starting of monday (6+7+7+7+2 days) + # A 29-day february starting on monday (6+7+7+7+2 days) self.check_weeks(1960, 2, (6, 7, 7, 7, 2)) - # A 29-day february starting of saturday (1+7+7+7+7 days) + # A 29-day february starting on saturday (1+7+7+7+7 days) self.check_weeks(1964, 2, (1, 7, 7, 7, 7)) def test_april(self): - # A 30-day april starting of sunday (7+7+7+7+2 days) + # A 30-day april starting on sunday (7+7+7+7+2 days) self.check_weeks(1923, 4, (7, 7, 7, 7, 2)) - # A 30-day april starting of monday (6+7+7+7+3 days) + # A 30-day april starting on monday (6+7+7+7+3 days) self.check_weeks(1918, 4, (6, 7, 7, 7, 3)) - # A 30-day april starting of saturday (1+7+7+7+7+1 days) + # A 30-day april starting on saturday (1+7+7+7+7+1 days) self.check_weeks(1950, 4, (1, 7, 7, 7, 7, 1)) - # A 30-day april starting of friday (2+7+7+7+7 days) + # A 30-day april starting on friday (2+7+7+7+7 days) self.check_weeks(1960, 4, (2, 7, 7, 7, 7)) - # A 30-day april starting of thursday (3+7+7+7+6 days) + # A 30-day april starting on thursday (3+7+7+7+6 days) self.check_weeks(1909, 4, (3, 7, 7, 7, 6)) def test_december(self): - # A 31-day december starting of sunday (7+7+7+7+3 days) + # A 31-day december starting on sunday (7+7+7+7+3 days) self.check_weeks(2080, 12, (7, 7, 7, 7, 3)) - # A 31-day december starting of monday (6+7+7+7+4 days) + # A 31-day december starting on monday (6+7+7+7+4 days) self.check_weeks(1941, 12, (6, 7, 7, 7, 4)) - # A 31-day december starting of saturday (1+7+7+7+7+2 days) + # A 31-day december starting on saturday (1+7+7+7+7+2 days) self.check_weeks(1923, 12, (1, 7, 7, 7, 7, 2)) - # A 31-day december starting of wednesday (4+7+7+7+6 days) + # A 31-day december starting on wednesday (4+7+7+7+6 days) self.check_weeks(1948, 12, (4, 7, 7, 7, 6)) - # A 31-day december starting of thursday (3+7+7+7+7 days) + # A 31-day december starting on thursday (3+7+7+7+7 days) self.check_weeks(1927, 12, (3, 7, 7, 7, 7)) - # A 31-day december starting of friday (2+7+7+7+7+1 days) + # A 31-day december starting on friday (2+7+7+7+7+1 days) self.check_weeks(1995, 12, (2, 7, 7, 7, 7, 1)) def test_main(): test_support.run_unittest( + OutputTestCase, CalendarTestCase, MondayTestCase, SundayTestCase diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 1dd2461..cdd84bb 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -5,44 +5,51 @@ import sys from test import test_support import _testcapi -for name in dir(_testcapi): - if name.startswith('test_'): - test = getattr(_testcapi, name) +def test_main(): + + for name in dir(_testcapi): + if name.startswith('test_'): + test = getattr(_testcapi, name) + if test_support.verbose: + print "internal", name + try: + test() + except _testcapi.error: + raise test_support.TestFailed, sys.exc_info()[1] + + # some extra thread-state tests driven via _testcapi + def TestThreadState(): + import thread + import time + if test_support.verbose: - print "internal", name - try: - test() - except _testcapi.error: - raise test_support.TestFailed, sys.exc_info()[1] - -# some extra thread-state tests driven via _testcapi -def TestThreadState(): - import thread - import time - - if test_support.verbose: - print "auto-thread-state" - - idents = [] - - def callback(): - idents.append(thread.get_ident()) - - _testcapi._test_thread_state(callback) - time.sleep(1) - # Check our main thread is in the list exactly 3 times. - if idents.count(thread.get_ident()) != 3: - raise test_support.TestFailed, \ - "Couldn't find main thread correctly in the list" - -try: - _testcapi._test_thread_state - have_thread_state = True -except AttributeError: - have_thread_state = False - -if have_thread_state: - TestThreadState() - import threading - t=threading.Thread(target=TestThreadState) - t.start() + print "auto-thread-state" + + idents = [] + + def callback(): + idents.append(thread.get_ident()) + + _testcapi._test_thread_state(callback) + a = b = callback + time.sleep(1) + # Check our main thread is in the list exactly 3 times. + if idents.count(thread.get_ident()) != 3: + raise test_support.TestFailed, \ + "Couldn't find main thread correctly in the list" + + try: + _testcapi._test_thread_state + have_thread_state = True + except AttributeError: + have_thread_state = False + + if have_thread_state: + TestThreadState() + import threading + t=threading.Thread(target=TestThreadState) + t.start() + t.join() + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index a4a656d..018bec6 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -10,6 +10,9 @@ class CmdLineTest(unittest.TestCase): infp.close() data = outfp.read() outfp.close() + # try to cleanup the child so we don't appear to leak when running + # with regrtest -R. This should be a no-op on Windows. + popen2._cleanup() return data def exit_code(self, cmd_line): diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 913aa91..6ea49cc 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1,7 +1,7 @@ from test import test_support import unittest import codecs -import sys, StringIO +import sys, StringIO, _testcapi class Queue(object): """ @@ -781,9 +781,18 @@ class NameprepTest(unittest.TestCase): except Exception,e: raise test_support.TestFailed("Test 3.%d: %s" % (pos+1, str(e))) -class CodecTest(unittest.TestCase): - def test_builtin(self): +class IDNACodecTest(unittest.TestCase): + def test_builtin_decode(self): self.assertEquals(unicode("python.org", "idna"), u"python.org") + self.assertEquals(unicode("python.org.", "idna"), u"python.org.") + self.assertEquals(unicode("xn--pythn-mua.org", "idna"), u"pyth\xf6n.org") + self.assertEquals(unicode("xn--pythn-mua.org.", "idna"), u"pyth\xf6n.org.") + + def test_builtin_encode(self): + self.assertEquals(u"python.org".encode("idna"), "python.org") + self.assertEquals("python.org.".encode("idna"), "python.org.") + self.assertEquals(u"pyth\xf6n.org".encode("idna"), "xn--pythn-mua.org") + self.assertEquals(u"pyth\xf6n.org.".encode("idna"), "xn--pythn-mua.org.") def test_stream(self): import StringIO @@ -791,6 +800,64 @@ class CodecTest(unittest.TestCase): r.read(3) self.assertEquals(r.read(), u"") + def test_incremental_decode(self): + self.assertEquals( + "".join(codecs.iterdecode("python.org", "idna")), + u"python.org" + ) + self.assertEquals( + "".join(codecs.iterdecode("python.org.", "idna")), + u"python.org." + ) + self.assertEquals( + "".join(codecs.iterdecode("xn--pythn-mua.org.", "idna")), + u"pyth\xf6n.org." + ) + self.assertEquals( + "".join(codecs.iterdecode("xn--pythn-mua.org.", "idna")), + u"pyth\xf6n.org." + ) + + decoder = codecs.getincrementaldecoder("idna")() + self.assertEquals(decoder.decode("xn--xam", ), u"") + self.assertEquals(decoder.decode("ple-9ta.o", ), u"\xe4xample.") + self.assertEquals(decoder.decode(u"rg"), u"") + self.assertEquals(decoder.decode(u"", True), u"org") + + decoder.reset() + self.assertEquals(decoder.decode("xn--xam", ), u"") + self.assertEquals(decoder.decode("ple-9ta.o", ), u"\xe4xample.") + self.assertEquals(decoder.decode("rg."), u"org.") + self.assertEquals(decoder.decode("", True), u"") + + def test_incremental_encode(self): + self.assertEquals( + "".join(codecs.iterencode(u"python.org", "idna")), + "python.org" + ) + self.assertEquals( + "".join(codecs.iterencode(u"python.org.", "idna")), + "python.org." + ) + self.assertEquals( + "".join(codecs.iterencode(u"pyth\xf6n.org.", "idna")), + "xn--pythn-mua.org." + ) + self.assertEquals( + "".join(codecs.iterencode(u"pyth\xf6n.org.", "idna")), + "xn--pythn-mua.org." + ) + + encoder = codecs.getincrementalencoder("idna")() + self.assertEquals(encoder.encode(u"\xe4x"), "") + self.assertEquals(encoder.encode(u"ample.org"), "xn--xample-9ta.") + self.assertEquals(encoder.encode(u"", True), "org") + + encoder.reset() + self.assertEquals(encoder.encode(u"\xe4x"), "") + self.assertEquals(encoder.encode(u"ample.org."), "xn--xample-9ta.org.") + self.assertEquals(encoder.encode(u"", True), "") + class CodecsModuleTest(unittest.TestCase): def test_decode(self): @@ -1032,9 +1099,11 @@ class BasicUnicodeTest(unittest.TestCase): decodedresult += reader.read() self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) - # check incremental decoder/encoder and iterencode()/iterdecode() + # check incremental decoder/encoder (fetched via the Python + # and C API) and iterencode()/iterdecode() try: encoder = codecs.getincrementalencoder(encoding)() + cencoder = _testcapi.codec_incrementalencoder(encoding) except LookupError: # no IncrementalEncoder pass else: @@ -1042,10 +1111,24 @@ class BasicUnicodeTest(unittest.TestCase): encodedresult = "" for c in s: encodedresult += encoder.encode(c) + encodedresult += encoder.encode(u"", True) decoder = codecs.getincrementaldecoder(encoding)() decodedresult = u"" for c in encodedresult: decodedresult += decoder.decode(c) + decodedresult += decoder.decode("", True) + self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) + + # check C API + encodedresult = "" + for c in s: + encodedresult += cencoder.encode(c) + encodedresult += cencoder.encode(u"", True) + cdecoder = _testcapi.codec_incrementaldecoder(encoding) + decodedresult = u"" + for c in encodedresult: + decodedresult += cdecoder.decode(c) + decodedresult += cdecoder.decode("", True) self.assertEqual(decodedresult, s, "%r != %r (encoding=%r)" % (decodedresult, s, encoding)) # check iterencode()/iterdecode() @@ -1142,7 +1225,7 @@ def test_main(): PunycodeTest, UnicodeInternalTest, NameprepTest, - CodecTest, + IDNACodecTest, CodecsModuleTest, StreamReaderTest, Str2StrTest, diff --git a/Lib/test/test_coercion.py b/Lib/test/test_coercion.py index e12ef0d..964f161 100644 --- a/Lib/test/test_coercion.py +++ b/Lib/test/test_coercion.py @@ -1,6 +1,8 @@ import copy import sys import warnings +import unittest +from test.test_support import run_unittest # Fake a number that implements numeric methods through __coerce__ class CoerceNumber: @@ -16,10 +18,19 @@ class CoerceNumber: else: return (self.arg, other) +# New-style class version of CoerceNumber +class CoerceTo(object): + def __init__(self, arg): + self.arg = arg + def __coerce__(self, other): + if isinstance(other, CoerceTo): + return self.arg, other.arg + else: + return self.arg, other + # Fake a number that implements numeric ops through methods. class MethodNumber: - def __init__(self,arg): self.arg = arg @@ -50,6 +61,18 @@ class MethodNumber: def __rtruediv__(self,other): return other / self.arg + def __truediv__(self,other): + return self.arg / other + + def __rtruediv__(self,other): + return other / self.arg + + def __floordiv__(self,other): + return self.arg // other + + def __rfloordiv__(self,other): + return other // self.arg + def __pow__(self,other): return self.arg ** other @@ -66,11 +89,157 @@ class MethodNumber: return cmp(self.arg, other) -candidates = [ 2, 4.0, 2L, 2+0j, [1], (2,), None, - MethodNumber(2), CoerceNumber(2)] +candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, + MethodNumber(2), CoerceNumber(2)] + +infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ] + +TE = TypeError +# b = both normal and augmented give same result list +# s = single result lists for normal and augmented +# e = equals other results +# result lists: ['+', '-', '*', '**', '%', '//', ('classic /', 'new /')] +# ^^^^^^^^^^^^^^^^^^^^^^ +# 2-tuple if results differ +# else only one value +infix_results = { + # 2 + (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.0)]), + (0,1): ('e', (0,0)), + (0,2): ('b', [6.0, -2.0, 8.0, 16.0, 2.0, 0.0, 0.5]), + (0,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), + (0,4): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), + (0,5): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), + (0,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (0,7): ('e', (0,0)), + (0,8): ('e', (0,0)), + + # 2L + (1,0): ('e', (0,0)), + (1,1): ('e', (0,1)), + (1,2): ('e', (0,2)), + (1,3): ('e', (0,3)), + (1,4): ('e', (0,4)), + (1,5): ('e', (0,5)), + (1,6): ('e', (0,6)), + (1,7): ('e', (0,7)), + (1,8): ('e', (0,8)), + + # 4.0 + (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 2.0]), + (2,1): ('e', (2,0)), + (2,2): ('b', [8.0, 0.0, 16.0, 256.0, 0.0, 1.0, 1.0]), + (2,3): ('b', [6+0j, 2+0j, 8+0j, 16+0j, 0+0j, 2+0j, 2+0j]), + (2,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (2,5): ('e', (2,4)), + (2,6): ('e', (2,4)), + (2,7): ('e', (2,0)), + (2,8): ('e', (2,0)), + + # (2+0j) + (3,0): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), + (3,1): ('e', (3,0)), + (3,2): ('b', [6+0j, -2+0j, 8+0j, 16+0j, 2+0j, 0+0j, 0.5+0j]), + (3,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), + (3,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (3,5): ('e', (3,4)), + (3,6): ('e', (3,4)), + (3,7): ('e', (3,0)), + (3,8): ('e', (3,0)), + + # [1] + (4,0): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), + (4,1): ('e', (4,0)), + (4,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (4,3): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (4,4): ('b', [[1, 1], TE, TE, TE, TE, TE, TE]), + (4,5): ('s', [TE, TE, TE, TE, TE, TE, TE], [[1, 2], TE, TE, TE, TE, TE, TE]), + (4,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (4,7): ('e', (4,0)), + (4,8): ('e', (4,0)), + + # (2,) + (5,0): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), + (5,1): ('e', (5,0)), + (5,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (5,3): ('e', (5,2)), + (5,4): ('e', (5,2)), + (5,5): ('b', [(2, 2), TE, TE, TE, TE, TE, TE]), + (5,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (5,7): ('e', (5,0)), + (5,8): ('e', (5,0)), + + # None + (6,0): ('b', [TE, TE, TE, TE, TE, TE, TE]), + (6,1): ('e', (6,0)), + (6,2): ('e', (6,0)), + (6,3): ('e', (6,0)), + (6,4): ('e', (6,0)), + (6,5): ('e', (6,0)), + (6,6): ('e', (6,0)), + (6,7): ('e', (6,0)), + (6,8): ('e', (6,0)), + + # MethodNumber(2) + (7,0): ('e', (0,0)), + (7,1): ('e', (0,1)), + (7,2): ('e', (0,2)), + (7,3): ('e', (0,3)), + (7,4): ('e', (0,4)), + (7,5): ('e', (0,5)), + (7,6): ('e', (0,6)), + (7,7): ('e', (0,7)), + (7,8): ('e', (0,8)), + + # CoerceNumber(2) + (8,0): ('e', (0,0)), + (8,1): ('e', (0,1)), + (8,2): ('e', (0,2)), + (8,3): ('e', (0,3)), + (8,4): ('e', (0,4)), + (8,5): ('e', (0,5)), + (8,6): ('e', (0,6)), + (8,7): ('e', (0,7)), + (8,8): ('e', (0,8)), +} + +def process_infix_results(): + for key in sorted(infix_results): + val = infix_results[key] + if val[0] == 'e': + infix_results[key] = infix_results[val[1]] + else: + if val[0] == 's': + res = (val[1], val[2]) + elif val[0] == 'b': + res = (val[1], val[1]) + for i in range(1): + if isinstance(res[i][6], tuple): + if 1/2 == 0: + # testing with classic (floor) division + res[i][6] = res[i][6][0] + else: + # testing with -Qnew + res[i][6] = res[i][6][1] + infix_results[key] = res + + + +process_infix_results() +# now infix_results has two lists of results for every pairing. -infix_binops = [ '+', '-', '*', '/', '**', '%' ] prefix_binops = [ 'divmod' ] +prefix_results = [ + [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)], + [(1L,0L), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1L,0L)], + [(2.0,0.0), (2.0,0.0), (1.0,0.0), ((2+0j),0j), TE, TE, TE, TE, (2.0,0.0)], + [((1+0j),0j), ((1+0j),0j), (0j,(2+0j)), ((1+0j),0j), TE, TE, TE, TE, ((1+0j),0j)], + [TE, TE, TE, TE, TE, TE, TE, TE, TE], + [TE, TE, TE, TE, TE, TE, TE, TE, TE], + [TE, TE, TE, TE, TE, TE, TE, TE, TE], + [TE, TE, TE, TE, TE, TE, TE, TE, TE], + [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)] +] def format_float(value): if abs(value) < 0.01: @@ -87,83 +256,74 @@ def format_result(value): return format_float(value) return str(value) -def do_infix_binops(): - for a in candidates: - for b in candidates: - for op in infix_binops: - print '%s %s %s' % (a, op, b), - try: - x = eval('a %s b' % op) - except: - error = sys.exc_info()[:2] - print '... %s.%s' % (error[0].__module__, error[0].__name__) - else: - print '=', format_result(x) - try: - z = copy.copy(a) - except copy.Error: - z = a # assume it has no inplace ops - print '%s %s= %s' % (a, op, b), - try: - exec('z %s= b' % op) - except: - error = sys.exc_info()[:2] - print '... %s.%s' % (error[0].__module__, error[0].__name__) - else: - print '=>', format_result(z) - -def do_prefix_binops(): - for a in candidates: - for b in candidates: - for op in prefix_binops: - print '%s(%s, %s)' % (op, a, b), - try: - x = eval('%s(a, b)' % op) - except: - error = sys.exc_info()[:2] - print '... %s.%s' % (error[0].__module__, error[0].__name__) - else: - print '=', format_result(x) +class CoercionTest(unittest.TestCase): + def test_infix_binops(self): + for ia, a in enumerate(candidates): + for ib, b in enumerate(candidates): + results = infix_results[(ia, ib)] + for op, res, ires in zip(infix_binops, results[0], results[1]): + if res is TE: + self.assertRaises(TypeError, eval, + 'a %s b' % op, {'a': a, 'b': b}) + else: + self.assertEquals(format_result(res), + format_result(eval('a %s b' % op)), + '%s %s %s == %s failed' % (a, op, b, res)) + try: + z = copy.copy(a) + except copy.Error: + z = a # assume it has no inplace ops + if ires is TE: + try: + exec 'z %s= b' % op + except TypeError: + pass + else: + self.fail("TypeError not raised") + else: + exec('z %s= b' % op) + self.assertEquals(ires, z) -# New-style class version of CoerceNumber -class CoerceTo(object): - def __init__(self, arg): - self.arg = arg - def __coerce__(self, other): - if isinstance(other, CoerceTo): - return self.arg, other.arg - else: - return self.arg, other + def test_prefix_binops(self): + for ia, a in enumerate(candidates): + for ib, b in enumerate(candidates): + for op in prefix_binops: + res = prefix_results[ia][ib] + if res is TE: + self.assertRaises(TypeError, eval, + '%s(a, b)' % op, {'a': a, 'b': b}) + else: + self.assertEquals(format_result(res), + format_result(eval('%s(a, b)' % op)), + '%s(%s, %s) == %s failed' % (op, a, b, res)) + + def test_cmptypes(self): + # Built-in tp_compare slots expect their arguments to have the + # same type, but a user-defined __coerce__ doesn't have to obey. + # SF #980352 + evil_coercer = CoerceTo(42) + # Make sure these don't crash any more + self.assertNotEquals(cmp(u'fish', evil_coercer), 0) + self.assertNotEquals(cmp(slice(1), evil_coercer), 0) + # ...but that this still works + class WackyComparer(object): + def __cmp__(slf, other): + self.assert_(other == 42, 'expected evil_coercer, got %r' % other) + return 0 + self.assertEquals(cmp(WackyComparer(), evil_coercer), 0) + # ...and classic classes too, since that code path is a little different + class ClassicWackyComparer: + def __cmp__(slf, other): + self.assert_(other == 42, 'expected evil_coercer, got %r' % other) + return 0 + self.assertEquals(cmp(ClassicWackyComparer(), evil_coercer), 0) + +def test_main(): + warnings.filterwarnings("ignore", + r'complex divmod\(\), // and % are deprecated', + DeprecationWarning, + r'test.test_coercion$') + run_unittest(CoercionTest) -def assert_(expr, msg=None): - if not expr: - raise AssertionError, msg - -def do_cmptypes(): - # Built-in tp_compare slots expect their arguments to have the - # same type, but a user-defined __coerce__ doesn't have to obey. - # SF #980352 - evil_coercer = CoerceTo(42) - # Make sure these don't crash any more - assert_(cmp(u'fish', evil_coercer) != 0) - assert_(cmp(slice(1), evil_coercer) != 0) - # ...but that this still works - class WackyComparer(object): - def __cmp__(self, other): - assert_(other == 42, 'expected evil_coercer, got %r' % other) - return 0 - assert_(cmp(WackyComparer(), evil_coercer) == 0) - # ...and classic classes too, since that code path is a little different - class ClassicWackyComparer: - def __cmp__(self, other): - assert_(other == 42, 'expected evil_coercer, got %r' % other) - return 0 - assert_(cmp(ClassicWackyComparer(), evil_coercer) == 0) - -warnings.filterwarnings("ignore", - r'complex divmod\(\), // and % are deprecated', - DeprecationWarning, - r'test.test_coercion$') -do_infix_binops() -do_prefix_binops() -do_cmptypes() +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 6899926..2fde614 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -1,4 +1,6 @@ import sys +import unittest +from test import test_support class Empty: def __repr__(self): @@ -27,28 +29,31 @@ class Cmp: def __cmp__(self, other): return cmp(self.arg, other) +class ComparisonTest(unittest.TestCase): + set1 = [2, 2.0, 2L, 2+0j, Coerce(2), Cmp(2.0)] + set2 = [[1], (3,), None, Empty()] + candidates = set1 + set2 -candidates = [2, 2.0, 2L, 2+0j, [1], (3,), None, Empty(), Coerce(2), Cmp(2.0)] - -def test(): - for a in candidates: - for b in candidates: - try: - x = a == b - except: - print 'cmp(%s, %s) => %s' % (a, b, sys.exc_info()[0]) - else: - if x: - print "%s == %s" % (a, b) + def test_comparisons(self): + for a in self.candidates: + for b in self.candidates: + if ((a in self.set1) and (b in self.set1)) or a is b: + self.assertEqual(a, b) else: - print "%s != %s" % (a, b) - # Ensure default comparison compares id() of args - L = [] - for i in range(10): - L.insert(len(L)//2, Empty()) - for a in L: - for b in L: - if cmp(a, b) != cmp(id(a), id(b)): - print "ERROR:", cmp(a, b), cmp(id(a), id(b)), id(a), id(b) - -test() + self.assertNotEqual(a, b) + + def test_id_comparisons(self): + # Ensure default comparison compares id() of args + L = [] + for i in range(10): + L.insert(len(L)//2, Empty()) + for a in L: + for b in L: + self.assertEqual(cmp(a, b), cmp(id(a), id(b)), + 'a=%r, b=%r' % (a, b)) + +def test_main(): + test_support.run_unittest(ComparisonTest) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 1d47f91..72c4f7e 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -284,6 +284,10 @@ if 1: f1, f2 = f() self.assertNotEqual(id(f1.func_code), id(f2.func_code)) + def test_unicode_encoding(self): + code = u"# -*- coding: utf-8 -*-\npass\n" + self.assertRaises(SyntaxError, compile, code, "tmp", "exec") + def test_subscripts(self): # SF bug 1448804 # Class to make testing subscript results easy diff --git a/Lib/test/test_compiler.py b/Lib/test/test_compiler.py index 5e7b15c..a59d6aa 100644 --- a/Lib/test/test_compiler.py +++ b/Lib/test/test_compiler.py @@ -1,10 +1,12 @@ import compiler from compiler.ast import flatten -import os +import os, sys, time, unittest import test.test_support -import unittest from random import random +# How much time in seconds can pass before we print a 'Still working' message. +_PRINT_WORKING_MSG_INTERVAL = 5 * 60 + class CompilerTest(unittest.TestCase): def testCompileLibrary(self): @@ -13,11 +15,18 @@ class CompilerTest(unittest.TestCase): # that any of the code is correct, merely the compiler is able # to generate some kind of code for it. + next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL libdir = os.path.dirname(unittest.__file__) testdir = os.path.dirname(test.test_support.__file__) for dir in [libdir, testdir]: for basename in os.listdir(dir): + # Print still working message since this test can be really slow + if next_time <= time.time(): + next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + print >>sys.__stdout__, \ + ' testCompileLibrary still working, be patient...' + if not basename.endswith(".py"): continue if not TEST_ALL and random() < 0.98: diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index f8db88c..97470c7 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -2,12 +2,14 @@ from __future__ import with_statement +import sys import os import decimal import tempfile import unittest import threading from contextlib import * # Tests __all__ +from test.test_support import run_suite class ContextManagerTestCase(unittest.TestCase): @@ -45,6 +47,28 @@ class ContextManagerTestCase(unittest.TestCase): self.fail("Expected ZeroDivisionError") self.assertEqual(state, [1, 42, 999]) + def test_contextmanager_no_reraise(self): + @contextmanager + def whee(): + yield + ctx = whee().__context__() + ctx.__enter__() + # Calling __exit__ should not result in an exception + self.failIf(ctx.__exit__(TypeError, TypeError("foo"), None)) + + def test_contextmanager_trap_yield_after_throw(self): + @contextmanager + def whoo(): + try: + yield + except: + yield + ctx = whoo().__context__() + ctx.__enter__() + self.assertRaises( + RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None + ) + def test_contextmanager_except(self): state = [] @contextmanager @@ -62,6 +86,21 @@ class ContextManagerTestCase(unittest.TestCase): raise ZeroDivisionError(999) self.assertEqual(state, [1, 42, 999]) + def test_contextmanager_attribs(self): + def attribs(**kw): + def decorate(func): + for k,v in kw.items(): + setattr(func,k,v) + return func + return decorate + @contextmanager + @attribs(foo='bar') + def baz(spam): + """Whee!""" + self.assertEqual(baz.__name__,'baz') + self.assertEqual(baz.foo, 'bar') + self.assertEqual(baz.__doc__, "Whee!") + class NestedTestCase(unittest.TestCase): # XXX This needs more work @@ -274,21 +313,31 @@ class DecimalContextTestCase(unittest.TestCase): def testBasic(self): ctx = decimal.getcontext() - ctx.prec = save_prec = decimal.ExtendedContext.prec + 5 - with decimal.ExtendedContext: - self.assertEqual(decimal.getcontext().prec, - decimal.ExtendedContext.prec) - self.assertEqual(decimal.getcontext().prec, save_prec) + orig_context = ctx.copy() try: + ctx.prec = save_prec = decimal.ExtendedContext.prec + 5 with decimal.ExtendedContext: self.assertEqual(decimal.getcontext().prec, decimal.ExtendedContext.prec) - 1/0 - except ZeroDivisionError: self.assertEqual(decimal.getcontext().prec, save_prec) - else: - self.fail("Didn't raise ZeroDivisionError") + try: + with decimal.ExtendedContext: + self.assertEqual(decimal.getcontext().prec, + decimal.ExtendedContext.prec) + 1/0 + except ZeroDivisionError: + self.assertEqual(decimal.getcontext().prec, save_prec) + else: + self.fail("Didn't raise ZeroDivisionError") + finally: + decimal.setcontext(orig_context) + +# This is needed to make the test actually run under regrtest.py! +def test_main(): + run_suite( + unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]) + ) if __name__ == "__main__": - unittest.main() + test_main() diff --git a/Lib/test/test_copy_reg.py b/Lib/test/test_copy_reg.py index c41946a..c3d3964 100644 --- a/Lib/test/test_copy_reg.py +++ b/Lib/test/test_copy_reg.py @@ -8,6 +8,22 @@ class C: pass +class WithoutSlots(object): + pass + +class WithWeakref(object): + __slots__ = ('__weakref__',) + +class WithPrivate(object): + __slots__ = ('__spam',) + +class WithSingleString(object): + __slots__ = 'spam' + +class WithInherited(WithSingleString): + __slots__ = ('eggs',) + + class CopyRegTestCase(unittest.TestCase): def test_class(self): @@ -84,6 +100,19 @@ class CopyRegTestCase(unittest.TestCase): self.assertRaises(ValueError, copy_reg.add_extension, mod, func, code) + def test_slotnames(self): + self.assertEquals(copy_reg._slotnames(WithoutSlots), []) + self.assertEquals(copy_reg._slotnames(WithWeakref), []) + expected = ['_WithPrivate__spam'] + self.assertEquals(copy_reg._slotnames(WithPrivate), expected) + self.assertEquals(copy_reg._slotnames(WithSingleString), ['spam']) + expected = ['eggs', 'spam'] + expected.sort() + result = copy_reg._slotnames(WithInherited) + result.sort() + self.assertEquals(result, expected) + + def test_main(): test_support.run_unittest(CopyRegTestCase) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index a4a45a7..dc2f20b 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -24,6 +24,9 @@ term = os.environ.get('TERM') if not term or term == 'unknown': raise TestSkipped, "$TERM=%r, calling initscr() may cause exit" % term +if sys.platform == "cygwin": + raise TestSkipped("cygwin's curses mostly just hangs") + def window_funcs(stdscr): "Test the methods of windows" win = curses.newwin(10,10) @@ -201,11 +204,13 @@ def module_funcs(stdscr): curses.has_key(13) if hasattr(curses, 'getmouse'): - curses.mousemask(curses.BUTTON1_PRESSED) - curses.mouseinterval(10) - # just verify these don't cause errors - m = curses.getmouse() - curses.ungetmouse(*m) + (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) + # availmask indicates that mouse stuff not available. + if availmask != 0: + curses.mouseinterval(10) + # just verify these don't cause errors + m = curses.getmouse() + curses.ungetmouse(*m) def unit_tests(): from curses import ascii diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 27f42c6..2528b4a 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -1168,6 +1168,17 @@ class TestDateTime(TestDate): self.assertEqual(dt2 - dt1, us) self.assert_(dt1 < dt2) + def test_strftime_with_bad_tzname_replace(self): + # verify ok if tzinfo.tzname().replace() returns a non-string + class MyTzInfo(FixedOffset): + def tzname(self, dt): + class MyStr(str): + def replace(self, *args): + return None + return MyStr('name') + t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name')) + self.assertRaises(TypeError, t.strftime, '%Z') + def test_bad_constructor_arguments(self): # bad years self.theclass(MINYEAR, 1, 1) # no exception diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 1d33ec4..341ad6d 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -29,7 +29,8 @@ import glob import os, sys import pickle, copy from decimal import * -from test.test_support import TestSkipped, run_unittest, run_doctest, is_resource_enabled +from test.test_support import (TestSkipped, run_unittest, run_doctest, + is_resource_enabled) import random try: import threading @@ -39,12 +40,15 @@ except ImportError: # Useful Test Constant Signals = getcontext().flags.keys() -# Tests are built around these assumed context defaults -DefaultContext.prec=9 -DefaultContext.rounding=ROUND_HALF_EVEN -DefaultContext.traps=dict.fromkeys(Signals, 0) -setcontext(DefaultContext) - +# Tests are built around these assumed context defaults. +# test_main() restores the original context. +def init(): + global ORIGINAL_CONTEXT + ORIGINAL_CONTEXT = getcontext().copy() + DefaultContext.prec = 9 + DefaultContext.rounding = ROUND_HALF_EVEN + DefaultContext.traps = dict.fromkeys(Signals, 0) + setcontext(DefaultContext) TESTDATADIR = 'decimaltestdata' if __name__ == '__main__': @@ -503,16 +507,17 @@ class DecimalImplicitConstructionTest(unittest.TestCase): self.assertEqual(eval('Decimal(10) != E()'), 'ne 10') # insert operator methods and then exercise them - for sym, lop, rop in ( - ('+', '__add__', '__radd__'), - ('-', '__sub__', '__rsub__'), - ('*', '__mul__', '__rmul__'), - ('/', '__truediv__', '__rtruediv__'), - ('%', '__mod__', '__rmod__'), - ('//', '__floordiv__', '__rfloordiv__'), - ('**', '__pow__', '__rpow__'), - ): - + oplist = [ + ('+', '__add__', '__radd__'), + ('-', '__sub__', '__rsub__'), + ('*', '__mul__', '__rmul__'), + ('/', '__truediv__', '__rtruediv__') + ('%', '__mod__', '__rmod__'), + ('//', '__floordiv__', '__rfloordiv__'), + ('**', '__pow__', '__rpow__') + ] + + for sym, lop, rop in oplist: setattr(E, lop, lambda self, other: 'str' + lop + str(other)) setattr(E, rop, lambda self, other: str(other) + rop + 'str') self.assertEqual(eval('E()' + sym + 'Decimal(10)'), @@ -1059,6 +1064,7 @@ def test_main(arith=False, verbose=None): is enabled in regrtest.py """ + init() global TEST_ALL TEST_ALL = arith or is_resource_enabled('decimal') @@ -1073,10 +1079,12 @@ def test_main(arith=False, verbose=None): DecimalTest, ] - run_unittest(*test_classes) - import decimal as DecimalModule - run_doctest(DecimalModule, verbose) - + try: + run_unittest(*test_classes) + import decimal as DecimalModule + run_doctest(DecimalModule, verbose) + finally: + setcontext(ORIGINAL_CONTEXT) if __name__ == '__main__': # Calling with no arguments runs all tests. diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 185edb3..89cebb0 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1638,7 +1638,9 @@ def specials(): c1 = C() c2 = C() verify(not not c1) - vereq(hash(c1), id(c1)) + verify(id(c1) != id(c2)) + hash(c1) + hash(c2) vereq(cmp(c1, c2), cmp(id(c1), id(c2))) vereq(c1, c1) verify(c1 != c2) @@ -1660,7 +1662,9 @@ def specials(): d1 = D() d2 = D() verify(not not d1) - vereq(hash(d1), id(d1)) + verify(id(d1) != id(d2)) + hash(d1) + hash(d2) vereq(cmp(d1, d2), cmp(id(d1), id(d2))) vereq(d1, d1) verify(d1 != d2) @@ -2914,7 +2918,7 @@ def subclasspropagation(): class D(B, C): pass d = D() - vereq(hash(d), id(d)) + orig_hash = hash(d) # related to id(d) in platform-dependent ways A.__hash__ = lambda self: 42 vereq(hash(d), 42) C.__hash__ = lambda self: 314 @@ -2930,7 +2934,7 @@ def subclasspropagation(): del C.__hash__ vereq(hash(d), 42) del A.__hash__ - vereq(hash(d), id(d)) + vereq(hash(d), orig_hash) d.foo = 42 d.bar = 42 vereq(d.foo, 42) diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py index 52feef0..83fad15 100644 --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -152,6 +152,10 @@ class TestSFpatches(unittest.TestCase): difflib.SequenceMatcher(None, old, new).get_opcodes() -Doctests = doctest.DocTestSuite(difflib) +def test_main(): + difflib.HtmlDiff._default_prefix = 0 + Doctests = doctest.DocTestSuite(difflib) + run_unittest(TestSFpatches, TestSFbugs, Doctests) -run_unittest(TestSFpatches, TestSFbugs, Doctests) +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_dl.py b/Lib/test/test_dl.py index d1f73b2..b70a4cf 100755 --- a/Lib/test/test_dl.py +++ b/Lib/test/test_dl.py @@ -10,6 +10,7 @@ sharedlibs = [ ('/usr/lib/libc.so', 'getpid'), ('/lib/libc.so.6', 'getpid'), ('/usr/bin/cygwin1.dll', 'getpid'), + ('/usr/lib/libc.dylib', 'getpid'), ] for s, func in sharedlibs: diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 1f89ac2..b17607d 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -604,8 +604,8 @@ DocTestFinder finds the line number of each example: ... >>> for x in range(10): ... ... print x, ... 0 1 2 3 4 5 6 7 8 9 - ... >>> x/2 - ... 6.0 + ... >>> x//2 + ... 6 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> [e.lineno for e in test.examples] @@ -679,8 +679,8 @@ statistics. Here's a simple DocTest case we can use: ... >>> x = 12 ... >>> print x ... 12 - ... >>> x/2 - ... 6.0 + ... >>> x//2 + ... 6 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] @@ -700,8 +700,8 @@ the failure and proceeds to the next example: ... >>> x = 12 ... >>> print x ... 14 - ... >>> x/2 - ... 6.0 + ... >>> x//2 + ... 6 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=True).run(test) @@ -723,9 +723,9 @@ the failure and proceeds to the next example: Got: 12 Trying: - x/2 + x//2 Expecting: - 6.0 + 6 ok (1, 3) """ @@ -738,8 +738,8 @@ output: ... >>> x = 12 ... >>> print x ... 12 - ... >>> x/2 - ... 6.0 + ... >>> x//2 + ... 6 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] @@ -754,9 +754,9 @@ output: 12 ok Trying: - x/2 + x//2 Expecting: - 6.0 + 6 ok (0, 3) @@ -784,9 +784,9 @@ iff `-v` appears in sys.argv: 12 ok Trying: - x/2 + x//2 Expecting: - 6.0 + 6 ok (0, 3) @@ -806,9 +806,9 @@ replaced with any other string: >>> def f(x): ... ''' ... >>> x = 12 - ... >>> print x/0 + ... >>> print x//0 ... Traceback (most recent call last): - ... ZeroDivisionError: float division + ... ZeroDivisionError: integer division or modulo by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -822,10 +822,10 @@ unexpected exception: >>> def f(x): ... ''' ... >>> x = 12 - ... >>> print 'pre-exception output', x/0 + ... >>> print 'pre-exception output', x//0 ... pre-exception output ... Traceback (most recent call last): - ... ZeroDivisionError: float division + ... ZeroDivisionError: integer division or modulo by zero ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -833,10 +833,10 @@ unexpected exception: ********************************************************************** File ..., line 4, in f Failed example: - print 'pre-exception output', x/0 + print 'pre-exception output', x//0 Exception raised: ... - ZeroDivisionError: float division + ZeroDivisionError: integer division or modulo by zero (1, 2) Exception messages may contain newlines: @@ -920,7 +920,7 @@ unexpected exception: >>> def f(x): ... r''' - ... >>> 1/0 + ... >>> 1//0 ... 0 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] @@ -929,11 +929,11 @@ unexpected exception: ********************************************************************** File ..., line 3, in f Failed example: - 1/0 + 1//0 Exception raised: Traceback (most recent call last): ... - ZeroDivisionError: float division + ZeroDivisionError: integer division or modulo by zero (1, 1) """ def optionflags(): r""" diff --git a/Lib/test/test_email_renamed.py b/Lib/test/test_email_renamed.py new file mode 100644 index 0000000..c3af598 --- /dev/null +++ b/Lib/test/test_email_renamed.py @@ -0,0 +1,13 @@ +# Copyright (C) 2001-2006 Python Software Foundation +# email package unit tests + +import unittest +# The specific tests now live in Lib/email/test +from email.test.test_email_renamed import suite +from test.test_support import run_suite + +def test_main(): + run_suite(suite()) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index fd5670a..ab3da86 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -100,12 +100,18 @@ else: print "writelines accepted sequence of non-string objects" f.close() -try: - sys.stdin.seek(-1) -except IOError: - pass +# This causes the interpreter to exit on OSF1 v5.1. +if sys.platform != 'osf1V5': + try: + sys.stdin.seek(-1) + except IOError: + pass + else: + print "should not be able to seek on sys.stdin" else: - print "should not be able to seek on sys.stdin" + print >>sys.__stdout__, ( + ' Skipping sys.stdin.seek(-1), it may crash the interpreter.' + ' Test manually.') try: sys.stdin.truncate() diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py index f3a7841..301769e 100644 --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -162,7 +162,10 @@ if verbose: print "15. Unicode filenames" try: t1 = writeTmp(1, ["A\nB"]) - fi = FileInput(files=unicode(t1, sys.getfilesystemencoding())) + encoding = sys.getfilesystemencoding() + if encoding is None: + encoding = 'ascii' + fi = FileInput(files=unicode(t1, encoding)) lines = list(fi) verify(lines == ["A\n", "B"]) finally: diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index aca7a84..cba5fc7 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -1,75 +1,23 @@ """This test checks for correct fork() behavior. - -We want fork1() semantics -- only the forking thread survives in the -child after a fork(). - -On some systems (e.g. Solaris without posix threads) we find that all -active threads survive in the child after a fork(); this is an error. - -While BeOS doesn't officially support fork and native threading in -the same application, the present example should work just fine. DC """ -import os, sys, time, thread -from test.test_support import verify, verbose, TestSkipped +import os +from test.fork_wait import ForkWait +from test.test_support import TestSkipped, run_unittest try: os.fork except AttributeError: raise TestSkipped, "os.fork not defined -- skipping test_fork1" -LONGSLEEP = 2 - -SHORTSLEEP = 0.5 - -NUM_THREADS = 4 - -alive = {} - -stop = 0 - -def f(id): - while not stop: - alive[id] = os.getpid() - try: - time.sleep(SHORTSLEEP) - except IOError: - pass - -def main(): - for i in range(NUM_THREADS): - thread.start_new(f, (i,)) - - time.sleep(LONGSLEEP) - - a = alive.keys() - a.sort() - verify(a == range(NUM_THREADS)) - - prefork_lives = alive.copy() - - if sys.platform in ['unixware7']: - cpid = os.fork1() - else: - cpid = os.fork() - - if cpid == 0: - # Child - time.sleep(LONGSLEEP) - n = 0 - for key in alive.keys(): - if alive[key] != prefork_lives[key]: - n = n+1 - os._exit(n) - else: - # Parent +class ForkTest(ForkWait): + def wait_impl(self, cpid): spid, status = os.waitpid(cpid, 0) - verify(spid == cpid) - verify(status == 0, - "cause = %d, exit = %d" % (status&0xff, status>>8) ) - global stop - # Tell threads to die - stop = 1 - time.sleep(2*SHORTSLEEP) # Wait for threads to die + self.assertEqual(spid, cpid) + self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) + +def test_main(): + run_unittest(ForkTest) -main() +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 4be1b4c..a60a768 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -421,7 +421,6 @@ Subject: Re: PEP 255: Simple Generators ... self.name = name ... self.parent = None ... self.generator = self.generate() -... self.close = self.generator.close ... ... def generate(self): ... while not self.parent: @@ -484,8 +483,6 @@ A->A B->G C->A D->G E->G F->A G->G H->G I->A J->G K->A L->A M->G merged A 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 ->>> for s in sets: s.close() # break cycles - """ # Emacs turd ' @@ -593,7 +590,6 @@ arguments are iterable -- a LazyList is the same as a generator to times(). ... def __init__(self, g): ... self.sofar = [] ... self.fetch = g.next -... self.close = g.close ... ... def __getitem__(self, i): ... sofar, fetch = self.sofar, self.fetch @@ -624,8 +620,6 @@ efficient. [200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375, 384] [400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675] ->>> m235.close() - Ye olde Fibonacci generator, LazyList style. >>> def fibgen(a, b): @@ -648,7 +642,6 @@ Ye olde Fibonacci generator, LazyList style. >>> fib = LazyList(fibgen(1, 2)) >>> firstn(iter(fib), 17) [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584] ->>> fib.close() Running after your tail with itertools.tee (new in version 2.4) @@ -685,7 +678,8 @@ m235 to share a single generator". ... merge(times(3, m3), ... times(5, m5))): ... yield n -... m2, m3, m5, mRes = tee(_m235(), 4) +... m1 = _m235() +... m2, m3, m5, mRes = tee(m1, 4) ... return mRes >>> it = m235() @@ -702,10 +696,9 @@ result for as long as it has not been "consumed" from all of the duplicated iterators, whereupon it is deleted. You can therefore print the hamming sequence during hours without increasing memory usage, or very little. -The beauty of it is that recursive running after their tail FP algorithms +The beauty of it is that recursive running-after-their-tail FP algorithms are quite straightforwardly expressed with this Python idiom. - Ye olde Fibonacci generator, tee style. >>> def fib(): @@ -721,7 +714,8 @@ Ye olde Fibonacci generator, tee style. ... for res in _isum(fibHead, fibTail): ... yield res ... -... fibHead, fibTail, fibRes = tee(_fib(), 3) +... realfib = _fib() +... fibHead, fibTail, fibRes = tee(realfib, 3) ... return fibRes >>> firstn(fib(), 17) @@ -1545,6 +1539,9 @@ caught ValueError (1) >>> g.throw(ValueError, TypeError(1)) # mismatched type, rewrapped caught ValueError (1) +>>> g.throw(ValueError, ValueError(1), None) # explicit None traceback +caught ValueError (1) + >>> g.throw(ValueError(1), "foo") # bad args Traceback (most recent call last): ... @@ -1592,8 +1589,7 @@ ValueError: 7 >>> f().throw("abc") # throw on just-opened generator Traceback (most recent call last): ... -TypeError: exceptions must be classes, or instances, not str - +abc Now let's try closing a generator: @@ -1711,6 +1707,81 @@ enclosing function a generator: """ +refleaks_tests = """ +Prior to adding cycle-GC support to itertools.tee, this code would leak +references. We add it to the standard suite so the routine refleak-tests +would trigger if it starts being uncleanable again. + +>>> import itertools +>>> def leak(): +... class gen: +... def __iter__(self): +... return self +... def next(self): +... return self.item +... g = gen() +... head, tail = itertools.tee(g) +... g.item = head +... return head +>>> it = leak() + +Make sure to also test the involvement of the tee-internal teedataobject, +which stores returned items. + +>>> item = it.next() + + + +This test leaked at one point due to generator finalization/destruction. +It was copied from Lib/test/leakers/test_generator_cycle.py before the file +was removed. + +>>> def leak(): +... def gen(): +... while True: +... yield g +... g = gen() + +>>> leak() + + + +This test isn't really generator related, but rather exception-in-cleanup +related. The coroutine tests (above) just happen to cause an exception in +the generator's __del__ (tp_del) method. We can also test for this +explicitly, without generators. We do have to redirect stderr to avoid +printing warnings and to doublecheck that we actually tested what we wanted +to test. + +>>> import sys, StringIO +>>> old = sys.stderr +>>> try: +... sys.stderr = StringIO.StringIO() +... class Leaker: +... def __del__(self): +... raise RuntimeError +... +... l = Leaker() +... del l +... err = sys.stderr.getvalue().strip() +... err.startswith( +... "Exception exceptions.RuntimeError: RuntimeError() in <" +... ) +... err.endswith("> ignored") +... len(err.splitlines()) +... finally: +... sys.stderr = old +True +True +1 + + + +These refleak tests should perhaps be in a testfile of their own, +test_generators just happened to be the test that drew these out. + +""" + __test__ = {"tut": tutorial_tests, "pep": pep_tests, "email": email_tests, @@ -1719,6 +1790,7 @@ __test__ = {"tut": tutorial_tests, "conjoin": conjoin_tests, "weakref": weakref_tests, "coroutine": coroutine_tests, + "refleaks": refleaks_tests, } # Magic test name that regrtest.py invokes *after* importing this module. diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index 1556604..e414757 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -129,7 +129,7 @@ Verify late binding for the innermost for-expression Verify re-use of tuples (a side benefit of using genexps over listcomps) >>> tupleids = map(id, ((i,i) for i in xrange(10))) - >>> max(tupleids) - min(tupleids) + >>> int(max(tupleids) - min(tupleids)) 0 Verify that syntax error's are raised for genexps used as lvalues diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index d4c681a..748ad44 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -48,7 +48,7 @@ LARGE = 0x7FFFFFFF VERY_LARGE = 0xFF0000121212121212121242L from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \ - INT_MIN, LONG_MIN, LONG_MAX + INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX # fake, they are not defined in Python's header files LLONG_MAX = 2**63-1 @@ -182,6 +182,23 @@ class Signed_TestCase(unittest.TestCase): self.failUnlessEqual(42, getargs_l(42L)) self.assertRaises(OverflowError, getargs_l, VERY_LARGE) + def test_n(self): + from _testcapi import getargs_n + # n returns 'Py_ssize_t', and does range checking + # (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX) + self.failUnlessEqual(3, getargs_n(3.14)) + self.failUnlessEqual(99, getargs_n(Long())) + self.failUnlessEqual(99, getargs_n(Int())) + + self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MIN-1) + self.failUnlessEqual(PY_SSIZE_T_MIN, getargs_n(PY_SSIZE_T_MIN)) + self.failUnlessEqual(PY_SSIZE_T_MAX, getargs_n(PY_SSIZE_T_MAX)) + self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MAX+1) + + self.failUnlessEqual(42, getargs_n(42)) + self.failUnlessEqual(42, getargs_n(42L)) + self.assertRaises(OverflowError, getargs_n, VERY_LARGE) + class LongLong_TestCase(unittest.TestCase): def test_L(self): diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 8a6ef7f..5ce09f9 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -80,6 +80,14 @@ class GlobTests(unittest.TestCase): eq(self.glob('?a?', '*F'), map(self.norm, [os.path.join('aaa', 'zzzF'), os.path.join('aab', 'F')])) + def test_glob_directory_with_trailing_slash(self): + # We are verifying that when there is wildcard pattern which + # ends with os.sep doesn't blow up. + res = glob.glob(self.tempdir + '*' + os.sep) + self.assertEqual(len(res), 1) + # either of these results are reasonable + self.assertTrue(res[0] in [self.tempdir, self.tempdir + os.sep]) + def test_glob_broken_symlinks(self): if hasattr(os, 'symlink'): eq = self.assertSequencesEqual_noorder diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 5b20ab3..4bb4e45 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -255,6 +255,10 @@ d22v(1, 2, 3, 4, 5) d22v(*(1, 2, 3, 4)) d22v(1, 2, *(3, 4, 5)) d22v(1, *(2, 3), **{'d': 4}) +def d31v((x)): pass +d31v(1) +def d32v((x,)): pass +d32v((1,)) ### lambdef: 'lambda' [varargslist] ':' test print 'lambdef' @@ -811,6 +815,11 @@ x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x)) x = 5; t = True; verify([(i,j) for i in range(10) for j in range(5)] == list(g)) +# Grammar allows multiple adjacent 'if's in listcomps and genexps, +# even though it's silly. Make sure it works (ifelse broke this.) +verify([ x for x in range(10) if x % 2 if x % 3 ], [1, 5, 7]) +verify((x for x in range(10) if x % 2 if x % 3), [1, 5, 7]) + # Test ifelse expressions in various cases def _checkeval(msg, ret): "helper to check that evaluation of expressions is done correctly" diff --git a/Lib/test/test_index.py b/Lib/test/test_index.py index e69de29..45b3b2b 100644 --- a/Lib/test/test_index.py +++ b/Lib/test/test_index.py @@ -0,0 +1,137 @@ +import unittest +from test import test_support +import operator + +class oldstyle: + def __index__(self): + return self.ind + +class newstyle(object): + def __index__(self): + return self.ind + +class BaseTestCase(unittest.TestCase): + def setUp(self): + self.o = oldstyle() + self.n = newstyle() + self.o2 = oldstyle() + self.n2 = newstyle() + + def test_basic(self): + self.o.ind = -2 + self.n.ind = 2 + assert(self.seq[self.n] == self.seq[2]) + assert(self.seq[self.o] == self.seq[-2]) + assert(operator.index(self.o) == -2) + assert(operator.index(self.n) == 2) + + def test_error(self): + self.o.ind = 'dumb' + self.n.ind = 'bad' + myfunc = lambda x, obj: obj.seq[x] + self.failUnlessRaises(TypeError, operator.index, self.o) + self.failUnlessRaises(TypeError, operator.index, self.n) + self.failUnlessRaises(TypeError, myfunc, self.o, self) + self.failUnlessRaises(TypeError, myfunc, self.n, self) + + def test_slice(self): + self.o.ind = 1 + self.o2.ind = 3 + self.n.ind = 2 + self.n2.ind = 4 + assert(self.seq[self.o:self.o2] == self.seq[1:3]) + assert(self.seq[self.n:self.n2] == self.seq[2:4]) + + def test_repeat(self): + self.o.ind = 3 + self.n.ind = 2 + assert(self.seq * self.o == self.seq * 3) + assert(self.seq * self.n == self.seq * 2) + assert(self.o * self.seq == self.seq * 3) + assert(self.n * self.seq == self.seq * 2) + + def test_wrappers(self): + n = self.n + n.ind = 5 + assert n.__index__() == 5 + assert 6 .__index__() == 6 + assert -7L.__index__() == -7 + assert self.seq.__getitem__(n) == self.seq[5] + assert self.seq.__mul__(n) == self.seq * 5 + assert self.seq.__rmul__(n) == self.seq * 5 + + def test_infinite_recusion(self): + class Trap1(int): + def __index__(self): + return self + class Trap2(long): + def __index__(self): + return self + self.failUnlessRaises(TypeError, operator.getitem, self.seq, Trap1()) + self.failUnlessRaises(TypeError, operator.getitem, self.seq, Trap2()) + + +class ListTestCase(BaseTestCase): + seq = [0,10,20,30,40,50] + + def test_setdelitem(self): + self.o.ind = -2 + self.n.ind = 2 + lst = list('ab!cdefghi!j') + del lst[self.o] + del lst[self.n] + lst[self.o] = 'X' + lst[self.n] = 'Y' + assert lst == list('abYdefghXj') + + lst = [5, 6, 7, 8, 9, 10, 11] + lst.__setitem__(self.n, "here") + assert lst == [5, 6, "here", 8, 9, 10, 11] + lst.__delitem__(self.n) + assert lst == [5, 6, 8, 9, 10, 11] + + def test_inplace_repeat(self): + self.o.ind = 2 + self.n.ind = 3 + lst = [6, 4] + lst *= self.o + assert lst == [6, 4, 6, 4] + lst *= self.n + assert lst == [6, 4, 6, 4] * 3 + + lst = [5, 6, 7, 8, 9, 11] + l2 = lst.__imul__(self.n) + assert l2 is lst + assert lst == [5, 6, 7, 8, 9, 11] * 3 + + +class TupleTestCase(BaseTestCase): + seq = (0,10,20,30,40,50) + +class StringTestCase(BaseTestCase): + seq = "this is a test" + +class UnicodeTestCase(BaseTestCase): + seq = u"this is a test" + + +class XRangeTestCase(unittest.TestCase): + + def test_xrange(self): + n = newstyle() + n.ind = 5 + assert xrange(1, 20)[n] == 6 + assert xrange(1, 20).__getitem__(n) == 6 + + +def test_main(): + test_support.run_unittest( + ListTestCase, + TupleTestCase, + StringTestCase, + UnicodeTestCase, + XRangeTestCase, + ) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 0bdf959..d9fd93d 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -295,10 +295,12 @@ class TestClassesAndFunctions(unittest.TestCase): self.assertArgSpecEquals(A.m, ['self']) def test_getargspec_sublistofone(self): - def sublistOfOne((foo)): return 1 - + def sublistOfOne((foo,)): return 1 self.assertArgSpecEquals(sublistOfOne, [['foo']]) + def fakeSublistOfOne((foo)): return 1 + self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) + def test_classify_newstyle(self): class A(object): diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 5939ff5..8c584ad 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -8,6 +8,7 @@ from test import test_support # Tell it we don't know about external files: mimetypes.knownfiles = [] mimetypes.inited = False +mimetypes._default_mime_types() class MimeTypesTestCase(unittest.TestCase): diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py index aef7931..4d02dee 100644 --- a/Lib/test/test_multibytecodec.py +++ b/Lib/test/test_multibytecodec.py @@ -7,13 +7,114 @@ from test import test_support from test import test_multibytecodec_support -import unittest, StringIO, codecs +import unittest, StringIO, codecs, sys + +class Test_MultibyteCodec(unittest.TestCase): + + def test_nullcoding(self): + self.assertEqual(''.decode('gb18030'), u'') + self.assertEqual(unicode('', 'gb18030'), u'') + self.assertEqual(u''.encode('gb18030'), '') + + def test_str_decode(self): + self.assertEqual('abcd'.encode('gb18030'), 'abcd') + + def test_errorcallback_longindex(self): + dec = codecs.getdecoder('euc-kr') + myreplace = lambda exc: (u'', sys.maxint+1) + codecs.register_error('test.cjktest', myreplace) + self.assertRaises(IndexError, dec, + 'apple\x92ham\x93spam', 'test.cjktest') + +class Test_IncrementalEncoder(unittest.TestCase): + + def test_stateless(self): + # cp949 encoder isn't stateful at all. + encoder = codecs.getincrementalencoder('cp949')() + self.assertEqual(encoder.encode(u'\ud30c\uc774\uc36c \ub9c8\uc744'), + '\xc6\xc4\xc0\xcc\xbd\xe3 \xb8\xb6\xc0\xbb') + self.assertEqual(encoder.reset(), None) + self.assertEqual(encoder.encode(u'\u2606\u223c\u2606', True), + '\xa1\xd9\xa1\xad\xa1\xd9') + self.assertEqual(encoder.reset(), None) + self.assertEqual(encoder.encode(u'', True), '') + self.assertEqual(encoder.encode(u'', False), '') + self.assertEqual(encoder.reset(), None) + + def test_stateful(self): + # jisx0213 encoder is stateful for a few codepoints. eg) + # U+00E6 => A9DC + # U+00E6 U+0300 => ABC4 + # U+0300 => ABDC + + encoder = codecs.getincrementalencoder('jisx0213')() + self.assertEqual(encoder.encode(u'\u00e6\u0300'), '\xab\xc4') + self.assertEqual(encoder.encode(u'\u00e6'), '') + self.assertEqual(encoder.encode(u'\u0300'), '\xab\xc4') + self.assertEqual(encoder.encode(u'\u00e6', True), '\xa9\xdc') + + self.assertEqual(encoder.reset(), None) + self.assertEqual(encoder.encode(u'\u0300'), '\xab\xdc') + + self.assertEqual(encoder.encode(u'\u00e6'), '') + self.assertEqual(encoder.encode('', True), '\xa9\xdc') + self.assertEqual(encoder.encode('', True), '') + + def test_stateful_keep_buffer(self): + encoder = codecs.getincrementalencoder('jisx0213')() + self.assertEqual(encoder.encode(u'\u00e6'), '') + self.assertRaises(UnicodeEncodeError, encoder.encode, u'\u0123') + self.assertEqual(encoder.encode(u'\u0300\u00e6'), '\xab\xc4') + self.assertRaises(UnicodeEncodeError, encoder.encode, u'\u0123') + self.assertEqual(encoder.reset(), None) + self.assertEqual(encoder.encode(u'\u0300'), '\xab\xdc') + self.assertEqual(encoder.encode(u'\u00e6'), '') + self.assertRaises(UnicodeEncodeError, encoder.encode, u'\u0123') + self.assertEqual(encoder.encode(u'', True), '\xa9\xdc') + + +class Test_IncrementalDecoder(unittest.TestCase): + + def test_dbcs(self): + # cp949 decoder is simple with only 1 or 2 bytes sequences. + decoder = codecs.getincrementaldecoder('cp949')() + self.assertEqual(decoder.decode('\xc6\xc4\xc0\xcc\xbd'), + u'\ud30c\uc774') + self.assertEqual(decoder.decode('\xe3 \xb8\xb6\xc0\xbb'), + u'\uc36c \ub9c8\uc744') + self.assertEqual(decoder.decode(''), u'') + + def test_dbcs_keep_buffer(self): + decoder = codecs.getincrementaldecoder('cp949')() + self.assertEqual(decoder.decode('\xc6\xc4\xc0'), u'\ud30c') + self.assertRaises(UnicodeDecodeError, decoder.decode, '', True) + self.assertEqual(decoder.decode('\xcc'), u'\uc774') + + self.assertEqual(decoder.decode('\xc6\xc4\xc0'), u'\ud30c') + self.assertRaises(UnicodeDecodeError, decoder.decode, '\xcc\xbd', True) + self.assertEqual(decoder.decode('\xcc'), u'\uc774') + + def test_iso2022(self): + decoder = codecs.getincrementaldecoder('iso2022-jp')() + ESC = '\x1b' + self.assertEqual(decoder.decode(ESC + '('), u'') + self.assertEqual(decoder.decode('B', True), u'') + self.assertEqual(decoder.decode(ESC + '$'), u'') + self.assertEqual(decoder.decode('B@$'), u'\u4e16') + self.assertEqual(decoder.decode('@$@'), u'\u4e16') + self.assertEqual(decoder.decode('$', True), u'\u4e16') + self.assertEqual(decoder.reset(), None) + self.assertEqual(decoder.decode('@$'), u'@$') + self.assertEqual(decoder.decode(ESC + '$'), u'') + self.assertRaises(UnicodeDecodeError, decoder.decode, '', True) + self.assertEqual(decoder.decode('B@$'), u'\u4e16') + class Test_StreamWriter(unittest.TestCase): if len(u'\U00012345') == 2: # UCS2 def test_gb18030(self): s= StringIO.StringIO() - c = codecs.lookup('gb18030')[3](s) + c = codecs.getwriter('gb18030')(s) c.write(u'123') self.assertEqual(s.getvalue(), '123') c.write(u'\U00012345') @@ -30,15 +131,16 @@ class Test_StreamWriter(unittest.TestCase): self.assertEqual(s.getvalue(), '123\x907\x959\x907\x959\x907\x959\x827\xcf5\x810\x851') - # standard utf-8 codecs has broken StreamReader - if test_multibytecodec_support.__cjkcodecs__: - def test_utf_8(self): - s= StringIO.StringIO() - c = codecs.lookup('utf-8')[3](s) - c.write(u'123') - self.assertEqual(s.getvalue(), '123') - c.write(u'\U00012345') - self.assertEqual(s.getvalue(), '123\xf0\x92\x8d\x85') + def test_utf_8(self): + s= StringIO.StringIO() + c = codecs.getwriter('utf-8')(s) + c.write(u'123') + self.assertEqual(s.getvalue(), '123') + c.write(u'\U00012345') + self.assertEqual(s.getvalue(), '123\xf0\x92\x8d\x85') + + # Python utf-8 codec can't buffer surrogate pairs yet. + if 0: c.write(u'\U00012345'[0]) self.assertEqual(s.getvalue(), '123\xf0\x92\x8d\x85') c.write(u'\U00012345'[1] + u'\U00012345' + u'\uac00\u00ac') @@ -61,14 +163,6 @@ class Test_StreamWriter(unittest.TestCase): else: # UCS4 pass - def test_nullcoding(self): - self.assertEqual(''.decode('gb18030'), u'') - self.assertEqual(unicode('', 'gb18030'), u'') - self.assertEqual(u''.encode('gb18030'), '') - - def test_str_decode(self): - self.assertEqual('abcd'.encode('gb18030'), 'abcd') - def test_streamwriter_strwrite(self): s = StringIO.StringIO() wr = codecs.getwriter('gb18030')(s) @@ -83,6 +177,9 @@ class Test_ISO2022(unittest.TestCase): def test_main(): suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(Test_MultibyteCodec)) + suite.addTest(unittest.makeSuite(Test_IncrementalEncoder)) + suite.addTest(unittest.makeSuite(Test_IncrementalDecoder)) suite.addTest(unittest.makeSuite(Test_StreamWriter)) suite.addTest(unittest.makeSuite(Test_ISO2022)) test_support.run_suite(suite) diff --git a/Lib/test/test_multibytecodec_support.py b/Lib/test/test_multibytecodec_support.py index 45a63e7..bec32de 100644 --- a/Lib/test/test_multibytecodec_support.py +++ b/Lib/test/test_multibytecodec_support.py @@ -3,15 +3,12 @@ # test_multibytecodec_support.py # Common Unittest Routines for CJK codecs # -# $CJKCodecs: test_multibytecodec_support.py,v 1.6 2004/06/19 06:09:55 perky Exp $ import sys, codecs, os.path import unittest from test import test_support from StringIO import StringIO -__cjkcodecs__ = 0 # define this as 0 for python - class TestBase: encoding = '' # codec name codec = None # codec tuple (with 4 elements) @@ -21,11 +18,17 @@ class TestBase: roundtriptest = 1 # set if roundtrip is possible with unicode has_iso10646 = 0 # set if this encoding contains whole iso10646 map xmlcharnametest = None # string to test xmlcharrefreplace + unmappedunicode = u'\udeee' # a unicode codepoint that is not mapped. def setUp(self): if self.codec is None: self.codec = codecs.lookup(self.encoding) - self.encode, self.decode, self.reader, self.writer = self.codec + self.encode = self.codec.encode + self.decode = self.codec.decode + self.reader = self.codec.streamreader + self.writer = self.codec.streamwriter + self.incrementalencoder = self.codec.incrementalencoder + self.incrementaldecoder = self.codec.incrementaldecoder def test_chunkcoding(self): for native, utf8 in zip(*[StringIO(f).readlines() @@ -47,51 +50,155 @@ class TestBase: else: self.assertRaises(UnicodeError, func, source, scheme) - if sys.hexversion >= 0x02030000: - def test_xmlcharrefreplace(self): - if self.has_iso10646: - return + def test_xmlcharrefreplace(self): + if self.has_iso10646: + return + + s = u"\u0b13\u0b23\u0b60 nd eggs" + self.assertEqual( + self.encode(s, "xmlcharrefreplace")[0], + "ଓଣୠ nd eggs" + ) + + def test_customreplace_encode(self): + if self.has_iso10646: + return + + from htmlentitydefs import codepoint2name + + def xmlcharnamereplace(exc): + if not isinstance(exc, UnicodeEncodeError): + raise TypeError("don't know how to handle %r" % exc) + l = [] + for c in exc.object[exc.start:exc.end]: + if ord(c) in codepoint2name: + l.append(u"&%s;" % codepoint2name[ord(c)]) + else: + l.append(u"&#%d;" % ord(c)) + return (u"".join(l), exc.end) + + codecs.register_error("test.xmlcharnamereplace", xmlcharnamereplace) - s = u"\u0b13\u0b23\u0b60 nd eggs" - self.assertEqual( - self.encode(s, "xmlcharrefreplace")[0], - "ଓଣୠ nd eggs" - ) + if self.xmlcharnametest: + sin, sout = self.xmlcharnametest + else: + sin = u"\xab\u211c\xbb = \u2329\u1234\u232a" + sout = "«ℜ» = ⟨ሴ⟩" + self.assertEqual(self.encode(sin, + "test.xmlcharnamereplace")[0], sout) + + def test_callback_wrong_objects(self): + def myreplace(exc): + return (ret, exc.end) + codecs.register_error("test.cjktest", myreplace) + + for ret in ([1, 2, 3], [], None, object(), 'string', ''): + self.assertRaises(TypeError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_long_index(self): + def myreplace(exc): + return (u'x', long(exc.end)) + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode(u'abcd' + self.unmappedunicode + u'efgh', + 'test.cjktest'), ('abcdxefgh', 9)) + + def myreplace(exc): + return (u'x', sys.maxint + 1) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(IndexError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_None_index(self): + def myreplace(exc): + return (u'x', None) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(TypeError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_callback_backward_index(self): + def myreplace(exc): + if myreplace.limit > 0: + myreplace.limit -= 1 + return (u'REPLACED', 0) + else: + return (u'TERMINAL', exc.end) + myreplace.limit = 3 + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode(u'abcd' + self.unmappedunicode + u'efgh', + 'test.cjktest'), + ('abcdREPLACEDabcdREPLACEDabcdREPLACEDabcdTERMINALefgh', 9)) + + def test_callback_forward_index(self): + def myreplace(exc): + return (u'REPLACED', exc.end + 2) + codecs.register_error("test.cjktest", myreplace) + self.assertEqual(self.encode(u'abcd' + self.unmappedunicode + u'efgh', + 'test.cjktest'), ('abcdREPLACEDgh', 9)) + + def test_callback_index_outofbound(self): + def myreplace(exc): + return (u'TERM', 100) + codecs.register_error("test.cjktest", myreplace) + self.assertRaises(IndexError, self.encode, self.unmappedunicode, + 'test.cjktest') + + def test_incrementalencoder(self): + UTF8Reader = codecs.getreader('utf-8') + for sizehint in [None] + range(1, 33) + \ + [64, 128, 256, 512, 1024]: + istream = UTF8Reader(StringIO(self.tstring[1])) + ostream = StringIO() + encoder = self.incrementalencoder() + while 1: + if sizehint is not None: + data = istream.read(sizehint) + else: + data = istream.read() - def test_customreplace(self): - if self.has_iso10646: - return + if not data: + break + e = encoder.encode(data) + ostream.write(e) - import htmlentitydefs + self.assertEqual(ostream.getvalue(), self.tstring[0]) - names = {} - for (key, value) in htmlentitydefs.entitydefs.items(): - if len(value)==1: - names[value.decode('latin-1')] = self.decode(key)[0] + def test_incrementaldecoder(self): + UTF8Writer = codecs.getwriter('utf-8') + for sizehint in [None, -1] + range(1, 33) + \ + [64, 128, 256, 512, 1024]: + istream = StringIO(self.tstring[0]) + ostream = UTF8Writer(StringIO()) + decoder = self.incrementaldecoder() + while 1: + data = istream.read(sizehint) + if not data: + break else: - names[unichr(int(value[2:-1]))] = self.decode(key)[0] - - def xmlcharnamereplace(exc): - if not isinstance(exc, UnicodeEncodeError): - raise TypeError("don't know how to handle %r" % exc) - l = [] - for c in exc.object[exc.start:exc.end]: - try: - l.append(u"&%s;" % names[c]) - except KeyError: - l.append(u"&#%d;" % ord(c)) - return (u"".join(l), exc.end) - - codecs.register_error( - "test.xmlcharnamereplace", xmlcharnamereplace) - - if self.xmlcharnametest: - sin, sout = self.xmlcharnametest - else: - sin = u"\xab\u211c\xbb = \u2329\u1234\u232a" - sout = "«ℜ» = ⟨ሴ⟩" - self.assertEqual(self.encode(sin, - "test.xmlcharnamereplace")[0], sout) + u = decoder.decode(data) + ostream.write(u) + + self.assertEqual(ostream.getvalue(), self.tstring[1]) + + def test_incrementalencoder_error_callback(self): + inv = self.unmappedunicode + + e = self.incrementalencoder() + self.assertRaises(UnicodeEncodeError, e.encode, inv, True) + + e.errors = 'ignore' + self.assertEqual(e.encode(inv, True), '') + + e.reset() + def tempreplace(exc): + return (u'called', exc.end) + codecs.register_error('test.incremental_error_callback', tempreplace) + e.errors = 'test.incremental_error_callback' + self.assertEqual(e.encode(inv, True), 'called') + + # again + e.errors = 'ignore' + self.assertEqual(e.encode(inv, True), '') def test_streamreader(self): UTF8Writer = codecs.getwriter('utf-8') @@ -113,11 +220,7 @@ class TestBase: self.assertEqual(ostream.getvalue(), self.tstring[1]) def test_streamwriter(self): - if __cjkcodecs__: - readfuncs = ('read', 'readline', 'readlines') - else: - # standard utf8 codec has broken readline and readlines. - readfuncs = ('read',) + readfuncs = ('read', 'readline', 'readlines') UTF8Reader = codecs.getreader('utf-8') for name in readfuncs: for sizehint in [None] + range(1, 33) + \ @@ -211,10 +314,5 @@ class TestBase_Mapping(unittest.TestCase): self.assertEqual(unicode(csetch, self.encoding), unich) def load_teststring(encoding): - if __cjkcodecs__: - etxt = open(os.path.join('sampletexts', encoding) + '.txt').read() - utxt = open(os.path.join('sampletexts', encoding) + '.utf8').read() - return (etxt, utxt) - else: - from test import cjkencodings_test - return cjkencodings_test.teststring[encoding] + from test import cjkencodings_test + return cjkencodings_test.teststring[encoding] diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index cf83d75..f656b9f 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -1382,8 +1382,15 @@ options: class TestHelp(BaseTest): def setUp(self): + self.orig_columns = os.environ.get('COLUMNS') self.parser = self.make_parser(80) + def tearDown(self): + if self.orig_columns is None: + del os.environ['COLUMNS'] + else: + os.environ['COLUMNS'] = self.orig_columns + def make_parser(self, columns): options = [ make_option("-a", type="string", dest='a', diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index 771fe9d..8aa1657 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -51,6 +51,10 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase): self.check_expr("[1, 2, 3]") self.check_expr("[x**3 for x in range(20)]") self.check_expr("[x**3 for x in range(20) if x % 3]") + self.check_expr("[x**3 for x in range(20) if x % 2 if x % 3]") + self.check_expr("list(x**3 for x in range(20))") + self.check_expr("list(x**3 for x in range(20) if x % 3)") + self.check_expr("list(x**3 for x in range(20) if x % 2 if x % 3)") self.check_expr("foo(*args)") self.check_expr("foo(*args, **kw)") self.check_expr("foo(**kw)") diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 200fba5..22307cd 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -63,7 +63,12 @@ class PlatformTest(unittest.TestCase): res = platform.dist() def test_libc_ver(self): - res = platform.libc_ver() + from sys import executable + import os + if os.path.isdir(executable) and os.path.exists(executable+'.exe'): + # Cygwin horror + executable = executable + '.exe' + res = platform.libc_ver(executable) def test_main(): test_support.run_unittest( diff --git a/Lib/test/test_popen2.py b/Lib/test/test_popen2.py index 18142ec..4db3cd1 100644 --- a/Lib/test/test_popen2.py +++ b/Lib/test/test_popen2.py @@ -35,6 +35,9 @@ def _test(): # same test as popen2._test(), but using the os.popen*() API print "Testing os module:" import popen2 + # When the test runs, there shouldn't be any open pipes + popen2._cleanup() + assert not popen2._active, "Active pipes when test starts " + repr([c.cmd for c in popen2._active]) cmd = "cat" teststr = "ab cd\n" if os.name == "nt": @@ -65,6 +68,7 @@ def _test(): raise ValueError("unexpected %r on stderr" % (got,)) for inst in popen2._active[:]: inst.wait() + popen2._cleanup() if popen2._active: raise ValueError("_active not empty") print "All OK" diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 1ccc62b..f98c723 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -73,6 +73,11 @@ class PosixTester(unittest.TestCase): finally: fp.close() + def test_confstr(self): + if hasattr(posix, 'confstr'): + self.assertRaises(ValueError, posix.confstr, "CS_garbage") + self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True) + def test_dup2(self): if hasattr(posix, 'dup2'): fp1 = open(test_support.TESTFN) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index f8ae479..99e01b6 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -18,6 +18,27 @@ else: def debug(msg): pass +def normalize_output(data): + # Some operating systems do conversions on newline. We could possibly + # fix that by doing the appropriate termios.tcsetattr()s. I couldn't + # figure out the right combo on Tru64 and I don't have an IRIX box. + # So just normalize the output and doc the problem O/Ses by allowing + # certain combinations for some platforms, but avoid allowing other + # differences (like extra whitespace, trailing garbage, etc.) + + # This is about the best we can do without getting some feedback + # from someone more knowledgable. + + # OSF/1 (Tru64) apparently turns \n into \r\r\n. + if data.endswith('\r\r\n'): + return data[:-3] + '\n' + + # IRIX apparently turns \n into \r\n. + if data.endswith('\r\n'): + return data[:-2] + '\n' + + return data + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. @@ -36,19 +57,16 @@ def test_basic_pty(): if not os.isatty(slave_fd) and sys.platform not in fickle_isatty: raise TestFailed, "slave_fd is not a tty" - # IRIX apparently turns \n into \r\n. Allow that, but avoid allowing other - # differences (like extra whitespace, trailing garbage, etc.) - debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) s1 = os.read(master_fd, 1024) - sys.stdout.write(s1.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s1)) debug("Writing chunked output") os.write(slave_fd, TEST_STRING_2[:5]) os.write(slave_fd, TEST_STRING_2[5:]) s2 = os.read(master_fd, 1024) - sys.stdout.write(s2.replace("\r\n", "\n")) + sys.stdout.write(normalize_output(s2)) os.close(slave_fd) os.close(master_fd) diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 0eb7d90..2410b03 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -97,6 +97,9 @@ class PyclbrTest(TestCase): self.assert_(isinstance(py_item, (FunctionType, BuiltinFunctionType))) else: self.failUnless(isinstance(py_item, (ClassType, type))) + if py_item.__module__ != moduleName: + continue # skip classes that came from somewhere else + real_bases = [base.__name__ for base in py_item.__bases__] pyclbr_bases = [ getattr(base, 'name', base) for base in value.super ] @@ -172,7 +175,7 @@ class PyclbrTest(TestCase): cm('pydoc') # Tests for modules inside packages - cm('email.Parser') + cm('email.parser') cm('test.test_pyclbr') diff --git a/Lib/test/test_queue.py b/Lib/test/test_queue.py index b55dd01..66977e6 100644 --- a/Lib/test/test_queue.py +++ b/Lib/test/test_queue.py @@ -221,7 +221,51 @@ def SimpleQueueTest(q): _doBlockingTest(q.get, (), q.put, ('empty',)) _doBlockingTest(q.get, (True, 10), q.put, ('empty',)) +cum = 0 +cumlock = threading.Lock() + +def worker(q): + global cum + while True: + x = q.get() + if x is None: + q.task_done() + return + cumlock.acquire() + try: + cum += x + finally: + cumlock.release() + q.task_done() + +def QueueJoinTest(q): + global cum + cum = 0 + for i in (0,1): + threading.Thread(target=worker, args=(q,)).start() + for i in xrange(100): + q.put(i) + q.join() + verify(cum==sum(range(100)), "q.join() did not block until all tasks were done") + for i in (0,1): + q.put(None) # instruct the threads to close + q.join() # verify that you can join twice + +def QueueTaskDoneTest(q): + try: + q.task_done() + except ValueError: + pass + else: + raise TestFailed("Did not detect task count going negative") + def test(): + q = Queue.Queue() + QueueTaskDoneTest(q) + QueueJoinTest(q) + QueueJoinTest(q) + QueueTaskDoneTest(q) + q = Queue.Queue(QUEUE_SIZE) # Do it a couple of times on the same queue SimpleQueueTest(q) diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py index ed66dfc..631c974 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py @@ -1,7 +1,7 @@ from test import test_support import unittest -import sys, os, cStringIO +import sys, os, cStringIO, subprocess import quopri @@ -176,17 +176,20 @@ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''') def test_scriptencode(self): (p, e) = self.STRINGS[-1] - (cin, cout) = os.popen2("%s -mquopri" % sys.executable) - cin.write(p) - cin.close() - self.assert_(cout.read() == e) + process = subprocess.Popen([sys.executable, "-mquopri"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + cout, cerr = process.communicate(p) + # On Windows, Python will output the result to stdout using + # CRLF, as the mode of stdout is text mode. To compare this + # with the expected result, we need to do a line-by-line comparison. + self.assert_(cout.splitlines() == e.splitlines()) def test_scriptdecode(self): (p, e) = self.STRINGS[-1] - (cin, cout) = os.popen2("%s -mquopri -d" % sys.executable) - cin.write(e) - cin.close() - self.assert_(cout.read() == p) + process = subprocess.Popen([sys.executable, "-mquopri", "-d"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + cout, cerr = process.communicate(e) + self.assert_(cout.splitlines() == p.splitlines()) def test_main(): test_support.run_unittest(QuopriTestCase) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 9c2e0d0..bba4c7c 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -93,10 +93,29 @@ class TestBasicOps(unittest.TestCase): self.gen.sample(set(range(20)), 2) self.gen.sample(range(20), 2) self.gen.sample(xrange(20), 2) - self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) self.gen.sample(str('abcdefghijklmnopqrst'), 2) self.gen.sample(tuple('abcdefghijklmnopqrst'), 2) + def test_sample_on_dicts(self): + self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) + + # SF bug #1460340 -- random.sample can raise KeyError + a = dict.fromkeys(range(10)+range(10,100,2)+range(100,110)) + self.gen.sample(a, 3) + + # A followup to bug #1460340: sampling from a dict could return + # a subset of its keys or of its values, depending on the size of + # the subset requested. + N = 30 + d = dict((i, complex(i, i)) for i in xrange(N)) + for k in xrange(N+1): + samp = self.gen.sample(d, k) + # Verify that we got ints back (keys); the values are complex. + for x in samp: + self.assert_(type(x) is int) + samp.sort() + self.assertEqual(samp, range(N)) + def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In # particular, through 2.2.1 it failed to reset a piece of state used diff --git a/Lib/test/test_regex.py b/Lib/test/test_regex.py deleted file mode 100644 index 2e2c8f65..0000000 --- a/Lib/test/test_regex.py +++ /dev/null @@ -1,113 +0,0 @@ -from test.test_support import verbose, sortdict -import warnings -warnings.filterwarnings("ignore", "the regex module is deprecated", - DeprecationWarning, __name__) -import regex -from regex_syntax import * - -re = 'a+b+c+' -print 'no match:', regex.match(re, 'hello aaaabcccc world') -print 'successful search:', regex.search(re, 'hello aaaabcccc world') -try: - cre = regex.compile('\(' + re) -except regex.error: - print 'caught expected exception' -else: - print 'expected regex.error not raised' - -print 'failed awk syntax:', regex.search('(a+)|(b+)', 'cdb') -prev = regex.set_syntax(RE_SYNTAX_AWK) -print 'successful awk syntax:', regex.search('(a+)|(b+)', 'cdb') -regex.set_syntax(prev) -print 'failed awk syntax:', regex.search('(a+)|(b+)', 'cdb') - -re = '\([0-9]+\) *\([0-9]+\)' -print 'matching with group names and compile()' -cre = regex.compile(re) -print cre.match('801 999') -try: - print cre.group('one') -except regex.error: - print 'caught expected exception' -else: - print 'expected regex.error not raised' - -print 'matching with group names and symcomp()' -cre = regex.symcomp(re) -print cre.match('801 999') -print cre.group(0) -print cre.group('one') -print cre.group(1, 2) -print cre.group('one', 'two') -print 'realpat:', cre.realpat -print 'groupindex:', sortdict(cre.groupindex) - -re = 'world' -cre = regex.compile(re) -print 'not case folded search:', cre.search('HELLO WORLD') -cre = regex.compile(re, regex.casefold) -print 'case folded search:', cre.search('HELLO WORLD') - -print '__members__:', cre.__members__ -print 'regs:', cre.regs -print 'last:', cre.last -print 'translate:', len(cre.translate) -print 'givenpat:', cre.givenpat - -print 'match with pos:', cre.match('hello world', 7) -print 'search with pos:', cre.search('hello world there world', 7) -print 'bogus group:', cre.group(0, 1, 3) -try: - print 'no name:', cre.group('one') -except regex.error: - print 'caught expected exception' -else: - print 'expected regex.error not raised' - -from regex_tests import * -if verbose: print 'Running regex_tests test suite' - -for t in tests: - pattern=s=outcome=repl=expected=None - if len(t)==5: - pattern, s, outcome, repl, expected = t - elif len(t)==3: - pattern, s, outcome = t - else: - raise ValueError, ('Test tuples should have 3 or 5 fields',t) - - try: - obj=regex.compile(pattern) - except regex.error: - if outcome==SYNTAX_ERROR: pass # Expected a syntax error - else: - # Regex syntax errors aren't yet reported, so for - # the official test suite they'll be quietly ignored. - pass - #print '=== Syntax error:', t - else: - try: - result=obj.search(s) - except regex.error, msg: - print '=== Unexpected exception', t, repr(msg) - if outcome==SYNTAX_ERROR: - # This should have been a syntax error; forget it. - pass - elif outcome==FAIL: - if result==-1: pass # No match, as expected - else: print '=== Succeeded incorrectly', t - elif outcome==SUCCEED: - if result!=-1: - # Matched, as expected, so now we compute the - # result string and compare it to our expected result. - start, end = obj.regs[0] - found=s[start:end] - groups=obj.group(1,2,3,4,5,6,7,8,9,10) - vardict=vars() - for i in range(len(groups)): - vardict['g'+str(i+1)]=str(groups[i]) - repl=eval(repl) - if repl!=expected: - print '=== grouping error', t, repr(repl)+' should be '+repr(expected) - else: - print '=== Failed incorrectly', t diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 6ff1215..0268be2 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -224,7 +224,7 @@ class TestJointOps(unittest.TestCase): # Bug #1257731 class H(self.thetype): def __hash__(self): - return id(self) + return int(id(self) & 0x7fffffff) s=H() f=set() f.add(s) @@ -421,7 +421,7 @@ class TestSet(TestJointOps): self.assertRaises(ReferenceError, str, p) # C API test only available in a debug build - if hasattr(sys, "gettotalrefcount"): + if hasattr(set, "test_c_api"): def test_c_api(self): self.assertEqual(set('abc').test_c_api(), True) diff --git a/Lib/test/test_setuptools.py b/Lib/test/test_setuptools.py new file mode 100644 index 0000000..a988303 --- /dev/null +++ b/Lib/test/test_setuptools.py @@ -0,0 +1,16 @@ +"""Tests for setuptools. + +The tests for setuptools are defined in the setuptools.tests package; +this runs them from there. +""" + +import test.test_support +from setuptools.command.test import ScanningLoader + +def test_main(): + test.test_support.run_suite( + ScanningLoader().loadTestsFromName('setuptools.tests') + ) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_sgmllib.py b/Lib/test/test_sgmllib.py index bc25bd0..8e8b02f 100644 --- a/Lib/test/test_sgmllib.py +++ b/Lib/test/test_sgmllib.py @@ -214,6 +214,20 @@ DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01//EN' ("starttag", "e", [("a", "rgb(1,2,3)")]), ]) + def test_attr_values_entities(self): + """Substitution of entities and charrefs in attribute values""" + # SF bug #1452246 + self.check_events("""""", + [("starttag", "a", [("b", "<"), + ("c", "<>"), + ("d", "<->"), + ("e", "< "), + ("f", "&xxx;"), + ("g", " !"), + ("h", "Ǵ"), + ("i", "x?a=b&c=d;"), ])]) + def test_attr_funky_names(self): self.check_events("""""", [ ("starttag", "a", [("a.b", "v"), ("c:d", "v"), ("e-f", "v")]), diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1899e78..6943080 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -268,9 +268,9 @@ class GeneralModuleTests(unittest.TestCase): # Probably a similar problem as above; skip this test return all_host_names = [hostname, hname] + aliases - fqhn = socket.getfqdn() + fqhn = socket.getfqdn(ip) if not fqhn in all_host_names: - self.fail("Error testing host resolution mechanisms.") + self.fail("Error testing host resolution mechanisms. (fqdn: %s, all: %s)" % (fqhn, repr(all_host_names))) def testRefCountGetNameInfo(self): # Testing reference count for getnameinfo @@ -469,6 +469,14 @@ class GeneralModuleTests(unittest.TestCase): sock.close() self.assertRaises(socket.error, sock.send, "spam") + def testNewAttributes(self): + # testing .family, .type and .protocol + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.assertEqual(sock.family, socket.AF_INET) + self.assertEqual(sock.type, socket.SOCK_STREAM) + self.assertEqual(sock.proto, 0) + sock.close() + class BasicTCPTest(SocketConnectedTest): def __init__(self, methodName='runTest'): @@ -817,6 +825,32 @@ class TestExceptions(unittest.TestCase): self.assert_(issubclass(socket.gaierror, socket.error)) self.assert_(issubclass(socket.timeout, socket.error)) +class TestLinuxAbstractNamespace(unittest.TestCase): + + UNIX_PATH_MAX = 108 + + def testLinuxAbstractNamespace(self): + address = "\x00python-test-hello\x00\xff" + s1 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s1.bind(address) + s1.listen(1) + s2 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s2.connect(s1.getsockname()) + s1.accept() + self.assertEqual(s1.getsockname(), address) + self.assertEqual(s2.getpeername(), address) + + def testMaxName(self): + address = "\x00" + "h" * (self.UNIX_PATH_MAX - 1) + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.bind(address) + self.assertEqual(s.getsockname(), address) + + def testNameOverflow(self): + address = "\x00" + "h" * self.UNIX_PATH_MAX + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.assertRaises(socket.error, s.bind, address) + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPTimeoutTest, TestExceptions] @@ -832,6 +866,8 @@ def test_main(): ]) if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) + if sys.platform == 'linux2': + tests.append(TestLinuxAbstractNamespace) test_support.run_unittest(*tests) if __name__ == "__main__": diff --git a/Lib/test/test_socket_ssl.py b/Lib/test/test_socket_ssl.py index 98680b9..1091383 100644 --- a/Lib/test/test_socket_ssl.py +++ b/Lib/test/test_socket_ssl.py @@ -1,5 +1,6 @@ # Test just the SSL support in the socket module, in a moderately bogus way. +import sys from test import test_support import socket @@ -13,6 +14,9 @@ def test_basic(): import urllib + if test_support.verbose: + print "test_basic ..." + socket.RAND_status() try: socket.RAND_egd(1) @@ -26,7 +30,41 @@ def test_basic(): buf = f.read() f.close() +def test_timeout(): + test_support.requires('network') + + if test_support.verbose: + print "test_timeout ..." + + # A service which issues a welcome banner (without need to write + # anything). + # XXX ("gmail.org", 995) has been unreliable so far, from time to time + # XXX non-responsive for hours on end (& across all buildbot slaves, + # XXX so that's not just a local thing). + ADDR = "gmail.org", 995 + + s = socket.socket() + s.settimeout(30.0) + try: + s.connect(ADDR) + except socket.timeout: + print >> sys.stderr, """\ + WARNING: an attempt to connect to %r timed out, in + test_timeout. That may be legitimate, but is not the outcome we hoped + for. If this message is seen often, test_timeout should be changed to + use a more reliable address.""" % (ADDR,) + return + + ss = socket.ssl(s) + # Read part of return welcome banner twice. + ss.read(1) + ss.read(1) + s.close() + def test_rude_shutdown(): + if test_support.verbose: + print "test_rude_shutdown ..." + try: import threading except ImportError: @@ -74,6 +112,7 @@ def test_main(): raise test_support.TestSkipped("socket module has no ssl support") test_rude_shutdown() test_basic() + test_timeout() if __name__ == "__main__": test_main() diff --git a/Lib/test/test_sqlite.py b/Lib/test/test_sqlite.py new file mode 100644 index 0000000..1b1d0e5 --- /dev/null +++ b/Lib/test/test_sqlite.py @@ -0,0 +1,16 @@ +from test.test_support import run_unittest, TestSkipped +import unittest + +try: + import _sqlite3 +except ImportError: + raise TestSkipped('no sqlite available') +from sqlite3.test import (dbapi, types, userfunctions, + factory, transactions) + +def test_main(): + run_unittest(dbapi.suite(), types.suite(), userfunctions.suite(), + factory.suite(), transactions.suite()) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_startfile.py b/Lib/test/test_startfile.py new file mode 100644 index 0000000..c4d12d7 --- /dev/null +++ b/Lib/test/test_startfile.py @@ -0,0 +1,37 @@ +# Ridiculously simple test of the os.startfile function for Windows. +# +# empty.vbs is an empty file (except for a comment), which does +# nothing when run with cscript or wscript. +# +# A possible improvement would be to have empty.vbs do something that +# we can detect here, to make sure that not only the os.startfile() +# call succeeded, but also the the script actually has run. + +import unittest +from test import test_support + +# use this form so that the test is skipped when startfile is not available: +from os import startfile, path + +class TestCase(unittest.TestCase): + def test_nonexisting(self): + self.assertRaises(OSError, startfile, "nonexisting.vbs") + + def test_nonexisting_u(self): + self.assertRaises(OSError, startfile, u"nonexisting.vbs") + + def test_empty(self): + empty = path.join(path.dirname(__file__), "empty.vbs") + startfile(empty) + startfile(empty, "open") + + def test_empty_u(self): + empty = path.join(path.dirname(__file__), "empty.vbs") + startfile(unicode(empty, "mbcs")) + startfile(unicode(empty, "mbcs"), "open") + +def test_main(): + test_support.run_unittest(TestCase) + +if __name__=="__main__": + test_main() diff --git a/Lib/test/test_sundry.py b/Lib/test/test_sundry.py index fd10b68..af13684 100644 --- a/Lib/test/test_sundry.py +++ b/Lib/test/test_sundry.py @@ -12,75 +12,50 @@ warnings.filterwarnings("ignore", from test.test_support import verbose import BaseHTTPServer +import DocXMLRPCServer import CGIHTTPServer -import Queue import SimpleHTTPServer -import SocketServer +import SimpleXMLRPCServer import aifc -import anydbm import audiodev import bdb +import cgitb import cmd import code -import codeop -import colorsys -import commands import compileall -try: - import curses # not available on Windows -except ImportError: - if verbose: - print "skipping curses" -import dircache -import dis -import distutils -import doctest -import dumbdbm import encodings -import fnmatch import formatter -import fpformat import ftplib import getpass -import glob import gopherlib import htmlentitydefs -import htmllib -import httplib -import imaplib +import ihooks import imghdr import imputil import keyword -import macpath +import linecache import macurl2path import mailcap -import mhlib -import mimetypes import mimify -import multifile import mutex import nntplib import nturl2path +import opcode +import os2emxpath import pdb import pipes #import poplib import posixfile -import profile import pstats import py_compile -#import reconvert -import repr +import pydoc +import rexec try: import rlcompleter # not available on Windows except ImportError: if verbose: print "skipping rlcompleter" -import robotparser import sched -import sgmllib -import shelve -import shlex -import shutil import smtplib import sndhdr import statvfs @@ -90,12 +65,17 @@ import sunaudio import symbol import tabnanny import telnetlib -import test +import timeit import toaiff -import urllib2 +import token +try: + import tty # not available on Windows +except ImportError: + if verbose: + print "skipping tty" + # Can't test the "user" module -- if the user has a ~/.pythonrc.py, it # can screw up all sorts of things (esp. if it prints!). #import user import webbrowser -import whichdb import xml diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b98c648..ae2a1c8 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -261,6 +261,11 @@ class SysModuleTest(unittest.TestCase): self.assert_(vi[3] in ("alpha", "beta", "candidate", "final")) self.assert_(isinstance(vi[4], int)) + def test_43581(self): + # Can't use sys.stdout, as this is a cStringIO object when + # the test runs under regrtest. + self.assert_(sys.__stdout__.encoding == sys.__stderr__.encoding) + def test_main(): test.test_support.run_unittest(SysModuleTest) diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index cb19d9e..4309e8c 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -113,8 +113,9 @@ class TimeoutTestCase(unittest.TestCase): # If we are too close to www.python.org, this test will fail. # Pick a host that should be farther away. - if socket.getfqdn().split('.')[-2:] == ['python', 'org']: - self.addr_remote = ('python.net', 80) + if (socket.getfqdn().split('.')[-2:] == ['python', 'org'] or + socket.getfqdn().split('.')[-2:-1] == ['xs4all']): + self.addr_remote = ('tut.fi', 80) _t1 = time.time() self.failUnlessRaises(socket.error, self.sock.connect, diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index d3c1cc4..b064967 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1,70 +1,30 @@ -from test.test_support import verbose, findfile, is_resource_enabled, TestFailed import os, glob, random +from cStringIO import StringIO +from test.test_support import (verbose, findfile, is_resource_enabled, + TestFailed) from tokenize import (tokenize, generate_tokens, untokenize, NUMBER, NAME, OP, STRING) -if verbose: - print 'starting...' - -f = file(findfile('tokenize_tests' + os.extsep + 'txt')) -tokenize(f.readline) -f.close() - - - -###### Test roundtrip for untokenize ########################## - +# Test roundtrip for `untokenize`. `f` is a file path. The source code in f +# is tokenized, converted back to source code via tokenize.untokenize(), +# and tokenized again from the latter. The test fails if the second +# tokenization doesn't match the first. def test_roundtrip(f): ## print 'Testing:', f - f = file(f) + fobj = open(f) try: - fulltok = list(generate_tokens(f.readline)) + fulltok = list(generate_tokens(fobj.readline)) finally: - f.close() + fobj.close() t1 = [tok[:2] for tok in fulltok] newtext = untokenize(t1) readline = iter(newtext.splitlines(1)).next t2 = [tok[:2] for tok in generate_tokens(readline)] - assert t1 == t2 - - -f = findfile('tokenize_tests' + os.extsep + 'txt') -test_roundtrip(f) - -testdir = os.path.dirname(f) or os.curdir -testfiles = glob.glob(testdir + os.sep + 'test*.py') -if not is_resource_enabled('compiler'): - testfiles = random.sample(testfiles, 10) - -for f in testfiles: - test_roundtrip(f) - - -###### Test detecton of IndentationError ###################### - -from cStringIO import StringIO - -sampleBadText = """ -def foo(): - bar - baz -""" - -try: - for tok in generate_tokens(StringIO(sampleBadText).readline): - pass -except IndentationError: - pass -else: - raise TestFailed("Did not detect IndentationError:") - - -###### Test example in the docs ############################### - -from decimal import Decimal -from cStringIO import StringIO + if t1 != t2: + raise TestFailed("untokenize() roundtrip failed for %r" % f) +# This is an example from the docs, set up as a doctest. def decistmt(s): """Substitute Decimals for floats in a string of statements. @@ -73,12 +33,21 @@ def decistmt(s): >>> decistmt(s) "print +Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7')" - >>> exec(s) - -3.21716034272e-007 + The format of the exponent is inherited from the platform C library. + Known cases are "e-007" (Windows) and "e-07" (not Windows). Since + we're only showing 12 digits, and the 13th isn't close to 5, the + rest of the output should be platform-independent. + + >>> exec(s) #doctest: +ELLIPSIS + -3.21716034272e-0...7 + + Output from calculations with Decimal should be identical across all + platforms. + >>> exec(decistmt(s)) -3.217160342717258261933904529E-7 - """ + result = [] g = generate_tokens(StringIO(s).readline) # tokenize the string for toknum, tokval, _, _, _ in g: @@ -93,8 +62,53 @@ def decistmt(s): result.append((toknum, tokval)) return untokenize(result) -import doctest -doctest.testmod() +def test_main(): + if verbose: + print 'starting...' + + # This displays the tokenization of tokenize_tests.py to stdout, and + # regrtest.py checks that this equals the expected output (in the + # test/output/ directory). + f = open(findfile('tokenize_tests' + os.extsep + 'txt')) + tokenize(f.readline) + f.close() + + # Now run test_roundtrip() over tokenize_test.py too, and over all + # (if the "compiler" resource is enabled) or a small random sample (if + # "compiler" is not enabled) of the test*.py files. + f = findfile('tokenize_tests' + os.extsep + 'txt') + test_roundtrip(f) + + testdir = os.path.dirname(f) or os.curdir + testfiles = glob.glob(testdir + os.sep + 'test*.py') + if not is_resource_enabled('compiler'): + testfiles = random.sample(testfiles, 10) + + for f in testfiles: + test_roundtrip(f) + + # Test detecton of IndentationError. + sampleBadText = """\ +def foo(): + bar + baz +""" + + try: + for tok in generate_tokens(StringIO(sampleBadText).readline): + pass + except IndentationError: + pass + else: + raise TestFailed("Did not detect IndentationError:") + + # Run the doctests in this module. + from test import test_tokenize # i.e., this module + from test.test_support import run_doctest + run_doctest(test_tokenize) + + if verbose: + print 'finished' -if verbose: - print 'finished' +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 944ff9a..4f946f7 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -13,7 +13,15 @@ basic.events = [(0, 'call'), (1, 'line'), (1, 'return')] -# Armin Rigo's failing example: +# Many of the tests below are tricky because they involve pass statements. +# If there is implicit control flow around a pass statement (in an except +# clause or else caluse) under what conditions do you set a line number +# following that clause? + + +# The entire "while 0:" statement is optimized away. No code +# exists for it, so the line numbers skip directly from "del x" +# to "x = 1". def arigo_example(): x = 1 del x @@ -24,7 +32,6 @@ def arigo_example(): arigo_example.events = [(0, 'call'), (1, 'line'), (2, 'line'), - (3, 'line'), (5, 'line'), (5, 'return')] @@ -60,14 +67,16 @@ no_pop_tops.events = [(0, 'call'), (2, 'return')] def no_pop_blocks(): - while 0: + y = 1 + while not y: bla x = 1 no_pop_blocks.events = [(0, 'call'), (1, 'line'), - (3, 'line'), - (3, 'return')] + (2, 'line'), + (4, 'line'), + (4, 'return')] def called(): # line -3 x = 1 @@ -127,6 +136,13 @@ settrace_and_raise.events = [(2, 'exception'), (4, 'return')] # implicit return example +# This test is interesting because of the else: pass +# part of the code. The code generate for the true +# part of the if contains a jump past the else branch. +# The compiler then generates an implicit "return None" +# Internally, the compiler visits the pass statement +# and stores its line number for use on the next instruction. +# The next instruction is the implicit return None. def ireturn_example(): a = 5 b = 5 @@ -140,7 +156,8 @@ ireturn_example.events = [(0, 'call'), (2, 'line'), (3, 'line'), (4, 'line'), - (4, 'return')] + (6, 'line'), + (6, 'return')] # Tight loop with while(1) example (SF #765624) def tightloop_example(): @@ -221,14 +238,12 @@ class TraceTestCase(unittest.TestCase): def test_01_basic(self): self.run_test(basic) -## XXX: These tests fail with the new ast compiler. They must -## be fixed before a release. -## def test_02_arigo(self): -## self.run_test(arigo_example) + def test_02_arigo(self): + self.run_test(arigo_example) def test_03_one_instr(self): self.run_test(one_instr_line) -## def test_04_no_pop_blocks(self): -## self.run_test(no_pop_blocks) + def test_04_no_pop_blocks(self): + self.run_test(no_pop_blocks) ## def test_05_no_pop_tops(self): ## self.run_test(no_pop_tops) def test_06_call(self): @@ -240,8 +255,8 @@ class TraceTestCase(unittest.TestCase): self.run_test2(settrace_and_return) def test_09_settrace_and_raise(self): self.run_test2(settrace_and_raise) -## def test_10_ireturn(self): -## self.run_test(ireturn_example) + def test_10_ireturn(self): + self.run_test(ireturn_example) def test_11_tightloop(self): self.run_test(tightloop_example) def test_12_tighterloop(self): @@ -579,17 +594,14 @@ class JumpTestCase(unittest.TestCase): self.run_test(no_jump_too_far_forwards) def test_09_no_jump_too_far_backwards(self): self.run_test(no_jump_too_far_backwards) -# XXX: These tests cause the interpreter to crash. The frame_setlineno() -# function no longer works correctly because the lineno table generated by -# the AST compiler is slightly different than with the old compiler. -# def test_10_no_jump_to_except_1(self): -# self.run_test(no_jump_to_except_1) -# def test_11_no_jump_to_except_2(self): -# self.run_test(no_jump_to_except_2) -# def test_12_no_jump_to_except_3(self): -# self.run_test(no_jump_to_except_3) -# def test_13_no_jump_to_except_4(self): -# self.run_test(no_jump_to_except_4) + def test_10_no_jump_to_except_1(self): + self.run_test(no_jump_to_except_1) + def test_11_no_jump_to_except_2(self): + self.run_test(no_jump_to_except_2) + def test_12_no_jump_to_except_3(self): + self.run_test(no_jump_to_except_3) + def test_13_no_jump_to_except_4(self): + self.run_test(no_jump_to_except_4) def test_14_no_jump_forwards_into_block(self): self.run_test(no_jump_forwards_into_block) def test_15_no_jump_backwards_into_block(self): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 29a120f..22c0456 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -24,6 +24,9 @@ class TracebackCases(unittest.TestCase): # XXX why doesn't compile raise the same traceback? import test.badsyntax_nocaret + def syntax_error_bad_indentation(self): + compile("def spam():\n print 1\n print 2", "?", "exec") + def test_caret(self): err = self.get_exception_format(self.syntax_error_with_caret, SyntaxError) @@ -40,6 +43,13 @@ class TracebackCases(unittest.TestCase): self.assert_(len(err) == 3) self.assert_(err[1].strip() == "[x for x in x] = x") + def test_bad_indentation(self): + err = self.get_exception_format(self.syntax_error_bad_indentation, + IndentationError) + self.assert_(len(err) == 4) + self.assert_("^" in err[2]) + self.assert_(err[1].strip() == "print 2") + def test_bug737473(self): import sys, os, tempfile, time diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 49ef29d..c7113b5 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -810,6 +810,22 @@ class UnicodeTest( self.assertEqual(str(Foo9("foo")), "string") self.assertEqual(unicode(Foo9("foo")), u"not unicode") + def test_unicode_repr(self): + class s1: + def __repr__(self): + return '\\n' + + class s2: + def __repr__(self): + return u'\\n' + + self.assertEqual(repr(s1()), '\\n') + self.assertEqual(repr(s2()), '\\n') + + + + + def test_main(): test_support.run_unittest(UnicodeTest) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 2843138..64a2ee9 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -13,8 +13,7 @@ from urllib2 import Request, OpenerDirector # parse_keqv_list, parse_http_list (I'm leaving this for Anthony Baxter # and Greg Stein, since they're doing Digest Authentication) # Authentication stuff (ditto) -# ProxyHandler, CustomProxy, CustomProxyHandler (I don't use a proxy) -# GopherHandler (haven't used gopher for a decade or so...) +# CustomProxy, CustomProxyHandler class TrivialTests(unittest.TestCase): def test_trivial(self): @@ -90,6 +89,7 @@ class FakeMethod: return self.handle(self.meth_name, self.action, *args) class MockHandler: + handler_order = 500 def __init__(self, methods): self._define_methods(methods) def _define_methods(self, methods): @@ -154,7 +154,7 @@ def add_ordered_mock_handlers(opener, meth_spec): for meths in meth_spec: class MockHandlerSubclass(MockHandler): pass h = MockHandlerSubclass(meths) - h.handler_order = count + h.handler_order += count h.add_parent(opener) count = count + 1 handlers.append(h) @@ -349,13 +349,19 @@ class HandlerTests(unittest.TestCase): TESTFN = test_support.TESTFN urlpath = sanepathname2url(os.path.abspath(TESTFN)) towrite = "hello, world\n" - for url in [ + urls = [ "file://localhost%s" % urlpath, "file://%s" % urlpath, "file://%s%s" % (socket.gethostbyname('localhost'), urlpath), - "file://%s%s" % (socket.gethostbyname(socket.gethostname()), - urlpath), - ]: + ] + try: + localaddr = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + localaddr = '' + if localaddr: + urls.append("file://%s%s" % (localaddr, urlpath)) + + for url in urls: f = open(TESTFN, "wb") try: try: @@ -636,6 +642,23 @@ class HandlerTests(unittest.TestCase): o.open("http://www.example.com/") self.assert_(not hh.req.has_header("Cookie")) + def test_proxy(self): + o = OpenerDirector() + ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128")) + o.add_handler(ph) + meth_spec = [ + [("http_open", "return response")] + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://acme.example.com/") + self.assertEqual(req.get_host(), "acme.example.com") + r = o.open(req) + self.assertEqual(req.get_host(), "proxy.example.com:3128") + + self.assertEqual([(handlers[0], "http_open")], + [tup[0:2] for tup in o.calls]) + class MiscTests(unittest.TestCase): @@ -821,6 +844,7 @@ class NetworkTests(unittest.TestCase): def test_main(verbose=None): + test_support.run_doctest(urllib2, verbose) tests = (TrivialTests, OpenerDirectorTests, HandlerTests, diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 39ada06..5cee458 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -12,15 +12,53 @@ class UrlParseTestCase(unittest.TestCase): def checkRoundtrips(self, url, parsed, split): result = urlparse.urlparse(url) self.assertEqual(result, parsed) + t = (result.scheme, result.netloc, result.path, + result.params, result.query, result.fragment) + self.assertEqual(t, parsed) # put it back together and it should be the same result2 = urlparse.urlunparse(result) self.assertEqual(result2, url) + self.assertEqual(result2, result.geturl()) + + # the result of geturl() is a fixpoint; we can always parse it + # again to get the same result: + result3 = urlparse.urlparse(result.geturl()) + self.assertEqual(result3.geturl(), result.geturl()) + self.assertEqual(result3, result) + self.assertEqual(result3.scheme, result.scheme) + self.assertEqual(result3.netloc, result.netloc) + self.assertEqual(result3.path, result.path) + self.assertEqual(result3.params, result.params) + self.assertEqual(result3.query, result.query) + self.assertEqual(result3.fragment, result.fragment) + self.assertEqual(result3.username, result.username) + self.assertEqual(result3.password, result.password) + self.assertEqual(result3.hostname, result.hostname) + self.assertEqual(result3.port, result.port) # check the roundtrip using urlsplit() as well result = urlparse.urlsplit(url) self.assertEqual(result, split) + t = (result.scheme, result.netloc, result.path, + result.query, result.fragment) + self.assertEqual(t, split) result2 = urlparse.urlunsplit(result) self.assertEqual(result2, url) + self.assertEqual(result2, result.geturl()) + + # check the fixpoint property of re-parsing the result of geturl() + result3 = urlparse.urlsplit(result.geturl()) + self.assertEqual(result3.geturl(), result.geturl()) + self.assertEqual(result3, result) + self.assertEqual(result3.scheme, result.scheme) + self.assertEqual(result3.netloc, result.netloc) + self.assertEqual(result3.path, result.path) + self.assertEqual(result3.query, result.query) + self.assertEqual(result3.fragment, result.fragment) + self.assertEqual(result3.username, result.username) + self.assertEqual(result3.password, result.password) + self.assertEqual(result3.hostname, result.hostname) + self.assertEqual(result3.port, result.port) def test_roundtrips(self): testcases = [ @@ -187,6 +225,69 @@ class UrlParseTestCase(unittest.TestCase): ]: self.assertEqual(urlparse.urldefrag(url), (defrag, frag)) + def test_urlsplit_attributes(self): + url = "HTTP://WWW.PYTHON.ORG/doc/#frag" + p = urlparse.urlsplit(url) + self.assertEqual(p.scheme, "http") + self.assertEqual(p.netloc, "WWW.PYTHON.ORG") + self.assertEqual(p.path, "/doc/") + self.assertEqual(p.query, "") + self.assertEqual(p.fragment, "frag") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, "www.python.org") + self.assertEqual(p.port, None) + # geturl() won't return exactly the original URL in this case + # since the scheme is always case-normalized + #self.assertEqual(p.geturl(), url) + + url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" + p = urlparse.urlsplit(url) + self.assertEqual(p.scheme, "http") + self.assertEqual(p.netloc, "User:Pass@www.python.org:080") + self.assertEqual(p.path, "/doc/") + self.assertEqual(p.query, "query=yes") + self.assertEqual(p.fragment, "frag") + self.assertEqual(p.username, "User") + self.assertEqual(p.password, "Pass") + self.assertEqual(p.hostname, "www.python.org") + self.assertEqual(p.port, 80) + self.assertEqual(p.geturl(), url) + + def test_attributes_bad_port(self): + """Check handling of non-integer ports.""" + p = urlparse.urlsplit("http://www.example.net:foo") + self.assertEqual(p.netloc, "www.example.net:foo") + self.assertRaises(ValueError, lambda: p.port) + + p = urlparse.urlparse("http://www.example.net:foo") + self.assertEqual(p.netloc, "www.example.net:foo") + self.assertRaises(ValueError, lambda: p.port) + + def test_attributes_without_netloc(self): + # This example is straight from RFC 3261. It looks like it + # should allow the username, hostname, and port to be filled + # in, but doesn't. Since it's a URI and doesn't use the + # scheme://netloc syntax, the netloc and related attributes + # should be left empty. + uri = "sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15" + p = urlparse.urlsplit(uri) + self.assertEqual(p.netloc, "") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, None) + self.assertEqual(p.port, None) + self.assertEqual(p.geturl(), uri) + + p = urlparse.urlparse(uri) + self.assertEqual(p.netloc, "") + self.assertEqual(p.username, None) + self.assertEqual(p.password, None) + self.assertEqual(p.hostname, None) + self.assertEqual(p.port, None) + self.assertEqual(p.geturl(), uri) + + def test_main(): test_support.run_unittest(UrlParseTestCase) diff --git a/Lib/test/test_wait3.py b/Lib/test/test_wait3.py new file mode 100644 index 0000000..f6a41a6 --- /dev/null +++ b/Lib/test/test_wait3.py @@ -0,0 +1,32 @@ +"""This test checks for correct wait3() behavior. +""" + +import os +from test.fork_wait import ForkWait +from test.test_support import TestSkipped, run_unittest + +try: + os.fork +except AttributeError: + raise TestSkipped, "os.fork not defined -- skipping test_wait3" + +try: + os.wait3 +except AttributeError: + raise TestSkipped, "os.wait3 not defined -- skipping test_wait3" + +class Wait3Test(ForkWait): + def wait_impl(self, cpid): + while 1: + spid, status, rusage = os.wait3(0) + if spid == cpid: + break + self.assertEqual(spid, cpid) + self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) + self.assertTrue(rusage) + +def test_main(): + run_unittest(Wait3Test) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_wait4.py b/Lib/test/test_wait4.py new file mode 100644 index 0000000..027e5c3 --- /dev/null +++ b/Lib/test/test_wait4.py @@ -0,0 +1,29 @@ +"""This test checks for correct wait4() behavior. +""" + +import os +from test.fork_wait import ForkWait +from test.test_support import TestSkipped, run_unittest + +try: + os.fork +except AttributeError: + raise TestSkipped, "os.fork not defined -- skipping test_wait4" + +try: + os.wait4 +except AttributeError: + raise TestSkipped, "os.wait4 not defined -- skipping test_wait4" + +class Wait4Test(ForkWait): + def wait_impl(self, cpid): + spid, status, rusage = os.wait4(cpid, 0) + self.assertEqual(spid, cpid) + self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) + self.assertTrue(rusage) + +def test_main(): + run_unittest(Wait4Test) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index b7061c1..5d051a5 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -82,6 +82,10 @@ class TestModule(unittest.TestCase): self.assertEqual(msg.category, 'UserWarning') def test_main(verbose=None): + # Obscure hack so that this test passes after reloads or repeated calls + # to test_main (regrtest -R). + if '__warningregistry__' in globals(): + del globals()['__warningregistry__'] test_support.run_unittest(TestModule) if __name__ == "__main__": diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py index 77c432a..19d4459 100644 --- a/Lib/test/test_winsound.py +++ b/Lib/test/test_winsound.py @@ -3,6 +3,9 @@ import unittest from test import test_support import winsound, time +import os +import subprocess + class BeepTest(unittest.TestCase): @@ -44,6 +47,7 @@ class MessageBeepTest(unittest.TestCase): def test_question(self): winsound.MessageBeep(winsound.MB_ICONQUESTION) + class PlaySoundTest(unittest.TestCase): def test_errors(self): @@ -56,19 +60,54 @@ class PlaySoundTest(unittest.TestCase): ) def test_alias_asterisk(self): - winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS) + if _have_soundcard(): + winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + 'SystemAsterisk', winsound.SND_ALIAS + ) def test_alias_exclamation(self): - winsound.PlaySound('SystemExclamation', winsound.SND_ALIAS) + if _have_soundcard(): + winsound.PlaySound('SystemExclamation', winsound.SND_ALIAS) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + 'SystemExclamation', winsound.SND_ALIAS + ) def test_alias_exit(self): - winsound.PlaySound('SystemExit', winsound.SND_ALIAS) + if _have_soundcard(): + winsound.PlaySound('SystemExit', winsound.SND_ALIAS) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + 'SystemExit', winsound.SND_ALIAS + ) def test_alias_hand(self): - winsound.PlaySound('SystemHand', winsound.SND_ALIAS) + if _have_soundcard(): + winsound.PlaySound('SystemHand', winsound.SND_ALIAS) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + 'SystemHand', winsound.SND_ALIAS + ) def test_alias_question(self): - winsound.PlaySound('SystemQuestion', winsound.SND_ALIAS) + if _have_soundcard(): + winsound.PlaySound('SystemQuestion', winsound.SND_ALIAS) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + 'SystemQuestion', winsound.SND_ALIAS + ) def test_alias_fallback(self): # This test can't be expected to work on all systems. The MS @@ -85,41 +124,83 @@ class PlaySoundTest(unittest.TestCase): return def test_alias_nofallback(self): - # Note that this is not the same as asserting RuntimeError - # will get raised: you cannot convert this to - # self.assertRaises(...) form. The attempt may or may not - # raise RuntimeError, but it shouldn't raise anything other - # than RuntimeError, and that's all we're trying to test here. - # The MS docs aren't clear about whether the SDK PlaySound() - # with SND_ALIAS and SND_NODEFAULT will return True or False when - # the alias is unknown. On Tim's WinXP box today, it returns - # True (no exception is raised). What we'd really like to test - # is that no sound is played, but that requires first wiring an - # eardrum class into unittest . - try: - winsound.PlaySound( - '!"$%&/(#+*', - winsound.SND_ALIAS | winsound.SND_NODEFAULT + if _have_soundcard(): + # Note that this is not the same as asserting RuntimeError + # will get raised: you cannot convert this to + # self.assertRaises(...) form. The attempt may or may not + # raise RuntimeError, but it shouldn't raise anything other + # than RuntimeError, and that's all we're trying to test + # here. The MS docs aren't clear about whether the SDK + # PlaySound() with SND_ALIAS and SND_NODEFAULT will return + # True or False when the alias is unknown. On Tim's WinXP + # box today, it returns True (no exception is raised). What + # we'd really like to test is that no sound is played, but + # that requires first wiring an eardrum class into unittest + # . + try: + winsound.PlaySound( + '!"$%&/(#+*', + winsound.SND_ALIAS | winsound.SND_NODEFAULT + ) + except RuntimeError: + pass + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + '!"$%&/(#+*', winsound.SND_ALIAS | winsound.SND_NODEFAULT ) - except RuntimeError: - pass def test_stopasync(self): - winsound.PlaySound( - 'SystemQuestion', - winsound.SND_ALIAS | winsound.SND_ASYNC | winsound.SND_LOOP - ) - time.sleep(0.5) - try: + if _have_soundcard(): winsound.PlaySound( 'SystemQuestion', - winsound.SND_ALIAS | winsound.SND_NOSTOP + winsound.SND_ALIAS | winsound.SND_ASYNC | winsound.SND_LOOP + ) + time.sleep(0.5) + try: + winsound.PlaySound( + 'SystemQuestion', + winsound.SND_ALIAS | winsound.SND_NOSTOP + ) + except RuntimeError: + pass + else: # the first sound might already be finished + pass + winsound.PlaySound(None, winsound.SND_PURGE) + else: + self.assertRaises( + RuntimeError, + winsound.PlaySound, + None, winsound.SND_PURGE ) - except RuntimeError: - pass - else: # the first sound might already be finished - pass - winsound.PlaySound(None, winsound.SND_PURGE) + + +def _get_cscript_path(): + """Return the full path to cscript.exe or None.""" + for dir in os.environ.get("PATH", "").split(os.pathsep): + cscript_path = os.path.join(dir, "cscript.exe") + if os.path.exists(cscript_path): + return cscript_path + +__have_soundcard_cache = None +def _have_soundcard(): + """Return True iff this computer has a soundcard.""" + global __have_soundcard_cache + if __have_soundcard_cache is None: + cscript_path = _get_cscript_path() + if cscript_path is None: + # Could not find cscript.exe to run our VBScript helper. Default + # to True: most computers these days *do* have a soundcard. + return True + + check_script = os.path.join(os.path.dirname(__file__), + "check_soundcard.vbs") + p = subprocess.Popen([cscript_path, check_script], + stdout=subprocess.PIPE) + __have_soundcard_cache = not p.wait() + return __have_soundcard_cache + def test_main(): test_support.run_unittest(BeepTest, MessageBeepTest, PlaySoundTest) diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index 4854436..48e00f4 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -494,6 +494,62 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin): self.assertAfterWithGeneratorInvariantsWithError(self.foo) self.assertAfterWithGeneratorInvariantsNoError(self.bar) + def testRaisedStopIteration1(self): + @contextmanager + def cm(): + yield + + def shouldThrow(): + with cm(): + raise StopIteration("from with") + + self.assertRaises(StopIteration, shouldThrow) + + def testRaisedStopIteration2(self): + class cm (object): + def __context__(self): + return self + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + pass + + def shouldThrow(): + with cm(): + raise StopIteration("from with") + + self.assertRaises(StopIteration, shouldThrow) + + def testRaisedGeneratorExit1(self): + @contextmanager + def cm(): + yield + + def shouldThrow(): + with cm(): + raise GeneratorExit("from with") + + self.assertRaises(GeneratorExit, shouldThrow) + + def testRaisedGeneratorExit2(self): + class cm (object): + def __context__(self): + return self + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + pass + + def shouldThrow(): + with cm(): + raise GeneratorExit("from with") + + self.assertRaises(GeneratorExit, shouldThrow) + class NonLocalFlowControlTestCase(unittest.TestCase): diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py index 40590cd..c0d1dbe 100644 --- a/Lib/test/test_xrange.py +++ b/Lib/test/test_xrange.py @@ -57,12 +57,7 @@ class XrangeTest(unittest.TestCase): self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) r = xrange(-sys.maxint, sys.maxint, 2) - if sys.maxint > 0x7fffffff: - # XXX raising ValueError is less than ideal, but this can't - # be fixed until range_length() returns a long in rangeobject.c - self.assertRaises(ValueError, len, r) - else: - self.assertEqual(len(r), sys.maxint) + self.assertEqual(len(r), sys.maxint) self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) def test_main(): diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index eb7cbf6..4e1a845 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -12,7 +12,12 @@ from test import test_support from test.test_importhooks import ImportHooksBaseTestCase, test_src, test_co import zipimport - +import linecache +import doctest +import inspect +import StringIO +from traceback import extract_tb, extract_stack, print_tb +raise_src = 'def do_raise(): raise TypeError\n' # so we only run testAFakeZlib once if this test is run repeatedly # which happens when we look for ref leaks @@ -54,7 +59,8 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): def setUp(self): # We're reusing the zip archive path, so we must clear the - # cached directory info. + # cached directory info and linecache + linecache.clearcache() zipimport._zip_directory_cache.clear() ImportHooksBaseTestCase.setUp(self) @@ -83,6 +89,11 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): mod = __import__(".".join(modules), globals(), locals(), ["__dummy__"]) + + call = kw.get('call') + if call is not None: + call(mod) + if expected_ext: file = mod.get_file() self.assertEquals(file, os.path.join(TEMP_ZIP, @@ -249,6 +260,74 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): self.doTest(".py", files, TESTMOD, stuff="Some Stuff"*31) + def assertModuleSource(self, module): + self.assertEqual(inspect.getsource(module), test_src) + + def testGetSource(self): + files = {TESTMOD + ".py": (NOW, test_src)} + self.doTest(".py", files, TESTMOD, call=self.assertModuleSource) + + def testGetCompiledSource(self): + pyc = make_pyc(compile(test_src, "", "exec"), NOW) + files = {TESTMOD + ".py": (NOW, test_src), + TESTMOD + pyc_ext: (NOW, pyc)} + self.doTest(pyc_ext, files, TESTMOD, call=self.assertModuleSource) + + def runDoctest(self, callback): + files = {TESTMOD + ".py": (NOW, test_src), + "xyz.txt": (NOW, ">>> log.append(True)\n")} + self.doTest(".py", files, TESTMOD, call=callback) + + def doDoctestFile(self, module): + log = [] + old_master, doctest.master = doctest.master, None + try: + doctest.testfile( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ) + finally: + doctest.master = old_master + self.assertEqual(log,[True]) + + def testDoctestFile(self): + self.runDoctest(self.doDoctestFile) + + def doDoctestSuite(self, module): + log = [] + doctest.DocFileTest( + 'xyz.txt', package=module, module_relative=True, + globs=locals() + ).run() + self.assertEqual(log,[True]) + + def testDoctestSuite(self): + self.runDoctest(self.doDoctestSuite) + + + def doTraceback(self, module): + try: + module.do_raise() + except: + tb = sys.exc_info()[2].tb_next + + f,lno,n,line = extract_tb(tb, 1)[0] + self.assertEqual(line, raise_src.strip()) + + f,lno,n,line = extract_stack(tb.tb_frame, 1)[0] + self.assertEqual(line, raise_src.strip()) + + s = StringIO.StringIO() + print_tb(tb, 1, s) + self.failUnless(s.getvalue().endswith(raise_src)) + else: + raise AssertionError("This ought to be impossible") + + def testTraceback(self): + files = {TESTMOD + ".py": (NOW, raise_src)} + self.doTest(None, files, TESTMOD, call=self.doTraceback) + + class CompressedZipImportTestCase(UncompressedZipImportTestCase): compression = ZIP_DEFLATED diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 2b40e6f..a30791c 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -182,7 +182,7 @@ def untokenize(iterable): for tok in iterable: toknum, tokval = tok[:2] - if toknum == NAME: + if toknum in (NAME, NUMBER): tokval += ' ' if toknum == INDENT: diff --git a/Lib/traceback.py b/Lib/traceback.py index 93a64b7..abda422 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -66,7 +66,7 @@ def print_tb(tb, limit=None, file=None): _print(file, ' File "%s", line %d, in %s' % (filename,lineno,name)) linecache.checkcache(filename) - line = linecache.getline(filename, lineno) + line = linecache.getline(filename, lineno, f.f_globals) if line: _print(file, ' ' + line.strip()) tb = tb.tb_next n = n+1 @@ -98,7 +98,7 @@ def extract_tb(tb, limit = None): filename = co.co_filename name = co.co_name linecache.checkcache(filename) - line = linecache.getline(filename, lineno) + line = linecache.getline(filename, lineno, f.f_globals) if line: line = line.strip() else: line = None list.append((filename, lineno, name, line)) @@ -158,14 +158,14 @@ def format_exception_only(etype, value): """ list = [] if (type(etype) == types.ClassType - or issubclass(etype, Exception)): + or (isinstance(etype, type) and issubclass(etype, Exception))): stype = etype.__name__ else: stype = etype if value is None: list.append(str(stype) + '\n') else: - if etype is SyntaxError: + if issubclass(etype, SyntaxError): try: msg, (filename, lineno, offset, line) = value except: @@ -279,7 +279,7 @@ def extract_stack(f=None, limit = None): filename = co.co_filename name = co.co_name linecache.checkcache(filename) - line = linecache.getline(filename, lineno) + line = linecache.getline(filename, lineno, f.f_globals) if line: line = line.strip() else: line = None list.append((filename, lineno, name, line)) diff --git a/Lib/urllib.py b/Lib/urllib.py index 136f42e..d65c0b0 100644 --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -442,9 +442,9 @@ class URLopener: return addinfourl(fp, noheaders(), "gopher:" + url) def open_file(self, url): + """Use local file or FTP depending on form of URL.""" if not isinstance(url, str): raise IOError, ('file error', 'proxy support for file protocol currently not implemented') - """Use local file or FTP depending on form of URL.""" if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/': return self.open_ftp(url) else: @@ -1032,7 +1032,7 @@ def splithost(url): global _hostprog if _hostprog is None: import re - _hostprog = re.compile('^//([^/]*)(.*)$') + _hostprog = re.compile('^//([^/?]*)(.*)$') match = _hostprog.match(url) if match: return match.group(1, 2) diff --git a/Lib/urllib2.py b/Lib/urllib2.py index 4c83bfc..ec01c8f 100644 --- a/Lib/urllib2.py +++ b/Lib/urllib2.py @@ -14,7 +14,7 @@ non-error returns. The HTTPRedirectHandler automatically deals with HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler deals with digest authentication. -urlopen(url, data=None) -- basic usage is that same as original +urlopen(url, data=None) -- basic usage is the same as original urllib. pass the url and optionally data to post to an HTTP URL, and get a file-like object back. One difference is that you can also pass a Request instance instead of URL. Raises a URLError (subclass of @@ -77,16 +77,13 @@ f = urllib2.urlopen('http://www.python.org/') # the handler knows that the problem was, e.g., that it didn't know # that hash algo that requested in the challenge, it would be good to # pass that information along to the client, too. - -# XXX to do: -# name! -# documentation (getting there) -# complex proxies -# abstract factory for opener # ftp errors aren't handled cleanly -# gopher can return a socket.error # check digest against correct (i.e. non-apache) implementation +# Possible extensions: +# complex proxies XXX not sure what exactly was meant by this +# abstract factory for opener + import base64 import ftplib import httplib @@ -111,15 +108,15 @@ try: except ImportError: from StringIO import StringIO -# not sure how many of these need to be gotten rid of -from urllib import (unwrap, unquote, splittype, splithost, +from urllib import (unwrap, unquote, splittype, splithost, quote, addinfourl, splitport, splitgophertype, splitquery, splitattr, ftpwrapper, noheaders, splituser, splitpasswd, splitvalue) # support for FileHandler, proxies via environment variables from urllib import localhost, url2pathname, getproxies -__version__ = "2.4" +# used in User-Agent header sent +__version__ = sys.version[:3] _opener = None def urlopen(url, data=None): @@ -330,8 +327,9 @@ class OpenerDirector: pass def _call_chain(self, chain, kind, meth_name, *args): - # XXX raise an exception if no one else should try to handle - # this url. return None if you can't but someone else could. + # Handlers raise an exception if no one else should try to handle + # the request, or return None if they can't but another handler + # could. Otherwise, they return the response. handlers = chain.get(kind, ()) for handler in handlers: func = getattr(handler, meth_name) @@ -507,6 +505,8 @@ class HTTPRedirectHandler(BaseHandler): # from the user (of urllib2, in this case). In practice, # essentially all clients do redirect in this case, so we # do the same. + # be conciliant with URIs containing a space + newurl = newurl.replace(' ', '%20') return Request(newurl, headers=req.headers, origin_req_host=req.get_origin_req_host(), @@ -561,6 +561,80 @@ class HTTPRedirectHandler(BaseHandler): "lead to an infinite loop.\n" \ "The last 30x error message was:\n" + +def _parse_proxy(proxy): + """Return (scheme, user, password, host/port) given a URL or an authority. + + If a URL is supplied, it must have an authority (host:port) component. + According to RFC 3986, having an authority component means the URL must + have two slashes after the scheme: + + >>> _parse_proxy('file:/ftp.example.com/') + Traceback (most recent call last): + ValueError: proxy URL with no authority: 'file:/ftp.example.com/' + + The first three items of the returned tuple may be None. + + Examples of authority parsing: + + >>> _parse_proxy('proxy.example.com') + (None, None, None, 'proxy.example.com') + >>> _parse_proxy('proxy.example.com:3128') + (None, None, None, 'proxy.example.com:3128') + + The authority component may optionally include userinfo (assumed to be + username:password): + + >>> _parse_proxy('joe:password@proxy.example.com') + (None, 'joe', 'password', 'proxy.example.com') + >>> _parse_proxy('joe:password@proxy.example.com:3128') + (None, 'joe', 'password', 'proxy.example.com:3128') + + Same examples, but with URLs instead: + + >>> _parse_proxy('http://proxy.example.com/') + ('http', None, None, 'proxy.example.com') + >>> _parse_proxy('http://proxy.example.com:3128/') + ('http', None, None, 'proxy.example.com:3128') + >>> _parse_proxy('http://joe:password@proxy.example.com/') + ('http', 'joe', 'password', 'proxy.example.com') + >>> _parse_proxy('http://joe:password@proxy.example.com:3128') + ('http', 'joe', 'password', 'proxy.example.com:3128') + + Everything after the authority is ignored: + + >>> _parse_proxy('ftp://joe:password@proxy.example.com/rubbish:3128') + ('ftp', 'joe', 'password', 'proxy.example.com') + + Test for no trailing '/' case: + + >>> _parse_proxy('http://joe:password@proxy.example.com') + ('http', 'joe', 'password', 'proxy.example.com') + + """ + from urlparse import _splitnetloc + scheme, r_scheme = splittype(proxy) + if not r_scheme.startswith("/"): + # authority + scheme = None + authority = proxy + else: + # URL + if not r_scheme.startswith("//"): + raise ValueError("proxy URL with no authority: %r" % proxy) + # We have an authority, so for RFC 3986-compliant URLs (by ss 3. + # and 3.3.), path is empty or starts with '/' + end = r_scheme.find("/", 2) + if end == -1: + end = None + authority = r_scheme[2:end] + userinfo, hostport = splituser(authority) + if userinfo is not None: + user, password = splitpasswd(userinfo) + else: + user = password = None + return scheme, user, password, hostport + class ProxyHandler(BaseHandler): # Proxies must be in front handler_order = 100 @@ -577,76 +651,27 @@ class ProxyHandler(BaseHandler): def proxy_open(self, req, proxy, type): orig_type = req.get_type() - type, r_type = splittype(proxy) - if not type or r_type.isdigit(): - # proxy is specified without protocol - type = orig_type - host = proxy - else: - host, r_host = splithost(r_type) - user_pass, host = splituser(host) - user, password = splitpasswd(user_pass) + proxy_type, user, password, hostport = _parse_proxy(proxy) + if proxy_type is None: + proxy_type = orig_type if user and password: - user, password = user_pass.split(':', 1) - user_pass = base64.encodestring('%s:%s' % (unquote(user), - unquote(password))).strip() - req.add_header('Proxy-authorization', 'Basic ' + user_pass) - host = unquote(host) - req.set_proxy(host, type) - if orig_type == type: + user_pass = '%s:%s' % (unquote(user), unquote(password)) + creds = base64.encodestring(user_pass).strip() + req.add_header('Proxy-authorization', 'Basic ' + creds) + hostport = unquote(hostport) + req.set_proxy(hostport, proxy_type) + if orig_type == proxy_type: # let other handlers take care of it - # XXX this only makes sense if the proxy is before the - # other handlers return None else: # need to start over, because the other handlers don't # grok the proxy's URL type + # e.g. if we have a constructor arg proxies like so: + # {'http': 'ftp://proxy.example.com'}, we may end up turning + # a request for http://acme.example.com/a into one for + # ftp://proxy.example.com/a return self.parent.open(req) -# feature suggested by Duncan Booth -# XXX custom is not a good name -class CustomProxy: - # either pass a function to the constructor or override handle - def __init__(self, proto, func=None, proxy_addr=None): - self.proto = proto - self.func = func - self.addr = proxy_addr - - def handle(self, req): - if self.func and self.func(req): - return 1 - - def get_proxy(self): - return self.addr - -class CustomProxyHandler(BaseHandler): - # Proxies must be in front - handler_order = 100 - - def __init__(self, *proxies): - self.proxies = {} - - def proxy_open(self, req): - proto = req.get_type() - try: - proxies = self.proxies[proto] - except KeyError: - return None - for p in proxies: - if p.handle(req): - req.set_proxy(p.get_proxy()) - return self.parent.open(req) - return None - - def do_proxy(self, p, req): - return self.parent.open(req) - - def add_proxy(self, cpo): - if cpo.proto in self.proxies: - self.proxies[cpo.proto].append(cpo) - else: - self.proxies[cpo.proto] = [cpo] - class HTTPPasswordMgr: def __init__(self): self.passwd = {} @@ -1128,8 +1153,11 @@ class FileHandler(BaseHandler): names = None def get_names(self): if FileHandler.names is None: - FileHandler.names = (socket.gethostbyname('localhost'), - socket.gethostbyname(socket.gethostname())) + try: + FileHandler.names = (socket.gethostbyname('localhost'), + socket.gethostbyname(socket.gethostname())) + except socket.gaierror: + FileHandler.names = (socket.gethostbyname('localhost'),) return FileHandler.names # not entirely sure what the rules are here @@ -1258,6 +1286,7 @@ class CacheFTPHandler(FTPHandler): class GopherHandler(BaseHandler): def gopher_open(self, req): + # XXX can raise socket.error import gopherlib # this raises DeprecationWarning in 2.5 host = req.get_host() if not host: @@ -1273,25 +1302,3 @@ class GopherHandler(BaseHandler): else: fp = gopherlib.send_selector(selector, host) return addinfourl(fp, noheaders(), req.get_full_url()) - -#bleck! don't use this yet -class OpenerFactory: - - default_handlers = [UnknownHandler, HTTPHandler, - HTTPDefaultErrorHandler, HTTPRedirectHandler, - FTPHandler, FileHandler] - handlers = [] - replacement_handlers = [] - - def add_handler(self, h): - self.handlers = self.handlers + [h] - - def replace_handler(self, h): - pass - - def build_opener(self): - opener = OpenerDirector() - for ph in self.default_handlers: - if inspect.isclass(ph): - ph = ph() - opener.add_handler(ph) diff --git a/Lib/urlparse.py b/Lib/urlparse.py index 8b75051..eade040 100644 --- a/Lib/urlparse.py +++ b/Lib/urlparse.py @@ -16,12 +16,12 @@ uses_netloc = ['ftp', 'http', 'gopher', 'nntp', 'telnet', 'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '', 'svn', 'svn+ssh', 'sftp'] non_hierarchical = ['gopher', 'hdl', 'mailto', 'news', - 'telnet', 'wais', 'imap', 'snews', 'sip'] + 'telnet', 'wais', 'imap', 'snews', 'sip', 'sips'] uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap', - 'https', 'shttp', 'rtsp', 'rtspu', 'sip', + 'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips', 'mms', '', 'sftp'] uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms', - 'gopher', 'rtsp', 'rtspu', 'sip', ''] + 'gopher', 'rtsp', 'rtspu', 'sip', 'sips', ''] uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news', 'nntp', 'wais', 'https', 'shttp', 'snews', 'file', 'prospero', ''] @@ -41,7 +41,111 @@ def clear_cache(): _parse_cache = {} -def urlparse(url, scheme='', allow_fragments=1): +class BaseResult(tuple): + """Base class for the parsed result objects. + + This provides the attributes shared by the two derived result + objects as read-only properties. The derived classes are + responsible for checking the right number of arguments were + supplied to the constructor. + + """ + + __slots__ = () + + # Attributes that access the basic components of the URL: + + @property + def scheme(self): + return self[0] + + @property + def netloc(self): + return self[1] + + @property + def path(self): + return self[2] + + @property + def query(self): + return self[-2] + + @property + def fragment(self): + return self[-1] + + # Additional attributes that provide access to parsed-out portions + # of the netloc: + + @property + def username(self): + netloc = self.netloc + if "@" in netloc: + userinfo = netloc.split("@", 1)[0] + if ":" in userinfo: + userinfo = userinfo.split(":", 1)[0] + return userinfo + return None + + @property + def password(self): + netloc = self.netloc + if "@" in netloc: + userinfo = netloc.split("@", 1)[0] + if ":" in userinfo: + return userinfo.split(":", 1)[1] + return None + + @property + def hostname(self): + netloc = self.netloc + if "@" in netloc: + netloc = netloc.split("@", 1)[1] + if ":" in netloc: + netloc = netloc.split(":", 1)[0] + return netloc.lower() or None + + @property + def port(self): + netloc = self.netloc + if "@" in netloc: + netloc = netloc.split("@", 1)[1] + if ":" in netloc: + port = netloc.split(":", 1)[1] + return int(port, 10) + return None + + +class SplitResult(BaseResult): + + __slots__ = () + + def __new__(cls, scheme, netloc, path, query, fragment): + return BaseResult.__new__( + cls, (scheme, netloc, path, query, fragment)) + + def geturl(self): + return urlunsplit(self) + + +class ParseResult(BaseResult): + + __slots__ = () + + def __new__(cls, scheme, netloc, path, params, query, fragment): + return BaseResult.__new__( + cls, (scheme, netloc, path, params, query, fragment)) + + @property + def params(self): + return self[3] + + def geturl(self): + return urlunparse(self) + + +def urlparse(url, scheme='', allow_fragments=True): """Parse a URL into 6 components: :///;?# Return a 6-tuple: (scheme, netloc, path, params, query, fragment). @@ -53,7 +157,7 @@ def urlparse(url, scheme='', allow_fragments=1): url, params = _splitparams(url) else: params = '' - return scheme, netloc, url, params, query, fragment + return ParseResult(scheme, netloc, url, params, query, fragment) def _splitparams(url): if '/' in url: @@ -73,12 +177,13 @@ def _splitnetloc(url, start=0): delim = len(url) return url[start:delim], url[delim:] -def urlsplit(url, scheme='', allow_fragments=1): +def urlsplit(url, scheme='', allow_fragments=True): """Parse a URL into 5 components: :///?# Return a 5-tuple: (scheme, netloc, path, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escapes.""" + allow_fragments = bool(allow_fragments) key = url, scheme, allow_fragments cached = _parse_cache.get(key, None) if cached: @@ -97,9 +202,9 @@ def urlsplit(url, scheme='', allow_fragments=1): url, fragment = url.split('#', 1) if '?' in url: url, query = url.split('?', 1) - tuple = scheme, netloc, url, query, fragment - _parse_cache[key] = tuple - return tuple + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v for c in url[:i]: if c not in scheme_chars: break @@ -111,9 +216,9 @@ def urlsplit(url, scheme='', allow_fragments=1): url, fragment = url.split('#', 1) if scheme in uses_query and '?' in url: url, query = url.split('?', 1) - tuple = scheme, netloc, url, query, fragment - _parse_cache[key] = tuple - return tuple + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v def urlunparse((scheme, netloc, url, params, query, fragment)): """Put a parsed URL back together again. This may result in a @@ -136,7 +241,7 @@ def urlunsplit((scheme, netloc, url, query, fragment)): url = url + '#' + fragment return url -def urljoin(base, url, allow_fragments = 1): +def urljoin(base, url, allow_fragments=True): """Join a base URL and a possibly relative URL to form an absolute interpretation of the latter.""" if not base: diff --git a/Lib/uu.py b/Lib/uu.py index 40e8bf0..3ccedb0 100755 --- a/Lib/uu.py +++ b/Lib/uu.py @@ -132,7 +132,7 @@ def decode(in_file, out_file=None, mode=None, quiet=0): data = binascii.a2b_uu(s) except binascii.Error, v: # Workaround for broken uuencoders by /Fredrik Lundh - nbytes = (((ord(s[0])-32) & 63) * 4 + 5) / 3 + nbytes = (((ord(s[0])-32) & 63) * 4 + 5) // 3 data = binascii.a2b_uu(s[:nbytes]) if not quiet: sys.stderr.write("Warning: %s\n" % v) @@ -151,7 +151,7 @@ def test(): (options, args) = parser.parse_args() if len(args) > 2: - p.error('incorrect number of arguments') + parser.error('incorrect number of arguments') sys.exit(1) input = sys.stdin diff --git a/Lib/warnings.py b/Lib/warnings.py index e622b9a..bc0b818 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -58,10 +58,11 @@ def warn(message, category=None, stacklevel=1): if not filename: filename = module registry = globals.setdefault("__warningregistry__", {}) - warn_explicit(message, category, filename, lineno, module, registry) + warn_explicit(message, category, filename, lineno, module, registry, + globals) def warn_explicit(message, category, filename, lineno, - module=None, registry=None): + module=None, registry=None, module_globals=None): if module is None: module = filename or "" if module[-3:].lower() == ".py": @@ -92,6 +93,11 @@ def warn_explicit(message, category, filename, lineno, if action == "ignore": registry[key] = 1 return + + # Prime the linecache for formatting, in case the + # "file" is actually in a zipfile or something. + linecache.getlines(filename, module_globals) + if action == "error": raise message # Other actions diff --git a/Lib/xmlcore/dom/expatbuilder.py b/Lib/xmlcore/dom/expatbuilder.py index 81d9c2b..32ffa41 100644 --- a/Lib/xmlcore/dom/expatbuilder.py +++ b/Lib/xmlcore/dom/expatbuilder.py @@ -59,7 +59,7 @@ _typeinfo_map = { "NMTOKENS": minidom.TypeInfo(None, "nmtokens"), } -class ElementInfo(NewStyle): +class ElementInfo(object): __slots__ = '_attr_info', '_model', 'tagName' def __init__(self, tagName, model=None): @@ -460,7 +460,7 @@ class ExpatBuilder: # where allowed. _ALLOWED_FILTER_RETURNS = (FILTER_ACCEPT, FILTER_REJECT, FILTER_SKIP) -class FilterVisibilityController(NewStyle): +class FilterVisibilityController(object): """Wrapper around a DOMBuilderFilter which implements the checks to make the whatToShow filter attribute work.""" @@ -518,7 +518,7 @@ class FilterVisibilityController(NewStyle): } -class FilterCrutch(NewStyle): +class FilterCrutch(object): __slots__ = '_builder', '_level', '_old_start', '_old_end' def __init__(self, builder): @@ -908,7 +908,7 @@ class InternalSubsetExtractor(ExpatBuilder): raise ParseEscape() -def parse(file, namespaces=1): +def parse(file, namespaces=True): """Parse a document, returning the resulting Document node. 'file' may be either a file name or an open file object. @@ -929,7 +929,7 @@ def parse(file, namespaces=1): return result -def parseString(string, namespaces=1): +def parseString(string, namespaces=True): """Parse a document from a string, returning the resulting Document node. """ @@ -940,7 +940,7 @@ def parseString(string, namespaces=1): return builder.parseString(string) -def parseFragment(file, context, namespaces=1): +def parseFragment(file, context, namespaces=True): """Parse a fragment of a document, given the context from which it was originally extracted. context should be the parent of the node(s) which are in the fragment. @@ -963,7 +963,7 @@ def parseFragment(file, context, namespaces=1): return result -def parseFragmentString(string, context, namespaces=1): +def parseFragmentString(string, context, namespaces=True): """Parse a fragment of a document from a string, given the context from which it was originally extracted. context should be the parent of the node(s) which are in the fragment. diff --git a/Lib/xmlcore/dom/minicompat.py b/Lib/xmlcore/dom/minicompat.py index 364ca45..f99b7fe 100644 --- a/Lib/xmlcore/dom/minicompat.py +++ b/Lib/xmlcore/dom/minicompat.py @@ -4,10 +4,6 @@ # # The following names are defined: # -# isinstance -- version of the isinstance() function that accepts -# tuples as the second parameter regardless of the -# Python version -# # NodeList -- lightest possible NodeList implementation # # EmptyNodeList -- lightest possible NodeList that is guarateed to @@ -15,8 +11,6 @@ # # StringTypes -- tuple of defined string types # -# GetattrMagic -- base class used to make _get_ be magically -# invoked when available # defproperty -- function used in conjunction with GetattrMagic; # using these together is needed to make them work # as efficiently as possible in both Python 2.2+ @@ -41,14 +35,8 @@ # # defproperty() should be used for each version of # the relevant _get_() function. -# -# NewStyle -- base class to cause __slots__ to be honored in -# the new world -# -# True, False -- only for Python 2.2 and earlier -__all__ = ["NodeList", "EmptyNodeList", "NewStyle", - "StringTypes", "defproperty", "GetattrMagic"] +__all__ = ["NodeList", "EmptyNodeList", "StringTypes", "defproperty"] import xmlcore.dom @@ -60,125 +48,62 @@ else: StringTypes = type(''), type(unicode('')) -# define True and False only if not defined as built-ins -try: - True -except NameError: - True = 1 - False = 0 - __all__.extend(["True", "False"]) +class NodeList(list): + __slots__ = () + def item(self, index): + if 0 <= index < len(self): + return self[index] -try: - isinstance('', StringTypes) -except TypeError: - # - # Wrap isinstance() to make it compatible with the version in - # Python 2.2 and newer. - # - _isinstance = isinstance - def isinstance(obj, type_or_seq): - try: - return _isinstance(obj, type_or_seq) - except TypeError: - for t in type_or_seq: - if _isinstance(obj, t): - return 1 - return 0 - __all__.append("isinstance") - - -if list is type([]): - class NodeList(list): - __slots__ = () - - def item(self, index): - if 0 <= index < len(self): - return self[index] - - def _get_length(self): - return len(self) - - def _set_length(self, value): - raise xmlcore.dom.NoModificationAllowedErr( - "attempt to modify read-only attribute 'length'") - - length = property(_get_length, _set_length, - doc="The number of nodes in the NodeList.") - - def __getstate__(self): - return list(self) - - def __setstate__(self, state): - self[:] = state - - class EmptyNodeList(tuple): - __slots__ = () - - def __add__(self, other): - NL = NodeList() - NL.extend(other) - return NL - - def __radd__(self, other): - NL = NodeList() - NL.extend(other) - return NL - - def item(self, index): - return None - - def _get_length(self): - return 0 - - def _set_length(self, value): - raise xmlcore.dom.NoModificationAllowedErr( - "attempt to modify read-only attribute 'length'") - - length = property(_get_length, _set_length, - doc="The number of nodes in the NodeList.") + def _get_length(self): + return len(self) -else: - def NodeList(): - return [] + def _set_length(self, value): + raise xml.dom.NoModificationAllowedErr( + "attempt to modify read-only attribute 'length'") - def EmptyNodeList(): - return [] + length = property(_get_length, _set_length, + doc="The number of nodes in the NodeList.") + def __getstate__(self): + return list(self) -try: - property -except NameError: - def defproperty(klass, name, doc): - # taken care of by the base __getattr__() - pass + def __setstate__(self, state): + self[:] = state - class GetattrMagic: - def __getattr__(self, key): - if key.startswith("_"): - raise AttributeError, key +class EmptyNodeList(tuple): + __slots__ = () - try: - get = getattr(self, "_get_" + key) - except AttributeError: - raise AttributeError, key - return get() + def __add__(self, other): + NL = NodeList() + NL.extend(other) + return NL - class NewStyle: - pass + def __radd__(self, other): + NL = NodeList() + NL.extend(other) + return NL -else: - def defproperty(klass, name, doc): - get = getattr(klass, ("_get_" + name)).im_func - def set(self, value, name=name): - raise xmlcore.dom.NoModificationAllowedErr( - "attempt to modify read-only attribute " + repr(name)) - assert not hasattr(klass, "_set_" + name), \ - "expected not to find _set_" + name - prop = property(get, set, doc=doc) - setattr(klass, name, prop) - - class GetattrMagic: - pass - - NewStyle = object + def item(self, index): + return None + + def _get_length(self): + return 0 + + def _set_length(self, value): + raise xml.dom.NoModificationAllowedErr( + "attempt to modify read-only attribute 'length'") + + length = property(_get_length, _set_length, + doc="The number of nodes in the NodeList.") + + +def defproperty(klass, name, doc): + get = getattr(klass, ("_get_" + name)).im_func + def set(self, value, name=name): + raise xml.dom.NoModificationAllowedErr( + "attempt to modify read-only attribute " + repr(name)) + assert not hasattr(klass, "_set_" + name), \ + "expected not to find _set_" + name + prop = property(get, set, doc=doc) + setattr(klass, name, prop) diff --git a/Lib/xmlcore/dom/minidom.py b/Lib/xmlcore/dom/minidom.py index 54620e1..a8abd14 100644 --- a/Lib/xmlcore/dom/minidom.py +++ b/Lib/xmlcore/dom/minidom.py @@ -20,8 +20,6 @@ from xmlcore.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE, domreg from xmlcore.dom.minicompat import * from xmlcore.dom.xmlbuilder import DOMImplementationLS, DocumentLS -_TupleType = type(()) - # This is used by the ID-cache invalidation checks; the list isn't # actually complete, since the nodes being checked will never be the # DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is @@ -31,7 +29,7 @@ _nodeTypes_with_children = (xmlcore.dom.Node.ELEMENT_NODE, xmlcore.dom.Node.ENTITY_REFERENCE_NODE) -class Node(xmlcore.dom.Node, GetattrMagic): +class Node(xmlcore.dom.Node): namespaceURI = None # this is non-null only for elements and attributes parentNode = None ownerDocument = None @@ -459,7 +457,7 @@ defproperty(Attr, "localName", doc="Namespace-local name of this attribute.") defproperty(Attr, "schemaType", doc="Schema type for this attribute.") -class NamedNodeMap(NewStyle, GetattrMagic): +class NamedNodeMap(object): """The attribute list is a transient interface to the underlying dictionaries. Mutations here will change the underlying element's dictionary. @@ -523,7 +521,7 @@ class NamedNodeMap(NewStyle, GetattrMagic): return cmp(id(self), id(other)) def __getitem__(self, attname_or_tuple): - if isinstance(attname_or_tuple, _TupleType): + if isinstance(attname_or_tuple, tuple): return self._attrsNS[attname_or_tuple] else: return self._attrs[attname_or_tuple] @@ -613,7 +611,7 @@ defproperty(NamedNodeMap, "length", AttributeList = NamedNodeMap -class TypeInfo(NewStyle): +class TypeInfo(object): __slots__ = 'namespace', 'name' def __init__(self, namespace, name): @@ -1146,7 +1144,7 @@ class CDATASection(Text): writer.write("" % self.data) -class ReadOnlySequentialNamedNodeMap(NewStyle, GetattrMagic): +class ReadOnlySequentialNamedNodeMap(object): __slots__ = '_seq', def __init__(self, seq=()): @@ -1170,7 +1168,7 @@ class ReadOnlySequentialNamedNodeMap(NewStyle, GetattrMagic): return n def __getitem__(self, name_or_tuple): - if isinstance(name_or_tuple, _TupleType): + if isinstance(name_or_tuple, tuple): node = self.getNamedItemNS(*name_or_tuple) else: node = self.getNamedItem(name_or_tuple) @@ -1418,7 +1416,7 @@ class DOMImplementation(DOMImplementationLS): def _create_document(self): return Document() -class ElementInfo(NewStyle): +class ElementInfo(object): """Object that represents content-model information for an element. This implementation is not expected to be used in practice; DOM diff --git a/Lib/xmlcore/dom/xmlbuilder.py b/Lib/xmlcore/dom/xmlbuilder.py index d58c723..6566d3c 100644 --- a/Lib/xmlcore/dom/xmlbuilder.py +++ b/Lib/xmlcore/dom/xmlbuilder.py @@ -3,8 +3,6 @@ import copy import xmlcore.dom -from xmlcore.dom.minicompat import * - from xmlcore.dom.NodeFilter import NodeFilter @@ -211,7 +209,7 @@ def _name_xform(name): return name.lower().replace('-', '_') -class DOMEntityResolver(NewStyle): +class DOMEntityResolver(object): __slots__ = '_opener', def resolveEntity(self, publicId, systemId): @@ -255,7 +253,7 @@ class DOMEntityResolver(NewStyle): return param.split("=", 1)[1].lower() -class DOMInputSource(NewStyle): +class DOMInputSource(object): __slots__ = ('byteStream', 'characterStream', 'stringData', 'encoding', 'publicId', 'systemId', 'baseURI') diff --git a/Mac/Modules/carbonevt/_CarbonEvtmodule.c b/Mac/Modules/carbonevt/_CarbonEvtmodule.c index fee43cc..fd4b266 100755 --- a/Mac/Modules/carbonevt/_CarbonEvtmodule.c +++ b/Mac/Modules/carbonevt/_CarbonEvtmodule.c @@ -40,7 +40,7 @@ EventTypeSpec_Convert(PyObject *v, EventTypeSpec *out) PyMac_GetOSType, &(out->eventClass), &(out->eventKind))) return 1; - return NULL; + return 0; } /********** end EventTypeSpec *******/ @@ -78,7 +78,7 @@ EventHotKeyID_Convert(PyObject *v, EventHotKeyID *out) { if (PyArg_ParseTuple(v, "ll", &out->signature, &out->id)) return 1; - return NULL; + return 0; } /********** end EventHotKeyID *******/ diff --git a/Mac/Modules/cf/_CFmodule.c b/Mac/Modules/cf/_CFmodule.c index 22e9676..5f934c2 100644 --- a/Mac/Modules/cf/_CFmodule.c +++ b/Mac/Modules/cf/_CFmodule.c @@ -1329,7 +1329,7 @@ int CFDataRefObj_Convert(PyObject *v, CFDataRef *p_itself) if (v == Py_None) { *p_itself = NULL; return 1; } if (PyString_Check(v)) { char *cStr; - int cLen; + Py_ssize_t cLen; if( PyString_AsStringAndSize(v, &cStr, &cLen) < 0 ) return 0; *p_itself = CFDataCreate((CFAllocatorRef)NULL, (unsigned char *)cStr, cLen); return 1; @@ -1826,7 +1826,7 @@ int CFStringRefObj_Convert(PyObject *v, CFStringRef *p_itself) if (PyString_Check(v)) { char *cStr; if (!PyArg_Parse(v, "es", "ascii", &cStr)) - return NULL; + return 0; *p_itself = CFStringCreateWithCString((CFAllocatorRef)NULL, cStr, kCFStringEncodingASCII); return 1; } diff --git a/Mac/Modules/gestaltmodule.c b/Mac/Modules/gestaltmodule.c index f82687e..6d8673f 100644 --- a/Mac/Modules/gestaltmodule.c +++ b/Mac/Modules/gestaltmodule.c @@ -33,17 +33,10 @@ static PyObject * gestalt_gestalt(PyObject *self, PyObject *args) { OSErr iErr; - char *str; - int size; OSType selector; long response; - if (!PyArg_Parse(args, "s#", &str, &size)) + if (!PyArg_Parse(args, "O&", PyMac_GetOSType, &selector)) return NULL; - if (size != 4) { - PyErr_SetString(PyExc_TypeError, "gestalt arg must be 4-char string"); - return NULL; - } - selector = *(OSType*)str; iErr = Gestalt ( selector, &response ); if (iErr != 0) return PyMac_Error(iErr); diff --git a/Mac/Modules/qd/_Qdmodule.c b/Mac/Modules/qd/_Qdmodule.c index 118dfc4..bfb040f 100644 --- a/Mac/Modules/qd/_Qdmodule.c +++ b/Mac/Modules/qd/_Qdmodule.c @@ -5824,7 +5824,6 @@ static PyObject *Qd_MacDrawText(PyObject *_self, PyObject *_args) { PyObject *_res = NULL; char *textBuf__in__; - int textBuf__len__; int textBuf__in_len__; short firstByte; short byteCount; @@ -5885,7 +5884,6 @@ static PyObject *Qd_TextWidth(PyObject *_self, PyObject *_args) PyObject *_res = NULL; short _rv; char *textBuf__in__; - int textBuf__len__; int textBuf__in_len__; short firstByte; short byteCount; @@ -6471,7 +6469,6 @@ static PyObject *Qd_DrawText(PyObject *_self, PyObject *_args) { PyObject *_res = NULL; char *textBuf__in__; - int textBuf__len__; int textBuf__in_len__; short firstByte; short byteCount; @@ -6534,7 +6531,7 @@ static PyObject *Qd_RawBitMap(PyObject *_self, PyObject *_args) return NULL; if ( PyString_Size(source) != sizeof(BitMap) && PyString_Size(source) != sizeof(PixMap) ) { PyErr_Format(PyExc_TypeError, - "Argument size was %d, should be %d (sizeof BitMap) or %d (sizeof PixMap)", + "Argument size was %ld, should be %lu (sizeof BitMap) or %lu (sizeof PixMap)", PyString_Size(source), sizeof(BitMap), sizeof(PixMap)); return NULL; } diff --git a/Makefile.pre.in b/Makefile.pre.in index 056b578..f8a7481 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -30,6 +30,7 @@ VPATH= @srcdir@ CC= @CC@ CXX= @CXX@ +MAINCC= @MAINCC@ LINKCC= @LINKCC@ AR= @AR@ RANLIB= @RANLIB@ @@ -157,7 +158,6 @@ LIBC= @LIBC@ SYSLIBS= $(LIBM) $(LIBC) SHLIBS= @SHLIBS@ -MAINOBJ= @MAINOBJ@ THREADOBJ= @THREADOBJ@ DLINCLDIR= @DLINCLDIR@ DYNLOADFILE= @DYNLOADFILE@ @@ -219,13 +219,15 @@ PGENOBJS= $(PGENMAIN) $(POBJS) $(PGOBJS) ########################################################################## # AST -AST_H= $(srcdir)/Include/Python-ast.h -AST_C= $(srcdir)/Python/Python-ast.c +AST_H_DIR= $(srcdir)/Include +AST_H= $(AST_H_DIR)/Python-ast.h +AST_C_DIR= $(srcdir)/Python +AST_C= $(AST_C_DIR)/Python-ast.c AST_ASDL= $(srcdir)/Parser/Python.asdl ASDLGEN_FILES= $(srcdir)/Parser/asdl.py $(srcdir)/Parser/asdl_c.py # XXX Note that a build now requires Python exist before the build starts -ASDLGEN= $(srcdir)/Parser/asdl_c.py -h $(srcdir)/Include -c $(srcdir)/Python +ASDLGEN= $(srcdir)/Parser/asdl_c.py ########################################################################## # Python @@ -326,9 +328,9 @@ LIBRARY_OBJS= \ all: $(BUILDPYTHON) oldsharedmods sharedmods # Build the interpreter -$(BUILDPYTHON): Modules/$(MAINOBJ) $(LIBRARY) $(LDLIBRARY) +$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) $(LINKCC) $(LDFLAGS) $(LINKFORSHARED) -o $@ \ - Modules/$(MAINOBJ) \ + Modules/python.o \ $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) platform: $(BUILDPYTHON) @@ -448,8 +450,8 @@ Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile -DVPATH='"$(VPATH)"' \ -o $@ $(srcdir)/Modules/getpath.c -Modules/ccpython.o: $(srcdir)/Modules/ccpython.cc - $(CXX) -c $(PY_CFLAGS) -o $@ $(srcdir)/Modules/ccpython.cc +Modules/python.o: $(srcdir)/Modules/python.c + $(MAINCC) -c $(PY_CFLAGS) -o $@ $(srcdir)/Modules/python.c $(GRAMMAR_H) $(GRAMMAR_C): $(PGEN) $(GRAMMAR_INPUT) @@ -465,9 +467,12 @@ Parser/metagrammar.o: $(srcdir)/Parser/metagrammar.c Parser/tokenizer_pgen.o: $(srcdir)/Parser/tokenizer.c -$(AST_H) $(AST_C): $(AST_ASDL) $(ASDLGEN_FILES) - $(ASDLGEN) $(AST_ASDL) +$(AST_H): $(AST_ASDL) $(ASDLGEN_FILES) + $(ASDLGEN) -h $(AST_H_DIR) $(AST_ASDL) +$(AST_C): $(AST_ASDL) $(ASDLGEN_FILES) + $(ASDLGEN) -c $(AST_C_DIR) $(AST_ASDL) + Python/compile.o Python/symtable.o: $(GRAMMAR_H) $(AST_H) Python/getplatform.o: $(srcdir)/Python/getplatform.c @@ -537,7 +542,7 @@ PYTHON_HEADERS= \ Include/weakrefobject.h \ pyconfig.h -$(LIBRARY_OBJS) $(MODOBJS) Modules/$(MAINOBJ): $(PYTHON_HEADERS) +$(LIBRARY_OBJS) $(MODOBJS) Modules/python.o: $(PYTHON_HEADERS) ###################################################################### @@ -680,9 +685,13 @@ PLATMACDIRS= plat-mac plat-mac/Carbon plat-mac/lib-scriptpackages \ PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages LIBSUBDIRS= lib-old lib-tk site-packages test test/output test/data \ test/decimaltestdata \ - encodings email email/test email/test/data compiler hotshot \ - logging bsddb bsddb/test csv ctypes idlelib idlelib/Icons \ + encodings compiler hotshot \ + email email/mime email/test email/test/data \ + sqlite3 sqlite3/test \ + logging bsddb bsddb/test csv \ + ctypes ctypes/test ctypes/macholib idlelib idlelib/Icons \ distutils distutils/command distutils/tests $(XMLLIBSUBDIRS) \ + setuptools setuptools/command setuptools/tests setuptools.egg-info \ curses $(MACHDEPS) libinstall: $(BUILDPYTHON) $(srcdir)/Lib/$(PLATDIR) @for i in $(SCRIPTDIR) $(LIBDEST); \ @@ -718,6 +727,7 @@ libinstall: $(BUILDPYTHON) $(srcdir)/Lib/$(PLATDIR) do \ a=$(srcdir)/Lib/$$d; \ if test ! -d $$a; then continue; else true; fi; \ + if test `ls $$a | wc -l` -lt 1; then continue; fi; \ b=$(LIBDEST)/$$d; \ for i in $$a/*; \ do \ @@ -809,7 +819,7 @@ libainstall: all fi; \ fi $(INSTALL_DATA) Modules/config.c $(DESTDIR)$(LIBPL)/config.c - $(INSTALL_DATA) Modules/$(MAINOBJ) $(DESTDIR)$(LIBPL)/$(MAINOBJ) + $(INSTALL_DATA) Modules/python.o $(DESTDIR)$(LIBPL)/python.o $(INSTALL_DATA) $(srcdir)/Modules/config.c.in $(DESTDIR)$(LIBPL)/config.c.in $(INSTALL_DATA) Makefile $(DESTDIR)$(LIBPL)/Makefile $(INSTALL_DATA) Modules/Setup $(DESTDIR)$(LIBPL)/Setup @@ -817,6 +827,11 @@ libainstall: all $(INSTALL_DATA) Modules/Setup.config $(DESTDIR)$(LIBPL)/Setup.config $(INSTALL_SCRIPT) $(srcdir)/Modules/makesetup $(DESTDIR)$(LIBPL)/makesetup $(INSTALL_SCRIPT) $(srcdir)/install-sh $(DESTDIR)$(LIBPL)/install-sh + # Substitution happens here, as the completely-expanded BINDIR + # is not available in configure + sed -e "s,@BINDIR@,$(BINDIR)," < $(srcdir)/Misc/python-config.in >python-config + $(INSTALL_SCRIPT) python-config $(BINDIR)/python-config + rm python-config @if [ -s Modules/python.exp -a \ "`echo $(MACHDEP) | sed 's/^\(...\).*/\1/'`" = "aix" ]; then \ echo; echo "Installing support files for building shared extension modules on AIX:"; \ @@ -942,6 +957,10 @@ config.status: $(srcdir)/configure .c.o: $(CC) -c $(PY_CFLAGS) -o $@ $< +# Run reindent on the library +reindent: + ./python$(EXEEXT) $(srcdir)/Tools/scripts/reindent.py -r $(srcdir)/Lib + # Rerun configure with the same options as it was run last time, # provided the config.status script exists recheck: @@ -969,13 +988,14 @@ TAGS:: # Sanitation targets -- clean leaves libraries, executables and tags # files, which clobber removes those as well +pycremoval: + find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' -clean: +clean: pycremoval find . -name '*.o' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' - find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' - find $(srcdir) -name 'fficonfig.h' -exec rm -f {} ';' - find $(srcdir) -name 'fficonfig.py' -exec rm -f {} ';' + find $(srcdir)/build -name 'fficonfig.h' -exec rm -f {} ';' || true + find $(srcdir)/build -name 'fficonfig.py' -exec rm -f {} ';' || true clobber: clean -rm -f $(BUILDPYTHON) $(PGEN) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ diff --git a/Misc/ACKS b/Misc/ACKS index 9225031..a824a86 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -132,6 +132,7 @@ Jeffery Collins Matt Conway David M. Cooke Greg Copeland +Aldo Cortesi David Costanzo Scott Cotton Greg Couch @@ -477,6 +478,7 @@ Jean-Fran Dan Pierson Martijn Pieters François Pinard +Zach Pincus Michael Piotrowski Iustin Pop John Popplewell @@ -536,6 +538,7 @@ David Scherer Gregor Schmid Ralf Schmitt Peter Schneider-Kamp +Chad J. Schroeder Sam Schulenburg Stefan Schwarzer Dietmar Schwertberger diff --git a/Misc/BeOS-setup.py b/Misc/BeOS-setup.py index 07dbe15..991e608 100644 --- a/Misc/BeOS-setup.py +++ b/Misc/BeOS-setup.py @@ -176,8 +176,6 @@ class PyBuildExt(build_ext): # # Some modules that are normally always on: - exts.append( Extension('regex', ['regexmodule.c', 'regexpr.c']) ) - exts.append( Extension('_weakref', ['_weakref.c']) ) exts.append( Extension('_symtable', ['symtablemodule.c']) ) diff --git a/Misc/HISTORY b/Misc/HISTORY index d8847a8..be4ca88 100644 --- a/Misc/HISTORY +++ b/Misc/HISTORY @@ -8,6 +8,5293 @@ As you read on you go back to the dark ages of Python's history. ====================================================================== +What's New in Python 2.4 final? +=============================== + +*Release date: 30-NOV-2004* + +Core and builtins +----------------- + +- Bug 875692: Improve signal handling, especially when using threads, by + forcing an early re-execution of PyEval_EvalFrame() "periodic" code when + things_to_do is not cleared by Py_MakePendingCalls(). + + +What's New in Python 2.4 (release candidate 1) +============================================== + +*Release date: 18-NOV-2004* + +Core and builtins +----------------- + +- Bug 1061968: Fixes in 2.4a3 to address thread bug 1010677 reintroduced + the years-old thread shutdown race bug 225673. Numeric history lesson + aside, all bugs in all three reports are fixed now. + + +Library +------- + +- Bug 1052242: If exceptions are raised by an atexit handler function an + attempt is made to execute the remaining handlers. The last exception + raised is re-raised. + +- ``doctest``'s new support for adding ``pdb.set_trace()`` calls to + doctests was broken in a dramatic but shallow way. Fixed. + +- Bug 1065388: ``calendar``'s ``day_name``, ``day_abbr``, ``month_name``, + and ``month_abbr`` attributes emulate sequences of locale-correct + spellings of month and day names. Because the locale can change at + any time, the correct spelling is recomputed whenever one of these is + indexed. In the worst case, the index may be a slice object, so these + recomputed every day or month name each time they were indexed. This is + much slower than necessary in the usual case, when the index is just an + integer. In that case, only the single spelling needed is recomputed + now; and, when the index is a slice object, only the spellings needed + by the slice are recomputed now. + +- Patch 1061679: Added ``__all__`` to pickletools.py. + +Build +----- + +- Bug 1034277 / Patch 1035255: Remove compilation of core against CoreServices + and CoreFoundation on OS X. Involved removing PyMac_GetAppletScriptFile() + which has no known users. Thanks Bob Ippolito. + +C API +----- + +- The PyRange_New() function is deprecated. + + +What's New in Python 2.4 beta 2? +================================ + +*Release date: 03-NOV-2004* + +License +------- + +The Python Software Foundation changed the license under which Python +is released, to remove Python version numbers. There were no other +changes to the license. So, for example, wherever the license for +Python 2.3 said "Python 2.3", the new license says "Python". The +intent is to make it possible to refer to the PSF license in a more +durable way. For example, some people say they're confused by that +the Open Source Initiative's entry for the Python Software Foundation +License:: + + http://www.opensource.org/licenses/PythonSoftFoundation.php + +says "Python 2.1.1" all over it, wondering whether it applies only +to Python 2.1.1. + +The official name of the new license is the Python Software Foundation +License Version 2. + +Core and builtins +----------------- + +- Bug #1055820 Cyclic garbage collection was not protecting against that + calling a live weakref to a piece of cyclic trash could resurrect an + insane mutation of the trash if any Python code ran during gc (via + running a dead object's __del__ method, running another callback on a + weakref to a dead object, or via any Python code run in any other thread + that managed to obtain the GIL while a __del__ or callback was running + in the thread doing gc). The most likely symptom was "impossible" + ``AttributeError`` exceptions, appearing seemingly at random, on weakly + referenced objects. The cure was to clear all weakrefs to unreachable + objects before allowing any callbacks to run. + +- Bug #1054139 _PyString_Resize() now invalidates its cached hash value. + +Extension Modules +----------------- + +- Bug #1048870: the compiler now generates distinct code objects for + functions with identical bodies. This was producing confusing + traceback messages which pointed to the function where the code + object was first defined rather than the function being executed. + +Library +------- + +- Patch #1056967 changes the semantics of Template.safe_substitute() so that + no ValueError is raised on an 'invalid' match group. Now the delimiter is + returned. + +- Bug #1052503 pdb.runcall() was not passing along keyword arguments. + +- Bug #902037: XML.sax.saxutils.prepare_input_source() now combines relative + paths with a base path before checking os.path.isfile(). + +- The whichdb module can now be run from the command line. + +- Bug #1045381: time.strptime() can now infer the date using %U or %W (week of + the year) when the day of the week and year are also specified. + +- Bug #1048816: fix bug in Ctrl-K at start of line in curses.textpad.Textbox + +- Bug #1017553: fix bug in tarfile.filemode() + +- Patch #737473: fix bug that old source code is shown in tracebacks even if + the source code is updated and reloaded. + +Build +----- + +- Patch #1044395: --enable-shared is allowed in FreeBSD also. + +What's New in Python 2.4 beta 1? +================================ + +*Release date: 15-OCT-2004* + +Core and builtins +----------------- + +- Patch #975056: Restartable signals were not correctly disabled on + BSD systems. Consistently use PyOS_setsig() instead of signal(). + +- The internal portable implementation of thread-local storage (TLS), used + by the ``PyGILState_Ensure()``/``PyGILState_Release()`` API, was not + thread-correct. This could lead to a variety of problems, up to and + including segfaults. See bug 1041645 for an example. + +- Added a command line option, -m module, which searches sys.path for the + module and then runs it. (Contributed by Nick Coghlan.) + +- The bytecode optimizer now folds tuples of constants into a single + constant. + +- SF bug #513866: Float/long comparison anomaly. Prior to 2.4b1, when + an integer was compared to a float, the integer was coerced to a float. + That could yield spurious overflow errors (if the integer was very + large), and to anomalies such as + ``long(1e200)+1 == 1e200 == long(1e200)-1``. Coercion to float is no + longer performed, and cases like ``long(1e200)-1 < 1e200``, + ``long(1e200)+1 > 1e200`` and ``(1 << 20000) > 1e200`` are computed + correctly now. + +Extension modules +----------------- + +- ``collections.deque`` objects didn't play quite right with garbage + collection, which could lead to a segfault in a release build, or + an assert failure in a debug build. Also, added overflow checks, + better detection of mutation during iteration, and shielded deque + comparisons from unusual subclass overrides of the __iter__() method. + +Library +------- + +- Patch 1046644: distutils build_ext grew two new options - --swig for + specifying the swig executable to use, and --swig-opts to specify + options to pass to swig. --swig-opts="-c++" is the new way to spell + --swig-cpp. + +- Patch 983206: distutils now obeys environment variable LDSHARED, if + it is set. + +- Added Peter Astrand's subprocess.py module. See PEP 324 for details. + +- time.strptime() now properly escapes timezones and all other locale-specific + strings for regex-specific symbols. Was breaking under Japanese Windows when + the timezone was specified as "Tokyo (standard time)". + Closes bug #1039270. + +- Updates for the email package: + + + email.Utils.formatdate() grew a 'usegmt' argument for HTTP support. + + All deprecated APIs that in email 2.x issued warnings have been removed: + _encoder argument to the MIMEText constructor, Message.add_payload(), + Utils.dump_address_pair(), Utils.decode(), Utils.encode() + + New deprecations: Generator.__call__(), Message.get_type(), + Message.get_main_type(), Message.get_subtype(), the 'strict' argument to + the Parser constructor. These will be removed in email 3.1. + + Support for Python earlier than 2.3 has been removed (see PEP 291). + + All defect classes have been renamed to end in 'Defect'. + + Some FeedParser fixes; also a MultipartInvariantViolationDefect will be + added to messages that claim to be multipart but really aren't. + + Updates to documentation. + +- re's findall() and finditer() functions now take an optional flags argument + just like the compile(), search(), and match() functions. Also, documented + the previously existing start and stop parameters for the findall() and + finditer() methods of regular expression objects. + +- rfc822 Messages now support iterating over the headers. + +- The (undocumented) tarfile.Tarfile.membernames has been removed; + applications should use the getmember function. + +- httplib now offers symbolic constants for the HTTP status codes. + +- SF bug #1028306: Trying to compare a ``datetime.date`` to a + ``datetime.datetime`` mistakenly compared only the year, month and day. + Now it acts like a mixed-type comparison: ``False`` for ``==``, + ``True`` for ``!=``, and raises ``TypeError`` for other comparison + operators. Because datetime is a subclass of date, comparing only the + base class (date) members can still be done, if that's desired, by + forcing using of the approprate date method; e.g., + ``a_date.__eq__(a_datetime)`` is true if and only if the year, month + and day members of ``a_date`` and ``a_datetime`` are equal. + +- bdist_rpm now supports command line options --force-arch, + {pre,post}-install, {pre,post}-uninstall, and + {prep,build,install,clean,verify}-script. + +- SF patch #998993: The UTF-8 and the UTF-16 stateful decoders now support + decoding incomplete input (when the input stream is temporarily exhausted). + ``codecs.StreamReader`` now implements buffering, which enables proper + readline support for the UTF-16 decoders. ``codecs.StreamReader.read()`` + has a new argument ``chars`` which specifies the number of characters to + return. ``codecs.StreamReader.readline()`` and + ``codecs.StreamReader.readlines()`` have a new argument ``keepends``. + Trailing "\n"s will be stripped from the lines if ``keepends`` is false. + +- The documentation for doctest is greatly expanded, and now covers all + the new public features (of which there are many). + +- ``doctest.master`` was put back in, and ``doctest.testmod()`` once again + updates it. This isn't good, because every ``testmod()`` call + contributes to bloating the "hidden" state of ``doctest.master``, but + some old code apparently relies on it. For now, all we can do is + encourage people to stitch doctests together via doctest's unittest + integration features instead. + +- httplib now handles ipv6 address/port pairs. + +- SF bug #1017864: ConfigParser now correctly handles default keys, + processing them with ``ConfigParser.optionxform`` when supplied, + consistent with the handling of config file entries and runtime-set + options. + +- SF bug #997050: Document, test, & check for non-string values in + ConfigParser. Moved the new string-only restriction added in + rev. 1.65 to the SafeConfigParser class, leaving existing + ConfigParser & RawConfigParser behavior alone, and documented the + conditions under which non-string values work. + +Build +----- + +- Building on darwin now includes /opt/local/include and /opt/local/lib for + building extension modules. This is so as to include software installed as + a DarwinPorts port + +- pyport.h now defines a Py_IS_NAN macro. It works as-is when the + platform C computes true for ``x != x`` if and only if X is a NaN. + Other platforms can override the default definition with a platform- + specific spelling in that platform's pyconfig.h. You can also override + pyport.h's default Py_IS_INFINITY definition now. + +C API +----- + +- SF patch 1044089: New function ``PyEval_ThreadsInitialized()`` returns + non-zero if PyEval_InitThreads() has been called. + +- The undocumented and unused extern int ``_PyThread_Started`` was removed. + +- The C API calls ``PyInterpreterState_New()`` and ``PyThreadState_New()`` + are two of the very few advertised as being safe to call without holding + the GIL. However, this wasn't true in a debug build, as bug 1041645 + demonstrated. In a debug build, Python redirects the ``PyMem`` family + of calls to Python's small-object allocator, to get the benefit of + its extra debugging capabilities. But Python's small-object allocator + isn't threadsafe, relying on the GIL to avoid the expense of doing its + own locking. ``PyInterpreterState_New()`` and ``PyThreadState_New()`` + call the platform ``malloc()`` directly now, regardless of build type. + +- PyLong_AsUnsignedLong[Mask] now support int objects as well. + +- SF patch #998993: ``PyUnicode_DecodeUTF8Stateful`` and + ``PyUnicode_DecodeUTF16Stateful`` have been added, which implement stateful + decoding. + +Tests +----- + +- test__locale ported to unittest + +Mac +--- + +- ``plistlib`` now supports non-dict root objects. There is also a new + interface for reading and writing plist files: ``readPlist(pathOrFile)`` + and ``writePlist(rootObject, pathOrFile)`` + +Tools/Demos +----------- + +- The text file comparison scripts ``ndiff.py`` and ``diff.py`` now + read the input files in universal-newline mode. This spares them + from consuming a great deal of time to deduce the useless result that, + e.g., a file with Windows line ends and a file with Linux line ends + have no lines in common. + + +What's New in Python 2.4 alpha 3? +================================= + +*Release date: 02-SEP-2004* + +Core and builtins +----------------- + +- SF patch #1007189: ``from ... import ...`` statements now allow the name + list to be surrounded by parentheses. + +- Some speedups for long arithmetic, thanks to Trevor Perrin. Gradeschool + multiplication was sped a little by optimizing the C code. Gradeschool + squaring was sped by about a factor of 2, by exploiting that about half + the digit products are duplicates in a square. Because exponentiation + uses squaring often, this also speeds long power. For example, the time + to compute 17**1000000 dropped from about 14 seconds to 9 on my box due + to this much. The cutoff for Karatsuba multiplication was raised, + since gradeschool multiplication got quicker, and the cutoff was + aggressively small regardless. The exponentiation algorithm was switched + from right-to-left to left-to-right, which is more efficient for small + bases. In addition, if the exponent is large, the algorithm now does + 5 bits (instead of 1 bit) at a time. That cut the time to compute + 17**1000000 on my box in half again, down to about 4.5 seconds. + +- OverflowWarning is no longer generated. PEP 237 scheduled this to + occur in Python 2.3, but since OverflowWarning was disabled by default, + nobody realized it was still being generated. On the chance that user + code is still using them, the Python builtin OverflowWarning, and + corresponding C API PyExc_OverflowWarning, will exist until Python 2.5. + +- Py_InitializeEx has been added. + +- Fix the order of application of decorators. The proper order is bottom-up; + the first decorator listed is the last one called. + +- SF patch #1005778. Fix a seg fault if the list size changed while + calling list.index(). This could happen if a rich comparison function + modified the list. + +- The ``func_name`` (a.k.a. ``__name__``) attribute of user-defined + functions is now writable. + +- code_new (a.k.a new.code()) now checks its arguments sufficiently + carefully that passing them on to PyCode_New() won't trigger calls + to Py_FatalError() or PyErr_BadInternalCall(). It is still the case + that the returned code object might be entirely insane. + +- Subclasses of string can no longer be interned. The semantics of + interning were not clear here -- a subclass could be mutable, for + example -- and had bugs. Explicitly interning a subclass of string + via intern() will raise a TypeError. Internal operations that attempt + to intern a string subclass will have no effect. + +- Bug 1003935: xrange() could report bogus OverflowErrors. Documented + what xrange() intends, and repaired tests accordingly. + +Extension modules +----------------- + +- difflib now supports HTML side-by-side diff. + +- os.urandom has been added for systems that support sources of random + data. + +- Patch 1012740: truncate() on a writeable cStringIO now resets the + position to the end of the stream. This is consistent with the original + StringIO module and avoids inadvertently resurrecting data that was + supposed to have been truncated away. + +- Added socket.socketpair(). + +- Added CurrentByteIndex, CurrentColumnNumber, CurrentLineNumber + members to xml.parsers.expat.XMLParser object. + +- The mpz, rotor, and xreadlines modules, all deprecated in earlier + versions of Python, have now been removed. + +Library +------- + +- Patch #934356: if a module defines __all__, believe that rather than using + heuristics for filtering out imported names. + +- Patch #941486: added os.path.lexists(), which returns True for broken + symlinks, unlike os.path.exists(). + +- the random module now uses os.urandom() for seeding if it is available. + Added a new generator based on os.urandom(). + +- difflib and diff.py can now generate HTML. + +- bdist_rpm now includes version and release in the BuildRoot, and + replaces - by ``_`` in version and release. + +- distutils build/build_scripts now has an -e option to specify the + path to the Python interpreter for installed scripts. + +- PEP 292 classes Template and SafeTemplate are added to the string module. + +- tarfile now generates GNU tar files by default. + +- HTTPResponse has now a getheaders method. + +- Patch #1006219: let inspect.getsource handle '@' decorators. Thanks Simon + Percivall. + +- logging.handlers.SMTPHandler.date_time has been removed; + the class now uses email.Utils.formatdate to generate the time stamp. + +- A new function tkFont.nametofont was added to return an existing + font. The Font class constructor now has an additional exists argument + which, if True, requests to return/configure an existing font, rather + than creating a new one. + +- Updated the decimal package's min() and max() methods to match the + latest revision of the General Decimal Arithmetic Specification. + Quiet NaNs are ignored and equal values are sorted based on sign + and exponent. + +- The decimal package's Context.copy() method now returns deep copies. + +- Deprecated sys.exitfunc in favor of the atexit module. The sys.exitfunc + attribute will be kept around for backwards compatibility and atexit + will just become the one preferred way to do it. + +- patch #675551: Add get_history_item and replace_history_item functions + to the readline module. + +- bug #989672: pdb.doc and the help messages for the help_d and help_u methods + of the pdb.Pdb class gives have been corrected. d(own) goes to a newer + frame, u(p) to an older frame, not the other way around. + +- bug #990669: os.path.realpath() will resolve symlinks before normalizing the + path, as normalizing the path may alter the meaning of the path if it + contains symlinks. + +- bug #851123: shutil.copyfile will raise an exception when trying to copy a + file onto a link to itself. Thanks Gregory Ball. + +- bug #570300: Fix inspect to resolve file locations using os.path.realpath() + so as to properly list all functions in a module when the module itself is + reached through a symlink. Thanks Johannes Gijsbers. + +- doctest refactoring continued. See the docs for details. As part of + this effort, some old and little- (never?) used features are now + deprecated: the Tester class, the module is_private() function, and the + isprivate argument to testmod(). The Tester class supplied a feeble + "by hand" way to combine multiple doctests, if you knew exactly what + you were doing. The newer doctest features for unittest integration + already did a better job of that, are stronger now than ever, and the + new DocTestRunner class is a saner foundation if you want to do it by + hand. The "private name" filtering gimmick was a mistake from the + start, and testmod() changed long ago to ignore it by default. If + you want to filter out tests, the new DocTestFinder class can be used + to return a list of all doctests, and you can filter that list by + any computable criteria before passing it to a DocTestRunner instance. + +- Bug #891637, patch #1005466: fix inspect.getargs() crash on def foo((bar)). + +Tools/Demos +----------- + +- IDLE's shortcut keys for windows are now case insensitive so that + Control-V works the same as Control-v. + +- pygettext.py: Generate POT-Creation-Date header in ISO format. + +Build +----- + +- Backward incompatibility: longintrepr.h now triggers a compile-time + error if SHIFT (the number of bits in a Python long "digit") isn't + divisible by 5. This new requirement allows simple code for the new + 5-bits-at-a-time long_pow() implementation. If necessary, the + restriction could be removed (by complicating long_pow(), or by + falling back to the 1-bit-at-a-time algorithm), but there are no + plans to do so. + +- bug #991962: When building with --disable-toolbox-glue on Darwin no + attempt to build Mac-specific modules occurs. + +- The --with-tsc flag to configure to enable VM profiling with the + processor's timestamp counter now works on PPC platforms. + +- patch #1006629: Define _XOPEN_SOURCE to 500 on Solaris 8/9 to match + GCC's definition and avoid redefinition warnings. + +- Detect pthreads support (provided by gnu pth pthread emulation) on + GNU/k*BSD systems. + +- bug #1005737, #1007249: Fixed several build problems and warnings + found on old/legacy C compilers of HP-UX, IRIX and Tru64. + +C API +----- + +.. + +Documentation +------------- + +- patch #1005936, bug #1009373: fix index entries which contain + an underscore when viewed with Acrobat. + +- bug #990669: os.path.normpath may alter the meaning of a path if + it contains symbolic links. This has been documented in a comment + since 1992, but is now in the library reference as well. + +New platforms +------------- + +- FreeBSD 6 is now supported. + +Tests +----- + +.. + +Windows +------- + +- Boosted the stack reservation for python.exe and pythonw.exe from + the default 1MB to 2MB. Stack frames under VC 7.1 for 2.4 are enough + bigger than under VC 6.0 for 2.3.4 that deeply recursive progams + within the default sys.getrecursionlimit() default value of 1000 were + able to suffer undetected C stack overflows. The standard test program + test_compiler was one such program. If a Python process on Windows + "just vanishes" without a trace, and without an error message of any + kind, but with an exit code of 128, undetected stack overflow may be + the problem. + +Mac +--- + +.. + + +What's New in Python 2.4 alpha 2? +================================= + +*Release date: 05-AUG-2004* + +Core and builtins +----------------- + +- Patch #980695: Implements efficient string concatenation for statements + of the form s=s+t and s+=t. This will vary across implementations. + Accordingly, the str.join() method is strongly preferred for performance + sensitive code. + +- PEP-0318, Function Decorators have been added to the language. These are + implemented using the Java-style @decorator syntax, like so:: + + @staticmethod + def foo(bar): + + (The PEP needs to be updated to reflect the current state) + +- When importing a module M raises an exception, Python no longer leaves M + in sys.modules. Before 2.4a2 it did, and a subsequent import of M would + succeed, picking up a module object from sys.modules reflecting as much + of the initialization of M as completed before the exception was raised. + Subsequent imports got no indication that M was in a partially- + initialized state, and the importers could get into arbitrarily bad + trouble as a result (the M they got was in an unintended state, + arbitrarily far removed from M's author's intent). Now subsequent + imports of M will continue raising exceptions (but if, for example, the + source code for M is edited between import attempts, then perhaps later + attempts will succeed, or raise a different exception). + + This can break existing code, but in such cases the code was probably + working before by accident. In the Python source, the only case of + breakage discovered was in a test accidentally relying on a damaged + module remaining in sys.modules. Cases are also known where tests + deliberately provoking import errors remove damaged modules from + sys.modules themselves, and such tests will break now if they do an + unconditional del sys.modules[M]. + +- u'%s' % obj will now try obj.__unicode__() first and fallback to + obj.__str__() if no __unicode__ method can be found. + +- Patch #550732: Add PyArg_VaParseTupleAndKeywords(). Analogous to + PyArg_VaParse(). Both are now documented. Thanks Greg Chapman. + +- Allow string and unicode return types from .encode()/.decode() + methods on string and unicode objects. Added unicode.decode() + which was missing for no apparent reason. + +- An attempt to fix the mess that is Python's behaviour with + signal handlers and threads, complicated by readline's behaviour. + It's quite possible that there are still bugs here. + +- Added C macros Py_CLEAR and Py_VISIT to ease the implementation of + types that support garbage collection. + +- Compiler now treats None as a constant. + +- The type of values returned by __int__, __float__, __long__, + __oct__, and __hex__ are now checked. Returning an invalid type + will cause a TypeError to be raised. This matches the behavior of + Jython. + +- Implemented bind_textdomain_codeset() in locale module. + +- Added a workaround for proper string operations in BSDs. str.split + and str.is* methods can now work correctly with UTF-8 locales. + +- Bug #989185: unicode.iswide() and unicode.width() is dropped and + the East Asian Width support is moved to unicodedata extension + module. + +- Patch #941229: The source code encoding in interactive mode + now refers sys.stdin.encoding not just ISO-8859-1 anymore. This + allows for non-latin-1 users to write unicode strings directly. + +Extension modules +----------------- + +- cpickle now supports the same keyword arguments as pickle. + +Library +------- + +- Added new codecs and aliases for ISO_8859-11, ISO_8859-16 and + TIS-620 + +- Thanks to Edward Loper, doctest has been massively refactored, and + many new features were added. Full docs will appear later. For now + the doctest module comments and new test cases give good coverage. + The refactoring provides many hook points for customizing behavior + (such as how to report errors, and how to compare expected to actual + output). New features include a marker for expected + output containing blank lines, options to produce unified or context + diffs when actual output doesn't match expectations, an option to + normalize whitespace before comparing, and an option to use an + ellipsis to signify "don't care" regions of output. + +- Tkinter now supports the wish -sync and -use options. + +- The following methods in time support passing of None: ctime(), gmtime(), + and localtime(). If None is provided, the current time is used (the + same as when the argument is omitted). + [SF bug 658254, patch 663482] + +- nntplib does now allow to ignore a .netrc file. + +- urllib2 now recognizes Basic authentication even if other authentication + schemes are offered. + +- Bug #1001053. wave.open() now accepts unicode filenames. + +- gzip.GzipFile has a new fileno() method, to retrieve the handle of the + underlying file object (provided it has a fileno() method). This is + needed if you want to use os.fsync() on a GzipFile. + +- imaplib has two new methods: deleteacl and myrights. + +- nntplib has two new methods: description and descriptions. They + use a more RFC-compliant way of getting a newsgroup description. + +- Bug #993394. Fix a possible red herring of KeyError in 'threading' being + raised during interpreter shutdown from a registered function with atexit + when dummy_threading is being used. + +- Bug #857297/Patch #916874. Fix an error when extracting a hard link + from a tarfile. + +- Patch #846659. Fix an error in tarfile.py when using + GNU longname/longlink creation. + +- The obsolete FCNTL.py has been deleted. The builtin fcntl module + has been available (on platforms that support fcntl) since Python + 1.5a3, and all FCNTL.py did is export fcntl's names, after generating + a deprecation warning telling you to use fcntl directly. + +- Several new unicode codecs are added: big5hkscs, euc_jis_2004, + iso2022_jp_2004, shift_jis_2004. + +- Bug #788520. Queue.{get, get_nowait, put, put_nowait} have new + implementations, exploiting Conditions (which didn't exist at the time + Queue was introduced). A minor semantic change is that the Full and + Empty exceptions raised by non-blocking calls now occur only if the + queue truly was full or empty at the instant the queue was checked (of + course the Queue may no longer be full or empty by the time a calling + thread sees those exceptions, though). Before, the exceptions could + also be raised if it was "merely inconvenient" for the implementation + to determine the true state of the Queue (because the Queue was locked + by some other method in progress). + +- Bugs #979794 and #980117: difflib.get_grouped_opcodes() now handles the + case of comparing two empty lists. This affected both context_diff() and + unified_diff(), + +- Bug #980938: smtplib now prints debug output to sys.stderr. + +- Bug #930024: posixpath.realpath() now handles infinite loops in symlinks by + returning the last point in the path that was not part of any loop. Thanks + AM Kuchling. + +- Bug #980327: ntpath not handles compressing erroneous slashes between the + drive letter and the rest of the path. Also clearly handles UNC addresses now + as well. Thanks Paul Moore. + +- bug #679953: zipfile.py should now work for files over 2 GB. The packed data + for file sizes (compressed and uncompressed) was being stored as signed + instead of unsigned. + +- decimal.py now only uses signals in the IBM spec. The other conditions are + no longer part of the public API. + +- codecs module now has two new generic APIs: encode() and decode() + which don't restrict the return types (unlike the unicode and + string methods of the same name). + +- Non-blocking SSL sockets work again; they were broken in Python 2.3. + SF patch 945642. + +- doctest unittest integration improvements: + + o Improved the unitest test output for doctest-based unit tests + + o Can now pass setUp and tearDown functions when creating + DocTestSuites. + +- The threading module has a new class, local, for creating objects + that provide thread-local data. + +- Bug #990307: when keep_empty_values is True, cgi.parse_qsl() + no longer returns spurious empty fields. + +- Implemented bind_textdomain_codeset() in gettext module. + +- Introduced in gettext module the l*gettext() family of functions, + which return translation strings encoded in the preferred encoding, + as informed by locale module's getpreferredencoding(). + +- optparse module (and tests) upgraded to Optik 1.5a1. Changes: + + - Add expansion of default values in help text: the string + "%default" in an option's help string is expanded to str() of + that option's default value, or "none" if no default value. + + - Bug #955889: option default values that happen to be strings are + now processed in the same way as values from the command line; this + allows generation of nicer help when using custom types. Can + be disabled with parser.set_process_default_values(False). + + - Bug #960515: don't crash when generating help for callback + options that specify 'type', but not 'dest' or 'metavar'. + + - Feature #815264: change the default help format for short options + that take an argument from e.g. "-oARG" to "-o ARG"; add + set_short_opt_delimiter() and set_long_opt_delimiter() methods to + HelpFormatter to allow (slight) customization of the formatting. + + - Patch #736940: internationalize Optik: all built-in user- + targeted literal strings are passed through gettext.gettext(). (If + you want translations (.po files), they're not included with Python + -- you'll find them in the Optik source distribution from + http://optik.sourceforge.net/ .) + + - Bug #878453: respect $COLUMNS environment variable for + wrapping help output. + + - Feature #988122: expand "%prog" in the 'description' passed + to OptionParser, just like in the 'usage' and 'version' strings. + (This is *not* done in the 'description' passed to OptionGroup.) + +C API +----- + +- PyImport_ExecCodeModule() and PyImport_ExecCodeModuleEx(): if an + error occurs while loading the module, these now delete the module's + entry from sys.modules. All ways of loading modules eventually call + one of these, so this is an error-case change in semantics for all + ways of loading modules. In rare cases, a module loader may wish + to keep a module object in sys.modules despite that the module's + code cannot be executed. In such cases, the module loader must + arrange to reinsert the name and module object in sys.modules. + PyImport_ReloadModule() has been changed to reinsert the original + module object into sys.modules if the module reload fails, so that + its visible semantics have not changed. + +- A large pile of datetime field-extraction macros is now documented, + thanks to Anthony Tuininga (patch #986010). + +Documentation +------------- + +- Improved the tutorial on creating types in C. + + - point out the importance of reassigning data members before + assigning their values + + - correct my misconception about return values from visitprocs. Sigh. + + - mention the labor saving Py_VISIT and Py_CLEAR macros. + +- Major rewrite of the math module docs, to address common confusions. + +Tests +----- + +- The test data files for the decimal test suite are now installed on + platforms that use the Makefile. + +- SF patch 995225: The test file testtar.tar accidentally contained + CVS keywords (like $Id$), which could cause spurious failures in + test_tarfile.py depending on how the test file was checked out. + + +What's New in Python 2.4 alpha 1? +================================= + +*Release date: 08-JUL-2004* + +Core and builtins +----------------- + +- weakref.ref is now the type object also known as + weakref.ReferenceType; it can be subclassed like any other new-style + class. There's less per-entry overhead in WeakValueDictionary + objects now (one object instead of three). + +- Bug #951851: Python crashed when reading import table of certain + Windows DLLs. + +- Bug #215126. The locals argument to eval(), execfile(), and exec now + accept any mapping type. + +- marshal now shares interned strings. This change introduces + a new .pyc magic. + +- Bug #966623. classes created with type() in an exec(, {}) don't + have a __module__, but code in typeobject assumed it would always + be there. + +- Python no longer relies on the LC_NUMERIC locale setting to be + the "C" locale; as a result, it no longer tries to prevent changing + the LC_NUMERIC category. + +- Bug #952807: Unpickling pickled instances of subclasses of + datetime.date, datetime.datetime and datetime.time could yield insane + objects. Thanks to Jiwon Seo for a fix. + +- Bug #845802: Python crashes when __init__.py is a directory. + +- Unicode objects received two new methods: iswide() and width(). + These query East Asian width information, as specified in Unicode + TR11. + +- Improved the tuple hashing algorithm to give fewer collisions in + common cases. Fixes bug #942952. + +- Implemented generator expressions (PEP 289). Coded by Jiwon Seo. + +- Enabled the profiling of C extension functions (and builtins) - check + new documentation and modified profile and bdb modules for more details + +- Set file.name to the object passed to open (instead of a new string) + +- Moved tracebackobject into traceback.h and renamed to PyTracebackObject + +- Optimized the byte coding for multiple assignments like "a,b=b,a" and + "a,b,c=1,2,3". Improves their speed by 25% to 30%. + +- Limit the nested depth of a tuple for the second argument to isinstance() + and issubclass() to the recursion limit of the interpreter. + Fixes bug #858016 . + +- Optimized dict iterators, creating separate types for each + and having them reveal their length. Also optimized the + methods: keys(), values(), and items(). + +- Implemented a newcode opcode, LIST_APPEND, that simplifies + the generated bytecode for list comprehensions and further + improves their performance (about 35%). + +- Implemented rich comparisons for floats, which seems to make + comparisons involving NaNs somewhat less surprising when the + underlying C compiler actually implements C99 semantics. + +- Optimized list.extend() to save memory and no longer create + intermediate sequences. Also, extend() now pre-allocates the + needed memory whenever the length of the iterable is known in + advance -- this halves the time to extend the list. + +- Optimized list resize operations to make fewer calls to the system + realloc(). Significantly speeds up list appends, list pops, + list comprehensions, and the list constructor (when the input iterable + length is not known). + +- Changed the internal list over-allocation scheme. For larger lists, + overallocation ranged between 3% and 25%. Now, it is a constant 12%. + For smaller lists (n<8), overallocation was upto eight elements. Now, + the overallocation is no more than three elements -- this improves space + utilization for applications that have large numbers of small lists. + +- Most list bodies now get re-used rather than freed. Speeds up list + instantiation and deletion by saving calls to malloc() and free(). + +- The dict.update() method now accepts all the same argument forms + as the dict() constructor. This now includes item lists and/or + keyword arguments. + +- Support for arbitrary objects supporting the read-only buffer + interface as the co_code field of code objects (something that was + only possible to create from C code) has been removed. + +- Made omitted callback and None equivalent for weakref.ref() and + weakref.proxy(); the None case wasn't handled correctly in all + cases. + +- Fixed problem where PyWeakref_NewRef() and PyWeakref_NewProxy() + assumed that initial existing entries in an object's weakref list + would not be removed while allocating a new weakref object. Since + GC could be invoked at that time, however, that assumption was + invalid. In a truly obscure case of GC being triggered during + creation for a new weakref object for an referent which already + has a weakref without a callback which is only referenced from + cyclic trash, a memory error can occur. This consistently created a + segfault in a debug build, but provided less predictable behavior in + a release build. + +- input() builtin function now respects compiler flags such as + __future__ statements. SF patch 876178. + +- Removed PendingDeprecationWarning from apply(). apply() remains + deprecated, but the nuisance warning will not be issued. + +- At Python shutdown time (Py_Finalize()), 2.3 called cyclic garbage + collection twice, both before and after tearing down modules. The + call after tearing down modules has been disabled, because too much + of Python has been torn down then for __del__ methods and weakref + callbacks to execute sanely. The most common symptom was a sequence + of uninformative messages on stderr when Python shut down, produced + by threads trying to raise exceptions, but unable to report the nature + of their problems because too much of the sys module had already been + destroyed. + +- Removed FutureWarnings related to hex/oct literals and conversions + and left shifts. (Thanks to Kalle Svensson for SF patch 849227.) + This addresses most of the remaining semantic changes promised by + PEP 237, except for repr() of a long, which still shows the trailing + 'L'. The PEP appears to promise warnings for operations that + changed semantics compared to Python 2.3, but this is not + implemented; we've suffered through enough warnings related to + hex/oct literals and I think it's best to be silent now. + +- For str and unicode objects, the ljust(), center(), and rjust() + methods now accept an optional argument specifying a fill + character other than a space. + +- When method objects have an attribute that can be satisfied either + by the function object or by the method object, the function + object's attribute usually wins. Christian Tismer pointed out that + that this is really a mistake, because this only happens for special + methods (like __reduce__) where the method object's version is + really more appropriate than the function's attribute. So from now + on, all method attributes will have precedence over function + attributes with the same name. + +- Critical bugfix, for SF bug 839548: if a weakref with a callback, + its callback, and its weakly referenced object, all became part of + cyclic garbage during a single run of garbage collection, the order + in which they were torn down was unpredictable. It was possible for + the callback to see partially-torn-down objects, leading to immediate + segfaults, or, if the callback resurrected garbage objects, to + resurrect insane objects that caused segfaults (or other surprises) + later. In one sense this wasn't surprising, because Python's cyclic gc + had no knowledge of Python's weakref objects. It does now. When + weakrefs with callbacks become part of cyclic garbage now, those + weakrefs are cleared first. The callbacks don't trigger then, + preventing the problems. If you need callbacks to trigger, then just + as when cyclic gc is not involved, you need to write your code so + that weakref objects outlive the objects they weakly reference. + +- Critical bugfix, for SF bug 840829: if cyclic garbage collection + happened to occur during a weakref callback for a new-style class + instance, subtle memory corruption was the result (in a release build; + in a debug build, a segfault occurred reliably very soon after). + This has been repaired. + +- Compiler flags set in PYTHONSTARTUP are now active in __main__. + +- Added two builtin types, set() and frozenset(). + +- Added a reversed() builtin function that returns a reverse iterator + over a sequence. + +- Added a sorted() builtin function that returns a new sorted list + from any iterable. + +- CObjects are now mutable (on the C level) through PyCObject_SetVoidPtr. + +- list.sort() now supports three keyword arguments: cmp, key, and reverse. + The key argument can be a function of one argument that extracts a + comparison key from the original record: mylist.sort(key=str.lower). + The reverse argument is a boolean value and if True will change the + sort order as if the comparison arguments were reversed. In addition, + the documentation has been amended to provide a guarantee that all sorts + starting with Py2.3 are guaranteed to be stable (the relative order of + records with equal keys is unchanged). + +- Added test whether wchar_t is signed or not. A signed wchar_t is not + usable as internal unicode type base for Py_UNICODE since the + unicode implementation assumes an unsigned type. + +- Fixed a bug in the cache of length-one Unicode strings that could + lead to a seg fault. The specific problem occurred when an earlier, + non-fatal error left an uninitialized Unicode object in the + freelist. + +- The % formatting operator now supports '%F' which is equivalent to + '%f'. This has always been documented but never implemented. + +- complex(obj) could leak a little memory if obj wasn't a string or + number. + +- zip() with no arguments now returns an empty list instead of raising + a TypeError exception. + +- obj.__contains__() now returns True/False instead of 1/0. SF patch + 820195. + +- Python no longer tries to be smart about recursive comparisons. + When comparing containers with cyclic references to themselves it + will now just hit the recursion limit. See SF patch 825639. + +- str and unicode builtin types now have an rsplit() method that is + same as split() except that it scans the string from the end + working towards the beginning. See SF feature request 801847. + +- Fixed a bug in object.__reduce_ex__ when using protocol 2. Failure + to clear the error when attempts to get the __getstate__ attribute + fail caused intermittent errors and odd behavior. + +- buffer objects based on other objects no longer cache a pointer to + the data and the data length. Instead, the appropriate tp_as_buffer + method is called as necessary. + +- fixed: if a file is opened with an explicit buffer size >= 1, repeated + close() calls would attempt to free() the buffer already free()ed on + the first call. + + +Extension modules +----------------- + +- Added socket.getservbyport(), and make the second argument in + getservbyname() and getservbyport() optional. + +- time module code that deals with input POSIX timestamps will now raise + ValueError if more than a second is lost in precision when the + timestamp is cast to the platform C time_t type. There's no chance + that the platform will do anything sensible with the result in such + cases. This includes ctime(), localtime() and gmtime(). Assorted + fromtimestamp() and utcfromtimestamp() methods in the datetime module + were also protected. Closes bugs #919012 and 975996. + +- fcntl.ioctl now warns if the mutate flag is not specified. + +- nt now properly allows to refer to UNC roots, e.g. in nt.stat(). + +- the weakref module now supports additional objects: array.array, + sre.pattern_objects, file objects, and sockets. + +- operator.isMappingType() and operator.isSequenceType() now give + fewer false positives. + +- socket.sslerror is now a subclass of socket.error . Also added + socket.error to the socket module's C API. + +- Bug #920575: A problem where the _locale module segfaults on + nl_langinfo(ERA) caused by GNU libc's illegal NULL return is fixed. + +- array objects now support the copy module. Also, their resizing + scheme has been updated to match that used for list objects. This improves + the performance (speed and memory usage) of append() operations. + Also, array.array() and array.extend() now accept any iterable argument + for repeated appends without needing to create another temporary array. + +- cStringIO.writelines() now accepts any iterable argument and writes + the lines one at a time rather than joining them and writing once. + Made a parallel change to StringIO.writelines(). Saves memory and + makes suitable for use with generator expressions. + +- time.strftime() now checks that the values in its time tuple argument + are within the proper boundaries to prevent possible crashes from the + platform's C library implementation of strftime(). Can possibly + break code that uses values outside the range that didn't cause + problems previously (such as sitting day of year to 0). Fixes bug + #897625. + +- The socket module now supports Bluetooth sockets, if the + system has + +- Added a collections module containing a new datatype, deque(), + offering high-performance, thread-safe, memory friendly appends + and pops on either side of the deque. + +- Several modules now take advantage of collections.deque() for + improved performance: Queue, mutex, shlex, threading, and pydoc. + +- The operator module has two new functions, attrgetter() and + itemgetter() which are useful for creating fast data extractor + functions for map(), list.sort(), itertools.groupby(), and + other functions that expect a function argument. + +- socket.SHUT_{RD,WR,RDWR} was added. + +- os.getsid was added. + +- The pwd module incorrectly advertised its struct type as + struct_pwent; this has been renamed to struct_passwd. (The old name + is still supported for backwards compatibility.) + +- The xml.parsers.expat module now provides Expat 1.95.7. + +- socket.IPPROTO_IPV6 was added. + +- readline.clear_history was added. + +- select.select() now accepts sequences for its first three arguments. + +- cStringIO now supports the f.closed attribute. + +- The signal module now exposes SIGRTMIN and SIGRTMAX (if available). + +- curses module now supports use_default_colors(). [patch #739124] + +- Bug #811028: ncurses.h breakage on FreeBSD/MacOS X + +- Bug #814613: INET_ADDRSTRLEN fix needed for all compilers on SGI + +- Implemented non-recursive SRE matching scheme (#757624). + +- Implemented (?(id/name)yes|no) support in SRE (#572936). + +- random.seed() with no arguments or None uses time.time() as a default + seed. Modified to match Py2.2 behavior and use fractional seconds so + that successive runs are more likely to produce different sequences. + +- random.Random has a new method, getrandbits(k), which returns an int + with k random bits. This method is now an optional part of the API + for user defined generators. Any generator that defines genrandbits() + can now use randrange() for ranges with a length >= 2**53. Formerly, + randrange would return only even numbers for ranges that large (see + SF bug #812202). Generators that do not define genrandbits() now + issue a warning when randrange() is called with a range that large. + +- itertools has a new function, groupby() for aggregating iterables + into groups sharing the same key (as determined by a key function). + It offers some of functionality of SQL's groupby keyword and of + the Unix uniq filter. + +- itertools now has a new tee() function which produces two independent + iterators from a single iterable. + +- itertools.izip() with no arguments now returns an empty iterator instead + of raising a TypeError exception. + +- Fixed #853061: allow BZ2Compressor.compress() to receive an empty string + as parameter. + +Library +------- + +- Added a new module: cProfile, a C profiler with the same interface as the + profile module. cProfile avoids some of the drawbacks of the hotshot + profiler and provides a bit more information than the other two profilers. + Based on "lsprof" (patch #1212837). + +- Bug #1266283: The new function "lexists" is now in os.path.__all__. + +- Bug #981530: Fix UnboundLocalError in shutil.rmtree(). This affects + the documented behavior: the function passed to the onerror() + handler can now also be os.listdir. + +- Bug #754449: threading.Thread objects no longer mask exceptions raised during + interpreter shutdown with another exception from attempting to handle the + original exception. + +- Added decimal.py per PEP 327. + +- Bug #981299: rsync is now a recognized protocol in urlparse that uses a + "netloc" portion of a URL. + +- Bug #919012: shutil.move() will not try to move a directory into itself. + Thanks Johannes Gijsbers. + +- Bug #934282: pydoc.stripid() is now case-insensitive. Thanks Robin Becker. + +- Bug #823209: cmath.log() now takes an optional base argument so that its + API matches math.log(). + +- Bug #957381: distutils bdist_rpm no longer fails on recent RPM versions + that generate a -debuginfo.rpm + +- os.path.devnull has been added for all supported platforms. + +- Fixed #877165: distutils now picks the right C++ compiler command + on cygwin and mingw32. + +- urllib.urlopen().readline() now handles HTTP/0.9 correctly. + +- refactored site.py into functions. Also wrote regression tests for the + module. + +- The distutils install command now supports the --home option and + installation scheme for all platforms. + +- asyncore.loop now has a repeat count parameter that defaults to + looping forever. + +- The distutils sdist command now ignores all .svn directories, in + addition to CVS and RCS directories. .svn directories hold + administrative files for the Subversion source control system. + +- Added a new module: cookielib. Automatic cookie handling for HTTP + clients. Also, support for cookielib has been added to urllib2, so + urllib2.urlopen() can transparently handle cookies. + +- stringprep.py now uses built-in set() instead of sets.Set(). + +- Bug #876278: Unbounded recursion in modulefinder + +- Bug #780300: Swap public and system ID in LexicalHandler.startDTD. + Applications relying on the wrong order need to be corrected. + +- Bug #926075: Fixed a bug that returns a wrong pattern object + for a string or unicode object in sre.compile() when a different + type pattern with the same value exists. + +- Added countcallers arg to trace.Trace class (--trackcalls command line arg + when run from the command prompt). + +- Fixed a caching bug in platform.platform() where the argument of 'terse' was + not taken into consideration when caching value. + +- Added two new command-line arguments for profile (output file and + default sort). + +- Added global runctx function to profile module + +- Add hlist missing entryconfigure and entrycget methods. + +- The ptcp154 codec was added for Kazakh character set support. + +- Support non-anonymous ftp URLs in urllib2. + +- The encodings package will now apply codec name aliases + first before starting to try the import of the codec module. + This simplifies overriding built-in codecs with external + packages, e.g. the included CJK codecs with the JapaneseCodecs + package, by adjusting the aliases dictionary in encodings.aliases + accordingly. + +- base64 now supports RFC 3548 Base16, Base32, and Base64 encoding and + decoding standards. + +- urllib2 now supports processors. A processor is a handler that + implements an xxx_request or xxx_response method. These methods are + called for all requests. + +- distutils compilers now compile source files in the same order as + they are passed to the compiler. + +- pprint.pprint() and pprint.pformat() now have additional parameters + indent, width and depth. + +- Patch #750542: pprint now will pretty print subclasses of list, tuple + and dict too, as long as they don't overwrite __repr__(). + +- Bug #848614: distutils' msvccompiler fails to find the MSVC6 + compiler because of incomplete registry entries. + +- httplib.HTTP.putrequest now offers to omit the implicit Accept-Encoding. + +- Patch #841977: modulefinder didn't find extension modules in packages + +- imaplib.IMAP4.thread was added. + +- Plugged a minor hole in tempfile.mktemp() due to the use of + os.path.exists(), switched to using os.lstat() directly if possible. + +- bisect.py and heapq.py now have underlying C implementations + for better performance. + +- heapq.py has two new functions, nsmallest() and nlargest(). + +- traceback.format_exc has been added (similar to print_exc but it returns + a string). + +- xmlrpclib.MultiCall has been added. + +- poplib.POP3_SSL has been added. + +- tmpfile.mkstemp now returns an absolute path even if dir is relative. + +- urlparse is RFC 2396 compliant. + +- The fieldnames argument to the csv module's DictReader constructor is now + optional. If omitted, the first row of the file will be used as the + list of fieldnames. + +- encodings.bz2_codec was added for access to bz2 compression + using "a long string".encode('bz2') + +- Various improvements to unittest.py, realigned with PyUnit CVS. + +- dircache now passes exceptions to the caller, instead of returning + empty lists. + +- The bsddb module and dbhash module now support the iterator and + mapping protocols which make them more substitutable for dictionaries + and shelves. + +- The csv module's DictReader and DictWriter classes now accept keyword + arguments. This was an omission in the initial implementation. + +- The email package handles some RFC 2231 parameters with missing + CHARSET fields better. It also includes a patch to parameter + parsing when semicolons appear inside quotes. + +- sets.py now runs under Py2.2. In addition, the argument restrictions + for most set methods (but not the operators) have been relaxed to + allow any iterable. + +- _strptime.py now has a behind-the-scenes caching mechanism for the most + recent TimeRE instance used along with the last five unique directive + patterns. The overall module was also made more thread-safe. + +- random.cunifvariate() and random.stdgamma() were deprecated in Py2.3 + and removed in Py2.4. + +- Bug #823328: urllib2.py's HTTP Digest Auth support works again. + +- Patch #873597: CJK codecs are imported into rank of default codecs. + +Tools/Demos +----------- + +- A hotshotmain script was added to the Tools/scripts directory that + makes it easy to run a script under control of the hotshot profiler. + +- The db2pickle and pickle2db scripts can now dump/load gdbm files. + +- The file order on the command line of the pickle2db script was reversed. + It is now [ picklefile ] dbfile. This provides better symmetry with + db2pickle. The file arguments to both scripts are now source followed by + destination in situations where both files are given. + +- The pydoc script will display a link to the module documentation for + modules determined to be part of the core distribution. The documentation + base directory defaults to http://www.python.org/doc/current/lib/ but can + be changed by setting the PYTHONDOCS environment variable. + +- texcheck.py now detects double word errors. + +- md5sum.py mistakenly opened input files in text mode by default, a + silent and dangerous change from previous releases. It once again + opens input files in binary mode by default. The -t and -b flags + remain for compatibility with the 2.3 release, but -b is the default + now. + +- py-electric-colon now works when pending-delete/delete-selection mode is + in effect + +- py-help-at-point is no longer bound to the F1 key - it's still bound to + C-c C-h + +- Pynche was fixed to not crash when there is no ~/.pynche file and no + -d option was given. + +Build +----- + +- Bug #978645: Modules/getpath.c now builds properly in --disable-framework + build under OS X. + +- Profiling using gprof is now available if Python is configured with + --enable-profiling. + +- Profiling the VM using the Pentium TSC is now possible if Python + is configured --with-tsc. + +- In order to find libraries, setup.py now also looks in /lib64, for use + on AMD64. + +- Bug #934635: Fixed a bug where the configure script couldn't detect + getaddrinfo() properly if the KAME stack had SCTP support. + +- Support for missing ANSI C header files (limits.h, stddef.h, etc) was + removed. + +- Systems requiring the D4, D6 or D7 variants of pthreads are no longer + supported (see PEP 11). + +- Universal newline support can no longer be disabled (see PEP 11). + +- Support for DGUX, SunOS 4, IRIX 4 and Minix was removed (see PEP 11). + +- Support for systems requiring --with-dl-dld or --with-sgi-dl was removed + (see PEP 11). + +- Tests for sizeof(char) were removed since ANSI C mandates that + sizeof(char) must be 1. + +C API +----- + +- Thanks to Anthony Tuininga, the datetime module now supplies a C API + containing type-check macros and constructors. See new docs in the + Python/C API Reference Manual for details. + +- Private function _PyTime_DoubleToTimet added, to convert a Python + timestamp (C double) to platform time_t with some out-of-bounds + checking. Declared in new header file timefuncs.h. It would be + good to expose some other internal timemodule.c functions there. + +- New public functions PyEval_EvaluateFrame and PyGen_New to expose + generator objects. + +- New public functions Py_IncRef() and Py_DecRef(), exposing the + functionality of the Py_XINCREF() and Py_XDECREF macros. Useful for + runtime dynamic embedding of Python. See patch #938302, by Bob + Ippolito. + +- Added a new macro, PySequence_Fast_ITEMS, which retrieves a fast sequence's + underlying array of PyObject pointers. Useful for high speed looping. + +- Created a new method flag, METH_COEXIST, which causes a method to be loaded + even if already defined by a slot wrapper. This allows a __contains__ + method, for example, to co-exist with a defined sq_contains slot. This + is helpful because the PyCFunction can take advantage of optimized calls + whenever METH_O or METH_NOARGS flags are defined. + +- Added a new function, PyDict_Contains(d, k) which is like + PySequence_Contains() but is specific to dictionaries and executes + about 10% faster. + +- Added three new macros: Py_RETURN_NONE, Py_RETURN_TRUE, and Py_RETURN_FALSE. + Each return the singleton they mention after Py_INCREF()ing them. + +- Added a new function, PyTuple_Pack(n, ...) for constructing tuples from a + variable length argument list of Python objects without having to invoke + the more complex machinery of Py_BuildValue(). PyTuple_Pack(3, a, b, c) + is equivalent to Py_BuildValue("(OOO)", a, b, c). + +Windows +------- + +- The _winreg module could segfault when reading very large registry + values, due to unchecked alloca() calls (SF bug 851056). The fix is + uses either PyMem_Malloc(n) or PyString_FromStringAndSize(NULL, n), + as appropriate, followed by a size check. + +- file.truncate() could misbehave if the file was open for update + (modes r+, rb+, w+, wb+), and the most recent file operation before + the truncate() call was an input operation. SF bug 801631. + + +What's New in Python 2.3 final? +=============================== + +*Release date: 29-Jul-2003* + +IDLE +---- + +- Bug 778400: IDLE hangs when selecting "Edit with IDLE" from explorer. + This was unique to Windows, and was fixed by adding an -n switch to + the command the Windows installer creates to execute "Edit with IDLE" + context-menu actions. + +- IDLE displays a new message upon startup: some "personal firewall" + kinds of programs (for example, ZoneAlarm) open a dialog of their + own when any program opens a socket. IDLE does use sockets, talking + on the computer's internal loopback interface. This connection is not + visible on any external interface and no data is sent to or received + from the Internet. So, if you get such a dialog when opening IDLE, + asking whether to let pythonw.exe talk to address 127.0.0.1, say yes, + and rest assured no communication external to your machine is taking + place. If you don't allow it, IDLE won't be able to start. + + +What's New in Python 2.3 release candidate 2? +============================================= + +*Release date: 24-Jul-2003* + +Core and builtins +----------------- + +- It is now possible to import from zipfiles containing additional + data bytes before the zip compatible archive. Zipfiles containing a + comment at the end are still unsupported. + +Extension modules +----------------- + +- A longstanding bug in the parser module's initialization could cause + fatal internal refcount confusion when the module got initialized more + than once. This has been fixed. + +- Fixed memory leak in pyexpat; using the parser's ParseFile() method + with open files that aren't instances of the standard file type + caused an instance of the bound .read() method to be leaked on every + call. + +- Fixed some leaks in the locale module. + +Library +------- + +- Lib/encodings/rot_13.py when used as a script, now more properly + uses the first Python interpreter on your path. + +- Removed caching of TimeRE (and thus LocaleTime) in _strptime.py to + fix a locale related bug in the test suite. Although another patch + was needed to actually fix the problem, the cache code was not + restored. + +IDLE +---- + +- Calltips patches. + +Build +----- + +- For MacOSX, added -mno-fused-madd to BASECFLAGS to fix test_coercion + on Panther (OSX 10.3). + +C API +----- + +Windows +------- + +- The tempfile module could do insane imports on Windows if PYTHONCASEOK + was set, making temp file creation impossible. Repaired. + +- Add a patch to workaround pthread_sigmask() bugs in Cygwin. + +Mac +--- + +- Various fixes to pimp. + +- Scripts runs with pythonw no longer had full window manager access. + +- Don't force boot-disk-only install, for reasons unknown it causes + more problems than it solves. + + +What's New in Python 2.3 release candidate 1? +============================================= + +*Release date: 18-Jul-2003* + +Core and builtins +----------------- + +- The new function sys.getcheckinterval() returns the last value set + by sys.setcheckinterval(). + +- Several bugs in the symbol table phase of the compiler have been + fixed. Errors could be lost and compilation could fail without + reporting an error. SF patch 763201. + +- The interpreter is now more robust about importing the warnings + module. In an executable generated by freeze or similar programs, + earlier versions of 2.3 would fail if the warnings module could + not be found on the file system. Fixes SF bug 771097. + +- A warning about assignments to module attributes that shadow + builtins, present in earlier releases of 2.3, has been removed. + +- It is not possible to create subclasses of builtin types like str + and tuple that define an itemsize. Earlier releases of Python 2.3 + allowed this by mistake, leading to crashes and other problems. + +- The thread_id is now initialized to 0 in a non-thread build. SF bug + 770247. + +- SF bug 762891: "del p[key]" on proxy object no longer raises SystemError. + +Extension modules +----------------- + +- weakref.proxy() can now handle "del obj[i]" for proxy objects + defining __delitem__. Formerly, it generated a SystemError. + +- SSL no longer crashes the interpreter when the remote side disconnects. + +- On Unix the mmap module can again be used to map device files. + +- time.strptime now exclusively uses the Python implementation + contained within the _strptime module. + +- The print slot of weakref proxy objects was removed, because it was + not consistent with the object's repr slot. + +- The mmap module only checks file size for regular files, not + character or block devices. SF patch 708374. + +- The cPickle Pickler garbage collection support was fixed to traverse + the find_class attribute, if present. + +- There are several fixes for the bsddb3 wrapper module. + + bsddb3 no longer crashes if an environment is closed before a cursor + (SF bug 763298). + + The DB and DBEnv set_get_returns_none function was extended to take + a level instead of a boolean flag. The new level 2 means that in + addition, cursor.set()/.get() methods return None instead of raising + an exception. + + A typo was fixed in DBCursor.join_item(), preventing a crash. + +Library +------- + +- distutils now supports MSVC 7.1 + +- doctest now examines all docstrings by default. Previously, it would + skip over functions with private names (as indicated by the underscore + naming convention). The old default created too much of a risk that + user tests were being skipped inadvertently. Note, this change could + break code in the unlikely case that someone had intentionally put + failing tests in the docstrings of private functions. The breakage + is easily fixable by specifying the old behavior when calling testmod() + or Tester(). + +- There were several fixes to the way dumbdbms are closed. It's vital + that a dumbdbm database be closed properly, else the on-disk data + and directory files can be left in mutually inconsistent states. + dumbdbm.py's _Database.__del__() method attempted to close the + database properly, but a shutdown race in _Database._commit() could + prevent this from working, so that a program trusting __del__() to + get the on-disk files in synch could be badly surprised. The race + has been repaired. A sync() method was also added so that shelve + can guarantee data is written to disk. + + The close() method can now be called more than once without complaint. + +- The classes in threading.py are now new-style classes. That they + weren't before was an oversight. + +- The urllib2 digest authentication handlers now define the correct + auth_header. The earlier versions would fail at runtime. + +- SF bug 763023: fix uncaught ZeroDivisionError in difflib ratio methods + when there are no lines. + +- SF bug 763637: fix exception in Tkinter with after_cancel + which could occur with Tk 8.4 + +- SF bug 770601: CGIHTTPServer.py now passes the entire environment + to child processes. + +- SF bug 765238: add filter to fnmatch's __all__. + +- SF bug 748201: make time.strptime() error messages more helpful. + +- SF patch 764470: Do not dump the args attribute of a Fault object in + xmlrpclib. + +- SF patch 549151: urllib and urllib2 now redirect POSTs on 301 + responses. + +- SF patch 766650: The whichdb module was fixed to recognize dbm files + generated by gdbm on OS/2 EMX. + +- SF bugs 763047 and 763052: fixes bug of timezone value being left as + -1 when ``time.tzname[0] == time.tzname[1] and not time.daylight`` + is true when it should only when time.daylight is true. + +- SF bug 764548: re now allows subclasses of str and unicode to be + used as patterns. + +- SF bug 763637: In Tkinter, change after_cancel() to handle tuples + of varying sizes. Tk 8.4 returns a different number of values + than Tk 8.3. + +- SF bug 763023: difflib.ratio() did not catch zero division. + +- The Queue module now has an __all__ attribute. + +Tools/Demos +----------- + +- See Lib/idlelib/NEWS.txt for IDLE news. + +- SF bug 753592: webchecker/wsgui now handles user supplied directories. + +- The trace.py script has been removed. It is now in the standard library. + +Build +----- + +- Python now compiles with -fno-strict-aliasing if possible (SF bug 766696). + +- The socket module compiles on IRIX 6.5.10. + +- An irix64 system is treated the same way as an irix6 system (SF + patch 764560). + +- Several definitions were missing on FreeBSD 5.x unless the + __BSD_VISIBLE symbol was defined. configure now defines it as + needed. + +C API +----- + +- Unicode objects now support mbcs as a built-in encoding, so the C + API can use it without deferring to the encodings package. + +Windows +------- + +- The Windows implementation of PyThread_start_new_thread() never + checked error returns from Windows functions correctly. As a result, + it could claim to start a new thread even when the Microsoft + _beginthread() function failed (due to "too many threads" -- this is + on the order of thousands when it happens). In these cases, the + Python exception :: + + thread.error: can't start new thread + + is raised now. + +- SF bug 766669: Prevent a GPF on interpreter exit when sockets are in + use. The interpreter now calls WSACleanup() from Py_Finalize() + instead of from DLL teardown. + +Mac +--- + +- Bundlebuilder now inherits default values in the right way. It was + previously possible for app bundles to get a type of "BNDL" instead + of "APPL." Other improvements include, a --build-id option to + specify the CFBundleIdentifier and using the --python option to set + the executable in the bundle. + +- Fixed two bugs in MacOSX framework handling. + +- pythonw did not allow user interaction in 2.3rc1, this has been fixed. + +- Python is now compiled with -mno-fused-madd, making all tests pass + on Panther. + +What's New in Python 2.3 beta 2? +================================ + +*Release date: 29-Jun-2003* + +Core and builtins +----------------- + +- A program can now set the environment variable PYTHONINSPECT to some + string value in Python, and cause the interpreter to enter the + interactive prompt at program exit, as if Python had been invoked + with the -i option. + +- list.index() now accepts optional start and stop arguments. Similar + changes were made to UserList.index(). SF feature request 754014. + +- SF patch 751998 fixes an unwanted side effect of the previous fix + for SF bug 742860 (the next item). + +- SF bug 742860: "WeakKeyDictionary __delitem__ uses iterkeys". This + wasn't threadsafe, was very inefficient (expected time O(len(dict)) + instead of O(1)), and could raise a spurious RuntimeError if another + thread mutated the dict during __delitem__, or if a comparison function + mutated it. It also neglected to raise KeyError when the key wasn't + present; didn't raise TypeError when the key wasn't of a weakly + referencable type; and broke various more-or-less obscure dict + invariants by using a sequence of equality comparisons over the whole + set of dict keys instead of computing the key's hash code to narrow + the search to those keys with the same hash code. All of these are + considered to be bugs. A new implementation of __delitem__ repairs all + that, but note that fixing these bugs may change visible behavior in + code relying (whether intentionally or accidentally) on old behavior. + +- SF bug 734869: Fixed a compiler bug that caused a fatal error when + compiling a list comprehension that contained another list comprehension + embedded in a lambda expression. + +- SF bug 705231: builtin pow() no longer lets the platform C pow() + raise -1.0 to integer powers, because (at least) glibc gets it wrong + in some cases. The result should be -1.0 if the power is odd and 1.0 + if the power is even, and any float with a sufficiently large exponent + is (mathematically) an exact even integer. + +- SF bug 759227: A new-style class that implements __nonzero__() must + return a bool or int (but not an int subclass) from that method. This + matches the restriction on classic classes. + +- The encoding attribute has been added for file objects, and set to + the terminal encoding on Unix and Windows. + +- The softspace attribute of file objects became read-only by oversight. + It's writable again. + +- Reverted a 2.3 beta 1 change to iterators for subclasses of list and + tuple. By default, the iterators now access data elements directly + instead of going through __getitem__. If __getitem__ access is + preferred, then __iter__ can be overridden. + +- SF bug 735247: The staticmethod and super types participate in + garbage collection. Before this change, it was possible for leaks to + occur in functions with non-global free variables that used these types. + +Extension modules +----------------- + +- the socket module has a new exception, socket.timeout, to allow + timeouts to be handled separately from other socket errors. + +- SF bug 751276: cPickle has fixed to propagate exceptions raised in + user code. In earlier versions, cPickle caught and ignored any + exception when it performed operations that it expected to raise + specific exceptions like AttributeError. + +- cPickle Pickler and Unpickler objects now participate in garbage + collection. + +- mimetools.choose_boundary() could return duplicate strings at times, + especially likely on Windows. The strings returned are now guaranteed + unique within a single program run. + +- thread.interrupt_main() raises KeyboardInterrupt in the main thread. + dummy_thread has also been modified to try to simulate the behavior. + +- array.array.insert() now treats negative indices as being relative + to the end of the array, just like list.insert() does. (SF bug #739313) + +- The datetime module classes datetime, time, and timedelta are now + properly subclassable. + +- _tkinter.{get|set}busywaitinterval was added. + +- itertools.islice() now accepts stop=None as documented. + Fixes SF bug #730685. + +- the bsddb185 module is built in one restricted instance - + /usr/include/db.h exists and defines HASHVERSION to be 2. This is true + for many BSD-derived systems. + + +Library +------- + +- Some happy doctest extensions from Jim Fulton have been added to + doctest.py. These are already being used in Zope3. The two + primary ones: + + doctest.debug(module, name) extracts the doctests from the named object + in the given module, puts them in a temp file, and starts pdb running + on that file. This is great when a doctest fails. + + doctest.DocTestSuite(module=None) returns a synthesized unittest + TestSuite instance, to be run by the unittest framework, which + runs all the doctests in the module. This allows writing tests in + doctest style (which can be clearer and shorter than writing tests + in unittest style), without losing unittest's powerful testing + framework features (which doctest lacks). + +- For compatibility with doctests created before 2.3, if an expected + output block consists solely of "1" and the actual output block + consists solely of "True", it's accepted as a match; similarly + for "0" and "False". This is quite un-doctest-like, but is practical. + The behavior can be disabled by passing the new doctest module + constant DONT_ACCEPT_TRUE_FOR_1 to the new optionflags optional + argument. + +- ZipFile.testzip() now only traps BadZipfile exceptions. Previously, + a bare except caught to much and reported all errors as a problem + in the archive. + +- The logging module now has a new function, makeLogRecord() making + LogHandler easier to interact with DatagramHandler and SocketHandler. + +- The cgitb module has been extended to support plain text display (SF patch + 569574). + +- A brand new version of IDLE (from the IDLEfork project at + SourceForge) is now included as Lib/idlelib. The old Tools/idle is + no more. + +- Added a new module: trace (documentation missing). This module used + to be distributed in Tools/scripts. It uses sys.settrace() to trace + code execution -- either function calls or individual lines. It can + generate tracing output during execution or a post-mortem report of + code coverage. + +- The threading module has new functions settrace() and setprofile() + that cooperate with the functions of the same name in the sys + module. A function registered with the threading module will + be used for all threads it creates. The new trace module uses this + to provide tracing for code running in threads. + +- copy.py: applied SF patch 707900, fixing bug 702858, by Steven + Taschuk. Copying a new-style class that had a reference to itself + didn't work. (The same thing worked fine for old-style classes.) + Builtin functions are now treated as atomic, fixing bug #746304. + +- difflib.py has two new functions: context_diff() and unified_diff(). + +- More fixes to urllib (SF 549151): (a) When redirecting, always use + GET. This is common practice and more-or-less sanctioned by the + HTTP standard. (b) Add a handler for 307 redirection, which becomes + an error for POST, but a regular redirect for GET and HEAD + +- Added optional 'onerror' argument to os.walk(), to control error + handling. + +- inspect.is{method|data}descriptor was added, to allow pydoc display + __doc__ of data descriptors. + +- Fixed socket speed loss caused by use of the _socketobject wrapper class + in socket.py. + +- timeit.py now checks the current directory for imports. + +- urllib2.py now knows how to order proxy classes, so the user doesn't + have to insert it in front of other classes, nor do dirty tricks like + inserting a "dummy" HTTPHandler after a ProxyHandler when building an + opener with proxy support. + +- Iterators have been added for dbm keys. + +- random.Random objects can now be pickled. + +Tools/Demos +----------- + +- pydoc now offers help on keywords and topics. + +- Tools/idle is gone; long live Lib/idlelib. + +- diff.py prints file diffs in context, unified, or ndiff formats, + providing a command line interface to difflib.py. + +- texcheck.py is a new script for making a rough validation of Python LaTeX + files. + +Build +----- + +- Setting DESTDIR during 'make install' now allows specifying a + different root directory. + +C API +----- + +- PyType_Ready(): If a type declares that it participates in gc + (Py_TPFLAGS_HAVE_GC), and its base class does not, and its base class's + tp_free slot is the default _PyObject_Del, and type does not define + a tp_free slot itself, _PyObject_GC_Del is assigned to type->tp_free. + Previously _PyObject_Del was inherited, which could at best lead to a + segfault. In addition, if even after this magic the type's tp_free + slot is _PyObject_Del or NULL, and the type is a base type + (Py_TPFLAGS_BASETYPE), TypeError is raised: since the type is a base + type, its dealloc function must call type->tp_free, and since the type + is gc'able, tp_free must not be NULL or _PyObject_Del. + +- PyThreadState_SetAsyncExc(): A new API (deliberately accessible only + from C) to interrupt a thread by sending it an exception. It is + intentional that you have to write your own C extension to call it + from Python. + + +New platforms +------------- + +None this time. + +Tests +----- + +- test_imp rewritten so that it doesn't raise RuntimeError if run as a + side effect of being imported ("import test.autotest"). + +Windows +------- + +- The Windows installer ships with Tcl/Tk 8.4.3 (upgraded from 8.4.1). + +- The installer always suggested that Python be installed on the C: + drive, due to a hardcoded "C:" generated by the Wise installation + wizard. People with machines where C: is not the system drive + usually want Python installed on whichever drive is their system drive + instead. We removed the hardcoded "C:", and two testers on machines + where C: is not the system drive report that the installer now + suggests their system drive. Note that you can always select the + directory you want in the "Select Destination Directory" dialog -- + that's what it's for. + +Mac +--- + +- There's a new module called "autoGIL", which offers a mechanism to + automatically release the Global Interpreter Lock when an event loop + goes to sleep, allowing other threads to run. It's currently only + supported on OSX, in the Mach-O version. +- The OSA modules now allow direct access to properties of the + toplevel application class (in AppleScript terminology). +- The Package Manager can now update itself. + +SourceForge Bugs and Patches Applied +------------------------------------ + +430160, 471893, 501716, 542562, 549151, 569574, 595837, 596434, +598163, 604210, 604716, 610332, 612627, 614770, 620190, 621891, +622042, 639139, 640236, 644345, 649742, 649742, 658233, 660022, +661318, 661676, 662807, 662923, 666219, 672855, 678325, 682347, +683486, 684981, 685773, 686254, 692776, 692959, 693094, 696777, +697989, 700827, 703666, 708495, 708604, 708901, 710733, 711902, +713722, 715782, 718286, 719359, 719367, 723136, 723831, 723962, +724588, 724767, 724767, 725942, 726150, 726446, 726869, 727051, +727719, 727719, 727805, 728277, 728563, 728656, 729096, 729103, +729293, 729297, 729300, 729317, 729395, 729622, 729817, 730170, +730296, 730594, 730685, 730826, 730963, 731209, 731403, 731504, +731514, 731626, 731635, 731643, 731644, 731644, 731689, 732124, +732143, 732234, 732284, 732284, 732479, 732761, 732783, 732951, +733667, 733781, 734118, 734231, 734869, 735051, 735293, 735527, +735613, 735694, 736962, 736962, 737970, 738066, 739313, 740055, +740234, 740301, 741806, 742126, 742741, 742860, 742860, 742911, +744041, 744104, 744238, 744687, 744877, 745055, 745478, 745525, +745620, 746012, 746304, 746366, 746801, 746953, 747348, 747667, +747954, 748846, 748849, 748973, 748975, 749191, 749210, 749759, +749831, 749911, 750008, 750092, 750542, 750595, 751038, 751107, +751276, 751451, 751916, 751941, 751956, 751998, 752671, 753451, +753602, 753617, 753845, 753925, 754014, 754340, 754447, 755031, +755087, 755147, 755245, 755683, 755987, 756032, 756996, 757058, +757229, 757818, 757821, 757822, 758112, 758910, 759227, 759889, +760257, 760703, 760792, 761104, 761337, 761519, 761830, 762455 + + +What's New in Python 2.3 beta 1? +================================ + +*Release date: 25-Apr-2003* + +Core and builtins +----------------- + +- New format codes B, H, I, k and K have been implemented for + PyArg_ParseTuple and PyBuild_Value. + +- New builtin function sum(seq, start=0) returns the sum of all the + items in iterable object seq, plus start (items are normally numbers, + and cannot be strings). + +- bool() called without arguments now returns False rather than + raising an exception. This is consistent with calling the + constructors for the other builtin types -- called without argument + they all return the false value of that type. (SF patch #724135) + +- In support of PEP 269 (making the pgen parser generator accessible + from Python), some changes to the pgen code structure were made; a + few files that used to be linked only with pgen are now linked with + Python itself. + +- The repr() of a weakref object now shows the __name__ attribute of + the referenced object, if it has one. + +- super() no longer ignores data descriptors, except __class__. See + the thread started at + http://mail.python.org/pipermail/python-dev/2003-April/034338.html + +- list.insert(i, x) now interprets negative i as it would be + interpreted by slicing, so negative values count from the end of the + list. This was the only place where such an interpretation was not + placed on a list index. + +- range() now works even if the arguments are longs with magnitude + larger than sys.maxint, as long as the total length of the sequence + fits. E.g., range(2**100, 2**101, 2**100) is the following list: + [1267650600228229401496703205376L]. (SF patch #707427.) + +- Some horridly obscure problems were fixed involving interaction + between garbage collection and old-style classes with "ambitious" + getattr hooks. If an old-style instance didn't have a __del__ method, + but did have a __getattr__ hook, and the instance became reachable + only from an unreachable cycle, and the hook resurrected or deleted + unreachable objects when asked to resolve "__del__", anything up to + a segfault could happen. That's been repaired. + +- dict.pop now takes an optional argument specifying a default + value to return if the key is not in the dict. If a default is not + given and the key is not found, a KeyError will still be raised. + Parallel changes were made to UserDict.UserDict and UserDict.DictMixin. + [SF patch #693753] (contributed by Michael Stone.) + +- sys.getfilesystemencoding() was added to expose + Py_FileSystemDefaultEncoding. + +- New function sys.exc_clear() clears the current exception. This is + rarely needed, but can sometimes be useful to release objects + referenced by the traceback held in sys.exc_info()[2]. (SF patch + #693195.) + +- On 64-bit systems, a dictionary could contain duplicate long/int keys + if the key value was larger than 2**32. See SF bug #689659. + +- Fixed SF bug #663074. The codec system was using global static + variables to store internal data. As a result, any attempts to use the + unicode system with multiple active interpreters, or successive + interpreter executions, would fail. + +- "%c" % u"a" now returns a unicode string instead of raising a + TypeError. u"%c" % 0xffffffff now raises a OverflowError instead + of a ValueError to be consistent with "%c" % 256. See SF patch #710127. + +Extension modules +----------------- + +- The socket module now provides the functions inet_pton and inet_ntop + for converting between string and packed representation of IP + addresses. There is also a new module variable, has_ipv6, which is + True iff the current Python has IPv6 support. See SF patch #658327. + +- Tkinter wrappers around Tcl variables now pass objects directly + to Tcl, instead of first converting them to strings. + +- The .*? pattern in the re module is now special-cased to avoid the + recursion limit. (SF patch #720991 -- many thanks to Gary Herron + and Greg Chapman.) + +- New function sys.call_tracing() allows pdb to debug code + recursively. + +- New function gc.get_referents(obj) returns a list of objects + directly referenced by obj. In effect, it exposes what the object's + tp_traverse slot does, and can be helpful when debugging memory + leaks. + +- The iconv module has been removed from this release. + +- The platform-independent routines for packing floats in IEEE formats + (struct.pack's f, d codes; pickle and cPickle's protocol 1 + pickling of floats) ignored that rounding can cause a carry to + propagate. The worst consequence was that, in rare cases, f + could produce strings that, when unpacked again, were a factor of 2 + away from the original float. This has been fixed. See SF bug + #705836. + +- New function time.tzset() provides access to the C library tzset() + function, if supported. (SF patch #675422.) + +- Using createfilehandler, deletefilehandler, createtimerhandler functions + on Tkinter.tkinter (_tkinter module) no longer crashes the interpreter. + See SF bug #692416. + +- Modified the fcntl.ioctl() function to allow modification of a passed + mutable buffer (for details see the reference documentation). + +- Made user requested changes to the itertools module. + Subsumed the times() function into repeat(). + Added chain() and cycle(). + +- The rotor module is now deprecated; the encryption algorithm it uses + is not believed to be secure, and including crypto code with Python + has implications for exporting and importing it in various countries. + +- The socket module now always uses the _socketobject wrapper class, even on + platforms which have dup(2). The makefile() method is built directly + on top of the socket without duplicating the file descriptor, allowing + timeouts to work properly. + +Library +------- + +- New generator function os.walk() is an easy-to-use alternative to + os.path.walk(). See os module docs for details. os.path.walk() + isn't deprecated at this time, but may become deprecated in a + future release. + +- Added new module "platform" which provides a wide range of tools + for querying platform dependent features. + +- netrc now allows ASCII punctuation characters in passwords. + +- shelve now supports the optional writeback argument, and exposes + pickle protocol versions. + +- Several methods of nntplib.NNTP have grown an optional file argument + which specifies a file where to divert the command's output + (already supported by the body() method). (SF patch #720468) + +- The self-documenting XML server library DocXMLRPCServer was added. + +- Support for internationalized domain names has been added through + the 'idna' and 'punycode' encodings, the 'stringprep' module, the + 'mkstringprep' tool, and enhancements to the socket and httplib + modules. + +- htmlentitydefs has two new dictionaries: name2codepoint maps + HTML entity names to Unicode codepoints (as integers). + codepoint2name is the reverse mapping. See SF patch #722017. + +- pdb has a new command, "debug", which lets you step through + arbitrary code from the debugger's (pdb) prompt. + +- unittest.failUnlessEqual and its equivalent unittest.assertEqual now + return 'not a == b' rather than 'a != b'. This gives the desired + result for classes that define __eq__ without defining __ne__. + +- sgmllib now supports SGML marked sections, in particular the + MS Office extensions. + +- The urllib module now offers support for the iterator protocol. + SF patch 698520 contributed by Brett Cannon. + +- New module timeit provides a simple framework for timing the + execution speed of expressions and statements. + +- sets.Set objects now support mixed-type __eq__ and __ne__, instead + of raising TypeError. If x is a Set object and y is a non-Set object, + x == y is False, and x != y is True. This is akin to the change made + for mixed-type comparisons of datetime objects in 2.3a2; more info + about the rationale is in the NEWS entry for that. See also SF bug + report . + +- On Unix platforms, if os.listdir() is called with a Unicode argument, + it now returns Unicode strings. (This behavior was added earlier + to the Windows NT/2k/XP version of os.listdir().) + +- Distutils: both 'py_modules' and 'packages' keywords can now be specified + in core.setup(). Previously you could supply one or the other, but + not both of them. (SF patch #695090 from Bernhard Herzog) + +- New csv package makes it easy to read/write CSV files. + +- Module shlex has been extended to allow posix-like shell parsings, + including a split() function for easy spliting of quoted strings and + commands. An iterator interface was also implemented. + +Tools/Demos +----------- + +- New script combinerefs.py helps analyze new PYTHONDUMPREFS output. + See the module docstring for details. + +Build +----- + +- Fix problem building on OSF1 because the compiler only accepted + preprocessor directives that start in column 1. (SF bug #691793.) + +C API +----- + +- Added PyGC_Collect(), equivalent to calling gc.collect(). + +- PyThreadState_GetDict() was changed not to raise an exception or + issue a fatal error when no current thread state is available. This + makes it possible to print dictionaries when no thread is active. + +- LONG_LONG was renamed to PY_LONG_LONG. Extensions that use this and + need compatibility with previous versions can use this: + + #ifndef PY_LONG_LONG + #define PY_LONG_LONG LONG_LONG + #endif + +- Added PyObject_SelfIter() to fill the tp_iter slot for the + typical case where the method returns its self argument. + +- The extended type structure used for heap types (new-style + classes defined by Python code using a class statement) is now + exported from object.h as PyHeapTypeObject. (SF patch #696193.) + +New platforms +------------- + +None this time. + +Tests +----- + +- test_timeout now requires -u network to be passed to regrtest to run. + See SF bug #692988. + +Windows +------- + +- os.fsync() now exists on Windows, and calls the Microsoft _commit() + function. + +- New function winsound.MessageBeep() wraps the Win32 API + MessageBeep(). + +Mac +--- + +- os.listdir() now returns Unicode strings on MacOS X when called with + a Unicode argument. See the general news item under "Library". + +- A new method MacOS.WMAvailable() returns true if it is safe to access + the window manager, false otherwise. + +- EasyDialogs dialogs are now movable-modal, and if the application is + currently in the background they will ask to be moved to the foreground + before displaying. + +- OSA Scripting support has improved a lot, and gensuitemodule.py can now + be used by mere mortals. The documentation is now also more or less + complete. + +- The IDE (in a framework build) now includes introductory documentation + in Apple Help Viewer format. + + +What's New in Python 2.3 alpha 2? +================================= + +*Release date: 19-Feb-2003* + +Core and builtins +----------------- + +- Negative positions returned from PEP 293 error callbacks are now + treated as being relative to the end of the input string. Positions + that are out of bounds raise an IndexError. + +- sys.path[0] (the directory from which the script is loaded) is now + turned into an absolute pathname, unless it is the empty string. + (SF patch #664376.) + +- Finally fixed the bug in compile() and exec where a string ending + with an indented code block but no newline would raise SyntaxError. + This would have been a four-line change in parsetok.c... Except + codeop.py depends on this behavior, so a compilation flag had to be + invented that causes the tokenizer to revert to the old behavior; + this required extra changes to 2 .h files, 2 .c files, and 2 .py + files. (Fixes SF bug #501622.) + +- If a new-style class defines neither __new__ nor __init__, its + constructor would ignore all arguments. This is changed now: the + constructor refuses arguments in this case. This might break code + that worked under Python 2.2. The simplest fix is to add a no-op + __init__: ``def __init__(self, *args, **kw): pass``. + +- Through a bytecode optimizer bug (and I bet you didn't even know + Python *had* a bytecode optimizer :-), "unsigned" hex/oct constants + with a leading minus sign would come out with the wrong sign. + ("Unsigned" hex/oct constants are those with a face value in the + range sys.maxint+1 through sys.maxint*2+1, inclusive; these have + always been interpreted as negative numbers through sign folding.) + E.g. 0xffffffff is -1, and -(0xffffffff) is 1, but -0xffffffff would + come out as -4294967295. This was the case in Python 2.2 through + 2.2.2 and 2.3a1, and in Python 2.4 it will once again have that + value, but according to PEP 237 it really needs to be 1 now. This + will be backported to Python 2.2.3 a well. (SF #660455) + +- int(s, base) sometimes sign-folds hex and oct constants; it only + does this when base is 0 and s.strip() starts with a '0'. When the + sign is actually folded, as in int("0xffffffff", 0) on a 32-bit + machine, which returns -1, a FutureWarning is now issued; in Python + 2.4, this will return 4294967295L, as do int("+0xffffffff", 0) and + int("0xffffffff", 16) right now. (PEP 347) + +- super(X, x): x may now be a proxy for an X instance, i.e. + issubclass(x.__class__, X) but not issubclass(type(x), X). + +- isinstance(x, X): if X is a new-style class, this is now equivalent + to issubclass(type(x), X) or issubclass(x.__class__, X). Previously + only type(x) was tested. (For classic classes this was already the + case.) + +- compile(), eval() and the exec statement now fully support source code + passed as unicode strings. + +- int subclasses can be initialized with longs if the value fits in an int. + See SF bug #683467. + +- long(string, base) takes time linear in len(string) when base is a power + of 2 now. It used to take time quadratic in len(string). + +- filter returns now Unicode results for Unicode arguments. + +- raw_input can now return Unicode objects. + +- List objects' sort() method now accepts None as the comparison function. + Passing None is semantically identical to calling sort() with no + arguments. + +- Fixed crash when printing a subclass of str and __str__ returned self. + See SF bug #667147. + +- Fixed an invalid RuntimeWarning and an undetected error when trying + to convert a long integer into a float which couldn't fit. + See SF bug #676155. + +- Function objects now have a __module__ attribute that is bound to + the name of the module in which the function was defined. This + applies for C functions and methods as well as functions and methods + defined in Python. This attribute is used by pickle.whichmodule(), + which changes the behavior of whichmodule slightly. In Python 2.2 + whichmodule() returns "__main__" for functions that are not defined + at the top-level of a module (examples: methods, nested functions). + Now whichmodule() will return the proper module name. + +Extension modules +----------------- + +- operator.isNumberType() now checks that the object has a nb_int or + nb_float slot, rather than simply checking whether it has a non-NULL + tp_as_number pointer. + +- The imp module now has ways to acquire and release the "import + lock": imp.acquire_lock() and imp.release_lock(). Note: this is a + reentrant lock, so releasing the lock only truly releases it when + this is the last release_lock() call. You can check with + imp.lock_held(). (SF bug #580952 and patch #683257.) + +- Change to cPickle to match pickle.py (see below and PEP 307). + +- Fix some bugs in the parser module. SF bug #678518. + +- Thanks to Scott David Daniels, a subtle bug in how the zlib + extension implemented flush() was fixed. Scott also rewrote the + zlib test suite using the unittest module. (SF bug #640230 and + patch #678531.) + +- Added an itertools module containing high speed, memory efficient + looping constructs inspired by tools from Haskell and SML. + +- The SSL module now handles sockets with a timeout set correctly (SF + patch #675750, fixing SF bug #675552). + +- os/posixmodule has grown the sysexits.h constants (EX_OK and friends). + +- Fixed broken threadstate swap in readline that could cause fatal + errors when a readline hook was being invoked while a background + thread was active. (SF bugs #660476 and #513033.) + +- fcntl now exposes the strops.h I_* constants. + +- Fix a crash on Solaris that occurred when calling close() on + an mmap'ed file which was already closed. (SF patch #665913) + +- Fixed several serious bugs in the zipimport implementation. + +- datetime changes: + + The date class is now properly subclassable. (SF bug #720908) + + The datetime and datetimetz classes have been collapsed into a single + datetime class, and likewise the time and timetz classes into a single + time class. Previously, a datetimetz object with tzinfo=None acted + exactly like a datetime object, and similarly for timetz. This wasn't + enough of a difference to justify distinct classes, and life is simpler + now. + + today() and now() now round system timestamps to the closest + microsecond . This repairs an + irritation most likely seen on Windows systems. + + In dt.astimezone(tz), if tz.utcoffset(dt) returns a duration, + ValueError is raised if tz.dst(dt) returns None (2.3a1 treated it + as 0 instead, but a tzinfo subclass wishing to participate in + time zone conversion has to take a stand on whether it supports + DST; if you don't care about DST, then code dst() to return 0 minutes, + meaning that DST is never in effect). + + The tzinfo methods utcoffset() and dst() must return a timedelta object + (or None) now. In 2.3a1 they could also return an int or long, but that + was an unhelpfully redundant leftover from an earlier version wherein + they couldn't return a timedelta. TOOWTDI. + + The example tzinfo class for local time had a bug. It was replaced + by a later example coded by Guido. + + datetime.astimezone(tz) no longer raises an exception when the + input datetime has no UTC equivalent in tz. For typical "hybrid" time + zones (a single tzinfo subclass modeling both standard and daylight + time), this case can arise one hour per year, at the hour daylight time + ends. See new docs for details. In short, the new behavior mimics + the local wall clock's behavior of repeating an hour in local time. + + dt.astimezone() can no longer be used to convert between naive and aware + datetime objects. If you merely want to attach, or remove, a tzinfo + object, without any conversion of date and time members, use + dt.replace(tzinfo=whatever) instead, where "whatever" is None or a + tzinfo subclass instance. + + A new method tzinfo.fromutc(dt) can be overridden in tzinfo subclasses + to give complete control over how a UTC time is to be converted to + a local time. The default astimezone() implementation calls fromutc() + as its last step, so a tzinfo subclass can affect that too by overriding + fromutc(). It's expected that the default fromutc() implementation will + be suitable as-is for "almost all" time zone subclasses, but the + creativity of political time zone fiddling appears unbounded -- fromutc() + allows the highly motivated to emulate any scheme expressible in Python. + + datetime.now(): The optional tzinfo argument was undocumented (that's + repaired), and its name was changed to tz ("tzinfo" is overloaded enough + already). With a tz argument, now(tz) used to return the local date + and time, and attach tz to it, without any conversion of date and time + members. This was less than useful. Now now(tz) returns the current + date and time as local time in tz's time zone, akin to :: + + tz.fromutc(datetime.utcnow().replace(tzinfo=utc)) + + where "utc" is an instance of a tzinfo subclass modeling UTC. Without + a tz argument, now() continues to return the current local date and time, + as a naive datetime object. + + datetime.fromtimestamp(): Like datetime.now() above, this had less than + useful behavior when the optional tinzo argument was specified. See + also SF bug report . + + date and datetime comparison: In order to prevent comparison from + falling back to the default compare-object-addresses strategy, these + raised TypeError whenever they didn't understand the other object type. + They still do, except when the other object has a "timetuple" attribute, + in which case they return NotImplemented now. This gives other + datetime objects (e.g., mxDateTime) a chance to intercept the + comparison. + + date, time, datetime and timedelta comparison: When the exception + for mixed-type comparisons in the last paragraph doesn't apply, if + the comparison is == then False is returned, and if the comparison is + != then True is returned. Because dict lookup and the "in" operator + only invoke __eq__, this allows, for example, :: + + if some_datetime in some_sequence: + + and :: + + some_dict[some_timedelta] = whatever + + to work as expected, without raising TypeError just because the + sequence is heterogeneous, or the dict has mixed-type keys. [This + seems like a good idea to implement for all mixed-type comparisons + that don't want to allow falling back to address comparison.] + + The constructors building a datetime from a timestamp could raise + ValueError if the platform C localtime()/gmtime() inserted "leap + seconds". Leap seconds are ignored now. On such platforms, it's + possible to have timestamps that differ by a second, yet where + datetimes constructed from them are equal. + + The pickle format of date, time and datetime objects has changed + completely. The undocumented pickler and unpickler functions no + longer exist. The undocumented __setstate__() and __getstate__() + methods no longer exist either. + +Library +------- + +- The logging module was updated slightly; the WARN level was renamed + to WARNING, and the matching function/method warn() to warning(). + +- The pickle and cPickle modules were updated with a new pickling + protocol (documented by pickletools.py, see below) and several + extensions to the pickle customization API (__reduce__, __setstate__ + etc.). The copy module now uses more of the pickle customization + API to copy objects that don't implement __copy__ or __deepcopy__. + See PEP 307 for details. + +- The distutils "register" command now uses http://www.python.org/pypi + as the default repository. (See PEP 301.) + +- the platform dependent path related variables sep, altsep, extsep, + pathsep, curdir, pardir and defpath are now defined in the platform + dependent path modules (e.g. ntpath.py) rather than os.py, so these + variables are now available via os.path. They continue to be + available from the os module. + (see ). + +- array.array was added to the types repr.py knows about (see + ). + +- The new pickletools.py contains lots of documentation about pickle + internals, and supplies some helpers for working with pickles, such as + a symbolic pickle disassembler. + +- Xmlrpclib.py now supports the builtin boolean type. + +- py_compile has a new 'doraise' flag and a new PyCompileError + exception. + +- SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler + class. + +- The sets module now raises TypeError in __cmp__, to clarify that + sets are not intended to be three-way-compared; the comparison + operators are overloaded as subset/superset tests. + +- Bastion.py and rexec.py are disabled. These modules are not safe in + Python 2.2. or 2.3. + +- realpath is now exported when doing ``from poxixpath import *``. + It is also exported for ntpath, macpath, and os2emxpath. + See SF bug #659228. + +- New module tarfile from Lars Gustäbel provides a comprehensive interface + to tar archive files with transparent gzip and bzip2 compression. + See SF patch #651082. + +- urlparse can now parse imap:// URLs. See SF feature request #618024. + +- Tkinter.Canvas.scan_dragto() provides an optional parameter to support + the gain value which is passed to Tk. SF bug# 602259. + +- Fix logging.handlers.SysLogHandler protocol when using UNIX domain sockets. + See SF patch #642974. + +- The dospath module was deleted. Use the ntpath module when manipulating + DOS paths from other platforms. + +Tools/Demos +----------- + +- Two new scripts (db2pickle.py and pickle2db.py) were added to the + Tools/scripts directory to facilitate conversion from the old bsddb module + to the new one. While the user-visible API of the new module is + compatible with the old one, it's likely that the version of the + underlying database library has changed. To convert from the old library, + run the db2pickle.py script using the old version of Python to convert it + to a pickle file. After upgrading Python, run the pickle2db.py script + using the new version of Python to reconstitute your database. For + example: + + % python2.2 db2pickle.py -h some.db > some.pickle + % python2.3 pickle2db.py -h some.db.new < some.pickle + + Run the scripts without any args to get a usage message. + + +Build +----- + +- The audio driver tests (test_ossaudiodev.py and + test_linuxaudiodev.py) are no longer run by default. This is + because they don't always work, depending on your hardware and + software. To run these tests, you must use an invocation like :: + + ./python Lib/test/regrtest.py -u audio test_ossaudiodev + +- On systems which build using the configure script, compiler flags which + used to be lumped together using the OPT flag have been split into two + groups, OPT and BASECFLAGS. OPT is meant to carry just optimization- and + debug-related flags like "-g" and "-O3". BASECFLAGS is meant to carry + compiler flags that are required to get a clean compile. On some + platforms (many Linux flavors in particular) BASECFLAGS will be empty by + default. On others, such as Mac OS X and SCO, it will contain required + flags. This change allows people building Python to override OPT without + fear of clobbering compiler flags which are required to get a clean build. + +- On Darwin/Mac OS X platforms, /sw/lib and /sw/include are added to the + relevant search lists in setup.py. This allows users building Python to + take advantage of the many packages available from the fink project + . + +- A new Makefile target, scriptsinstall, installs a number of useful scripts + from the Tools/scripts directory. + +C API +----- + +- PyEval_GetFrame() is now declared to return a ``PyFrameObject *`` + instead of a plain ``PyObject *``. (SF patch #686601.) + +- PyNumber_Check() now checks that the object has a nb_int or nb_float + slot, rather than simply checking whether it has a non-NULL + tp_as_number pointer. + +- A C type that inherits from a base type that defines tp_as_buffer + will now inherit the tp_as_buffer pointer if it doesn't define one. + (SF #681367) + +- The PyArg_Parse functions now issue a DeprecationWarning if a float + argument is provided when an integer is specified (this affects the 'b', + 'B', 'h', 'H', 'i', and 'l' codes). Future versions of Python will + raise a TypeError. + +Tests +----- + +- Several tests weren't being run from regrtest.py (test_timeout.py, + test_tarfile.py, test_netrc.py, test_multifile.py, + test_importhooks.py and test_imp.py). Now they are. (Note to + developers: please read Lib/test/README when creating a new test, to + make sure to do it right! All tests need to use either unittest or + pydoc.) + +- Added test_posix.py, a test suite for the posix module. + +- Added test_hexoct.py, a test suite for hex/oct constant folding. + +Windows +------- + +- The timeout code for socket connect() didn't work right; this has + now been fixed. test_timeout.py should pass (at least most of the + time). + +- distutils' msvccompiler class now passes the preprocessor options to + the resource compiler. See SF patch #669198. + +- The bsddb module now ships with Sleepycat's 4.1.25.NC, the latest + release without strong cryptography. + +- sys.path[0], if it contains a directory name, is now always an + absolute pathname. (SF patch #664376.) + +- The new logging package is now installed by the Windows installer. It + wasn't in 2.3a1 due to oversight. + +Mac +--- + +- There are new dialogs EasyDialogs.AskFileForOpen, AskFileForSave + and AskFolder. The old macfs.StandardGetFile and friends are deprecated. + +- Most of the standard library now uses pathnames or FSRefs in preference + of FSSpecs, and use the underlying Carbon.File and Carbon.Folder modules + in stead of macfs. macfs will probably be deprecated in the future. + +- Type Carbon.File.FSCatalogInfo and supporting methods have been implemented. + This also makes macfs.FSSpec.SetDates() work again. + +- There is a new module pimp, the package install manager for Python, and + accompanying applet PackageManager. These allow you to easily download + and install pretested extension packages either in source or binary + form. Only in MacPython-OSX. + +- Applets are now built with bundlebuilder in MacPython-OSX, which should make + them more robust and also provides a path towards BuildApplication. The + downside of this change is that applets can no longer be run from the + Terminal window, this will hopefully be fixed in the 2.3b1. + + +What's New in Python 2.3 alpha 1? +================================= + +*Release date: 31-Dec-2002* + +Type/class unification and new-style classes +-------------------------------------------- + +- One can now assign to __bases__ and __name__ of new-style classes. + +- dict() now accepts keyword arguments so that dict(one=1, two=2) + is the equivalent of {"one": 1, "two": 2}. Accordingly, + the existing (but undocumented) 'items' keyword argument has + been eliminated. This means that dict(items=someMapping) now has + a different meaning than before. + +- int() now returns a long object if the argument is outside the + integer range, so int("4" * 1000), int(1e200) and int(1L<<1000) will + all return long objects instead of raising an OverflowError. + +- Assignment to __class__ is disallowed if either the old or the new + class is a statically allocated type object (such as defined by an + extension module). This prevents anomalies like 2.__class__ = bool. + +- New-style object creation and deallocation have been sped up + significantly; they are now faster than classic instance creation + and deallocation. + +- The __slots__ variable can now mention "private" names, and the + right thing will happen (e.g. __slots__ = ["__foo"]). + +- The built-ins slice() and buffer() are now callable types. The + types classobj (formerly class), code, function, instance, and + instancemethod (formerly instance-method), which have no built-in + names but are accessible through the types module, are now also + callable. The type dict-proxy is renamed to dictproxy. + +- Cycles going through the __class__ link of a new-style instance are + now detected by the garbage collector. + +- Classes using __slots__ are now properly garbage collected. + [SF bug 519621] + +- Tightened the __slots__ rules: a slot name must be a valid Python + identifier. + +- The constructor for the module type now requires a name argument and + takes an optional docstring argument. Previously, this constructor + ignored its arguments. As a consequence, deriving a class from a + module (not from the module type) is now illegal; previously this + created an unnamed module, just like invoking the module type did. + [SF bug 563060] + +- A new type object, 'basestring', is added. This is a common base type + for 'str' and 'unicode', and can be used instead of + types.StringTypes, e.g. to test whether something is "a string": + isinstance(x, basestring) is True for Unicode and 8-bit strings. This + is an abstract base class and cannot be instantiated directly. + +- Changed new-style class instantiation so that when C's __new__ + method returns something that's not a C instance, its __init__ is + not called. [SF bug #537450] + +- Fixed super() to work correctly with class methods. [SF bug #535444] + +- If you try to pickle an instance of a class that has __slots__ but + doesn't define or override __getstate__, a TypeError is now raised. + This is done by adding a bozo __getstate__ to the class that always + raises TypeError. (Before, this would appear to be pickled, but the + state of the slots would be lost.) + +Core and builtins +----------------- + +- Import from zipfiles is now supported. The name of a zipfile placed + on sys.path causes the import statement to look for importable Python + modules (with .py, pyc and .pyo extensions) and packages inside the + zipfile. The zipfile import follows the specification (though not + the sample implementation) of PEP 273. The semantics of __path__ are + compatible with those that have been implemented in Jython since + Jython 2.1. + +- PEP 302 has been accepted. Although it was initially developed to + support zipimport, it offers a new, general import hook mechanism. + Several new variables have been added to the sys module: + sys.meta_path, sys.path_hooks, and sys.path_importer_cache; these + make extending the import statement much more convenient than + overriding the __import__ built-in function. For a description of + these, see PEP 302. + +- A frame object's f_lineno attribute can now be written to from a + trace function to change which line will execute next. A command to + exploit this from pdb has been added. [SF patch #643835] + +- The _codecs support module for codecs.py was turned into a builtin + module to assure that at least the builtin codecs are available + to the Python parser for source code decoding according to PEP 263. + +- issubclass now supports a tuple as the second argument, just like + isinstance does. ``issubclass(X, (A, B))`` is equivalent to + ``issubclass(X, A) or issubclass(X, B)``. + +- Thanks to Armin Rigo, the last known way to provoke a system crash + by cleverly arranging for a comparison function to mutate a list + during a list.sort() operation has been fixed. The effect of + attempting to mutate a list, or even to inspect its contents or + length, while a sort is in progress, is not defined by the language. + The C implementation of Python 2.3 attempts to detect mutations, + and raise ValueError if one occurs, but there's no guarantee that + all mutations will be caught, or that any will be caught across + releases or implementations. + +- Unicode file name processing for Windows (PEP 277) is implemented. + All platforms now have an os.path.supports_unicode_filenames attribute, + which is set to True on Windows NT/2000/XP, and False elsewhere. + +- Codec error handling callbacks (PEP 293) are implemented. + Error handling in unicode.encode or str.decode can now be customized. + +- A subtle change to the semantics of the built-in function intern(): + interned strings are no longer immortal. You must keep a reference + to the return value intern() around to get the benefit. + +- Use of 'None' as a variable, argument or attribute name now + issues a SyntaxWarning. In the future, None may become a keyword. + +- SET_LINENO is gone. co_lnotab is now consulted to determine when to + call the trace function. C code that accessed f_lineno should call + PyCode_Addr2Line instead (f_lineno is still there, but only kept up + to date when there is a trace function set). + +- There's a new warning category, FutureWarning. This is used to warn + about a number of situations where the value or sign of an integer + result will change in Python 2.4 as a result of PEP 237 (integer + unification). The warnings implement stage B0 mentioned in that + PEP. The warnings are about the following situations: + + - Octal and hex literals without 'L' prefix in the inclusive range + [0x80000000..0xffffffff]; these are currently negative ints, but + in Python 2.4 they will be positive longs with the same bit + pattern. + + - Left shifts on integer values that cause the outcome to lose + bits or have a different sign than the left operand. To be + precise: x< -*-" in the first + or second line of a Python source file indicates the encoding. + +- list.sort() has a new implementation. While cross-platform results + may vary, and in data-dependent ways, this is much faster on many + kinds of partially ordered lists than the previous implementation, + and reported to be just as fast on randomly ordered lists on + several major platforms. This sort is also stable (if A==B and A + precedes B in the list at the start, A precedes B after the sort too), + although the language definition does not guarantee stability. A + potential drawback is that list.sort() may require temp space of + len(list)*2 bytes (``*4`` on a 64-bit machine). It's therefore possible + for list.sort() to raise MemoryError now, even if a comparison function + does not. See for full details. + +- All standard iterators now ensure that, once StopIteration has been + raised, all future calls to next() on the same iterator will also + raise StopIteration. There used to be various counterexamples to + this behavior, which could caused confusion or subtle program + breakage, without any benefits. (Note that this is still an + iterator's responsibility; the iterator framework does not enforce + this.) + +- Ctrl+C handling on Windows has been made more consistent with + other platforms. KeyboardInterrupt can now reliably be caught, + and Ctrl+C at an interactive prompt no longer terminates the + process under NT/2k/XP (it never did under Win9x). Ctrl+C will + interrupt time.sleep() in the main thread, and any child processes + created via the popen family (on win2k; we can't make win9x work + reliably) are also interrupted (as generally happens on for Linux/Unix.) + [SF bugs 231273, 439992 and 581232] + +- sys.getwindowsversion() has been added on Windows. This + returns a tuple with information about the version of Windows + currently running. + +- Slices and repetitions of buffer objects now consistently return + a string. Formerly, strings would be returned most of the time, + but a buffer object would be returned when the repetition count + was one or when the slice range was all inclusive. + +- Unicode objects in sys.path are no longer ignored but treated + as directory names. + +- Fixed string.startswith and string.endswith builtin methods + so they accept negative indices. [SF bug 493951] + +- Fixed a bug with a continue inside a try block and a yield in the + finally clause. [SF bug 567538] + +- Most builtin sequences now support "extended slices", i.e. slices + with a third "stride" parameter. For example, "hello world"[::-1] + gives "dlrow olleh". + +- A new warning PendingDeprecationWarning was added to provide + direction on features which are in the process of being deprecated. + The warning will not be printed by default. To see the pending + deprecations, use -Walways::PendingDeprecationWarning:: + as a command line option or warnings.filterwarnings() in code. + +- Deprecated features of xrange objects have been removed as + promised. The start, stop, and step attributes and the tolist() + method no longer exist. xrange repetition and slicing have been + removed. + +- New builtin function enumerate(x), from PEP 279. Example: + enumerate("abc") is an iterator returning (0,"a"), (1,"b"), (2,"c"). + The argument can be an arbitrary iterable object. + +- The assert statement no longer tests __debug__ at runtime. This means + that assert statements cannot be disabled by assigning a false value + to __debug__. + +- A method zfill() was added to str and unicode, that fills a numeric + string to the left with zeros. For example, + "+123".zfill(6) -> "+00123". + +- Complex numbers supported divmod() and the // and % operators, but + these make no sense. Since this was documented, they're being + deprecated now. + +- String and unicode methods lstrip(), rstrip() and strip() now take + an optional argument that specifies the characters to strip. For + example, "Foo!!!?!?!?".rstrip("?!") -> "Foo". + +- There's a new dictionary constructor (a class method of the dict + class), dict.fromkeys(iterable, value=None). It constructs a + dictionary with keys taken from the iterable and all values set to a + single value. It can be used for building sets and for removing + duplicates from sequences. + +- Added a new dict method pop(key). This removes and returns the + value corresponding to key. [SF patch #539949] + +- A new built-in type, bool, has been added, as well as built-in + names for its two values, True and False. Comparisons and sundry + other operations that return a truth value have been changed to + return a bool instead. Read PEP 285 for an explanation of why this + is backward compatible. + +- Fixed two bugs reported as SF #535905: under certain conditions, + deallocating a deeply nested structure could cause a segfault in the + garbage collector, due to interaction with the "trashcan" code; + access to the current frame during destruction of a local variable + could access a pointer to freed memory. + +- The optional object allocator ("pymalloc") has been enabled by + default. The recommended practice for memory allocation and + deallocation has been streamlined. A header file is included, + Misc/pymemcompat.h, which can be bundled with 3rd party extensions + and lets them use the same API with Python versions from 1.5.2 + onwards. + +- PyErr_Display will provide file and line information for all exceptions + that have an attribute print_file_and_line, not just SyntaxErrors. + +- The UTF-8 codec will now encode and decode Unicode surrogates + correctly and without raising exceptions for unpaired ones. + +- Universal newlines (PEP 278) is implemented. Briefly, using 'U' + instead of 'r' when opening a text file for reading changes the line + ending convention so that any of '\r', '\r\n', and '\n' is + recognized (even mixed in one file); all three are converted to + '\n', the standard Python line end character. + +- file.xreadlines() now raises a ValueError if the file is closed: + Previously, an xreadlines object was returned which would raise + a ValueError when the xreadlines.next() method was called. + +- sys.exit() inadvertently allowed more than one argument. + An exception will now be raised if more than one argument is used. + +- Changed evaluation order of dictionary literals to conform to the + general left to right evaluation order rule. Now {f1(): f2()} will + evaluate f1 first. + +- Fixed bug #521782: when a file was in non-blocking mode, file.read() + could silently lose data or wrongly throw an unknown error. + +- The sq_repeat, sq_inplace_repeat, sq_concat and sq_inplace_concat + slots are now always tried after trying the corresponding nb_* slots. + This fixes a number of minor bugs (see bug #624807). + +- Fix problem with dynamic loading on 64-bit AIX (see bug #639945). + +Extension modules +----------------- + +- Added three operators to the operator module: + operator.pow(a,b) which is equivalent to: a**b. + operator.is_(a,b) which is equivalent to: a is b. + operator.is_not(a,b) which is equivalent to: a is not b. + +- posix.openpty now works on all systems that have /dev/ptmx. + +- A module zipimport exists to support importing code from zip + archives. + +- The new datetime module supplies classes for manipulating dates and + times. The basic design came from the Zope "fishbowl process", and + favors practical commercial applications over calendar esoterica. See + + http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage + +- _tkinter now returns Tcl objects, instead of strings. Objects which + have Python equivalents are converted to Python objects, other objects + are wrapped. This can be configured through the wantobjects method, + or Tkinter.wantobjects. + +- The PyBSDDB wrapper around the Sleepycat Berkeley DB library has + been added as the package bsddb. The traditional bsddb module is + still available in source code, but not built automatically anymore, + and is now named bsddb185. This supports Berkeley DB versions from + 3.0 to 4.1. For help converting your databases from the old module (which + probably used an obsolete version of Berkeley DB) to the new module, see + the db2pickle.py and pickle2db.py scripts described in the Tools/Demos + section above. + +- unicodedata was updated to Unicode 3.2. It supports normalization + and names for Hangul syllables and CJK unified ideographs. + +- resource.getrlimit() now returns longs instead of ints. + +- readline now dynamically adjusts its input/output stream if + sys.stdin/stdout changes. + +- The _tkinter module (and hence Tkinter) has dropped support for + Tcl/Tk 8.0 and 8.1. Only Tcl/Tk versions 8.2, 8.3 and 8.4 are + supported. + +- cPickle.BadPickleGet is now a class. + +- The time stamps in os.stat_result are floating point numbers + after stat_float_times has been called. + +- If the size passed to mmap.mmap() is larger than the length of the + file on non-Windows platforms, a ValueError is raised. [SF bug 585792] + +- The xreadlines module is slated for obsolescence. + +- The strptime function in the time module is now always available (a + Python implementation is used when the C library doesn't define it). + +- The 'new' module is no longer an extension, but a Python module that + only exists for backwards compatibility. Its contents are no longer + functions but callable type objects. + +- The bsddb.*open functions can now take 'None' as a filename. + This will create a temporary in-memory bsddb that won't be + written to disk. + +- posix.getloadavg, posix.lchown, posix.killpg, posix.mknod, and + posix.getpgid have been added where available. + +- The locale module now exposes the C library's gettext interface. It + also has a new function getpreferredencoding. + +- A security hole ("double free") was found in zlib-1.1.3, a popular + third party compression library used by some Python modules. The + hole was quickly plugged in zlib-1.1.4, and the Windows build of + Python now ships with zlib-1.1.4. + +- pwd, grp, and resource return enhanced tuples now, with symbolic + field names. + +- array.array is now a type object. A new format character + 'u' indicates Py_UNICODE arrays. For those, .tounicode and + .fromunicode methods are available. Arrays now support __iadd__ + and __imul__. + +- dl now builds on every system that has dlfcn.h. Failure in case + of sizeof(int)!=sizeof(long)!=sizeof(void*) is delayed until dl.open + is called. + +- The sys module acquired a new attribute, api_version, which evaluates + to the value of the PYTHON_API_VERSION macro with which the + interpreter was compiled. + +- Fixed bug #470582: sre module would return a tuple (None, 'a', 'ab') + when applying the regular expression '^((a)c)?(ab)$' on 'ab'. It now + returns (None, None, 'ab'), as expected. Also fixed handling of + lastindex/lastgroup match attributes in similar cases. For example, + when running the expression r'(a)(b)?b' over 'ab', lastindex must be + 1, not 2. + +- Fixed bug #581080: sre scanner was not checking the buffer limit + before increasing the current pointer. This was creating an infinite + loop in the search function, once the pointer exceeded the buffer + limit. + +- The os.fdopen function now enforces a file mode starting with the + letter 'r', 'w' or 'a', otherwise a ValueError is raised. This fixes + bug #623464. + +- The linuxaudiodev module is now deprecated; it is being replaced by + ossaudiodev. The interface has been extended to cover a lot more of + OSS (see www.opensound.com), including most DSP ioctls and the + OSS mixer API. Documentation forthcoming in 2.3a2. + +Library +------- + +- imaplib.py now supports SSL (Tino Lange and Piers Lauder). + +- Freeze's modulefinder.py has been moved to the standard library; + slightly improved so it will issue less false missing submodule + reports (see sf path #643711 for details). Documentation will follow + with Python 2.3a2. + +- os.path exposes getctime. + +- unittest.py now has two additional methods called assertAlmostEqual() + and failIfAlmostEqual(). They implement an approximate comparison + by rounding the difference between the two arguments and comparing + the result to zero. Approximate comparison is essential for + unit tests of floating point results. + +- calendar.py now depends on the new datetime module rather than + the time module. As a result, the range of allowable dates + has been increased. + +- pdb has a new 'j(ump)' command to select the next line to be + executed. + +- The distutils created windows installers now can run a + postinstallation script. + +- doctest.testmod can now be called without argument, which means to + test the current module. + +- When canceling a server that implemented threading with a keyboard + interrupt, the server would shut down but not terminate (waiting on + client threads). A new member variable, daemon_threads, was added to + the ThreadingMixIn class in SocketServer.py to make it explicit that + this behavior needs to be controlled. + +- A new module, optparse, provides a fancy alternative to getopt for + command line parsing. It is a slightly modified version of Greg + Ward's Optik package. + +- UserDict.py now defines a DictMixin class which defines all dictionary + methods for classes that already have a minimum mapping interface. + This greatly simplifies writing classes that need to be substitutable + for dictionaries (such as the shelve module). + +- shelve.py now subclasses from UserDict.DictMixin. Now shelve supports + all dictionary methods. This eases the transition to persistent + storage for scripts originally written with dictionaries in mind. + +- shelve.open and the various classes in shelve.py now accept an optional + binary flag, which defaults to False. If True, the values stored in the + shelf are binary pickles. + +- A new package, logging, implements the logging API defined by PEP + 282. The code is written by Vinay Sajip. + +- StreamReader, StreamReaderWriter and StreamRecoder in the codecs + modules are iterators now. + +- gzip.py now handles files exceeding 2GB. Files over 4GB also work + now (provided the OS supports it, and Python is configured with large + file support), but in that case the underlying gzip file format can + record only the least-significant 32 bits of the file size, so that + some tools working with gzipped files may report an incorrect file + size. + +- xml.sax.saxutils.unescape has been added, to replace entity references + with their entity value. + +- Queue.Queue.{put,get} now support an optional timeout argument. + +- Various features of Tk 8.4 are exposed in Tkinter.py. The multiple + option of tkFileDialog is exposed as function askopenfile{,name}s. + +- Various configure methods of Tkinter have been stream-lined, so that + tag_configure, image_configure, window_configure now return a + dictionary when invoked with no argument. + +- Importing the readline module now no longer has the side effect of + calling setlocale(LC_CTYPE, ""). The initial "C" locale, or + whatever locale is explicitly set by the user, is preserved. If you + want repr() of 8-bit strings in your preferred encoding to preserve + all printable characters of that encoding, you have to add the + following code to your $PYTHONSTARTUP file or to your application's + main(): + + import locale + locale.setlocale(locale.LC_CTYPE, "") + +- shutil.move was added. shutil.copytree now reports errors as an + exception at the end, instead of printing error messages. + +- Encoding name normalization was generalized to not only + replace hyphens with underscores, but also all other non-alphanumeric + characters (with the exception of the dot which is used for Python + package names during lookup). The aliases.py mapping was updated + to the new standard. + +- mimetypes has two new functions: guess_all_extensions() which + returns a list of all known extensions for a mime type, and + add_type() which adds one mapping between a mime type and + an extension to the database. + +- New module: sets, defines the class Set that implements a mutable + set type using the keys of a dict to represent the set. There's + also a class ImmutableSet which is useful when you need sets of sets + or when you need to use sets as dict keys, and a class BaseSet which + is the base class of the two. + +- Added random.sample(population,k) for random sampling without replacement. + Returns a k length list of unique elements chosen from the population. + +- random.randrange(-sys.maxint-1, sys.maxint) no longer raises + OverflowError. That is, it now accepts any combination of 'start' + and 'stop' arguments so long as each is in the range of Python's + bounded integers. + +- Thanks to Raymond Hettinger, random.random() now uses a new core + generator. The Mersenne Twister algorithm is implemented in C, + threadsafe, faster than the previous generator, has an astronomically + large period (2**19937-1), creates random floats to full 53-bit + precision, and may be the most widely tested random number generator + in existence. + + The random.jumpahead(n) method has different semantics for the new + generator. Instead of jumping n steps ahead, it uses n and the + existing state to create a new state. This means that jumpahead() + continues to support multi-threaded code needing generators of + non-overlapping sequences. However, it will break code which relies + on jumpahead moving a specific number of steps forward. + + The attributes random.whseed and random.__whseed have no meaning for + the new generator. Code using these attributes should switch to a + new class, random.WichmannHill which is provided for backward + compatibility and to make an alternate generator available. + +- New "algorithms" module: heapq, implements a heap queue. Thanks to + Kevin O'Connor for the code and François Pinard for an entertaining + write-up explaining the theory and practical uses of heaps. + +- New encoding for the Palm OS character set: palmos. + +- binascii.crc32() and the zipfile module had problems on some 64-bit + platforms. These have been fixed. On a platform with 8-byte C longs, + crc32() now returns a signed-extended 4-byte result, so that its value + as a Python int is equal to the value computed a 32-bit platform. + +- xml.dom.minidom.toxml and toprettyxml now take an optional encoding + argument. + +- Some fixes in the copy module: when an object is copied through its + __reduce__ method, there was no check for a __setstate__ method on + the result [SF patch 565085]; deepcopy should treat instances of + custom metaclasses the same way it treats instances of type 'type' + [SF patch 560794]. + +- Sockets now support timeout mode. After s.settimeout(T), where T is + a float expressing seconds, subsequent operations raise an exception + if they cannot be completed within T seconds. To disable timeout + mode, use s.settimeout(None). There's also a module function, + socket.setdefaulttimeout(T), which sets the default for all sockets + created henceforth. + +- getopt.gnu_getopt was added. This supports GNU-style option + processing, where options can be mixed with non-option arguments. + +- Stop using strings for exceptions. String objects used for + exceptions are now classes deriving from Exception. The objects + changed were: Tkinter.TclError, bdb.BdbQuit, macpath.norm_error, + tabnanny.NannyNag, and xdrlib.Error. + +- Constants BOM_UTF8, BOM_UTF16, BOM_UTF16_LE, BOM_UTF16_BE, + BOM_UTF32, BOM_UTF32_LE and BOM_UTF32_BE that represent the Byte + Order Mark in UTF-8, UTF-16 and UTF-32 encodings for little and + big endian systems were added to the codecs module. The old names + BOM32_* and BOM64_* were off by a factor of 2. + +- Added conversion functions math.degrees() and math.radians(). + +- math.log() now takes an optional argument: math.log(x[, base]). + +- ftplib.retrlines() now tests for callback is None rather than testing + for False. Was causing an error when given a callback object which + was callable but also returned len() as zero. The change may + create new breakage if the caller relied on the undocumented behavior + and called with callback set to [] or some other False value not + identical to None. + +- random.gauss() uses a piece of hidden state used by nothing else, + and the .seed() and .whseed() methods failed to reset it. In other + words, setting the seed didn't completely determine the sequence of + results produced by random.gauss(). It does now. Programs repeatedly + mixing calls to a seed method with calls to gauss() may see different + results now. + +- The pickle.Pickler class grew a clear_memo() method to mimic that + provided by cPickle.Pickler. + +- difflib's SequenceMatcher class now does a dynamic analysis of + which elements are so frequent as to constitute noise. For + comparing files as sequences of lines, this generally works better + than the IS_LINE_JUNK function, and function ndiff's linejunk + argument defaults to None now as a result. A happy benefit is + that SequenceMatcher may run much faster now when applied + to large files with many duplicate lines (for example, C program + text with lots of repeated "}" and "return NULL;" lines). + +- New Text.dump() method in Tkinter module. + +- New distutils commands for building packagers were added to + support pkgtool on Solaris and swinstall on HP-UX. + +- distutils now has a new abstract binary packager base class + command/bdist_packager, which simplifies writing packagers. + This will hopefully provide the missing bits to encourage + people to submit more packagers, e.g. for Debian, FreeBSD + and other systems. + +- The UTF-16, -LE and -BE stream readers now raise a + NotImplementedError for all calls to .readline(). Previously, they + used to just produce garbage or fail with an encoding error -- + UTF-16 is a 2-byte encoding and the C lib's line reading APIs don't + work well with these. + +- compileall now supports quiet operation. + +- The BaseHTTPServer now implements optional HTTP/1.1 persistent + connections. + +- socket module: the SSL support was broken out of the main + _socket module C helper and placed into a new _ssl helper + which now gets imported by socket.py if available and working. + +- encodings package: added aliases for all supported IANA character + sets + +- ftplib: to safeguard the user's privacy, anonymous login will use + "anonymous@" as default password, rather than the real user and host + name. + +- webbrowser: tightened up the command passed to os.system() so that + arbitrary shell code can't be executed because a bogus URL was + passed in. + +- gettext.translation has an optional fallback argument, and + gettext.find an optional all argument. Translations will now fallback + on a per-message basis. The module supports plural forms, by means + of gettext.[d]ngettext and Translation.[u]ngettext. + +- distutils bdist commands now offer a --skip-build option. + +- warnings.warn now accepts a Warning instance as first argument. + +- The xml.sax.expatreader.ExpatParser class will no longer create + circular references by using itself as the locator that gets passed + to the content handler implementation. [SF bug #535474] + +- The email.Parser.Parser class now properly parses strings regardless + of their line endings, which can be any of \r, \n, or \r\n (CR, LF, + or CRLF). Also, the Header class's constructor default arguments + has changed slightly so that an explicit maxlinelen value is always + honored, and so unicode conversion error handling can be specified. + +- distutils' build_ext command now links C++ extensions with the C++ + compiler available in the Makefile or CXX environment variable, if + running under \*nix. + +- New module bz2: provides a comprehensive interface for the bz2 compression + library. It implements a complete file interface, one-shot (de)compression + functions, and types for sequential (de)compression. + +- New pdb command 'pp' which is like 'p' except that it pretty-prints + the value of its expression argument. + +- Now bdist_rpm distutils command understands a verify_script option in + the config file, including the contents of the referred filename in + the "%verifyscript" section of the rpm spec file. + +- Fixed bug #495695: webbrowser module would run graphic browsers in a + unix environment even if DISPLAY was not set. Also, support for + skipstone browser was included. + +- Fixed bug #636769: rexec would run unallowed code if subclasses of + strings were used as parameters for certain functions. + +Tools/Demos +----------- + +- pygettext.py now supports globbing on Windows, and accepts module + names in addition to accepting file names. + +- The SGI demos (Demo/sgi) have been removed. Nobody thought they + were interesting any more. (The SGI library modules and extensions + are still there; it is believed that at least some of these are + still used and useful.) + +- IDLE supports the new encoding declarations (PEP 263); it can also + deal with legacy 8-bit files if they use the locale's encoding. It + allows non-ASCII strings in the interactive shell and executes them + in the locale's encoding. + +- freeze.py now produces binaries which can import shared modules, + unlike before when this failed due to missing symbol exports in + the generated binary. + +Build +----- + +- On Unix, IDLE is now installed automatically. + +- The fpectl module is not built by default; it's dangerous or useless + except in the hands of experts. + +- The public Python C API will generally be declared using PyAPI_FUNC + and PyAPI_DATA macros, while Python extension module init functions + will be declared with PyMODINIT_FUNC. DL_EXPORT/DL_IMPORT macros + are deprecated. + +- A bug was fixed that could cause COUNT_ALLOCS builds to segfault, or + get into infinite loops, when a new-style class got garbage-collected. + Unfortunately, to avoid this, the way COUNT_ALLOCS works requires + that new-style classes be immortal in COUNT_ALLOCS builds. Note that + COUNT_ALLOCS is not enabled by default, in either release or debug + builds, and that new-style classes are immortal only in COUNT_ALLOCS + builds. + +- Compiling out the cyclic garbage collector is no longer an option. + The old symbol WITH_CYCLE_GC is now ignored, and Python.h arranges + that it's always defined (for the benefit of any extension modules + that may be conditionalizing on it). A bonus is that any extension + type participating in cyclic gc can choose to participate in the + Py_TRASHCAN mechanism now too; in the absence of cyclic gc, this used + to require editing the core to teach the trashcan mechanism about the + new type. + +- According to Annex F of the current C standard, + + The Standard C macro HUGE_VAL and its float and long double analogs, + HUGE_VALF and HUGE_VALL, expand to expressions whose values are + positive infinities. + + Python only uses the double HUGE_VAL, and only to #define its own symbol + Py_HUGE_VAL. Some platforms have incorrect definitions for HUGE_VAL. + pyport.h used to try to worm around that, but the workarounds triggered + other bugs on other platforms, so we gave up. If your platform defines + HUGE_VAL incorrectly, you'll need to #define Py_HUGE_VAL to something + that works on your platform. The only instance of this I'm sure about + is on an unknown subset of Cray systems, described here: + + http://www.cray.com/swpubs/manuals/SN-2194_2.0/html-SN-2194_2.0/x3138.htm + + Presumably 2.3a1 breaks such systems. If anyone uses such a system, help! + +- The configure option --without-doc-strings can be used to remove the + doc strings from the builtin functions and modules; this reduces the + size of the executable. + +- The universal newlines option (PEP 278) is on by default. On Unix + it can be disabled by passing --without-universal-newlines to the + configure script. On other platforms, remove + WITH_UNIVERSAL_NEWLINES from pyconfig.h. + +- On Unix, a shared libpython2.3.so can be created with --enable-shared. + +- All uses of the CACHE_HASH, INTERN_STRINGS, and DONT_SHARE_SHORT_STRINGS + preprocessor symbols were eliminated. The internal decisions they + controlled stopped being experimental long ago. + +- The tools used to build the documentation now work under Cygwin as + well as Unix. + +- The bsddb and dbm module builds have been changed to try and avoid version + skew problems and disable linkage with Berkeley DB 1.85 unless the + installer knows what s/he's doing. See the section on building these + modules in the README file for details. + +C API +----- + +- PyNumber_Check() now returns true for string and unicode objects. + This is a result of these types having a partially defined + tp_as_number slot. (This is not a feature, but an indication that + PyNumber_Check() is not very useful to determine numeric behavior. + It may be deprecated.) + +- The string object's layout has changed: the pointer member + ob_sinterned has been replaced by an int member ob_sstate. On some + platforms (e.g. most 64-bit systems) this may change the offset of + the ob_sval member, so as a precaution the API_VERSION has been + incremented. The apparently unused feature of "indirect interned + strings", supported by the ob_sinterned member, is gone. Interned + strings are now usually mortal; there is a new API, + PyString_InternImmortal() that creates immortal interned strings. + (The ob_sstate member can only take three values; however, while + making it a char saves a few bytes per string object on average, in + it also slowed things down a bit because ob_sval was no longer + aligned.) + +- The Py_InitModule*() functions now accept NULL for the 'methods' + argument. Modules without global functions are becoming more common + now that factories can be types rather than functions. + +- New C API PyUnicode_FromOrdinal() which exposes unichr() at C + level. + +- New functions PyErr_SetExcFromWindowsErr() and + PyErr_SetExcFromWindowsErrWithFilename(). Similar to + PyErr_SetFromWindowsErrWithFilename() and + PyErr_SetFromWindowsErr(), but they allow to specify + the exception type to raise. Available on Windows. + +- Py_FatalError() is now declared as taking a const char* argument. It + was previously declared without const. This should not affect working + code. + +- Added new macro PySequence_ITEM(o, i) that directly calls + sq_item without rechecking that o is a sequence and without + adjusting for negative indices. + +- PyRange_New() now raises ValueError if the fourth argument is not 1. + This is part of the removal of deprecated features of the xrange + object. + +- PyNumber_Coerce() and PyNumber_CoerceEx() now also invoke the type's + coercion if both arguments have the same type but this type has the + CHECKTYPES flag set. This is to better support proxies. + +- The type of tp_free has been changed from "``void (*)(PyObject *)``" to + "``void (*)(void *)``". + +- PyObject_Del, PyObject_GC_Del are now functions instead of macros. + +- A type can now inherit its metatype from its base type. Previously, + when PyType_Ready() was called, if ob_type was found to be NULL, it + was always set to &PyType_Type; now it is set to base->ob_type, + where base is tp_base, defaulting to &PyObject_Type. + +- PyType_Ready() accidentally did not inherit tp_is_gc; now it does. + +- The PyCore_* family of APIs have been removed. + +- The "u#" parser marker will now pass through Unicode objects as-is + without going through the buffer API. + +- The enumerators of cmp_op have been renamed to use the prefix ``PyCmp_``. + +- An old #define of ANY as void has been removed from pyport.h. This + hasn't been used since Python's pre-ANSI days, and the #define has + been marked as obsolete since then. SF bug 495548 says it created + conflicts with other packages, so keeping it around wasn't harmless. + +- Because Python's magic number scheme broke on January 1st, we decided + to stop Python development. Thanks for all the fish! + +- Some of us don't like fish, so we changed Python's magic number + scheme to a new one. See Python/import.c for details. + +New platforms +------------- + +- OpenVMS is now supported. + +- AtheOS is now supported. + +- the EMX runtime environment on OS/2 is now supported. + +- GNU/Hurd is now supported. + +Tests +----- + +- The regrtest.py script's -u option now provides a way to say "allow + all resources except this one." For example, to allow everything + except bsddb, give the option '-uall,-bsddb'. + +Windows +------- + +- The Windows distribution now ships with version 4.0.14 of the + Sleepycat Berkeley database library. This should be a huge + improvement over the previous Berkeley DB 1.85, which had many + bugs. + XXX What are the licensing issues here? + XXX If a user has a database created with a previous version of + XXX Python, what must they do to convert it? + XXX I'm still not sure how to link this thing (see PCbuild/readme.txt). + XXX The version # is likely to change before 2.3a1. + +- The Windows distribution now ships with a Secure Sockets Library (SLL) + module (_ssl.pyd) + +- The Windows distribution now ships with Tcl/Tk version 8.4.1 (it + previously shipped with Tcl/Tk 8.3.2). + +- When Python is built under a Microsoft compiler, sys.version now + includes the compiler version number (_MSC_VER). For example, under + MSVC 6, sys.version contains the substring "MSC v.1200 ". 1200 is + the value of _MSC_VER under MSVC 6. + +- Sometimes the uninstall executable (UNWISE.EXE) vanishes. One cause + of that has been fixed in the installer (disabled Wise's "delete in- + use files" uninstall option). + +- Fixed a bug in urllib's proxy handling in Windows. [SF bug #503031] + +- The installer now installs Start menu shortcuts under (the local + equivalent of) "All Users" when doing an Admin install. + +- file.truncate([newsize]) now works on Windows for all newsize values. + It used to fail if newsize didn't fit in 32 bits, reflecting a + limitation of MS _chsize (which is no longer used). + +- os.waitpid() is now implemented for Windows, and can be used to block + until a specified process exits. This is similar to, but not exactly + the same as, os.waitpid() on POSIX systems. If you're waiting for + a specific process whose pid was obtained from one of the spawn() + functions, the same Python os.waitpid() code works across platforms. + See the docs for details. The docs were changed to clarify that + spawn functions return, and waitpid requires, a process handle on + Windows (not the same thing as a Windows process id). + +- New tempfile.TemporaryFile implementation for Windows: this doesn't + need a TemporaryFileWrapper wrapper anymore, and should be immune + to a nasty problem: before 2.3, if you got a temp file on Windows, it + got wrapped in an object whose close() method first closed the + underlying file, then deleted the file. This usually worked fine. + However, the spawn family of functions on Windows create (at a low C + level) the same set of open files in the spawned process Q as were + open in the spawning process P. If a temp file f was among them, then + doing f.close() in P first closed P's C-level file handle on f, but Q's + C-level file handle on f remained open, so the attempt in P to delete f + blew up with a "Permission denied" error (Windows doesn't allow + deleting open files). This was surprising, subtle, and difficult to + work around. + +- The os module now exports all the symbolic constants usable with the + low-level os.open() on Windows: the new constants in 2.3 are + O_NOINHERIT, O_SHORT_LIVED, O_TEMPORARY, O_RANDOM and O_SEQUENTIAL. + The others were also available in 2.2: O_APPEND, O_BINARY, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TEXT, O_TRUNC and O_WRONLY. Contrary + to Microsoft docs, O_SHORT_LIVED does not seem to imply O_TEMPORARY + (so specify both if you want both; note that neither is useful unless + specified with O_CREAT too). + +Mac +---- + +- Mac/Relnotes is gone, the release notes are now here. + +- Python (the OSX-only, unix-based version, not the OS9-compatible CFM + version) now fully supports unicode strings as arguments to various file + system calls, eg. open(), file(), os.stat() and os.listdir(). + +- The current naming convention for Python on the Macintosh is that MacPython + refers to the unix-based OSX-only version, and MacPython-OS9 refers to the + CFM-based version that runs on both OS9 and OSX. + +- All MacPython-OS9 functionality is now available in an OSX unix build, + including the Carbon modules, the IDE, OSA support, etc. A lot of this + will only work correctly in a framework build, though, because you cannot + talk to the window manager unless your application is run from a .app + bundle. There is a command line tool "pythonw" that runs your script + with an interpreter living in such a .app bundle, this interpreter should + be used to run any Python script using the window manager (including + Tkinter or wxPython scripts). + +- Most of Mac/Lib has moved to Lib/plat-mac, which is again used both in + MacPython-OSX and MacPython-OS9. The only modules remaining in Mac/Lib + are specifically for MacPython-OS9 (CFM support, preference resources, etc). + +- A new utility PythonLauncher will start a Python interpreter when a .py or + .pyw script is double-clicked in the Finder. By default .py scripts are + run with a normal Python interpreter in a Terminal window and .pyw + files are run with a window-aware pythonw interpreter without a Terminal + window, but all this can be customized. + +- MacPython-OS9 is now Carbon-only, so it runs on Mac OS 9 or Mac OS X and + possibly on Mac OS 8.6 with the right CarbonLib installed, but not on earlier + releases. + +- Many tools such as BuildApplet.py and gensuitemodule.py now support a command + line interface too. + +- All the Carbon classes are now PEP253 compliant, meaning that you can + subclass them from Python. Most of the attributes have gone, you should + now use the accessor function call API, which is also what Apple's + documentation uses. Some attributes such as grafport.visRgn are still + available for convenience. + +- New Carbon modules File (implementing the APIs in Files.h and Aliases.h) + and Folder (APIs from Folders.h). The old macfs builtin module is + gone, and replaced by a Python wrapper around the new modules. + +- Pathname handling should now be fully consistent: MacPython-OSX always uses + unix pathnames and MacPython-OS9 always uses colon-separated Mac pathnames + (also when running on Mac OS X). + +- New Carbon modules Help and AH give access to the Carbon Help Manager. + There are hooks in the IDE to allow accessing the Python documentation + (and Apple's Carbon and Cocoa documentation) through the Help Viewer. + See Mac/OSX/README for converting the Python documentation to a + Help Viewer compatible form and installing it. + +- OSA support has been redesigned and the generated Python classes now + mirror the inheritance defined by the underlying OSA classes. + +- MacPython no longer maps both \r and \n to \n on input for any text file. + This feature has been replaced by universal newline support (PEP278). + +- The default encoding for Python sourcefiles in MacPython-OS9 is no longer + mac-roman (or whatever your local Mac encoding was) but "ascii", like on + other platforms. If you really need sourcefiles with Mac characters in them + you can change this in site.py. + + +What's New in Python 2.2 final? +=============================== + +*Release date: 21-Dec-2001* + +Type/class unification and new-style classes +-------------------------------------------- + +- pickle.py, cPickle: allow pickling instances of new-style classes + with a custom metaclass. + +Core and builtins +----------------- + +- weakref proxy object: when comparing, unwrap both arguments if both + are proxies. + +Extension modules +----------------- + +- binascii.b2a_base64(): fix a potential buffer overrun when encoding + very short strings. + +- cPickle: the obscure "fast" mode was suspected of causing stack + overflows on the Mac. Hopefully fixed this by setting the recursion + limit much smaller. If the limit is too low (it only affects + performance), you can change it by defining PY_CPICKLE_FAST_LIMIT + when compiling cPickle.c (or in pyconfig.h). + +Library +------- + +- dumbdbm.py: fixed a dumb old bug (the file didn't get synched at + close or delete time). + +- rfc822.py: fixed a bug where the address '<>' was converted to None + instead of an empty string (also fixes the email.Utils module). + +- xmlrpclib.py: version 1.0.0; uses precision for doubles. + +- test suite: the pickle and cPickle tests were not executing any code + when run from the standard regression test. + +Tools/Demos +----------- + +Build +----- + +C API +----- + +New platforms +------------- + +Tests +----- + +Windows +------- + +- distutils package: fixed broken Windows installers (bdist_wininst). + +- tempfile.py: prevent mysterious warnings when TemporaryFileWrapper + instances are deleted at process exit time. + +- socket.py: prevent mysterious warnings when socket instances are + deleted at process exit time. + +- posixmodule.c: fix a Windows crash with stat() of a filename ending + in backslash. + +Mac +---- + +- The Carbon toolbox modules have been upgraded to Universal Headers + 3.4, and experimental CoreGraphics and CarbonEvents modules have + been added. All only for framework-enabled MacOSX. + + +What's New in Python 2.2c1? +=========================== + +*Release date: 14-Dec-2001* + +Type/class unification and new-style classes +-------------------------------------------- + +- Guido's tutorial introduction to the new type/class features has + been extensively updated. See + + http://www.python.org/2.2/descrintro.html + + That remains the primary documentation in this area. + +- Fixed a leak: instance variables declared with __slots__ were never + deleted! + +- The "delete attribute" method of descriptor objects is called + __delete__, not __del__. In previous releases, it was mistakenly + called __del__, which created an unfortunate overloading condition + with finalizers. (The "get attribute" and "set attribute" methods + are still called __get__ and __set__, respectively.) + +- Some subtle issues with the super built-in were fixed: + + (a) When super itself is subclassed, its __get__ method would still + return an instance of the base class (i.e., of super). + + (b) super(C, C()).__class__ would return C rather than super. This + is confusing. To fix this, I decided to change the semantics of + super so that it only applies to code attributes, not to data + attributes. After all, overriding data attributes is not + supported anyway. + + (c) The __get__ method didn't check whether the argument was an + instance of the type used in creation of the super instance. + +- Previously, hash() of an instance of a subclass of a mutable type + (list or dictionary) would return some value, rather than raising + TypeError. This has been fixed. Also, directly calling + dict.__hash__ and list.__hash__ now raises the same TypeError + (previously, these were the same as object.__hash__). + +- New-style objects now support deleting their __dict__. This is for + all intents and purposes equivalent to assigning a brand new empty + dictionary, but saves space if the object is not used further. + +Core and builtins +----------------- + +- -Qnew now works as documented in PEP 238: when -Qnew is passed on + the command line, all occurrences of "/" use true division instead + of classic division. See the PEP for details. Note that "all" + means all instances in library and 3rd-party modules, as well as in + your own code. As the PEP says, -Qnew is intended for use only in + educational environments with control over the libraries in use. + Note that test_coercion.py in the standard Python test suite fails + under -Qnew; this is expected, and won't be repaired until true + division becomes the default (in the meantime, test_coercion is + testing the current rules). + +- complex() now only allows the first argument to be a string + argument, and raises TypeError if either the second arg is a string + or if the second arg is specified when the first is a string. + +Extension modules +----------------- + +- gc.get_referents was renamed to gc.get_referrers. + +Library +------- + +- Functions in the os.spawn() family now release the global interpreter + lock around calling the platform spawn. They should always have done + this, but did not before 2.2c1. Multithreaded programs calling + an os.spawn function with P_WAIT will no longer block all Python threads + until the spawned program completes. It's possible that some programs + relies on blocking, although more likely by accident than by design. + +- webbrowser defaults to netscape.exe on OS/2 now. + +- Tix.ResizeHandle exposes detach_widget, hide, and show. + +- The charset alias windows_1252 has been added. + +- types.StringTypes is a tuple containing the defined string types; + usually this will be (str, unicode), but if Python was compiled + without Unicode support it will be just (str,). + +- The pulldom and minidom modules were synchronized to PyXML. + +Tools/Demos +----------- + +- A new script called Tools/scripts/google.py was added, which fires + off a search on Google. + +Build +----- + +- Note that release builds of Python should arrange to define the + preprocessor symbol NDEBUG on the command line (or equivalent). + In the 2.2 pre-release series we tried to define this by magic in + Python.h instead, but it proved to cause problems for extension + authors. The Unix, Windows and Mac builds now all define NDEBUG in + release builds via cmdline (or equivalent) instead. Ports to + other platforms should do likewise. + +- It is no longer necessary to use --with-suffix when building on a + case-insensitive file system (such as Mac OS X HFS+). In the build + directory an extension is used, but not in the installed python. + +C API +----- + +- New function PyDict_MergeFromSeq2() exposes the builtin dict + constructor's logic for updating a dictionary from an iterable object + producing key-value pairs. + +- PyArg_ParseTupleAndKeywords() requires that the number of entries in + the keyword list equal the number of argument specifiers. This + wasn't checked correctly, and PyArg_ParseTupleAndKeywords could even + dump core in some bad cases. This has been repaired. As a result, + PyArg_ParseTupleAndKeywords may raise RuntimeError in bad cases that + previously went unchallenged. + +New platforms +------------- + +Tests +----- + +Windows +------- + +Mac +---- + +- In unix-Python on Mac OS X (and darwin) sys.platform is now "darwin", + without any trailing digits. + +- Changed logic for finding python home in Mac OS X framework Pythons. + Now sys.executable points to the executable again, in stead of to + the shared library. The latter is used only for locating the python + home. + + +What's New in Python 2.2b2? +=========================== + +*Release date: 16-Nov-2001* + +Type/class unification and new-style classes +-------------------------------------------- + +- Multiple inheritance mixing new-style and classic classes in the + list of base classes is now allowed, so this works now: + + class Classic: pass + class Mixed(Classic, object): pass + + The MRO (method resolution order) for each base class is respected + according to its kind, but the MRO for the derived class is computed + using new-style MRO rules if any base class is a new-style class. + This needs to be documented. + +- The new builtin dictionary() constructor, and dictionary type, have + been renamed to dict. This reflects a decade of common usage. + +- dict() now accepts an iterable object producing 2-sequences. For + example, dict(d.items()) == d for any dictionary d. The argument, + and the elements of the argument, can be any iterable objects. + +- New-style classes can now have a __del__ method, which is called + when the instance is deleted (just like for classic classes). + +- Assignment to object.__dict__ is now possible, for objects that are + instances of new-style classes that have a __dict__ (unless the base + class forbids it). + +- Methods of built-in types now properly check for keyword arguments + (formerly these were silently ignored). The only built-in methods + that take keyword arguments are __call__, __init__ and __new__. + +- The socket function has been converted to a type; see below. + +Core and builtins +----------------- + +- Assignment to __debug__ raises SyntaxError at compile-time. This + was promised when 2.1c1 was released as "What's New in Python 2.1c1" + (see below) says. + +- Clarified the error messages for unsupported operands to an operator + (like 1 + ''). + +Extension modules +----------------- + +- mmap has a new keyword argument, "access", allowing a uniform way for + both Windows and Unix users to create read-only, write-through and + copy-on-write memory mappings. This was previously possible only on + Unix. A new keyword argument was required to support this in a + uniform way because the mmap() signatures had diverged across + platforms. Thanks to Jay T Miller for repairing this! + +- By default, the gc.garbage list now contains only those instances in + unreachable cycles that have __del__ methods; in 2.1 it contained all + instances in unreachable cycles. "Instances" here has been generalized + to include instances of both new-style and old-style classes. + +- The socket module defines a new method for socket objects, + sendall(). This is like send() but may make multiple calls to + send() until all data has been sent. Also, the socket function has + been converted to a subclassable type, like list and tuple (etc.) + before it; socket and SocketType are now the same thing. + +- Various bugfixes to the curses module. There is now a test suite + for the curses module (you have to run it manually). + +- binascii.b2a_base64 no longer places an arbitrary restriction of 57 + bytes on its input. + +Library +------- + +- tkFileDialog exposes a Directory class and askdirectory + convenience function. + +- Symbolic group names in regular expressions must be unique. For + example, the regexp r'(?P)(?P)' is not allowed, because a + single name can't mean both "group 1" and "group 2" simultaneously. + Python 2.2 detects this error at regexp compilation time; + previously, the error went undetected, and results were + unpredictable. Also in sre, the pattern.split(), pattern.sub(), and + pattern.subn() methods have been rewritten in C. Also, an + experimental function/method finditer() has been added, which works + like findall() but returns an iterator. + +- Tix exposes more commands through the classes DirSelectBox, + DirSelectDialog, ListNoteBook, Meter, CheckList, and the + methods tix_addbitmapdir, tix_cget, tix_configure, tix_filedialog, + tix_getbitmap, tix_getimage, tix_option_get, and tix_resetoptions. + +- Traceback objects are now scanned by cyclic garbage collection, so + cycles created by casual use of sys.exc_info() no longer cause + permanent memory leaks (provided garbage collection is enabled). + +- os.extsep -- a new variable needed by the RISCOS support. It is the + separator used by extensions, and is '.' on all platforms except + RISCOS, where it is '/'. There is no need to use this variable + unless you have a masochistic desire to port your code to RISCOS. + +- mimetypes.py has optional support for non-standard, but commonly + found types. guess_type() and guess_extension() now accept an + optional 'strict' flag, defaulting to true, which controls whether + recognize non-standard types or not. A few non-standard types we + know about have been added. Also, when run as a script, there are + new -l and -e options. + +- statcache is now deprecated. + +- email.Utils.formatdate() now produces the preferred RFC 2822 style + dates with numeric timezones (it used to produce obsolete dates + hard coded to "GMT" timezone). An optional 'localtime' flag is + added to produce dates in the local timezone, with daylight savings + time properly taken into account. + +- In pickle and cPickle, instead of masking errors in load() by + transforming them into SystemError, we let the original exception + propagate out. Also, implement support for __safe_for_unpickling__ + in pickle, as it already was supported in cPickle. + +Tools/Demos +----------- + +Build +----- + +- The dbm module is built using libdb1 if available. The bsddb module + is built with libdb3 if available. + +- Misc/Makefile.pre.in has been removed by BDFL pronouncement. + +C API +----- + +- New function PySequence_Fast_GET_SIZE() returns the size of a non- + NULL result from PySequence_Fast(), more quickly than calling + PySequence_Size(). + +- New argument unpacking function PyArg_UnpackTuple() added. + +- New functions PyObject_CallFunctionObjArgs() and + PyObject_CallMethodObjArgs() have been added to make it more + convenient and efficient to call functions and methods from C. + +- PyArg_ParseTupleAndKeywords() no longer masks errors, so it's + possible that this will propagate errors it didn't before. + +- New function PyObject_CheckReadBuffer(), which returns true if its + argument supports the single-segment readable buffer interface. + +New platforms +------------- + +- We've finally confirmed that this release builds on HP-UX 11.00, + *with* threads, and passes the test suite. + +- Thanks to a series of patches from Michael Muller, Python may build + again under OS/2 Visual Age C++. + +- Updated RISCOS port by Dietmar Schwertberger. + +Tests +----- + +- Added a test script for the curses module. It isn't run automatically; + regrtest.py must be run with '-u curses' to enable it. + +Windows +------- + +Mac +---- + +- PythonScript has been moved to unsupported and is slated to be + removed completely in the next release. + +- It should now be possible to build applets that work on both OS9 and + OSX. + +- The core is now linked with CoreServices not Carbon; as a side + result, default 8bit encoding on OSX is now ASCII. + +- Python should now build on OSX 10.1.1 + + +What's New in Python 2.2b1? +=========================== + +*Release date: 19-Oct-2001* + +Type/class unification and new-style classes +-------------------------------------------- + +- New-style classes are now always dynamic (except for built-in and + extension types). There is no longer a performance penalty, and I + no longer see another reason to keep this baggage around. One relic + remains: the __dict__ of a new-style class is a read-only proxy; you + must set the class's attribute to modify it. As a consequence, the + __defined__ attribute of new-style types no longer exists, for lack + of need: there is once again only one __dict__ (although in the + future a __cache__ may be resurrected with a similar function, if I + can prove that it actually speeds things up). + +- C.__doc__ now works as expected for new-style classes (in 2.2a4 it + always returned None, even when there was a class docstring). + +- doctest now finds and runs docstrings attached to new-style classes, + class methods, static methods, and properties. + +Core and builtins +----------------- + +- A very subtle syntactical pitfall in list comprehensions was fixed. + For example: [a+b for a in 'abc', for b in 'def']. The comma in + this example is a mistake. Previously, this would silently let 'a' + iterate over the singleton tuple ('abc',), yielding ['abcd', 'abce', + 'abcf'] rather than the intended ['ad', 'ae', 'af', 'bd', 'be', + 'bf', 'cd', 'ce', 'cf']. Now, this is flagged as a syntax error. + Note that [a for a in ] is a convoluted way to say + [] anyway, so it's not like any expressiveness is lost. + +- getattr(obj, name, default) now only catches AttributeError, as + documented, rather than returning the default value for all + exceptions (which could mask bugs in a __getattr__ hook, for + example). + +- Weak reference objects are now part of the core and offer a C API. + A bug which could allow a core dump when binary operations involved + proxy reference has been fixed. weakref.ReferenceError is now a + built-in exception. + +- unicode(obj) now behaves more like str(obj), accepting arbitrary + objects, and calling a __unicode__ method if it exists. + unicode(obj, encoding) and unicode(obj, encoding, errors) still + require an 8-bit string or character buffer argument. + +- isinstance() now allows any object as the first argument and a + class, a type or something with a __bases__ tuple attribute for the + second argument. The second argument may also be a tuple of a + class, type, or something with __bases__, in which case isinstance() + will return true if the first argument is an instance of any of the + things contained in the second argument tuple. E.g. + + isinstance(x, (A, B)) + + returns true if x is an instance of A or B. + +Extension modules +----------------- + +- thread.start_new_thread() now returns the thread ID (previously None). + +- binascii has now two quopri support functions, a2b_qp and b2a_qp. + +- readline now supports setting the startup_hook and the + pre_event_hook, and adds the add_history() function. + +- os and posix supports chroot(), setgroups() and unsetenv() where + available. The stat(), fstat(), statvfs() and fstatvfs() functions + now return "pseudo-sequences" -- the various fields can now be + accessed as attributes (e.g. os.stat("/").st_mtime) but for + backwards compatibility they also behave as a fixed-length sequence. + Some platform-specific fields (e.g. st_rdev) are only accessible as + attributes. + +- time: localtime(), gmtime() and strptime() now return a + pseudo-sequence similar to the os.stat() return value, with + attributes like tm_year etc. + +- Decompression objects in the zlib module now accept an optional + second parameter to decompress() that specifies the maximum amount + of memory to use for the uncompressed data. + +- optional SSL support in the socket module now exports OpenSSL + functions RAND_add(), RAND_egd(), and RAND_status(). These calls + are useful on platforms like Solaris where OpenSSL does not + automatically seed its PRNG. Also, the keyfile and certfile + arguments to socket.ssl() are now optional. + +- posixmodule (and by extension, the os module on POSIX platforms) now + exports O_LARGEFILE, O_DIRECT, O_DIRECTORY, and O_NOFOLLOW. + +Library +------- + +- doctest now excludes functions and classes not defined by the module + being tested, thanks to Tim Hochberg. + +- HotShot, a new profiler implemented using a C-based callback, has + been added. This substantially reduces the overhead of profiling, + but it is still quite preliminary. Support modules and + documentation will be added in upcoming releases (before 2.2 final). + +- profile now produces correct output in situations where an exception + raised in Python is cleared by C code (e.g. hasattr()). This used + to cause wrong output, including spurious claims of recursive + functions and attribution of time spent to the wrong function. + + The code and documentation for the derived OldProfile and HotProfile + profiling classes was removed. The code hasn't worked for years (if + you tried to use them, they raised exceptions). OldProfile + intended to reproduce the behavior of the profiler Python used more + than 7 years ago, and isn't interesting anymore. HotProfile intended + to provide a faster profiler (but producing less information), and + that's a worthy goal we intend to meet via a different approach (but + without losing information). + +- Profile.calibrate() has a new implementation that should deliver + a much better system-specific calibration constant. The constant can + now be specified in an instance constructor, or as a Profile class or + instance variable, instead of by editing profile.py's source code. + Calibration must still be done manually (see the docs for the profile + module). + + Note that Profile.calibrate() must be overridden by subclasses. + Improving the accuracy required exploiting detailed knowledge of + profiler internals; the earlier method abstracted away the details + and measured a simplified model instead, but consequently computed + a constant too small by a factor of 2 on some modern machines. + +- quopri's encode and decode methods take an optional header parameter, + which indicates whether output is intended for the header 'Q' + encoding. + +- The SocketServer.ThreadingMixIn class now closes the request after + finish_request() returns. (Not when it errors out though.) + +- The nntplib module's NNTP.body() method has grown a 'file' argument + to allow saving the message body to a file. + +- The email package has added a class email.Parser.HeaderParser which + only parses headers and does not recurse into the message's body. + Also, the module/class MIMEAudio has been added for representing + audio data (contributed by Anthony Baxter). + +- ftplib should be able to handle files > 2GB. + +- ConfigParser.getboolean() now also interprets TRUE, FALSE, YES, NO, + ON, and OFF. + +- xml.dom.minidom NodeList objects now support the length attribute + and item() method as required by the DOM specifications. + +Tools/Demos +----------- + +- Demo/dns was removed. It no longer serves any purpose; a package + derived from it is now maintained by Anthony Baxter, see + http://PyDNS.SourceForge.net. + +- The freeze tool has been made more robust, and two new options have + been added: -X and -E. + +Build +----- + +- configure will use CXX in LINKCC if CXX is used to build main() and + the system requires to link a C++ main using the C++ compiler. + +C API +----- + +- The documentation for the tp_compare slot is updated to require that + the return value must be -1, 0, 1; an arbitrary number <0 or >0 is + not correct. This is not yet enforced but will be enforced in + Python 2.3; even later, we may use -2 to indicate errors and +2 for + "NotImplemented". Right now, -1 should be used for an error return. + +- PyLong_AsLongLong() now accepts int (as well as long) arguments. + Consequently, PyArg_ParseTuple's 'L' code also accepts int (as well + as long) arguments. + +- PyThread_start_new_thread() now returns a long int giving the thread + ID, if one can be calculated; it returns -1 for error, 0 if no + thread ID is calculated (this is an incompatible change, but only + the thread module used this API). This code has only really been + tested on Linux and Windows; other platforms please beware (and + report any bugs or strange behavior). + +- PyUnicode_FromEncodedObject() no longer accepts Unicode objects as + input. + +New platforms +------------- + +Tests +----- + +Windows +------- + +- Installer: If you install IDLE, and don't disable file-extension + registration, a new "Edit with IDLE" context (right-click) menu entry + is created for .py and .pyw files. + +- The signal module now supports SIGBREAK on Windows, thanks to Steven + Scott. Note that SIGBREAK is unique to Windows. The default SIGBREAK + action remains to call Win32 ExitProcess(). This can be changed via + signal.signal(). For example:: + + # Make Ctrl+Break raise KeyboardInterrupt, like Python's default Ctrl+C + # (SIGINT) behavior. + import signal + signal.signal(signal.SIGBREAK, signal.default_int_handler) + + try: + while 1: + pass + except KeyboardInterrupt: + # We get here on Ctrl+C or Ctrl+Break now; if we had not changed + # SIGBREAK, only on Ctrl+C (and Ctrl+Break would terminate the + # program without the possibility for any Python-level cleanup). + print "Clean exit" + + +What's New in Python 2.2a4? +=========================== + +*Release date: 28-Sep-2001* + +Type/class unification and new-style classes +-------------------------------------------- + +- pydoc and inspect are now aware of new-style classes; + e.g. help(list) at the interactive prompt now shows proper + documentation for all operations on list objects. + +- Applications using Jim Fulton's ExtensionClass module can now safely + be used with Python 2.2. In particular, Zope 2.4.1 now works with + Python 2.2 (as well as with Python 2.1.1). The Demo/metaclass + examples also work again. It is hoped that Gtk and Boost also work + with 2.2a4 and beyond. (If you can confirm this, please write + webmaster@python.org; if there are still problems, please open a bug + report on SourceForge.) + +- property() now takes 4 keyword arguments: fget, fset, fdel and doc. + These map to read-only attributes 'fget', 'fset', 'fdel', and '__doc__' + in the constructed property object. fget, fset and fdel weren't + discoverable from Python in 2.2a3. __doc__ is new, and allows to + associate a docstring with a property. + +- Comparison overloading is now more completely implemented. For + example, a str subclass instance can properly be compared to a str + instance, and it can properly overload comparison. Ditto for most + other built-in object types. + +- The repr() of new-style classes has changed; instead of a new-style class is now rendered as , + *except* for built-in types, which are still rendered as (to avoid upsetting existing code that might parse or + otherwise rely on repr() of certain type objects). + +- The repr() of new-style objects is now always ; + previously, it was sometimes . + +- For new-style classes, what was previously called __getattr__ is now + called __getattribute__. This method, if defined, is called for + *every* attribute access. A new __getattr__ hook more similar to the + one in classic classes is defined which is called only if regular + attribute access raises AttributeError; to catch *all* attribute + access, you can use __getattribute__ (for new-style classes). If + both are defined, __getattribute__ is called first, and if it raises + AttributeError, __getattr__ is called. + +- The __class__ attribute of new-style objects can be assigned to. + The new class must have the same C-level object layout as the old + class. + +- The builtin file type can be subclassed now. In the usual pattern, + "file" is the name of the builtin type, and file() is a new builtin + constructor, with the same signature as the builtin open() function. + file() is now the preferred way to open a file. + +- Previously, __new__ would only see sequential arguments passed to + the type in a constructor call; __init__ would see both sequential + and keyword arguments. This made no sense whatsoever any more, so + now both __new__ and __init__ see all arguments. + +- Previously, hash() applied to an instance of a subclass of str or + unicode always returned 0. This has been repaired. + +- Previously, an operation on an instance of a subclass of an + immutable type (int, long, float, complex, tuple, str, unicode), + where the subtype didn't override the operation (and so the + operation was handled by the builtin type), could return that + instance instead a value of the base type. For example, if s was of + a str subclass type, s[:] returned s as-is. Now it returns a str + with the same value as s. + +- Provisional support for pickling new-style objects has been added. + +Core +---- + +- file.writelines() now accepts any iterable object producing strings. + +- PyUnicode_FromEncodedObject() now works very much like + PyObject_Str(obj) in that it tries to use __str__/tp_str + on the object if the object is not a string or buffer. This + makes unicode() behave like str() when applied to non-string/buffer + objects. + +- PyFile_WriteObject now passes Unicode objects to the file's write + method. As a result, all file-like objects which may be the target + of a print statement must support Unicode objects, i.e. they must + at least convert them into ASCII strings. + +- Thread scheduling on Solaris should be improved; it is no longer + necessary to insert a small sleep at the start of a thread in order + to let other runnable threads be scheduled. + +Library +------- + +- StringIO.StringIO instances and cStringIO.StringIO instances support + read character buffer compatible objects for their .write() methods. + These objects are converted to strings and then handled as such + by the instances. + +- The "email" package has been added. This is basically a port of the + mimelib package with API changes + and some implementations updated to use iterators and generators. + +- difflib.ndiff() and difflib.Differ.compare() are generators now. This + restores the ability of Tools/scripts/ndiff.py to start producing output + before the entire comparison is complete. + +- StringIO.StringIO instances and cStringIO.StringIO instances support + iteration just like file objects (i.e. their .readline() method is + called for each iteration until it returns an empty string). + +- The codecs module has grown four new helper APIs to access + builtin codecs: getencoder(), getdecoder(), getreader(), + getwriter(). + +- SimpleXMLRPCServer: a new module (based upon SimpleHTMLServer) + simplifies writing XML RPC servers. + +- os.path.realpath(): a new function that returns the absolute pathname + after interpretation of symbolic links. On non-Unix systems, this + is an alias for os.path.abspath(). + +- operator.indexOf() (PySequence_Index() in the C API) now works with any + iterable object. + +- smtplib now supports various authentication and security features of + the SMTP protocol through the new login() and starttls() methods. + +- hmac: a new module implementing keyed hashing for message + authentication. + +- mimetypes now recognizes more extensions and file types. At the + same time, some mappings not sanctioned by IANA were removed. + +- The "compiler" package has been brought up to date to the state of + Python 2.2 bytecode generation. It has also been promoted from a + Tool to a standard library package. (Tools/compiler still exists as + a sample driver.) + +Build +----- + +- Large file support (LFS) is now automatic when the platform supports + it; no more manual configuration tweaks are needed. On Linux, at + least, it's possible to have a system whose C library supports large + files but whose kernel doesn't; in this case, large file support is + still enabled but doesn't do you any good unless you upgrade your + kernel or share your Python executable with another system whose + kernel has large file support. + +- The configure script now supplies plausible defaults in a + cross-compilation environment. This doesn't mean that the supplied + values are always correct, or that cross-compilation now works + flawlessly -- but it's a first step (and it shuts up most of + autoconf's warnings about AC_TRY_RUN). + +- The Unix build is now a bit less chatty, courtesy of the parser + generator. The build is completely silent (except for errors) when + using "make -s", thanks to a -q option to setup.py. + +C API +----- + +- The "structmember" API now supports some new flag bits to deny read + and/or write access to attributes in restricted execution mode. + +New platforms +------------- + +- Compaq's iPAQ handheld, running the "familiar" Linux distribution + (http://familiar.handhelds.org). + +Tests +----- + +- The "classic" standard tests, which work by comparing stdout to + an expected-output file under Lib/test/output/, no longer stop at + the first mismatch. Instead the test is run to completion, and a + variant of ndiff-style comparison is used to report all differences. + This is much easier to understand than the previous style of reporting. + +- The unittest-based standard tests now use regrtest's test_main() + convention, instead of running as a side-effect of merely being + imported. This allows these tests to be run in more natural and + flexible ways as unittests, outside the regrtest framework. + +- regrtest.py is much better integrated with unittest and doctest now, + especially in regard to reporting errors. + +Windows +------- + +- Large file support now also works for files > 4GB, on filesystems + that support it (NTFS under Windows 2000). See "What's New in + Python 2.2a3" for more detail. + + +What's New in Python 2.2a3? +=========================== + +*Release Date: 07-Sep-2001* + +Core +---- + +- Conversion of long to float now raises OverflowError if the long is too + big to represent as a C double. + +- The 3-argument builtin pow() no longer allows a third non-None argument + if either of the first two arguments is a float, or if both are of + integer types and the second argument is negative (in which latter case + the arguments are converted to float, so this is really the same + restriction). + +- The builtin dir() now returns more information, and sometimes much + more, generally naming all attributes of an object, and all attributes + reachable from the object via its class, and from its class's base + classes, and so on from them too. Example: in 2.2a2, dir([]) returned + an empty list. In 2.2a3, + + >>> dir([]) + ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', + '__eq__', '__ge__', '__getattr__', '__getitem__', '__getslice__', + '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__le__', + '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__repr__', + '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', + 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', + 'reverse', 'sort'] + + dir(module) continues to return only the module's attributes, though. + +- Overflowing operations on plain ints now return a long int rather + than raising OverflowError. This is a partial implementation of PEP + 237. You can use -Wdefault::OverflowWarning to enable a warning for + this situation, and -Werror::OverflowWarning to revert to the old + OverflowError exception. + +- A new command line option, -Q, is added to control run-time + warnings for the use of classic division. (See PEP 238.) Possible + values are -Qold, -Qwarn, -Qwarnall, and -Qnew. The default is + -Qold, meaning the / operator has its classic meaning and no + warnings are issued. Using -Qwarn issues a run-time warning about + all uses of classic division for int and long arguments; -Qwarnall + also warns about classic division for float and complex arguments + (for use with fixdiv.py). + [Note: the remainder of this item (preserved below) became + obsolete in 2.2c1 -- -Qnew has global effect in 2.2] :: + + Using -Qnew is questionable; it turns on new division by default, but + only in the __main__ module. You can usefully combine -Qwarn or + -Qwarnall and -Qnew: this gives the __main__ module new division, and + warns about classic division everywhere else. + +- Many built-in types can now be subclassed. This applies to int, + long, float, str, unicode, and tuple. (The types complex, list and + dictionary can also be subclassed; this was introduced earlier.) + Note that restrictions apply when subclassing immutable built-in + types: you can only affect the value of the instance by overloading + __new__. You can add mutable attributes, and the subclass instances + will have a __dict__ attribute, but you cannot change the "value" + (as implemented by the base class) of an immutable subclass instance + once it is created. + +- The dictionary constructor now takes an optional argument, a + mapping-like object, and initializes the dictionary from its + (key, value) pairs. + +- A new built-in type, super, has been added. This facilitates making + "cooperative super calls" in a multiple inheritance setting. For an + explanation, see http://www.python.org/2.2/descrintro.html#cooperation + +- A new built-in type, property, has been added. This enables the + creation of "properties". These are attributes implemented by + getter and setter functions (or only one of these for read-only or + write-only attributes), without the need to override __getattr__. + See http://www.python.org/2.2/descrintro.html#property + +- The syntax of floating-point and imaginary literals has been + liberalized, to allow leading zeroes. Examples of literals now + legal that were SyntaxErrors before: + + 00.0 0e3 0100j 07.5 00000000000000000008. + +- An old tokenizer bug allowed floating point literals with an incomplete + exponent, such as 1e and 3.1e-. Such literals now raise SyntaxError. + +Library +------- + +- telnetlib includes symbolic names for the options, and support for + setting an option negotiation callback. It also supports processing + of suboptions. + +- The new C standard no longer requires that math libraries set errno to + ERANGE on overflow. For platform libraries that exploit this new + freedom, Python's overflow-checking was wholly broken. A new overflow- + checking scheme attempts to repair that, but may not be reliable on all + platforms (C doesn't seem to provide anything both useful and portable + in this area anymore). + +- Asynchronous timeout actions are available through the new class + threading.Timer. + +- math.log and math.log10 now return sensible results for even huge + long arguments. For example, math.log10(10 ** 10000) ~= 10000.0. + +- A new function, imp.lock_held(), returns 1 when the import lock is + currently held. See the docs for the imp module. + +- pickle, cPickle and marshal on 32-bit platforms can now correctly read + dumps containing ints written on platforms where Python ints are 8 bytes. + When read on a box where Python ints are 4 bytes, such values are + converted to Python longs. + +- In restricted execution mode (using the rexec module), unmarshalling + code objects is no longer allowed. This plugs a security hole. + +- unittest.TestResult instances no longer store references to tracebacks + generated by test failures. This prevents unexpected dangling references + to objects that should be garbage collected between tests. + +Tools +----- + +- Tools/scripts/fixdiv.py has been added which can be used to fix + division operators as per PEP 238. + +Build +----- + +- If you are an adventurous person using Mac OS X you may want to look at + Mac/OSX. There is a Makefile there that will build Python as a real Mac + application, which can be used for experimenting with Carbon or Cocoa. + Discussion of this on pythonmac-sig, please. + +C API +----- + +- New function PyObject_Dir(obj), like Python __builtin__.dir(obj). + +- 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. */ + } + +- The GC API has been changed. Extensions that use the old API will still + compile but will not participate in GC. To upgrade an extension + module: + + - rename Py_TPFLAGS_GC to PyTPFLAGS_HAVE_GC + + - use PyObject_GC_New or PyObject_GC_NewVar to allocate objects and + PyObject_GC_Del to deallocate them + + - rename PyObject_GC_Init to PyObject_GC_Track and PyObject_GC_Fini + to PyObject_GC_UnTrack + + - remove PyGC_HEAD_SIZE from object size calculations + + - remove calls to PyObject_AS_GC and PyObject_FROM_GC + +- Two new functions: PyString_FromFormat() and PyString_FromFormatV(). + These can be used safely to construct string objects from a + sprintf-style format string (similar to the format string supported + by PyErr_Format()). + +New platforms +------------- + +- Stephen Hansen contributed patches sufficient to get a clean compile + under Borland C (Windows), but he reports problems running it and ran + out of time to complete the port. Volunteers? Expect a MemoryError + when importing the types module; this is probably shallow, and + causing later failures too. + +Tests +----- + +Windows +------- + +- Large file support is now enabled on Win32 platforms as well as on + Win64. This means that, for example, you can use f.tell() and f.seek() + to manipulate files larger than 2 gigabytes (provided you have enough + disk space, and are using a Windows filesystem that supports large + partitions). Windows filesystem limits: FAT has a 2GB (gigabyte) + filesize limit, and large file support makes no difference there. + FAT32's limit is 4GB, and files >= 2GB are easier to use from Python now. + NTFS has no practical limit on file size, and files of any size can be + used from Python now. + +- The w9xpopen hack is now used on Windows NT and 2000 too when COMPSPEC + points to command.com (patch from Brian Quinlan). + + +What's New in Python 2.2a2? +=========================== + +*Release Date: 22-Aug-2001* + +Build +----- + +- Tim Peters developed a brand new Windows installer using Wise 8.1, + generously donated to us by Wise Solutions. + +- configure supports a new option --enable-unicode, with the values + ucs2 and ucs4 (new in 2.2a1). With --disable-unicode, the Unicode + type and supporting code is completely removed from the interpreter. + +- A new configure option --enable-framework builds a Mac OS X framework, + which "make frameworkinstall" will install. This provides a starting + point for more mac-like functionality, join pythonmac-sig@python.org + if you are interested in helping. + +- The NeXT platform is no longer supported. + +- The 'new' module is now statically linked. + +Tools +----- + +- The new Tools/scripts/cleanfuture.py can be used to automatically + edit out obsolete future statements from Python source code. See + the module docstring for details. + +Tests +----- + +- regrtest.py now knows which tests are expected to be skipped on some + platforms, allowing to give clearer test result output. regrtest + also has optional --use/-u switch to run normally disabled tests + which require network access or consume significant disk resources. + +- Several new tests in the standard test suite, with special thanks to + Nick Mathewson. + +Core +---- + +- The floor division operator // has been added as outlined in PEP + 238. The / operator still provides classic division (and will until + Python 3.0) unless "from __future__ import division" is included, in + which case the / operator will provide true division. The operator + module provides truediv() and floordiv() functions. Augmented + assignment variants are included, as are the equivalent overloadable + methods and C API methods. See the PEP for a full discussion: + + +- Future statements are now effective in simulated interactive shells + (like IDLE). This should "just work" by magic, but read Michael + Hudson's "Future statements in simulated shells" PEP 264 for full + details: . + +- The type/class unification (PEP 252-253) was integrated into the + trunk and is not so tentative any more (the exact specification of + some features is still tentative). A lot of work has done on fixing + bugs and adding robustness and features (performance still has to + come a long way). + +- Warnings about a mismatch in the Python API during extension import + now use the Python warning framework (which makes it possible to + write filters for these warnings). + +- A function's __dict__ (aka func_dict) will now always be a + dictionary. It used to be possible to delete it or set it to None, + but now both actions raise TypeErrors. It is still legal to set it + to a dictionary object. Getting func.__dict__ before any attributes + have been assigned now returns an empty dictionary instead of None. + +- A new command line option, -E, was added which disables the use of + all environment variables, or at least those that are specifically + significant to Python. Usually those have a name starting with + "PYTHON". This was used to fix a problem where the tests fail if + the user happens to have PYTHONHOME or PYTHONPATH pointing to an + older distribution. + +Library +------- + +- New class Differ and new functions ndiff() and restore() in difflib.py. + These package the algorithms used by the popular Tools/scripts/ndiff.py, + for programmatic reuse. + +- New function xml.sax.saxutils.quoteattr(): Quote an XML attribute + value using the minimal quoting required for the value; more + reliable than using xml.sax.saxutils.escape() for attribute values. + +- Readline completion support for cmd.Cmd was added. + +- Calling os.tempnam() or os.tmpnam() generate RuntimeWarnings. + +- Added function threading.BoundedSemaphore() + +- Added Ka-Ping Yee's cgitb.py module. + +- The 'new' module now exposes the CO_xxx flags. + +- The gc module offers the get_referents function. + +New platforms +------------- + +C API +----- + +- Two new APIs PyOS_snprintf() and PyOS_vsnprintf() were added + which provide a cross-platform implementations for the + relatively new snprintf()/vsnprintf() C lib APIs. In contrast to + the standard sprintf() and vsprintf() C lib APIs, these versions + apply bounds checking on the used buffer which enhances protection + against buffer overruns. + +- Unicode APIs now use name mangling to assure that mixing interpreters + and extensions using different Unicode widths is rendered next to + impossible. Trying to import an incompatible Unicode-aware extension + will result in an ImportError. Unicode extensions writers must make + sure to check the Unicode width compatibility in their extensions by + using at least one of the mangled Unicode APIs in the extension. + +- Two new flags METH_NOARGS and METH_O are available in method definition + tables to simplify implementation of methods with no arguments and a + single untyped argument. Calling such methods is more efficient than + calling corresponding METH_VARARGS methods. METH_OLDARGS is now + deprecated. + +Windows +------- + +- "import module" now compiles module.pyw if it exists and nothing else + relevant is found. + + +What's New in Python 2.2a1? +=========================== + +*Release date: 18-Jul-2001* + +Core +---- + +- TENTATIVELY, a large amount of code implementing much of what's + described in PEP 252 (Making Types Look More Like Classes) and PEP + 253 (Subtyping Built-in Types) was added. This will be released + with Python 2.2a1. Documentation will be provided separately + through http://www.python.org/2.2/. The purpose of releasing this + with Python 2.2a1 is to test backwards compatibility. It is + possible, though not likely, that a decision is made not to release + this code as part of 2.2 final, if any serious backwards + incompatibilities are found during alpha testing that cannot be + repaired. + +- Generators were added; this is a new way to create an iterator (see + below) using what looks like a simple function containing one or + more 'yield' statements. See PEP 255. Since this adds a new + keyword to the language, this feature must be enabled by including a + future statement: "from __future__ import generators" (see PEP 236). + Generators will become a standard feature in a future release + (probably 2.3). Without this future statement, 'yield' remains an + ordinary identifier, but a warning is issued each time it is used. + (These warnings currently don't conform to the warnings framework of + PEP 230; we intend to fix this in 2.2a2.) + +- The UTF-16 codec was modified to be more RFC compliant. It will now + only remove BOM characters at the start of the string and then + only if running in native mode (UTF-16-LE and -BE won't remove a + leading BMO character). + +- Strings now have a new method .decode() to complement the already + existing .encode() method. These two methods provide direct access + to the corresponding decoders and encoders of the registered codecs. + + To enhance the usability of the .encode() method, the special + casing of Unicode object return values was dropped (Unicode objects + were auto-magically converted to string using the default encoding). + + Both methods will now return whatever the codec in charge of the + requested encoding returns as object, e.g. Unicode codecs will + return Unicode objects when decoding is requested ("äöü".decode("latin-1") + will return u"äöü"). This enables codec writer to create codecs + for various simple to use conversions. + + New codecs were added to demonstrate these new features (the .encode() + and .decode() columns indicate the type of the returned objects): + + +---------+-----------+-----------+-----------------------------+ + |Name | .encode() | .decode() | Description | + +=========+===========+===========+=============================+ + |uu | string | string | UU codec (e.g. for email) | + +---------+-----------+-----------+-----------------------------+ + |base64 | string | string | base64 codec | + +---------+-----------+-----------+-----------------------------+ + |quopri | string | string | quoted-printable codec | + +---------+-----------+-----------+-----------------------------+ + |zlib | string | string | zlib compression | + +---------+-----------+-----------+-----------------------------+ + |hex | string | string | 2-byte hex codec | + +---------+-----------+-----------+-----------------------------+ + |rot-13 | string | Unicode | ROT-13 Unicode charmap codec| + +---------+-----------+-----------+-----------------------------+ + +- Some operating systems now support the concept of a default Unicode + encoding for file system operations. Notably, Windows supports 'mbcs' + as the default. The Macintosh will also adopt this concept in the medium + term, although the default encoding for that platform will be other than + 'mbcs'. + + On operating system that support non-ASCII filenames, it is common for + functions that return filenames (such as os.listdir()) to return Python + string objects pre-encoded using the default file system encoding for + the platform. As this encoding is likely to be different from Python's + default encoding, converting this name to a Unicode object before passing + it back to the Operating System would result in a Unicode error, as Python + would attempt to use its default encoding (generally ASCII) rather than + the default encoding for the file system. + + In general, this change simply removes surprises when working with + Unicode and the file system, making these operations work as you expect, + increasing the transparency of Unicode objects in this context. + See [????] for more details, including examples. + +- Float (and complex) literals in source code were evaluated to full + precision only when running from a .py file; the same code loaded from a + .pyc (or .pyo) file could suffer numeric differences starting at about the + 12th significant decimal digit. For example, on a machine with IEEE-754 + floating arithmetic, + + x = 9007199254740992.0 + print long(x) + + printed 9007199254740992 if run directly from .py, but 9007199254740000 + if from a compiled (.pyc or .pyo) file. This was due to marshal using + str(float) instead of repr(float) when building code objects. marshal + now uses repr(float) instead, which should reproduce floats to full + machine precision (assuming the platform C float<->string I/O conversion + functions are of good quality). + + This may cause floating-point results to change in some cases, and + usually for the better, but may also cause numerically unstable + algorithms to break. + +- The implementation of dicts suffers fewer collisions, which has speed + benefits. However, the order in which dict entries appear in dict.keys(), + dict.values() and dict.items() may differ from previous releases for a + given dict. Nothing is defined about this order, so no program should + rely on it. Nevertheless, it's easy to write test cases that rely on the + order by accident, typically because of printing the str() or repr() of a + dict to an "expected results" file. See Lib/test/test_support.py's new + sortdict(dict) function for a simple way to display a dict in sorted + order. + +- Many other small changes to dicts were made, resulting in faster + operation along the most common code paths. + +- Dictionary objects now support the "in" operator: "x in dict" means + the same as dict.has_key(x). + +- The update() method of dictionaries now accepts generic mapping + objects. Specifically the argument object must support the .keys() + and __getitem__() methods. This allows you to say, for example, + {}.update(UserDict()) + +- Iterators were added; this is a generalized way of providing values + to a for loop. See PEP 234. There's a new built-in function iter() + to return an iterator. There's a new protocol to get the next value + from an iterator using the next() method (in Python) or the + tp_iternext slot (in C). There's a new protocol to get iterators + using the __iter__() method (in Python) or the tp_iter slot (in C). + Iterating (i.e. a for loop) over a dictionary generates its keys. + Iterating over a file generates its lines. + +- The following functions were generalized to work nicely with iterator + arguments:: + + map(), filter(), reduce(), zip() + list(), tuple() (PySequence_Tuple() and PySequence_Fast() in C API) + max(), min() + join() method of strings + extend() method of lists + 'x in y' and 'x not in y' (PySequence_Contains() in C API) + operator.countOf() (PySequence_Count() in C API) + right-hand side of assignment statements with multiple targets, such as :: + x, y, z = some_iterable_object_returning_exactly_3_values + +- Accessing module attributes is significantly faster (for example, + random.random or os.path or yourPythonModule.yourAttribute). + +- Comparing dictionary objects via == and != is faster, and now works even + if the keys and values don't support comparisons other than ==. + +- Comparing dictionaries in ways other than == and != is slower: there were + insecurities in the dict comparison implementation that could cause Python + to crash if the element comparison routines for the dict keys and/or + values mutated the dicts. Making the code bulletproof slowed it down. + +- Collisions in dicts are resolved via a new approach, which can help + dramatically in bad cases. For example, looking up every key in a dict + d with d.keys() == [i << 16 for i in range(20000)] is approximately 500x + faster now. Thanks to Christian Tismer for pointing out the cause and + the nature of an effective cure (last December! better late than never). + +- repr() is much faster for large containers (dict, list, tuple). + + +Library +------- + +- The constants ascii_letters, ascii_lowercase. and ascii_uppercase + were added to the string module. These a locale-independent + constants, unlike letters, lowercase, and uppercase. These are now + use in appropriate locations in the standard library. + +- The flags used in dlopen calls can now be configured using + sys.setdlopenflags and queried using sys.getdlopenflags. + +- Fredrik Lundh's xmlrpclib is now a standard library module. This + provides full client-side XML-RPC support. In addition, + Demo/xmlrpc/ contains two server frameworks (one SocketServer-based, + one asyncore-based). Thanks to Eric Raymond for the documentation. + +- The xrange() object is simplified: it no longer supports slicing, + repetition, comparisons, efficient 'in' checking, the tolist() + method, or the start, stop and step attributes. See PEP 260. + +- A new function fnmatch.filter to filter lists of file names was added. + +- calendar.py uses month and day names based on the current locale. + +- strop is now *really* obsolete (this was announced before with 1.6), + and issues DeprecationWarning when used (except for the four items + that are still imported into string.py). + +- Cookie.py now sorts key+value pairs by key in output strings. + +- pprint.isrecursive(object) didn't correctly identify recursive objects. + Now it does. + +- pprint functions now much faster for large containers (tuple, list, dict). + +- New 'q' and 'Q' format codes in the struct module, corresponding to C + types "long long" and "unsigned long long" (on Windows, __int64). In + native mode, these can be used only when the platform C compiler supports + these types (when HAVE_LONG_LONG is #define'd by the Python config + process), and then they inherit the sizes and alignments of the C types. + In standard mode, 'q' and 'Q' are supported on all platforms, and are + 8-byte integral types. + +- The site module installs a new built-in function 'help' that invokes + pydoc.help. It must be invoked as 'help()'; when invoked as 'help', + it displays a message reminding the user to use 'help()' or + 'help(object)'. + +Tests +----- + +- New test_mutants.py runs dict comparisons where the key and value + comparison operators mutate the dicts randomly during comparison. This + rapidly causes Python to crash under earlier releases (not for the faint + of heart: it can also cause Win9x to freeze or reboot!). + +- New test_pprint.py verifies that pprint.isrecursive() and + pprint.isreadable() return sensible results. Also verifies that simple + cases produce correct output. + +C API +----- + +- Removed the unused last_is_sticky argument from the internal + _PyTuple_Resize(). If this affects you, you were cheating. + What's New in Python 2.1 (final)? ================================= diff --git a/Misc/README.valgrind b/Misc/README.valgrind index 157bdc3..b5a9a32 100644 --- a/Misc/README.valgrind +++ b/Misc/README.valgrind @@ -24,6 +24,12 @@ You may need to run the tests in batches under Valgrind to keep the memory usage down to allow the tests to complete. It seems to take about 5 times longer to run --without-pymalloc. +Apr 15, 2006: + test_ctypes causes Valgrind 3.1.1 to fail (crash). + test_socket_ssl should be skipped when running valgrind. + The reason is that it purposely uses uninitialized memory. + This causes many spurious warnings, so it's easier to just skip it. + Details: -------- diff --git a/Misc/RPM/python-2.4.spec b/Misc/RPM/python-2.4.spec deleted file mode 100644 index bd4c7f7..0000000 --- a/Misc/RPM/python-2.4.spec +++ /dev/null @@ -1,382 +0,0 @@ -########################## -# User-modifiable configs -########################## - -# Is the resulting package and the installed binary named "python" or -# "python2"? -#WARNING: Commenting out doesn't work. Last line is what's used. -%define config_binsuffix none -%define config_binsuffix 2.4 - -# Build tkinter? "auto" enables it if /usr/bin/wish exists. -#WARNING: Commenting out doesn't work. Last line is what's used. -%define config_tkinter no -%define config_tkinter yes -%define config_tkinter auto - -# Use pymalloc? The last line (commented or not) determines wether -# pymalloc is used. -#WARNING: Commenting out doesn't work. Last line is what's used. -%define config_pymalloc no -%define config_pymalloc yes - -# Enable IPV6? -#WARNING: Commenting out doesn't work. Last line is what's used. -%define config_ipv6 yes -%define config_ipv6 no - -# Location of the HTML directory. -%define config_htmldir /var/www/html/python - -################################# -# End of user-modifiable configs -################################# - -%define name python -%define version 2.4 -%define libvers 2.4 -%define release 2pydotorg -%define __prefix /usr - -# kludge to get around rpm define weirdness -%define ipv6 %(if [ "%{config_ipv6}" = yes ]; then echo --enable-ipv6; else echo --disable-ipv6; fi) -%define pymalloc %(if [ "%{config_pymalloc}" = yes ]; then echo --with-pymalloc; else echo --without-pymalloc; fi) -%define binsuffix %(if [ "%{config_binsuffix}" = none ]; then echo ; else echo "%{config_binsuffix}"; fi) -%define include_tkinter %(if [ \\( "%{config_tkinter}" = auto -a -f /usr/bin/wish \\) -o "%{config_tkinter}" = yes ]; then echo 1; else echo 0; fi) -%define libdirname %(( uname -m | egrep -q '_64$' && [ -d /usr/lib64 ] && echo lib64 ) || echo lib) - -# detect if documentation is available -%define include_docs %(if [ -f "%{_sourcedir}/html-%{version}.tar.bz2" ]; then echo 1; else echo 0; fi) - -Summary: An interpreted, interactive, object-oriented programming language. -Name: %{name}%{binsuffix} -Version: %{version} -Release: %{release} -Copyright: Modified CNRI Open Source License -Group: Development/Languages -Source: Python-%{version}.tar.bz2 -%if %{include_docs} -Source1: html-%{version}.tar.bz2 -%endif -BuildRoot: %{_tmppath}/%{name}-%{version}-root -BuildPrereq: expat-devel -BuildPrereq: db4-devel -BuildPrereq: gdbm-devel -Prefix: %{__prefix} -Packager: Sean Reifschneider - -%description -Python is an interpreted, interactive, object-oriented programming -language. It incorporates modules, exceptions, dynamic typing, very high -level dynamic data types, and classes. Python combines remarkable power -with very clear syntax. It has interfaces to many system calls and -libraries, as well as to various window systems, and is extensible in C or -C++. It is also usable as an extension language for applications that need -a programmable interface. Finally, Python is portable: it runs on many -brands of UNIX, on PCs under Windows, MS-DOS, and OS/2, and on the -Mac. - -%package devel -Summary: The libraries and header files needed for Python extension development. -Prereq: python%{binsuffix} = %{PACKAGE_VERSION} -Group: Development/Libraries - -%description devel -The Python programming language's interpreter can be extended with -dynamically loaded extensions and can be embedded in other programs. -This package contains the header files and libraries needed to do -these types of tasks. - -Install python-devel if you want to develop Python extensions. The -python package will also need to be installed. You'll probably also -want to install the python-docs package, which contains Python -documentation. - -%if %{include_tkinter} -%package tkinter -Summary: A graphical user interface for the Python scripting language. -Group: Development/Languages -Prereq: python%{binsuffix} = %{PACKAGE_VERSION}-%{release} - -%description tkinter -The Tkinter (Tk interface) program is an graphical user interface for -the Python scripting language. - -You should install the tkinter package if you'd like to use a graphical -user interface for Python programming. -%endif - -%package tools -Summary: A collection of development tools included with Python. -Group: Development/Tools -Prereq: python%{binsuffix} = %{PACKAGE_VERSION}-%{release} - -%description tools -The Python package includes several development tools that are used -to build python programs. This package contains a selection of those -tools, including the IDLE Python IDE. - -Install python-tools if you want to use these tools to develop -Python programs. You will also need to install the python and -tkinter packages. - -%if %{include_docs} -%package docs -Summary: Python-related documentation. -Group: Development/Documentation - -%description docs -Documentation relating to the Python programming language in HTML and info -formats. -%endif - -%changelog -* Mon Dec 20 2004 Sean Reifschneider [2.4-2pydotorg] -- Changing the idle wrapper so that it passes arguments to idle. - -* Tue Oct 19 2004 Sean Reifschneider [2.4b1-1pydotorg] -- Updating to 2.4. - -* Thu Jul 22 2004 Sean Reifschneider [2.3.4-3pydotorg] -- Paul Tiemann fixes for %{prefix}. -- Adding permission changes for directory as suggested by reimeika.ca -- Adding code to detect when it should be using lib64. -- Adding a define for the location of /var/www/html for docs. - -* Thu May 27 2004 Sean Reifschneider [2.3.4-2pydotorg] -- Including changes from Ian Holsman to build under Red Hat 7.3. -- Fixing some problems with the /usr/local path change. - -* Sat Mar 27 2004 Sean Reifschneider [2.3.2-3pydotorg] -- Being more agressive about finding the paths to fix for - #!/usr/local/bin/python. - -* Sat Feb 07 2004 Sean Reifschneider [2.3.3-2pydotorg] -- Adding code to remove "#!/usr/local/bin/python" from particular files and - causing the RPM build to terminate if there are any unexpected files - which have that line in them. - -* Mon Oct 13 2003 Sean Reifschneider [2.3.2-1pydotorg] -- Adding code to detect wether documentation is available to build. - -* Fri Sep 19 2003 Sean Reifschneider [2.3.1-1pydotorg] -- Updating to the 2.3.1 release. - -* Mon Feb 24 2003 Sean Reifschneider [2.3b1-1pydotorg] -- Updating to 2.3b1 release. - -* Mon Feb 17 2003 Sean Reifschneider [2.3a1-1] -- Updating to 2.3 release. - -* Sun Dec 23 2001 Sean Reifschneider -[Release 2.2-2] -- Added -docs package. -- Added "auto" config_tkinter setting which only enables tk if - /usr/bin/wish exists. - -* Sat Dec 22 2001 Sean Reifschneider -[Release 2.2-1] -- Updated to 2.2. -- Changed the extension to "2" from "2.2". - -* Tue Nov 18 2001 Sean Reifschneider -[Release 2.2c1-1] -- Updated to 2.2c1. - -* Thu Nov 1 2001 Sean Reifschneider -[Release 2.2b1-3] -- Changed the way the sed for fixing the #! in pydoc works. - -* Wed Oct 24 2001 Sean Reifschneider -[Release 2.2b1-2] -- Fixed missing "email" package, thanks to anonymous report on sourceforge. -- Fixed missing "compiler" package. - -* Mon Oct 22 2001 Sean Reifschneider -[Release 2.2b1-1] -- Updated to 2.2b1. - -* Mon Oct 9 2001 Sean Reifschneider -[Release 2.2a4-4] -- otto@balinor.mat.unimi.it mentioned that the license file is missing. - -* Sun Sep 30 2001 Sean Reifschneider -[Release 2.2a4-3] -- Ignacio Vazquez-Abrams pointed out that I had a spruious double-quote in - the spec files. Thanks. - -* Wed Jul 25 2001 Sean Reifschneider -[Release 2.2a1-1] -- Updated to 2.2a1 release. -- Changed idle and pydoc to use binsuffix macro - -####### -# PREP -####### -%prep -%setup -n Python-%{version} - -######## -# BUILD -######## -%build -./configure --enable-unicode=ucs4 %{ipv6} %{pymalloc} --prefix=%{__prefix} -make - -########## -# INSTALL -########## -%install -# set the install path -echo '[install_scripts]' >setup.cfg -echo 'install_dir='"${RPM_BUILD_ROOT}%{__prefix}/bin" >>setup.cfg - -[ -d "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT -mkdir -p $RPM_BUILD_ROOT%{__prefix}/%{libdirname}/python%{libvers}/lib-dynload -make prefix=$RPM_BUILD_ROOT%{__prefix} install - -# REPLACE PATH IN PYDOC -if [ ! -z "%{binsuffix}" ] -then - ( - cd $RPM_BUILD_ROOT%{__prefix}/bin - mv pydoc pydoc.old - sed 's|#!.*|#!%{__prefix}/bin/env python'%{binsuffix}'|' \ - pydoc.old >pydoc - chmod 755 pydoc - rm -f pydoc.old - ) -fi - -# add the binsuffix -if [ ! -z "%{binsuffix}" ] -then - ( cd $RPM_BUILD_ROOT%{__prefix}/bin; rm -f python[0-9a-zA-Z]*; - mv -f python python"%{binsuffix}" ) - ( cd $RPM_BUILD_ROOT%{__prefix}/man/man1; mv python.1 python%{binsuffix}.1 ) - ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f pydoc pydoc"%{binsuffix}" ) - ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f idle idle"%{binsuffix}" ) -fi - -######## -# Tools -echo '#!%{__prefix}/bin/env python%{binsuffix}' >${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} -echo 'import os, sys' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} -echo 'os.execvp("%{__prefix}/bin/python%{binsuffix}", ["%{__prefix}/bin/python%{binsuffix}", "%{__prefix}/lib/python%{libvers}/idlelib/idle.py"] + sys.argv[1:])' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} -echo 'print "Failed to exec Idle"' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} -echo 'sys.exit(1)' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} -chmod 755 $RPM_BUILD_ROOT%{__prefix}/bin/idle%{binsuffix} -cp -a Tools $RPM_BUILD_ROOT%{__prefix}/%{libdirname}/python%{libvers} - -# MAKE FILE LISTS -rm -f mainpkg.files -find "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/lib-dynload -type f | - sed "s|^${RPM_BUILD_ROOT}|/|" | - grep -v -e '_tkinter.so$' >mainpkg.files -find "$RPM_BUILD_ROOT""%{__prefix}"/bin -type f | - sed "s|^${RPM_BUILD_ROOT}|/|" | - grep -v -e '/bin/idle%{binsuffix}$' >>mainpkg.files - -rm -f tools.files -find "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/idlelib \ - "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/Tools -type f | - sed "s|^${RPM_BUILD_ROOT}|/|" >tools.files -echo "%{__prefix}"/bin/idle%{binsuffix} >>tools.files - -###### -# Docs -%if %{include_docs} -mkdir -p "$RPM_BUILD_ROOT"%{config_htmldir} -( - cd "$RPM_BUILD_ROOT"%{config_htmldir} - bunzip2 < %{SOURCE1} | tar x -) -%endif - -# fix the #! line in installed files -find "$RPM_BUILD_ROOT" -type f -print0 | - xargs -0 grep -l /usr/local/bin/python | while read file -do - FIXFILE="$file" - sed 's|^#!.*python|#!%{__prefix}/bin/env python'"%{binsuffix}"'|' \ - "$FIXFILE" >/tmp/fix-python-path.$$ - cat /tmp/fix-python-path.$$ >"$FIXFILE" - rm -f /tmp/fix-python-path.$$ -done - -# check to see if there are any straggling #! lines -find "$RPM_BUILD_ROOT" -type f | xargs egrep -n '^#! */usr/local/bin/python' \ - | grep ':1:#!' >/tmp/python-rpm-files.$$ || true -if [ -s /tmp/python-rpm-files.$$ ] -then - echo '*****************************************************' - cat /tmp/python-rpm-files.$$ - cat <<@EOF - ***************************************************** - There are still files referencing /usr/local/bin/python in the - install directory. They are listed above. Please fix the .spec - file and try again. If you are an end-user, you probably want - to report this to jafo-rpms@tummy.com as well. - ***************************************************** -@EOF - rm -f /tmp/python-rpm-files.$$ - exit 1 -fi -rm -f /tmp/python-rpm-files.$$ - -######## -# CLEAN -######## -%clean -[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf $RPM_BUILD_ROOT -rm -f mainpkg.files tools.files - -######## -# FILES -######## -%files -f mainpkg.files -%defattr(-,root,root) -%doc Misc/README Misc/cheatsheet Misc/Porting -%doc LICENSE Misc/ACKS Misc/HISTORY Misc/NEWS -%{__prefix}/man/man1/python%{binsuffix}.1* - -%attr(755,root,root) %dir %{__prefix}/include/python%{libvers} -%attr(755,root,root) %dir %{__prefix}/%{libdirname}/python%{libvers}/ -%{__prefix}/%{libdirname}/python%{libvers}/*.txt -%{__prefix}/%{libdirname}/python%{libvers}/*.py* -%{__prefix}/%{libdirname}/python%{libvers}/pdb.doc -%{__prefix}/%{libdirname}/python%{libvers}/profile.doc -%{__prefix}/%{libdirname}/python%{libvers}/curses -%{__prefix}/%{libdirname}/python%{libvers}/distutils -%{__prefix}/%{libdirname}/python%{libvers}/encodings -%{__prefix}/%{libdirname}/python%{libvers}/plat-linux2 -%{__prefix}/%{libdirname}/python%{libvers}/site-packages -%{__prefix}/%{libdirname}/python%{libvers}/test -%{__prefix}/%{libdirname}/python%{libvers}/xml -%{__prefix}/%{libdirname}/python%{libvers}/email -%{__prefix}/%{libdirname}/python%{libvers}/compiler -%{__prefix}/%{libdirname}/python%{libvers}/bsddb -%{__prefix}/%{libdirname}/python%{libvers}/hotshot -%{__prefix}/%{libdirname}/python%{libvers}/logging -%{__prefix}/%{libdirname}/python%{libvers}/lib-old - -%files devel -%defattr(-,root,root) -%{__prefix}/include/python%{libvers}/*.h -%{__prefix}/%{libdirname}/python%{libvers}/config - -%files -f tools.files tools -%defattr(-,root,root) - -%if %{include_tkinter} -%files tkinter -%defattr(-,root,root) -%{__prefix}/%{libdirname}/python%{libvers}/lib-tk -%{__prefix}/%{libdirname}/python%{libvers}/lib-dynload/_tkinter.so* -%endif - -%if %{include_docs} -%files docs -%defattr(-,root,root) -%{config_htmldir}/* -%endif diff --git a/Misc/RPM/python-2.5.spec b/Misc/RPM/python-2.5.spec new file mode 100644 index 0000000..3515856 --- /dev/null +++ b/Misc/RPM/python-2.5.spec @@ -0,0 +1,385 @@ +########################## +# User-modifiable configs +########################## + +# Is the resulting package and the installed binary named "python" or +# "python2"? +#WARNING: Commenting out doesn't work. Last line is what's used. +%define config_binsuffix none +%define config_binsuffix 2.5 + +# Build tkinter? "auto" enables it if /usr/bin/wish exists. +#WARNING: Commenting out doesn't work. Last line is what's used. +%define config_tkinter no +%define config_tkinter yes +%define config_tkinter auto + +# Use pymalloc? The last line (commented or not) determines wether +# pymalloc is used. +#WARNING: Commenting out doesn't work. Last line is what's used. +%define config_pymalloc no +%define config_pymalloc yes + +# Enable IPV6? +#WARNING: Commenting out doesn't work. Last line is what's used. +%define config_ipv6 yes +%define config_ipv6 no + +# Location of the HTML directory. +%define config_htmldir /var/www/html/python + +################################# +# End of user-modifiable configs +################################# + +%define name python +%define version 2.5a1 +%define libvers 2.5 +%define release 1pydotorg +%define __prefix /usr + +# kludge to get around rpm define weirdness +%define ipv6 %(if [ "%{config_ipv6}" = yes ]; then echo --enable-ipv6; else echo --disable-ipv6; fi) +%define pymalloc %(if [ "%{config_pymalloc}" = yes ]; then echo --with-pymalloc; else echo --without-pymalloc; fi) +%define binsuffix %(if [ "%{config_binsuffix}" = none ]; then echo ; else echo "%{config_binsuffix}"; fi) +%define include_tkinter %(if [ \\( "%{config_tkinter}" = auto -a -f /usr/bin/wish \\) -o "%{config_tkinter}" = yes ]; then echo 1; else echo 0; fi) +%define libdirname %(( uname -m | egrep -q '_64$' && [ -d /usr/lib64 ] && echo lib64 ) || echo lib) + +# detect if documentation is available +%define include_docs %(if [ -f "%{_sourcedir}/html-%{version}.tar.bz2" ]; then echo 1; else echo 0; fi) + +Summary: An interpreted, interactive, object-oriented programming language. +Name: %{name}%{binsuffix} +Version: %{version} +Release: %{release} +Copyright: Modified CNRI Open Source License +Group: Development/Languages +Source: Python-%{version}.tar.bz2 +%if %{include_docs} +Source1: html-%{version}.tar.bz2 +%endif +BuildRoot: %{_tmppath}/%{name}-%{version}-root +BuildPrereq: expat-devel +BuildPrereq: db4-devel +BuildPrereq: gdbm-devel +BuildPrereq: sqlite-devel +Prefix: %{__prefix} +Packager: Sean Reifschneider + +%description +Python is an interpreted, interactive, object-oriented programming +language. It incorporates modules, exceptions, dynamic typing, very high +level dynamic data types, and classes. Python combines remarkable power +with very clear syntax. It has interfaces to many system calls and +libraries, as well as to various window systems, and is extensible in C or +C++. It is also usable as an extension language for applications that need +a programmable interface. Finally, Python is portable: it runs on many +brands of UNIX, on PCs under Windows, MS-DOS, and OS/2, and on the +Mac. + +%package devel +Summary: The libraries and header files needed for Python extension development. +Prereq: python%{binsuffix} = %{PACKAGE_VERSION} +Group: Development/Libraries + +%description devel +The Python programming language's interpreter can be extended with +dynamically loaded extensions and can be embedded in other programs. +This package contains the header files and libraries needed to do +these types of tasks. + +Install python-devel if you want to develop Python extensions. The +python package will also need to be installed. You'll probably also +want to install the python-docs package, which contains Python +documentation. + +%if %{include_tkinter} +%package tkinter +Summary: A graphical user interface for the Python scripting language. +Group: Development/Languages +Prereq: python%{binsuffix} = %{PACKAGE_VERSION}-%{release} + +%description tkinter +The Tkinter (Tk interface) program is an graphical user interface for +the Python scripting language. + +You should install the tkinter package if you'd like to use a graphical +user interface for Python programming. +%endif + +%package tools +Summary: A collection of development tools included with Python. +Group: Development/Tools +Prereq: python%{binsuffix} = %{PACKAGE_VERSION}-%{release} + +%description tools +The Python package includes several development tools that are used +to build python programs. This package contains a selection of those +tools, including the IDLE Python IDE. + +Install python-tools if you want to use these tools to develop +Python programs. You will also need to install the python and +tkinter packages. + +%if %{include_docs} +%package docs +Summary: Python-related documentation. +Group: Development/Documentation + +%description docs +Documentation relating to the Python programming language in HTML and info +formats. +%endif + +%changelog +* Mon Dec 20 2004 Sean Reifschneider [2.4-2pydotorg] +- Changing the idle wrapper so that it passes arguments to idle. + +* Tue Oct 19 2004 Sean Reifschneider [2.4b1-1pydotorg] +- Updating to 2.4. + +* Thu Jul 22 2004 Sean Reifschneider [2.3.4-3pydotorg] +- Paul Tiemann fixes for %{prefix}. +- Adding permission changes for directory as suggested by reimeika.ca +- Adding code to detect when it should be using lib64. +- Adding a define for the location of /var/www/html for docs. + +* Thu May 27 2004 Sean Reifschneider [2.3.4-2pydotorg] +- Including changes from Ian Holsman to build under Red Hat 7.3. +- Fixing some problems with the /usr/local path change. + +* Sat Mar 27 2004 Sean Reifschneider [2.3.2-3pydotorg] +- Being more agressive about finding the paths to fix for + #!/usr/local/bin/python. + +* Sat Feb 07 2004 Sean Reifschneider [2.3.3-2pydotorg] +- Adding code to remove "#!/usr/local/bin/python" from particular files and + causing the RPM build to terminate if there are any unexpected files + which have that line in them. + +* Mon Oct 13 2003 Sean Reifschneider [2.3.2-1pydotorg] +- Adding code to detect wether documentation is available to build. + +* Fri Sep 19 2003 Sean Reifschneider [2.3.1-1pydotorg] +- Updating to the 2.3.1 release. + +* Mon Feb 24 2003 Sean Reifschneider [2.3b1-1pydotorg] +- Updating to 2.3b1 release. + +* Mon Feb 17 2003 Sean Reifschneider [2.3a1-1] +- Updating to 2.3 release. + +* Sun Dec 23 2001 Sean Reifschneider +[Release 2.2-2] +- Added -docs package. +- Added "auto" config_tkinter setting which only enables tk if + /usr/bin/wish exists. + +* Sat Dec 22 2001 Sean Reifschneider +[Release 2.2-1] +- Updated to 2.2. +- Changed the extension to "2" from "2.2". + +* Tue Nov 18 2001 Sean Reifschneider +[Release 2.2c1-1] +- Updated to 2.2c1. + +* Thu Nov 1 2001 Sean Reifschneider +[Release 2.2b1-3] +- Changed the way the sed for fixing the #! in pydoc works. + +* Wed Oct 24 2001 Sean Reifschneider +[Release 2.2b1-2] +- Fixed missing "email" package, thanks to anonymous report on sourceforge. +- Fixed missing "compiler" package. + +* Mon Oct 22 2001 Sean Reifschneider +[Release 2.2b1-1] +- Updated to 2.2b1. + +* Mon Oct 9 2001 Sean Reifschneider +[Release 2.2a4-4] +- otto@balinor.mat.unimi.it mentioned that the license file is missing. + +* Sun Sep 30 2001 Sean Reifschneider +[Release 2.2a4-3] +- Ignacio Vazquez-Abrams pointed out that I had a spruious double-quote in + the spec files. Thanks. + +* Wed Jul 25 2001 Sean Reifschneider +[Release 2.2a1-1] +- Updated to 2.2a1 release. +- Changed idle and pydoc to use binsuffix macro + +####### +# PREP +####### +%prep +%setup -n Python-%{version} + +######## +# BUILD +######## +%build +./configure --enable-unicode=ucs4 %{ipv6} %{pymalloc} --prefix=%{__prefix} +make + +########## +# INSTALL +########## +%install +# set the install path +echo '[install_scripts]' >setup.cfg +echo 'install_dir='"${RPM_BUILD_ROOT}%{__prefix}/bin" >>setup.cfg + +[ -d "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT%{__prefix}/%{libdirname}/python%{libvers}/lib-dynload +make prefix=$RPM_BUILD_ROOT%{__prefix} install + +# REPLACE PATH IN PYDOC +if [ ! -z "%{binsuffix}" ] +then + ( + cd $RPM_BUILD_ROOT%{__prefix}/bin + mv pydoc pydoc.old + sed 's|#!.*|#!%{__prefix}/bin/env python'%{binsuffix}'|' \ + pydoc.old >pydoc + chmod 755 pydoc + rm -f pydoc.old + ) +fi + +# add the binsuffix +if [ ! -z "%{binsuffix}" ] +then + ( cd $RPM_BUILD_ROOT%{__prefix}/bin; rm -f python[0-9a-zA-Z]*; + mv -f python python"%{binsuffix}" ) + ( cd $RPM_BUILD_ROOT%{__prefix}/man/man1; mv python.1 python%{binsuffix}.1 ) + ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f pydoc pydoc"%{binsuffix}" ) + ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f idle idle"%{binsuffix}" ) +fi + +######## +# Tools +echo '#!%{__prefix}/bin/env python%{binsuffix}' >${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} +echo 'import os, sys' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} +echo 'os.execvp("%{__prefix}/bin/python%{binsuffix}", ["%{__prefix}/bin/python%{binsuffix}", "%{__prefix}/lib/python%{libvers}/idlelib/idle.py"] + sys.argv[1:])' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} +echo 'print "Failed to exec Idle"' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} +echo 'sys.exit(1)' >>${RPM_BUILD_ROOT}%{__prefix}/bin/idle%{binsuffix} +chmod 755 $RPM_BUILD_ROOT%{__prefix}/bin/idle%{binsuffix} +cp -a Tools $RPM_BUILD_ROOT%{__prefix}/%{libdirname}/python%{libvers} + +# MAKE FILE LISTS +rm -f mainpkg.files +find "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/lib-dynload -type f | + sed "s|^${RPM_BUILD_ROOT}|/|" | + grep -v -e '_tkinter.so$' >mainpkg.files +find "$RPM_BUILD_ROOT""%{__prefix}"/bin -type f | + sed "s|^${RPM_BUILD_ROOT}|/|" | + grep -v -e '/bin/idle%{binsuffix}$' >>mainpkg.files + +rm -f tools.files +find "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/idlelib \ + "$RPM_BUILD_ROOT""%{__prefix}"/%{libdirname}/python%{libvers}/Tools -type f | + sed "s|^${RPM_BUILD_ROOT}|/|" >tools.files +echo "%{__prefix}"/bin/idle%{binsuffix} >>tools.files + +###### +# Docs +%if %{include_docs} +mkdir -p "$RPM_BUILD_ROOT"%{config_htmldir} +( + cd "$RPM_BUILD_ROOT"%{config_htmldir} + bunzip2 < %{SOURCE1} | tar x +) +%endif + +# fix the #! line in installed files +find "$RPM_BUILD_ROOT" -type f -print0 | + xargs -0 grep -l /usr/local/bin/python | while read file +do + FIXFILE="$file" + sed 's|^#!.*python|#!%{__prefix}/bin/env python'"%{binsuffix}"'|' \ + "$FIXFILE" >/tmp/fix-python-path.$$ + cat /tmp/fix-python-path.$$ >"$FIXFILE" + rm -f /tmp/fix-python-path.$$ +done + +# check to see if there are any straggling #! lines +find "$RPM_BUILD_ROOT" -type f | xargs egrep -n '^#! */usr/local/bin/python' \ + | grep ':1:#!' >/tmp/python-rpm-files.$$ || true +if [ -s /tmp/python-rpm-files.$$ ] +then + echo '*****************************************************' + cat /tmp/python-rpm-files.$$ + cat <<@EOF + ***************************************************** + There are still files referencing /usr/local/bin/python in the + install directory. They are listed above. Please fix the .spec + file and try again. If you are an end-user, you probably want + to report this to jafo-rpms@tummy.com as well. + ***************************************************** +@EOF + rm -f /tmp/python-rpm-files.$$ + exit 1 +fi +rm -f /tmp/python-rpm-files.$$ + +######## +# CLEAN +######## +%clean +[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf $RPM_BUILD_ROOT +rm -f mainpkg.files tools.files + +######## +# FILES +######## +%files -f mainpkg.files +%defattr(-,root,root) +%doc Misc/README Misc/cheatsheet Misc/Porting +%doc LICENSE Misc/ACKS Misc/HISTORY Misc/NEWS +%{__prefix}/man/man1/python%{binsuffix}.1* + +%attr(755,root,root) %dir %{__prefix}/include/python%{libvers} +%attr(755,root,root) %dir %{__prefix}/%{libdirname}/python%{libvers}/ +%{__prefix}/%{libdirname}/python%{libvers}/*.txt +%{__prefix}/%{libdirname}/python%{libvers}/*.py* +%{__prefix}/%{libdirname}/python%{libvers}/pdb.doc +%{__prefix}/%{libdirname}/python%{libvers}/profile.doc +%{__prefix}/%{libdirname}/python%{libvers}/curses +%{__prefix}/%{libdirname}/python%{libvers}/distutils +%{__prefix}/%{libdirname}/python%{libvers}/encodings +%{__prefix}/%{libdirname}/python%{libvers}/plat-linux2 +%{__prefix}/%{libdirname}/python%{libvers}/site-packages +%{__prefix}/%{libdirname}/python%{libvers}/test +%{__prefix}/%{libdirname}/python%{libvers}/xml +%{__prefix}/%{libdirname}/python%{libvers}/email +%{__prefix}/%{libdirname}/python%{libvers}/email/mime +%{__prefix}/%{libdirname}/python%{libvers}/sqlite3 +%{__prefix}/%{libdirname}/python%{libvers}/compiler +%{__prefix}/%{libdirname}/python%{libvers}/bsddb +%{__prefix}/%{libdirname}/python%{libvers}/hotshot +%{__prefix}/%{libdirname}/python%{libvers}/logging +%{__prefix}/%{libdirname}/python%{libvers}/lib-old + +%files devel +%defattr(-,root,root) +%{__prefix}/include/python%{libvers}/*.h +%{__prefix}/%{libdirname}/python%{libvers}/config + +%files -f tools.files tools +%defattr(-,root,root) + +%if %{include_tkinter} +%files tkinter +%defattr(-,root,root) +%{__prefix}/%{libdirname}/python%{libvers}/lib-tk +%{__prefix}/%{libdirname}/python%{libvers}/lib-dynload/_tkinter.so* +%endif + +%if %{include_docs} +%files docs +%defattr(-,root,root) +%{config_htmldir}/* +%endif diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt index ae9ab4c..e0b3315 100644 --- a/Misc/SpecialBuilds.txt +++ b/Misc/SpecialBuilds.txt @@ -1,8 +1,8 @@ This file describes some special Python build types enabled via compile-time preprocessor defines. -It is best to define these options in the EXTRA_CFLAGS environment variable; -``EXTRA_CFLAGS="-DPy_REF_DEBUG" ./configure``. +It is best to define these options in the EXTRA_CFLAGS make variable; +``make EXTRA_CFLAGS="-DPy_REF_DEBUG"``. --------------------------------------------------------------------------- Py_REF_DEBUG introduced in 1.4 diff --git a/Misc/build.sh b/Misc/build.sh index 3c669a0..de51539 100755 --- a/Misc/build.sh +++ b/Misc/build.sh @@ -55,13 +55,21 @@ INSTALL_DIR="/tmp/python-test/local" RSYNC_OPTS="-aC -e ssh" REFLOG="build/reflog.txt.out" -# These tests are not stable and sometimes report leaks; however, -# test_generators really leaks. Since test_generators probably won't -# be fixed real soon, disable warning about it for now. +# These tests are not stable and falsely report leaks sometimes. # The entire leak report will be mailed if any test not in this list leaks. -LEAKY_TESTS="test_(capi|cfgparser|charmapcodec|cmd_line|compiler|filecmp|generators|quopri|socket|threaded_import|threadedtempfile|threading|threading_local|urllib2)" - -# Change this flag to "yes" for old releases to just update/build the docs. +# Note: test_XXX (none currently) really leak, but are disabled +# so we don't send spam. Any test which really leaks should only +# be listed here if there are also test cases under Lib/test/leakers. +LEAKY_TESTS="test_(ctypes|filecmp|socket|threadedtempfile|threading|urllib2)" + +# Skip these tests altogether when looking for leaks. These tests +# do not need to be stored above in LEAKY_TESTS too. +# test_compiler almost never finishes with the same number of refs +# since it depends on other modules, skip it. +# test_logging causes hangs, skip it. +LEAKY_SKIPS="-x test_compiler test_logging" + +# Change this flag to "yes" for old releases to only update/build the docs. BUILD_DISABLED="no" ## utility functions @@ -159,7 +167,9 @@ if [ $err = 0 -a "$BUILD_DISABLED" != "yes" ]; then ## run the tests looking for leaks F=make-test-refleak.out start=`current_time` - ./python ./Lib/test/regrtest.py -R 4:3:$REFLOG -u network >& build/$F + ## ensure that the reflog exists so the grep doesn't fail + touch $REFLOG + ./python ./Lib/test/regrtest.py -R 4:3:$REFLOG -u network $LEAKY_SKIPS >& build/$F NUM_FAILURES=`egrep -vc "$LEAKY_TESTS" $REFLOG` update_status "Testing refleaks ($NUM_FAILURES failures)" "$F" $start mail_on_failure "refleak" $REFLOG diff --git a/Misc/cheatsheet b/Misc/cheatsheet index b8de4281..024545d 100644 --- a/Misc/cheatsheet +++ b/Misc/cheatsheet @@ -1946,8 +1946,6 @@ quopri Conversions to/from quoted-printable transport encoding. rand Don't use unless you want compatibility with C's rand(). random Random variable generators re Regular Expressions. -reconvert Convert old ("regex") regular expressions to new syntax - ("re"). repr Redo repr() but with limits on most sizes. rexec Restricted execution facilities ("safe" exec, eval, etc). rfc822 RFC-822 message manipulation class. @@ -2025,7 +2023,6 @@ zipfile Read & write PK zipped files. array Obj efficiently representing arrays of basic values math Math functions of C standard time Time-related functions (also the newer datetime module) - regex Regular expression matching operations marshal Read and write some python values in binary format struct Convert between python values and C structs diff --git a/Misc/developers.txt b/Misc/developers.txt index c3dc306..ff8470e 100644 --- a/Misc/developers.txt +++ b/Misc/developers.txt @@ -17,6 +17,12 @@ the format to accommodate documentation needs as they arise. Permissions History ------------------- +- George Yoshida (SF name "quiver") added to the SourceForge Python + project 14 Apr 2006, by Tim Peters, as a tracker admin. See + contemporaneous python-checkins thread with the unlikely Subject: + + r45329 - python/trunk/Doc/whatsnew/whatsnew25.tex + - Ronald Oussoren was given SVN access on 3 Mar 2006 by NCN, for Mac related work. diff --git a/Misc/python-config.in b/Misc/python-config.in new file mode 100644 index 0000000..24e699e --- /dev/null +++ b/Misc/python-config.in @@ -0,0 +1,50 @@ +#!@BINDIR@/python + +import sys +import os +import getopt +from distutils import sysconfig + +valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags', + 'ldflags', 'help'] + +def exit_with_usage(code=1): + print >>sys.stderr, "Usage: %s [%s]" % (sys.argv[0], + '|'.join('--'+opt for opt in valid_opts)) + sys.exit(code) + +try: + opts, args = getopt.getopt(sys.argv[1:], '', valid_opts) +except getopt.error: + exit_with_usage() + +if not opts: + exit_with_usage() + +opt = opts[0][0] + +pyver = sysconfig.get_config_var('VERSION') +getvar = sysconfig.get_config_var + +if opt == '--help': + exit_with_usage(0) + +elif opt == '--prefix': + print sysconfig.PREFIX + +elif opt == '--exec-prefix': + print sysconfig.EXEC_PREFIX + +elif opt in ('--includes', '--cflags'): + flags = ['-I'+dir for dir in getvar('INCLDIRSTOMAKE').split()] + if opt == '--cflags': + flags.extend(getvar('CFLAGS').split()) + print ' '.join(flags) + +elif opt in ('--libs', '--ldflags'): + libs = sysconfig.get_config_var('LIBS').split() + libs.append('-lpython'+pyver) + if opt == '--ldflags': + libs.insert(0, '-L' + getvar('LIBPL')) + print ' '.join(libs) + diff --git a/Modules/Setup.dist b/Modules/Setup.dist index 3a512b5..49c8425 100644 --- a/Modules/Setup.dist +++ b/Modules/Setup.dist @@ -236,9 +236,9 @@ GLHACK=-Dclear=__GLclear # The md5 module implements the RSA Data Security, Inc. MD5 # Message-Digest Algorithm, described in RFC 1321. The necessary files -# md5c.c and md5.h are included here. +# md5.c and md5.h are included here. -#md5 md5module.c md5c.c +#md5 md5module.c md5.c # The sha module implements the SHA checksum algorithm. diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c index 8e9ec0f..1641e20 100644 --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -101,6 +101,10 @@ static char *rcs_id = "$Id$"; +#if (PY_VERSION_HEX < 0x02050000) +#define Py_ssize_t int +#endif + #ifdef WITH_THREAD /* These are for when calling Python --> C */ @@ -4688,7 +4692,11 @@ static PyMethodDef DB_methods[] = { static PyMappingMethods DB_mapping = { +#if (PY_VERSION_HEX < 0x02050000) + (inquiry)DB_length, /*mp_length*/ +#else (lenfunc)DB_length, /*mp_length*/ +#endif (binaryfunc)DB_subscript, /*mp_subscript*/ (objobjargproc)DB_ass_sub, /*mp_ass_subscript*/ }; @@ -5385,9 +5393,21 @@ DL_EXPORT(void) init_bsddb(void) ADD_INT(d, DB_SET_TXN_TIMEOUT); #endif + /* The exception name must be correct for pickled exception * + * objects to unpickle properly. */ +#ifdef PYBSDDB_STANDALONE /* different value needed for standalone pybsddb */ +#define PYBSDDB_EXCEPTION_BASE "bsddb3.db." +#else +#define PYBSDDB_EXCEPTION_BASE "bsddb.db." +#endif + + /* All the rest of the exceptions derive only from DBError */ +#define MAKE_EX(name) name = PyErr_NewException(PYBSDDB_EXCEPTION_BASE #name, DBError, NULL); \ + PyDict_SetItemString(d, #name, name) + /* The base exception class is DBError */ - DBError = PyErr_NewException("bsddb._db.DBError", NULL, NULL); - PyDict_SetItemString(d, "DBError", DBError); + DBError = NULL; /* used in MAKE_EX so that it derives from nothing */ + MAKE_EX(DBError); /* Some magic to make DBNotFoundError and DBKeyEmptyError derive * from both DBError and KeyError, since the API only supports @@ -5401,10 +5421,6 @@ DL_EXPORT(void) init_bsddb(void) PyDict_DelItemString(d, "KeyError"); - /* All the rest of the exceptions derive only from DBError */ -#define MAKE_EX(name) name = PyErr_NewException("bsddb._db." #name, DBError, NULL); \ - PyDict_SetItemString(d, #name, name) - #if !INCOMPLETE_IS_WARNING MAKE_EX(DBIncompleteError); #endif diff --git a/Modules/_csv.c b/Modules/_csv.c index 469cd64..4704c16 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -37,6 +37,28 @@ module instead. # define PyMODINIT_FUNC void # endif /* __cplusplus */ #endif + +#ifndef Py_CLEAR +#define Py_CLEAR(op) \ + do { \ + if (op) { \ + PyObject *tmp = (PyObject *)(op); \ + (op) = NULL; \ + Py_DECREF(tmp); \ + } \ + } while (0) +#endif +#ifndef Py_VISIT +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((PyObject *)(op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) +#endif + /* end 2.2 compatibility macros */ #define IS_BASESTRING(o) \ @@ -812,28 +834,18 @@ Reader_dealloc(ReaderObj *self) static int Reader_traverse(ReaderObj *self, visitproc visit, void *arg) { - int err; -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - VISIT(self->dialect); - VISIT(self->input_iter); - VISIT(self->fields); + Py_VISIT(self->dialect); + Py_VISIT(self->input_iter); + Py_VISIT(self->fields); return 0; } static int Reader_clear(ReaderObj *self) { - Py_XDECREF(self->dialect); - Py_XDECREF(self->input_iter); - Py_XDECREF(self->fields); - self->dialect = NULL; - self->input_iter = NULL; - self->fields = NULL; + Py_CLEAR(self->dialect); + Py_CLEAR(self->input_iter); + Py_CLEAR(self->fields); return 0; } @@ -1245,25 +1257,16 @@ Writer_dealloc(WriterObj *self) static int Writer_traverse(WriterObj *self, visitproc visit, void *arg) { - int err; -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - VISIT(self->dialect); - VISIT(self->writeline); + Py_VISIT(self->dialect); + Py_VISIT(self->writeline); return 0; } static int Writer_clear(WriterObj *self) { - Py_XDECREF(self->dialect); - Py_XDECREF(self->writeline); - self->dialect = NULL; - self->writeline = NULL; + Py_CLEAR(self->dialect); + Py_CLEAR(self->writeline); return 0; } @@ -1495,7 +1498,7 @@ PyDoc_STRVAR(csv_reader_doc, PyDoc_STRVAR(csv_writer_doc, " csv_writer = csv.writer(fileobj [, dialect='excel']\n" " [optional keyword args])\n" -" for row in csv_writer:\n" +" for row in sequence:\n" " csv_writer.writerow(row)\n" "\n" " [or]\n" diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 926c85b..e6d6aa4 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -105,6 +105,10 @@ bytes(cdata) #include #ifdef MS_WIN32 #include +#include +#ifndef IS_INTRESOURCE +#define IS_INTRESOURCE(x) (((size_t)(x) >> 16) == 0) +#endif # ifdef _WIN32_WCE /* Unlike desktop Windows, WinCE has both W and A variants of GetProcAddress, but the default W version is not what we want */ @@ -285,6 +289,7 @@ CDataType_from_param(PyObject *type, PyObject *value) if (PyCArg_CheckExact(value)) { PyCArgObject *p = (PyCArgObject *)value; PyObject *ob = p->obj; + const char *ob_name; StgDictObject *dict; dict = PyType_stgdict(type); @@ -296,10 +301,10 @@ CDataType_from_param(PyObject *type, PyObject *value) Py_INCREF(value); return value; } + ob_name = (ob) ? ob->ob_type->tp_name : "???"; PyErr_Format(PyExc_TypeError, "expected %s instance instead of pointer to %s", - ((PyTypeObject *)type)->tp_name, - ob->ob_type->tp_name); + ((PyTypeObject *)type)->tp_name, ob_name); return NULL; } #if 1 @@ -506,12 +511,12 @@ size property/method, and the sequence protocol. static int PointerType_SetProto(StgDictObject *stgdict, PyObject *proto) { - if (proto && !PyType_Check(proto)) { + if (!proto || !PyType_Check(proto)) { PyErr_SetString(PyExc_TypeError, "_type_ must be a type"); return -1; } - if (proto && !PyType_stgdict(proto)) { + if (!PyType_stgdict(proto)) { PyErr_SetString(PyExc_TypeError, "_type_ must have storage info"); return -1; @@ -543,8 +548,8 @@ PointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; stgdict->size = sizeof(void *); stgdict->align = getentry("P")->pffi_type->alignment; - stgdict->length = 2; - stgdict->ffi_type = ffi_type_pointer; + stgdict->length = 1; + stgdict->ffi_type_pointer = ffi_type_pointer; proto = PyDict_GetItemString(typedict, "_type_"); /* Borrowed ref */ if (proto && -1 == PointerType_SetProto(stgdict, proto)) { @@ -899,7 +904,7 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *typedict; int length; - int itemsize, itemalign, itemlen; + int itemsize, itemalign; typedict = PyTuple_GetItem(args, 2); if (!typedict) @@ -936,7 +941,6 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) itemsize = itemdict->size; itemalign = itemdict->align; - itemlen = itemdict->length; stgdict->size = itemsize * length; stgdict->align = itemalign; @@ -945,7 +949,7 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->proto = proto; /* Arrays are passed as pointers to function calls. */ - stgdict->ffi_type = ffi_type_pointer; + stgdict->ffi_type_pointer = ffi_type_pointer; /* create the new instance (which is a class, since we are a metatype!) */ @@ -1264,9 +1268,13 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject PyTypeObject *result; StgDictObject *stgdict; PyObject *name = PyTuple_GET_ITEM(args, 0); - PyObject *swapped_args = PyTuple_New(PyTuple_GET_SIZE(args)); + PyObject *swapped_args; static PyObject *suffix; - int i; + Py_ssize_t i; + + swapped_args = PyTuple_New(PyTuple_GET_SIZE(args)); + if (!swapped_args) + return NULL; if (suffix == NULL) #ifdef WORDS_BIGENDIAN @@ -1275,8 +1283,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject suffix = PyString_FromString("_be"); #endif - Py_INCREF(suffix); - PyString_ConcatAndDel(&name, suffix); + Py_INCREF(name); + PyString_Concat(&name, suffix); + if (name == NULL) + return NULL; PyTuple_SET_ITEM(swapped_args, 0, name); for (i=1; iffi_type = *fmt->pffi_type; + stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; stgdict->length = 0; stgdict->size = fmt->pffi_type->size; @@ -1355,7 +1365,7 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) fmt = getentry(PyString_AS_STRING(proto)); - stgdict->ffi_type = *fmt->pffi_type; + stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; stgdict->length = 0; stgdict->size = fmt->pffi_type->size; @@ -1450,6 +1460,7 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result); PyObject_SetAttrString(swapped, "__ctype_be__", swapped); #endif + Py_DECREF(swapped); }; return (PyObject *)result; @@ -1624,7 +1635,7 @@ make_funcptrtype_dict(StgDictObject *stgdict) stgdict->size = sizeof(void *); stgdict->setfunc = NULL; stgdict->getfunc = NULL; - stgdict->ffi_type = ffi_type_pointer; + stgdict->ffi_type_pointer = ffi_type_pointer; ob = PyDict_GetItemString((PyObject *)stgdict, "_flags_"); if (!ob || !PyInt_Check(ob)) { @@ -1846,7 +1857,7 @@ CData_clear(CDataObject *self) StgDictObject *dict = PyObject_stgdict((PyObject *)self); Py_CLEAR(self->b_objects); if ((self->b_needsfree) - && (dict->size > sizeof(self->b_value))) + && ((size_t)dict->size > sizeof(self->b_value))) PyMem_Free(self->b_ptr); self->b_ptr = NULL; Py_CLEAR(self->b_base); @@ -1873,8 +1884,9 @@ static PyMemberDef CData_members[] = { { NULL }, }; -static Py_ssize_t CData_GetBuffer(CDataObject *self, Py_ssize_t seg, void **pptr) +static Py_ssize_t CData_GetBuffer(PyObject *_self, Py_ssize_t seg, void **pptr) { + CDataObject *self = (CDataObject *)_self; if (seg != 0) { /* Hm. Must this set an exception? */ return -1; @@ -1883,7 +1895,7 @@ static Py_ssize_t CData_GetBuffer(CDataObject *self, Py_ssize_t seg, void **pptr return self->b_size; } -static Py_ssize_t CData_GetSegcount(CDataObject *self, Py_ssize_t *lenp) +static Py_ssize_t CData_GetSegcount(PyObject *_self, Py_ssize_t *lenp) { if (lenp) *lenp = 1; @@ -1891,10 +1903,10 @@ static Py_ssize_t CData_GetSegcount(CDataObject *self, Py_ssize_t *lenp) } static PyBufferProcs CData_as_buffer = { - (readbufferproc)CData_GetBuffer, - (writebufferproc)CData_GetBuffer, - (segcountproc)CData_GetSegcount, - (charbufferproc)NULL, + CData_GetBuffer, + CData_GetBuffer, + CData_GetSegcount, + NULL, }; /* @@ -1967,7 +1979,7 @@ PyTypeObject CData_Type = { static void CData_MallocBuffer(CDataObject *obj, StgDictObject *dict) { - if (dict->size <= sizeof(obj->b_value)) { + if ((size_t)dict->size <= sizeof(obj->b_value)) { /* No need to call malloc, can use the default buffer */ obj->b_ptr = (char *)&obj->b_value; obj->b_needsfree = 1; @@ -1975,7 +1987,7 @@ static void CData_MallocBuffer(CDataObject *obj, StgDictObject *dict) /* In python 2.4, and ctypes 0.9.6, the malloc call took about 33% of the creation time for c_int(). */ - obj->b_ptr = PyMem_Malloc(dict->size); + obj->b_ptr = (char *)PyMem_Malloc(dict->size); obj->b_needsfree = 1; memset(obj->b_ptr, 0, dict->size); } @@ -2040,7 +2052,7 @@ CData_AtAddress(PyObject *type, void *buf) if (!pd) return NULL; assert(CDataObject_Check(pd)); - pd->b_ptr = buf; + pd->b_ptr = (char *)buf; pd->b_length = dict->length; pd->b_size = dict->size; return (PyObject *)pd; @@ -2383,6 +2395,11 @@ static PPROC FindAddress(void *handle, char *name, PyObject *type) address = (PPROC)GetProcAddress(handle, name); if (address) return address; + + if (((size_t)name & ~0xFFFF) == 0) { + return NULL; + } + /* It should not happen that dict is NULL, but better be safe */ if (dict==NULL || dict->flags & FUNCFLAG_CDECL) return address; @@ -2391,7 +2408,7 @@ static PPROC FindAddress(void *handle, char *name, PyObject *type) funcname -> _funcname@ where n is 0, 4, 8, 12, ..., 128 */ - mangled_name = _alloca(strlen(name) + 1 + 1 + 1 + 3); /* \0 _ @ %d */ + mangled_name = alloca(strlen(name) + 1 + 1 + 1 + 3); /* \0 _ @ %d */ for (i = 0; i < 32; ++i) { sprintf(mangled_name, "_%s@%d", name, i*4); address = (PPROC)GetProcAddress(handle, mangled_name); @@ -2488,6 +2505,28 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) return 1; } +static int +_get_name(PyObject *obj, char **pname) +{ +#ifdef MS_WIN32 + if (PyInt_Check(obj) || PyLong_Check(obj)) { + /* We have to use MAKEINTRESOURCEA for Windows CE. + Works on Windows as well, of course. + */ + *pname = MAKEINTRESOURCEA(PyInt_AsUnsignedLongMask(obj) & 0xFFFF); + return 1; + } +#endif + if (PyString_Check(obj) || PyUnicode_Check(obj)) { + *pname = PyString_AsString(obj); + return *pname ? 1 : 0; + } + PyErr_SetString(PyExc_TypeError, + "function name must be string or integer"); + return 0; +} + + static PyObject * CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -2499,7 +2538,7 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) void *handle; PyObject *paramflags = NULL; - if (!PyArg_ParseTuple(args, "sO|O", &name, &dll, ¶mflags)) + if (!PyArg_ParseTuple(args, "(O&O)|O", _get_name, &name, &dll, ¶mflags)) return NULL; if (paramflags == Py_None) paramflags = NULL; @@ -2524,9 +2563,14 @@ CFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) #ifdef MS_WIN32 address = FindAddress(handle, name, (PyObject *)type); if (!address) { - PyErr_Format(PyExc_AttributeError, - "function '%s' not found", - name); + if (!IS_INTRESOURCE(name)) + PyErr_Format(PyExc_AttributeError, + "function '%s' not found", + name); + else + PyErr_Format(PyExc_AttributeError, + "function ordinal %d not found", + (WORD)(size_t)name); return NULL; } #else @@ -2603,8 +2647,9 @@ CFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds) "O" - must be a callable, creates a C callable function two or more argument forms (the third argument is a paramflags tuple) - "sO|O" - function name, dll object (with an integer handle) - "is|O" - vtable index, method name, creates callable calling COM vtbl + "(sO)|..." - (function name, dll object (with an integer handle)), paramflags + "(iO)|..." - (function ordinal, dll object (with an integer handle)), paramflags + "is|..." - vtable index, method name, creates callable calling COM vtbl */ static PyObject * CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -2612,19 +2657,18 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) CFuncPtrObject *self; PyObject *callable; StgDictObject *dict; - THUNK thunk; + ffi_info *thunk; if (PyTuple_GET_SIZE(args) == 0) return GenericCData_new(type, args, kwds); - /* Shouldn't the following better be done in __init__? */ - if (2 <= PyTuple_GET_SIZE(args)) { + if (1 <= PyTuple_GET_SIZE(args) && PyTuple_Check(PyTuple_GET_ITEM(args, 0))) + return CFuncPtr_FromDll(type, args, kwds); + #ifdef MS_WIN32 - if (PyInt_Check(PyTuple_GET_ITEM(args, 0))) - return CFuncPtr_FromVtblIndex(type, args, kwds); + if (2 <= PyTuple_GET_SIZE(args) && PyInt_Check(PyTuple_GET_ITEM(args, 0))) + return CFuncPtr_FromVtblIndex(type, args, kwds); #endif - return CFuncPtr_FromDll(type, args, kwds); - } if (1 == PyTuple_GET_SIZE(args) && (PyInt_Check(PyTuple_GET_ITEM(args, 0)) @@ -2781,7 +2825,7 @@ _get_arg(int *pindex, char *name, PyObject *defval, PyObject *inargs, PyObject * static PyObject * _build_callargs(CFuncPtrObject *self, PyObject *argtypes, PyObject *inargs, PyObject *kwds, - int *poutmask, int *pinoutmask, int *pnumretvals) + int *poutmask, int *pinoutmask, unsigned int *pnumretvals) { PyObject *paramflags = self->paramflags; PyObject *callargs; @@ -2835,8 +2879,14 @@ _build_callargs(CFuncPtrObject *self, PyObject *argtypes, switch (flag & (PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID)) { case PARAMFLAG_FIN | PARAMFLAG_FLCID: - /* ['in', 'lcid'] parameter. Always taken from defval */ - Py_INCREF(defval); + /* ['in', 'lcid'] parameter. Always taken from defval, + if given, else the integer 0. */ + if (defval == NULL) { + defval = PyInt_FromLong(0); + if (defval == NULL) + goto error; + } else + Py_INCREF(defval); PyTuple_SET_ITEM(callargs, i, defval); break; case (PARAMFLAG_FIN | PARAMFLAG_FOUT): @@ -2939,9 +2989,10 @@ _build_callargs(CFuncPtrObject *self, PyObject *argtypes, */ static PyObject * _build_result(PyObject *result, PyObject *callargs, - int outmask, int inoutmask, int numretvals) + int outmask, int inoutmask, unsigned int numretvals) { - int i, index, bit; + unsigned int i, index; + int bit; PyObject *tup = NULL; if (callargs == NULL) @@ -2952,6 +3003,7 @@ _build_result(PyObject *result, PyObject *callargs, } Py_DECREF(result); + /* tup will not be allocated if numretvals == 1 */ /* allocate tuple to hold the result */ if (numretvals > 1) { tup = PyTuple_New(numretvals); @@ -3009,7 +3061,7 @@ CFuncPtr_call(CFuncPtrObject *self, PyObject *inargs, PyObject *kwds) int inoutmask; int outmask; - int numretvals; + unsigned int numretvals; assert(dict); /* if not, it's a bug */ restype = self->restype ? self->restype : dict->restype; @@ -3145,9 +3197,11 @@ CFuncPtr_clear(CFuncPtrObject *self) Py_CLEAR(self->converters); Py_CLEAR(self->paramflags); - if (self->thunk) - FreeCallback(self->thunk); - self->thunk = NULL; + if (self->thunk) { + FreeClosure(self->thunk->pcl); + PyMem_Free(self->thunk); + self->thunk = NULL; + } return CData_clear((CDataObject *)self); } @@ -3241,7 +3295,7 @@ Struct_as_parameter(CDataObject *self) parg->tag = 'V'; stgdict = PyObject_stgdict((PyObject *)self); - parg->pffi_type = &stgdict->ffi_type; + parg->pffi_type = &stgdict->ffi_type_pointer; /* For structure parameters (by value), parg->value doesn't contain the structure data itself, instead parg->value.p *points* to the structure's data See also _ctypes.c, function _call_function_pointer(). @@ -3275,6 +3329,8 @@ Struct_init(PyObject *self, PyObject *args, PyObject *kwds) if (!fields) { PyErr_Clear(); fields = PyTuple_New(0); + if (!fields) + return -1; } if (PyTuple_GET_SIZE(args) > PySequence_Length(fields)) { @@ -3445,8 +3501,9 @@ Array_init(CDataObject *self, PyObject *args, PyObject *kw) } static PyObject * -Array_item(CDataObject *self, int index) +Array_item(PyObject *_self, Py_ssize_t index) { + CDataObject *self = (CDataObject *)_self; int offset, size; StgDictObject *stgdict; @@ -3469,8 +3526,9 @@ Array_item(CDataObject *self, int index) } static PyObject * -Array_slice(CDataObject *self, Py_ssize_t ilow, Py_ssize_t ihigh) +Array_slice(PyObject *_self, Py_ssize_t ilow, Py_ssize_t ihigh) { + CDataObject *self = (CDataObject *)_self; StgDictObject *stgdict, *itemdict; PyObject *proto; PyListObject *np; @@ -3504,15 +3562,16 @@ Array_slice(CDataObject *self, Py_ssize_t ilow, Py_ssize_t ihigh) return NULL; for (i = 0; i < len; i++) { - PyObject *v = Array_item(self, i+ilow); + PyObject *v = Array_item(_self, i+ilow); PyList_SET_ITEM(np, i, v); } return (PyObject *)np; } static int -Array_ass_item(CDataObject *self, int index, PyObject *value) +Array_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value) { + CDataObject *self = (CDataObject *)_self; int size, offset; StgDictObject *stgdict; char *ptr; @@ -3538,8 +3597,9 @@ Array_ass_item(CDataObject *self, int index, PyObject *value) } static int -Array_ass_slice(CDataObject *self, int ilow, int ihigh, PyObject *value) +Array_ass_slice(PyObject *_self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *value) { + CDataObject *self = (CDataObject *)_self; int i, len; if (value == NULL) { @@ -3570,7 +3630,7 @@ Array_ass_slice(CDataObject *self, int ilow, int ihigh, PyObject *value) int result; if (item == NULL) return -1; - result = Array_ass_item(self, i+ilow, item); + result = Array_ass_item(_self, i+ilow, item); Py_DECREF(item); if (result == -1) return -1; @@ -3578,20 +3638,21 @@ Array_ass_slice(CDataObject *self, int ilow, int ihigh, PyObject *value) return 0; } -static int -Array_length(CDataObject *self) +static Py_ssize_t +Array_length(PyObject *_self) { + CDataObject *self = (CDataObject *)_self; return self->b_length; } static PySequenceMethods Array_as_sequence = { - (lenfunc)Array_length, /* sq_length; */ + Array_length, /* sq_length; */ 0, /* sq_concat; */ 0, /* sq_repeat; */ - (ssizeargfunc)Array_item, /* sq_item; */ - (ssizessizeargfunc)Array_slice, /* sq_slice; */ - (ssizeobjargproc)Array_ass_item, /* sq_ass_item; */ - (ssizessizeobjargproc)Array_ass_slice, /* sq_ass_slice; */ + Array_item, /* sq_item; */ + Array_slice, /* sq_slice; */ + Array_ass_item, /* sq_ass_item; */ + Array_ass_slice, /* sq_ass_slice; */ 0, /* sq_contains; */ 0, /* sq_inplace_concat; */ @@ -3942,8 +4003,9 @@ static PyTypeObject Simple_Type = { Pointer_Type */ static PyObject * -Pointer_item(CDataObject *self, int index) +Pointer_item(PyObject *_self, Py_ssize_t index) { + CDataObject *self = (CDataObject *)_self; int size, offset; StgDictObject *stgdict, *itemdict; PyObject *proto; @@ -3969,8 +4031,9 @@ Pointer_item(CDataObject *self, int index) } static int -Pointer_ass_item(CDataObject *self, int index, PyObject *value) +Pointer_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value) { + CDataObject *self = (CDataObject *)_self; int size; StgDictObject *stgdict; @@ -4111,8 +4174,9 @@ Pointer_new(PyTypeObject *type, PyObject *args, PyObject *kw) } static PyObject * -Pointer_slice(CDataObject *self, Py_ssize_t ilow, Py_ssize_t ihigh) +Pointer_slice(PyObject *_self, Py_ssize_t ilow, Py_ssize_t ihigh) { + CDataObject *self = (CDataObject *)_self; PyListObject *np; StgDictObject *stgdict, *itemdict; PyObject *proto; @@ -4142,7 +4206,7 @@ Pointer_slice(CDataObject *self, Py_ssize_t ilow, Py_ssize_t ihigh) return NULL; for (i = 0; i < len; i++) { - PyObject *v = Pointer_item(self, i+ilow); + PyObject *v = Pointer_item(_self, i+ilow); PyList_SET_ITEM(np, i, v); } return (PyObject *)np; @@ -4152,9 +4216,9 @@ static PySequenceMethods Pointer_as_sequence = { 0, /* inquiry sq_length; */ 0, /* binaryfunc sq_concat; */ 0, /* intargfunc sq_repeat; */ - (ssizeargfunc)Pointer_item, /* intargfunc sq_item; */ - (ssizessizeargfunc)Pointer_slice, /* intintargfunc sq_slice; */ - (ssizeobjargproc)Pointer_ass_item, /* intobjargproc sq_ass_item; */ + Pointer_item, /* intargfunc sq_item; */ + Pointer_slice, /* intintargfunc sq_slice; */ + Pointer_ass_item, /* intobjargproc sq_ass_item; */ 0, /* intintobjargproc sq_ass_slice; */ 0, /* objobjproc sq_contains; */ /* Added in release 2.0 */ @@ -4334,6 +4398,42 @@ string_at(const char *ptr, Py_ssize_t size) return PyString_FromStringAndSize(ptr, size); } +static int +cast_check_pointertype(PyObject *arg) +{ + StgDictObject *dict; + + if (PointerTypeObject_Check(arg)) + return 1; + dict = PyType_stgdict(arg); + if (dict) { + if (PyString_Check(dict->proto) + && (strchr("sPzUZXO", PyString_AS_STRING(dict->proto)[0]))) { + /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */ + return 1; + } + } + PyErr_Format(PyExc_TypeError, + "cast() argument 2 must be a pointer type, not %s", + PyType_Check(arg) + ? ((PyTypeObject *)arg)->tp_name + : arg->ob_type->tp_name); + return 0; +} + +static PyObject * +cast(void *ptr, PyObject *ctype) +{ + CDataObject *result; + if (0 == cast_check_pointertype(ctype)) + return NULL; + result = (CDataObject *)PyObject_CallFunctionObjArgs(ctype, NULL); + if (result == NULL) + return NULL; + /* Should we assert that result is a pointer type? */ + memcpy(result->b_ptr, &ptr, sizeof(void *)); + return (PyObject *)result; +} #ifdef CTYPES_UNICODE static PyObject * @@ -4469,14 +4569,25 @@ init_ctypes(void) PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove)); PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset)); PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at)); + PyModule_AddObject(m, "_cast_addr", PyLong_FromVoidPtr(cast)); #ifdef CTYPES_UNICODE PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at)); #endif -#ifdef RTLD_LOCAL +/* If RTLD_LOCAL is not defined (Windows!), set it to zero. */ +#ifndef RTLD_LOCAL +#define RTLD_LOCAL 0 +#endif + +/* If RTLD_GLOBAL is not defined (cygwin), set it to the same value as + RTLD_LOCAL. +*/ +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL RTLD_LOCAL +#endif + PyModule_AddObject(m, "RTLD_LOCAL", PyInt_FromLong(RTLD_LOCAL)); PyModule_AddObject(m, "RTLD_GLOBAL", PyInt_FromLong(RTLD_GLOBAL)); -#endif PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL); if (PyExc_ArgError) { diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index a46f5e4..ad3b047 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -51,21 +51,21 @@ EXPORT(void) _testfunc_v(int a, int b, int *presult) *presult = a + b; } -EXPORT(int) _testfunc_i_bhilfd(char b, short h, int i, long l, float f, double d) +EXPORT(int) _testfunc_i_bhilfd(signed char b, short h, int i, long l, float f, double d) { // printf("_testfunc_i_bhilfd got %d %d %d %ld %f %f\n", // b, h, i, l, f, d); return (int)(b + h + i + l + f + d); } -EXPORT(float) _testfunc_f_bhilfd(char b, short h, int i, long l, float f, double d) +EXPORT(float) _testfunc_f_bhilfd(signed char b, short h, int i, long l, float f, double d) { // printf("_testfunc_f_bhilfd got %d %d %d %ld %f %f\n", // b, h, i, l, f, d); return (float)(b + h + i + l + f + d); } -EXPORT(double) _testfunc_d_bhilfd(char b, short h, int i, long l, float f, double d) +EXPORT(double) _testfunc_d_bhilfd(signed char b, short h, int i, long l, float f, double d) { // printf("_testfunc_d_bhilfd got %d %d %d %ld %f %f\n", // b, h, i, l, f, d); @@ -74,7 +74,7 @@ EXPORT(double) _testfunc_d_bhilfd(char b, short h, int i, long l, float f, doubl EXPORT(char *) _testfunc_p_p(void *s) { - return s; + return (char *)s; } EXPORT(void *) _testfunc_c_p_p(int *argcp, char **argv) @@ -89,7 +89,7 @@ EXPORT(void *) get_strchr(void) EXPORT(char *) my_strdup(char *src) { - char *dst = malloc(strlen(src)+1); + char *dst = (char *)malloc(strlen(src)+1); if (!dst) return NULL; strcpy(dst, src); @@ -99,8 +99,8 @@ EXPORT(char *) my_strdup(char *src) #ifdef HAVE_WCHAR_H EXPORT(wchar_t *) my_wcsdup(wchar_t *src) { - int len = wcslen(src); - wchar_t *ptr = malloc((len + 1) * sizeof(wchar_t)); + size_t len = wcslen(src); + wchar_t *ptr = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); if (ptr == NULL) return NULL; memcpy(ptr, src, (len+1) * sizeof(wchar_t)); @@ -152,13 +152,13 @@ EXPORT(int) _testfunc_callback_with_pointer(int (*func)(int *)) } #ifdef HAVE_LONG_LONG -EXPORT(PY_LONG_LONG) _testfunc_q_bhilfdq(char b, short h, int i, long l, float f, +EXPORT(PY_LONG_LONG) _testfunc_q_bhilfdq(signed char b, short h, int i, long l, float f, double d, PY_LONG_LONG q) { return (PY_LONG_LONG)(b + h + i + l + f + d + q); } -EXPORT(PY_LONG_LONG) _testfunc_q_bhilfd(char b, short h, int i, long l, float f, double d) +EXPORT(PY_LONG_LONG) _testfunc_q_bhilfd(signed char b, short h, int i, long l, float f, double d) { return (PY_LONG_LONG)(b + h + i + l + f + d); } @@ -191,7 +191,7 @@ EXPORT(int) _testfunc_ppp(char ***p) { static char message[] = "Hello, World"; if (p) { - *p = malloc(sizeof(char *)); + *p = (char **)malloc(sizeof(char *)); printf("malloc returned %p\n", *p); **p = message; return 1; @@ -385,7 +385,7 @@ PyMethodDef module_methods[] = { #define S last_tf_arg_s = (PY_LONG_LONG)c #define U last_tf_arg_u = (unsigned PY_LONG_LONG)c -EXPORT(char) tf_b(char c) { S; return c/3; } +EXPORT(signed char) tf_b(signed char c) { S; return c/3; } EXPORT(unsigned char) tf_B(unsigned char c) { U; return c/3; } EXPORT(short) tf_h(short c) { S; return c/3; } EXPORT(unsigned short) tf_H(unsigned short c) { U; return c/3; } @@ -399,7 +399,7 @@ EXPORT(float) tf_f(float c) { S; return c/3; } EXPORT(double) tf_d(double c) { S; return c/3; } #ifdef MS_WIN32 -EXPORT(char) __stdcall s_tf_b(char c) { S; return c/3; } +EXPORT(signed char) __stdcall s_tf_b(signed char c) { S; return c/3; } EXPORT(unsigned char) __stdcall s_tf_B(unsigned char c) { U; return c/3; } EXPORT(short) __stdcall s_tf_h(short c) { S; return c/3; } EXPORT(unsigned short) __stdcall s_tf_H(unsigned short c) { U; return c/3; } @@ -414,33 +414,33 @@ EXPORT(double) __stdcall s_tf_d(double c) { S; return c/3; } #endif /*******/ -EXPORT(char) tf_bb(char x, char c) { S; return c/3; } -EXPORT(unsigned char) tf_bB(char x, unsigned char c) { U; return c/3; } -EXPORT(short) tf_bh(char x, short c) { S; return c/3; } -EXPORT(unsigned short) tf_bH(char x, unsigned short c) { U; return c/3; } -EXPORT(int) tf_bi(char x, int c) { S; return c/3; } -EXPORT(unsigned int) tf_bI(char x, unsigned int c) { U; return c/3; } -EXPORT(long) tf_bl(char x, long c) { S; return c/3; } -EXPORT(unsigned long) tf_bL(char x, unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) tf_bq(char x, PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) tf_bQ(char x, unsigned PY_LONG_LONG c) { U; return c/3; } -EXPORT(float) tf_bf(char x, float c) { S; return c/3; } -EXPORT(double) tf_bd(char x, double c) { S; return c/3; } +EXPORT(signed char) tf_bb(signed char x, signed char c) { S; return c/3; } +EXPORT(unsigned char) tf_bB(signed char x, unsigned char c) { U; return c/3; } +EXPORT(short) tf_bh(signed char x, short c) { S; return c/3; } +EXPORT(unsigned short) tf_bH(signed char x, unsigned short c) { U; return c/3; } +EXPORT(int) tf_bi(signed char x, int c) { S; return c/3; } +EXPORT(unsigned int) tf_bI(signed char x, unsigned int c) { U; return c/3; } +EXPORT(long) tf_bl(signed char x, long c) { S; return c/3; } +EXPORT(unsigned long) tf_bL(signed char x, unsigned long c) { U; return c/3; } +EXPORT(PY_LONG_LONG) tf_bq(signed char x, PY_LONG_LONG c) { S; return c/3; } +EXPORT(unsigned PY_LONG_LONG) tf_bQ(signed char x, unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(float) tf_bf(signed char x, float c) { S; return c/3; } +EXPORT(double) tf_bd(signed char x, double c) { S; return c/3; } EXPORT(void) tv_i(int c) { S; return; } #ifdef MS_WIN32 -EXPORT(char) __stdcall s_tf_bb(char x, char c) { S; return c/3; } -EXPORT(unsigned char) __stdcall s_tf_bB(char x, unsigned char c) { U; return c/3; } -EXPORT(short) __stdcall s_tf_bh(char x, short c) { S; return c/3; } -EXPORT(unsigned short) __stdcall s_tf_bH(char x, unsigned short c) { U; return c/3; } -EXPORT(int) __stdcall s_tf_bi(char x, int c) { S; return c/3; } -EXPORT(unsigned int) __stdcall s_tf_bI(char x, unsigned int c) { U; return c/3; } -EXPORT(long) __stdcall s_tf_bl(char x, long c) { S; return c/3; } -EXPORT(unsigned long) __stdcall s_tf_bL(char x, unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) __stdcall s_tf_bq(char x, PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) __stdcall s_tf_bQ(char x, unsigned PY_LONG_LONG c) { U; return c/3; } -EXPORT(float) __stdcall s_tf_bf(char x, float c) { S; return c/3; } -EXPORT(double) __stdcall s_tf_bd(char x, double c) { S; return c/3; } +EXPORT(signed char) __stdcall s_tf_bb(signed char x, signed char c) { S; return c/3; } +EXPORT(unsigned char) __stdcall s_tf_bB(signed char x, unsigned char c) { U; return c/3; } +EXPORT(short) __stdcall s_tf_bh(signed char x, short c) { S; return c/3; } +EXPORT(unsigned short) __stdcall s_tf_bH(signed char x, unsigned short c) { U; return c/3; } +EXPORT(int) __stdcall s_tf_bi(signed char x, int c) { S; return c/3; } +EXPORT(unsigned int) __stdcall s_tf_bI(signed char x, unsigned int c) { U; return c/3; } +EXPORT(long) __stdcall s_tf_bl(signed char x, long c) { S; return c/3; } +EXPORT(unsigned long) __stdcall s_tf_bL(signed char x, unsigned long c) { U; return c/3; } +EXPORT(PY_LONG_LONG) __stdcall s_tf_bq(signed char x, PY_LONG_LONG c) { S; return c/3; } +EXPORT(unsigned PY_LONG_LONG) __stdcall s_tf_bQ(signed char x, unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(float) __stdcall s_tf_bf(signed char x, float c) { S; return c/3; } +EXPORT(double) __stdcall s_tf_bd(signed char x, double c) { S; return c/3; } EXPORT(void) __stdcall s_tv_i(int c) { S; return; } #endif diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 2948d98..8c29c55 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -264,16 +264,6 @@ if (x == NULL) _AddTraceback(what, __FILE__, __LINE__ - 1), PyErr_Print() PyGILState_Release(state); } -typedef struct { - ffi_closure *pcl; /* the C callable */ - ffi_cif cif; - PyObject *converters; - PyObject *callable; - SETFUNC setfunc; - ffi_type *restype; - ffi_type *atypes[0]; -} ffi_info; - static void closure_fcn(ffi_cif *cif, void *resp, void **args, @@ -289,16 +279,10 @@ static void closure_fcn(ffi_cif *cif, args); } -void FreeCallback(THUNK thunk) -{ - FreeClosure(((ffi_info *)thunk)->pcl); - PyMem_Free(thunk); -} - -THUNK AllocFunctionCallback(PyObject *callable, - PyObject *converters, - PyObject *restype, - int is_cdecl) +ffi_info *AllocFunctionCallback(PyObject *callable, + PyObject *converters, + PyObject *restype, + int is_cdecl) { int result; ffi_info *p; @@ -313,13 +297,14 @@ THUNK AllocFunctionCallback(PyObject *callable, } p->pcl = MallocClosure(); if (p->pcl == NULL) { - PyMem_Free(p); PyErr_NoMemory(); - return NULL; + goto error; } for (i = 0; i < nArgs; ++i) { PyObject *cnv = PySequence_GetItem(converters, i); + if (cnv == NULL) + goto error; p->atypes[i] = GetType(cnv); Py_DECREF(cnv); } @@ -330,12 +315,10 @@ THUNK AllocFunctionCallback(PyObject *callable, p->restype = &ffi_type_void; } else { StgDictObject *dict = PyType_stgdict(restype); - if (dict == NULL) { - PyMem_Free(p); - return NULL; - } + if (dict == NULL) + goto error; p->setfunc = dict->setfunc; - p->restype = &dict->ffi_type; + p->restype = &dict->ffi_type_pointer; } cc = FFI_DEFAULT_ABI; @@ -349,21 +332,26 @@ THUNK AllocFunctionCallback(PyObject *callable, if (result != FFI_OK) { PyErr_Format(PyExc_RuntimeError, "ffi_prep_cif failed with %d", result); - PyMem_Free(p); - return NULL; + goto error; } result = ffi_prep_closure(p->pcl, &p->cif, closure_fcn, p); if (result != FFI_OK) { PyErr_Format(PyExc_RuntimeError, "ffi_prep_closure failed with %d", result); - PyMem_Free(p); - return NULL; + goto error; } p->converters = converters; p->callable = callable; + return p; - return (THUNK)p; + error: + if (p) { + if (p->pcl) + FreeClosure(p->pcl); + PyMem_Free(p); + } + return NULL; } /**************************************************************************** diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 9d9e322..8163f49 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -64,14 +64,17 @@ #endif #ifdef MS_WIN32 -#define alloca _alloca +#include #endif #include #include "ctypes.h" -#ifdef _DEBUG -#define DEBUG_EXCEPTIONS /* */ +#if defined(_DEBUG) || defined(__MINGW32__) +/* Don't use structured exception handling on Windows if this is defined. + MingW, AFAIK, doesn't support it. +*/ +#define DONT_USE_SEH #endif #ifdef MS_WIN32 @@ -96,6 +99,7 @@ static TCHAR *FormatError(DWORD code) return lpMsgBuf; } +#ifndef DONT_USE_SEH void SetException(DWORD code, EXCEPTION_RECORD *pr) { TCHAR *lpMsgBuf; @@ -254,6 +258,7 @@ static DWORD HandleException(EXCEPTION_POINTERS *ptrs, *record = *ptrs->ExceptionRecord; return EXCEPTION_EXECUTE_HANDLER; } +#endif static PyObject * check_hresult(PyObject *self, PyObject *args) @@ -576,14 +581,14 @@ ffi_type *GetType(PyObject *obj) /* This little trick works correctly with MSVC. It returns small structures in registers */ - if (dict->ffi_type.type == FFI_TYPE_STRUCT) { - if (dict->ffi_type.size <= 4) + if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) { + if (dict->ffi_type_pointer.size <= 4) return &ffi_type_sint32; - else if (dict->ffi_type.size <= 8) + else if (dict->ffi_type_pointer.size <= 8) return &ffi_type_sint64; } #endif - return &dict->ffi_type; + return &dict->ffi_type_pointer; } @@ -612,9 +617,11 @@ static int _call_function_pointer(int flags, int cc; #ifdef MS_WIN32 int delta; +#ifndef DONT_USE_SEH DWORD dwExceptionCode = 0; EXCEPTION_RECORD record; #endif +#endif /* XXX check before here */ if (restype == NULL) { PyErr_SetString(PyExc_RuntimeError, @@ -640,14 +647,14 @@ static int _call_function_pointer(int flags, if ((flags & FUNCFLAG_PYTHONAPI) == 0) Py_UNBLOCK_THREADS #ifdef MS_WIN32 -#ifndef DEBUG_EXCEPTIONS +#ifndef DONT_USE_SEH __try { #endif delta = #endif ffi_call(&cif, (void *)pProc, resmem, avalues); #ifdef MS_WIN32 -#ifndef DEBUG_EXCEPTIONS +#ifndef DONT_USE_SEH } __except (HandleException(GetExceptionInformation(), &dwExceptionCode, &record)) { @@ -658,10 +665,12 @@ static int _call_function_pointer(int flags, if ((flags & FUNCFLAG_PYTHONAPI) == 0) Py_BLOCK_THREADS #ifdef MS_WIN32 +#ifndef DONT_USE_SEH if (dwExceptionCode) { SetException(dwExceptionCode, &record); return -1; } +#endif if (delta < 0) { if (flags & FUNCFLAG_CDECL) PyErr_Format(PyExc_ValueError, @@ -758,6 +767,8 @@ void Extend_Error_Info(PyObject *exc_class, char *fmt, ...) if (cls_str) { PyString_ConcatAndDel(&s, cls_str); PyString_ConcatAndDel(&s, PyString_FromString(": ")); + if (s == NULL) + goto error; } else PyErr_Clear(); msg_str = PyObject_Str(v); @@ -766,12 +777,15 @@ void Extend_Error_Info(PyObject *exc_class, char *fmt, ...) else { PyErr_Clear(); PyString_ConcatAndDel(&s, PyString_FromString("???")); + if (s == NULL) + goto error; } PyErr_SetObject(exc_class, s); +error: Py_XDECREF(tp); Py_XDECREF(v); Py_XDECREF(tb); - Py_DECREF(s); + Py_XDECREF(s); } @@ -1363,7 +1377,7 @@ static int converter(PyObject *obj, void **address) { *address = PyLong_AsVoidPtr(obj); - return address != NULL; + return *address != NULL; } static PyObject * @@ -1423,71 +1437,7 @@ set_conversion_mode(PyObject *self, PyObject *args) } #endif -static char cast_doc[] = -"cast(cobject, ctype) -> ctype-instance\n\ -\n\ -Create an instance of ctype, and copy the internal memory buffer\n\ -of cobject to the new instance. Should be used to cast one type\n\ -of pointer to another type of pointer.\n\ -Doesn't work correctly with ctypes integers.\n"; - -static int cast_check_pointertype(PyObject *arg, PyObject **pobj) -{ - StgDictObject *dict; - - if (PointerTypeObject_Check(arg)) { - *pobj = arg; - return 1; - } - dict = PyType_stgdict(arg); - if (dict) { - if (PyString_Check(dict->proto) - && (strchr("sPzUZXO", PyString_AS_STRING(dict->proto)[0]))) { - /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */ - *pobj = arg; - return 1; - } - } - if (PyType_Check(arg)) { - PyErr_Format(PyExc_TypeError, - "cast() argument 2 must be a pointer type, not %s", - ((PyTypeObject *)arg)->tp_name); - } else { - PyErr_Format(PyExc_TypeError, - "cast() argument 2 must be a pointer type, not a %s", - arg->ob_type->tp_name); - } - return 0; -} - -static PyObject *cast(PyObject *self, PyObject *args) -{ - PyObject *obj, *ctype; - struct argument a; - CDataObject *result; - - /* We could and should allow array types for the second argument - also, but we cannot use the simple memcpy below for them. */ - if (!PyArg_ParseTuple(args, "OO&:cast", &obj, &cast_check_pointertype, &ctype)) - return NULL; - if (-1 == ConvParam(obj, 1, &a)) - return NULL; - result = (CDataObject *)PyObject_CallFunctionObjArgs(ctype, NULL); - if (result == NULL) { - Py_XDECREF(a.keep); - return NULL; - } - // result->b_size - // a.ffi_type->size - memcpy(result->b_ptr, &a.value, - min(result->b_size, (int)a.ffi_type->size)); - Py_XDECREF(a.keep); - return (PyObject *)result; -} - - PyMethodDef module_methods[] = { - {"cast", cast, METH_VARARGS, cast_doc}, #ifdef CTYPES_UNICODE {"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc}, #endif diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 336f265..7bef412 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -499,7 +499,7 @@ b_set(void *ptr, PyObject *value, unsigned size) long val; if (get_long(value, &val) < 0) return NULL; - *(char *)ptr = (char)SET(*(char *)ptr, (char)val, size); + *(signed char *)ptr = (signed char)SET(*(signed char *)ptr, (signed char)val, size); _RET(value); } @@ -507,7 +507,7 @@ b_set(void *ptr, PyObject *value, unsigned size) static PyObject * b_get(void *ptr, unsigned size) { - char val = *(char *)ptr; + signed char val = *(signed char *)ptr; GET_BITFIELD(val, size); return PyInt_FromLong(val); } @@ -536,9 +536,12 @@ static PyObject * h_set(void *ptr, PyObject *value, unsigned size) { long val; + short x; if (get_long(value, &val) < 0) return NULL; - *(short *)ptr = (short)SET(*(short *)ptr, (short)val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, (short)val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -550,24 +553,28 @@ h_set_sw(void *ptr, PyObject *value, unsigned size) short field; if (get_long(value, &val) < 0) return NULL; - field = SWAP_2(*(short *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_2(field); field = SET(field, (short)val, size); - *(short *)ptr = SWAP_2(field); + field = SWAP_2(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } static PyObject * h_get(void *ptr, unsigned size) { - short val = *(short *)ptr; + short val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); - return PyInt_FromLong(val); + return PyInt_FromLong((long)val); } static PyObject * h_get_sw(void *ptr, unsigned size) { - short val = *(short *)ptr; + short val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_2(val); GET_BITFIELD(val, size); return PyInt_FromLong(val); @@ -577,10 +584,12 @@ static PyObject * H_set(void *ptr, PyObject *value, unsigned size) { unsigned long val; + unsigned short x; if (get_ulong(value, &val) < 0) return NULL; - *(unsigned short *)ptr = (unsigned short)SET(*(unsigned short *)ptr, - (unsigned short)val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, (unsigned short)val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -591,9 +600,11 @@ H_set_sw(void *ptr, PyObject *value, unsigned size) unsigned short field; if (get_ulong(value, &val) < 0) return NULL; - field = SWAP_2(*(unsigned short *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_2(field); field = SET(field, (unsigned short)val, size); - *(unsigned short *)ptr = SWAP_2(field); + field = SWAP_2(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } @@ -601,7 +612,8 @@ H_set_sw(void *ptr, PyObject *value, unsigned size) static PyObject * H_get(void *ptr, unsigned size) { - unsigned short val = *(unsigned short *)ptr; + unsigned short val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyInt_FromLong(val); } @@ -609,7 +621,8 @@ H_get(void *ptr, unsigned size) static PyObject * H_get_sw(void *ptr, unsigned size) { - unsigned short val = *(unsigned short *)ptr; + unsigned short val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_2(val); GET_BITFIELD(val, size); return PyInt_FromLong(val); @@ -619,9 +632,12 @@ static PyObject * i_set(void *ptr, PyObject *value, unsigned size) { long val; + int x; if (get_long(value, &val) < 0) return NULL; - *(int *)ptr = (int)SET(*(int *)ptr, (int)val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, (int)val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -632,9 +648,11 @@ i_set_sw(void *ptr, PyObject *value, unsigned size) int field; if (get_long(value, &val) < 0) return NULL; - field = SWAP_INT(*(int *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_INT(field); field = SET(field, (int)val, size); - *(int *)ptr = SWAP_INT(field); + field = SWAP_INT(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } @@ -642,7 +660,8 @@ i_set_sw(void *ptr, PyObject *value, unsigned size) static PyObject * i_get(void *ptr, unsigned size) { - int val = *(int *)ptr; + int val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyInt_FromLong(val); } @@ -650,7 +669,8 @@ i_get(void *ptr, unsigned size) static PyObject * i_get_sw(void *ptr, unsigned size) { - int val = *(int *)ptr; + int val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_INT(val); GET_BITFIELD(val, size); return PyInt_FromLong(val); @@ -684,9 +704,12 @@ static PyObject * I_set(void *ptr, PyObject *value, unsigned size) { unsigned long val; + unsigned int x; if (get_ulong(value, &val) < 0) return NULL; - *(unsigned int *)ptr = (unsigned int)SET(*(unsigned int *)ptr, (unsigned int)val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, (unsigned int)val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -697,9 +720,10 @@ I_set_sw(void *ptr, PyObject *value, unsigned size) unsigned int field; if (get_ulong(value, &val) < 0) return NULL; - field = SWAP_INT(*(unsigned int *)ptr); + memcpy(&field, ptr, sizeof(field)); field = (unsigned int)SET(field, (unsigned int)val, size); - *(unsigned int *)ptr = SWAP_INT(field); + field = SWAP_INT(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } @@ -707,7 +731,8 @@ I_set_sw(void *ptr, PyObject *value, unsigned size) static PyObject * I_get(void *ptr, unsigned size) { - unsigned int val = *(unsigned int *)ptr; + unsigned int val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyLong_FromUnsignedLong(val); } @@ -715,7 +740,8 @@ I_get(void *ptr, unsigned size) static PyObject * I_get_sw(void *ptr, unsigned size) { - unsigned int val = *(unsigned int *)ptr; + unsigned int val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_INT(val); GET_BITFIELD(val, size); return PyLong_FromUnsignedLong(val); @@ -725,9 +751,12 @@ static PyObject * l_set(void *ptr, PyObject *value, unsigned size) { long val; + long x; if (get_long(value, &val) < 0) return NULL; - *(long *)ptr = (long)SET(*(long *)ptr, val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -738,9 +767,11 @@ l_set_sw(void *ptr, PyObject *value, unsigned size) long field; if (get_long(value, &val) < 0) return NULL; - field = SWAP_LONG(*(long *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_LONG(field); field = (long)SET(field, val, size); - *(long *)ptr = SWAP_LONG(field); + field = SWAP_LONG(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } @@ -748,7 +779,8 @@ l_set_sw(void *ptr, PyObject *value, unsigned size) static PyObject * l_get(void *ptr, unsigned size) { - long val = *(long *)ptr; + long val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyInt_FromLong(val); } @@ -756,7 +788,8 @@ l_get(void *ptr, unsigned size) static PyObject * l_get_sw(void *ptr, unsigned size) { - long val = *(long *)ptr; + long val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG(val); GET_BITFIELD(val, size); return PyInt_FromLong(val); @@ -766,9 +799,12 @@ static PyObject * L_set(void *ptr, PyObject *value, unsigned size) { unsigned long val; + unsigned long x; if (get_ulong(value, &val) < 0) return NULL; - *(unsigned long *)ptr = (unsigned long)SET(*(unsigned long *)ptr, val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -779,9 +815,11 @@ L_set_sw(void *ptr, PyObject *value, unsigned size) unsigned long field; if (get_ulong(value, &val) < 0) return NULL; - field = SWAP_LONG(*(unsigned long *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_LONG(field); field = (unsigned long)SET(field, val, size); - *(unsigned long *)ptr = SWAP_LONG(field); + field = SWAP_LONG(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } @@ -789,7 +827,8 @@ L_set_sw(void *ptr, PyObject *value, unsigned size) static PyObject * L_get(void *ptr, unsigned size) { - unsigned long val = *(unsigned long *)ptr; + unsigned long val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyLong_FromUnsignedLong(val); } @@ -797,7 +836,8 @@ L_get(void *ptr, unsigned size) static PyObject * L_get_sw(void *ptr, unsigned size) { - unsigned long val = *(unsigned long *)ptr; + unsigned long val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG(val); GET_BITFIELD(val, size); return PyLong_FromUnsignedLong(val); @@ -808,9 +848,12 @@ static PyObject * q_set(void *ptr, PyObject *value, unsigned size) { PY_LONG_LONG val; + PY_LONG_LONG x; if (get_longlong(value, &val) < 0) return NULL; - *(PY_LONG_LONG *)ptr = (PY_LONG_LONG)SET(*(PY_LONG_LONG *)ptr, val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -821,16 +864,19 @@ q_set_sw(void *ptr, PyObject *value, unsigned size) PY_LONG_LONG field; if (get_longlong(value, &val) < 0) return NULL; - field = SWAP_8(*(PY_LONG_LONG *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_8(field); field = (PY_LONG_LONG)SET(field, val, size); - *(PY_LONG_LONG *)ptr = SWAP_8(field); + field = SWAP_8(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } static PyObject * q_get(void *ptr, unsigned size) { - PY_LONG_LONG val = *(PY_LONG_LONG *)ptr; + PY_LONG_LONG val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyLong_FromLongLong(val); } @@ -838,7 +884,8 @@ q_get(void *ptr, unsigned size) static PyObject * q_get_sw(void *ptr, unsigned size) { - PY_LONG_LONG val = *(PY_LONG_LONG *)ptr; + PY_LONG_LONG val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_8(val); GET_BITFIELD(val, size); return PyLong_FromLongLong(val); @@ -848,9 +895,12 @@ static PyObject * Q_set(void *ptr, PyObject *value, unsigned size) { unsigned PY_LONG_LONG val; + unsigned PY_LONG_LONG x; if (get_ulonglong(value, &val) < 0) return NULL; - *(unsigned PY_LONG_LONG *)ptr = (unsigned PY_LONG_LONG)SET(*(unsigned PY_LONG_LONG *)ptr, val, size); + memcpy(&x, ptr, sizeof(x)); + x = SET(x, val, size); + memcpy(ptr, &x, sizeof(x)); _RET(value); } @@ -861,16 +911,19 @@ Q_set_sw(void *ptr, PyObject *value, unsigned size) unsigned PY_LONG_LONG field; if (get_ulonglong(value, &val) < 0) return NULL; - field = SWAP_8(*(unsigned PY_LONG_LONG *)ptr); + memcpy(&field, ptr, sizeof(field)); + field = SWAP_8(field); field = (unsigned PY_LONG_LONG)SET(field, val, size); - *(unsigned PY_LONG_LONG *)ptr = SWAP_8(field); + field = SWAP_8(field); + memcpy(ptr, &field, sizeof(field)); _RET(value); } static PyObject * Q_get(void *ptr, unsigned size) { - unsigned PY_LONG_LONG val = *(unsigned PY_LONG_LONG *)ptr; + unsigned PY_LONG_LONG val; + memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); return PyLong_FromUnsignedLongLong(val); } @@ -878,7 +931,8 @@ Q_get(void *ptr, unsigned size) static PyObject * Q_get_sw(void *ptr, unsigned size) { - unsigned PY_LONG_LONG val = *(unsigned PY_LONG_LONG *)ptr; + unsigned PY_LONG_LONG val; + memcpy(&val, ptr, sizeof(val)); val = SWAP_8(val); GET_BITFIELD(val, size); return PyLong_FromUnsignedLongLong(val); @@ -903,14 +957,16 @@ d_set(void *ptr, PyObject *value, unsigned size) value->ob_type->tp_name); return NULL; } - *(double *)ptr = x; + memcpy(ptr, &x, sizeof(double)); _RET(value); } static PyObject * d_get(void *ptr, unsigned size) { - return PyFloat_FromDouble(*(double *)ptr); + double val; + memcpy(&val, ptr, sizeof(val)); + return PyFloat_FromDouble(val); } static PyObject * @@ -957,14 +1013,16 @@ f_set(void *ptr, PyObject *value, unsigned size) value->ob_type->tp_name); return NULL; } - *(float *)ptr = x; + memcpy(ptr, &x, sizeof(x)); _RET(value); } static PyObject * f_get(void *ptr, unsigned size) { - return PyFloat_FromDouble(*(float *)ptr); + float val; + memcpy(&val, ptr, sizeof(val)); + return PyFloat_FromDouble(val); } static PyObject * @@ -1317,6 +1375,7 @@ Z_set(void *ptr, PyObject *value, unsigned size) if (-1 == PyUnicode_AsWideChar((PyUnicodeObject *)value, buffer, PyUnicode_GET_SIZE(value))) { Py_DECREF(value); + Py_DECREF(keep); return NULL; } Py_DECREF(value); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 179dcf1..7988595 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -2,15 +2,6 @@ #if (PY_VERSION_HEX < 0x02050000) typedef int Py_ssize_t; -#define lenfunc inquiry -#define readbufferproc getreadbufferproc -#define writebufferproc getwritebufferproc -#define segcountproc getsegcountproc -#define charbufferproc getcharbufferproc -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc #endif #ifndef MS_WIN32 @@ -30,8 +21,9 @@ typedef int Py_ssize_t; #define PY_LONG_LONG LONG_LONG #endif -typedef int (*THUNK)(void); typedef struct tagCDataObject CDataObject; +typedef PyObject *(* GETFUNC)(void *, unsigned size); +typedef PyObject *(* SETFUNC)(void *, PyObject *value, unsigned size); /* A default buffer in CDataObject, which can be used for small C types. If this buffer is too small, PyMem_Malloc will be called to create a larger one, @@ -72,6 +64,16 @@ struct tagCDataObject { }; typedef struct { + ffi_closure *pcl; /* the C callable */ + ffi_cif cif; + PyObject *converters; + PyObject *callable; + SETFUNC setfunc; + ffi_type *restype; + ffi_type *atypes[0]; +} ffi_info; + +typedef struct { /* First part identical to tagCDataObject */ PyObject_HEAD char *b_ptr; /* pointer to memory block */ @@ -85,7 +87,7 @@ typedef struct { union value b_value; /* end of tagCDataObject, additional fields follow */ - THUNK thunk; + ffi_info *thunk; PyObject *callable; /* These two fields will override the ones in the type's stgdict if @@ -154,17 +156,12 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length); extern void init_callbacks_in_module(PyObject *m); -extern THUNK AllocFunctionCallback(PyObject *callable, - PyObject *converters, - PyObject *restype, - int stdcall); -extern void FreeCallback(THUNK); - extern PyMethodDef module_methods[]; -typedef PyObject *(* GETFUNC)(void *, unsigned size); -typedef PyObject *(* SETFUNC)(void *, PyObject *value, unsigned size); - +extern ffi_info *AllocFunctionCallback(PyObject *callable, + PyObject *converters, + PyObject *restype, + int stdcall); /* a table entry describing a predefined ctypes type */ struct fielddesc { char code; @@ -201,7 +198,7 @@ typedef struct { Py_ssize_t size; /* number of bytes */ Py_ssize_t align; /* alignment requirements */ Py_ssize_t length; /* number of fields */ - ffi_type ffi_type; + ffi_type ffi_type_pointer; PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ GETFUNC getfunc; /* Only for simple objects */ diff --git a/Modules/_ctypes/libffi/configure b/Modules/_ctypes/libffi/configure index 0991b63..c1e5cd4 100755 --- a/Modules/_ctypes/libffi/configure +++ b/Modules/_ctypes/libffi/configure @@ -3490,6 +3490,7 @@ i*86-*-solaris*) TARGET=X86; TARGETDIR=x86;; i*86-*-beos*) TARGET=X86; TARGETDIR=x86;; i*86-*-freebsd* | i*86-*-kfreebsd*-gnu) TARGET=X86; TARGETDIR=x86;; i*86-*-netbsdelf* | i*86-*-knetbsd*-gnu) TARGET=X86; TARGETDIR=x86;; +i*86-*-openbsd*) TARGET=X86; TARGETDIR=x86;; i*86-*-rtems*) TARGET=X86; TARGETDIR=x86;; i*86-*-win32*) TARGET=X86_WIN32; TARGETDIR=x86;; i*86-*-cygwin*) TARGET=X86_WIN32; TARGETDIR=x86;; diff --git a/Modules/_ctypes/libffi/configure.ac b/Modules/_ctypes/libffi/configure.ac index 76bf16e..c7f05d6 100644 --- a/Modules/_ctypes/libffi/configure.ac +++ b/Modules/_ctypes/libffi/configure.ac @@ -28,6 +28,7 @@ i*86-*-solaris*) TARGET=X86; TARGETDIR=x86;; i*86-*-beos*) TARGET=X86; TARGETDIR=x86;; i*86-*-freebsd* | i*86-*-kfreebsd*-gnu) TARGET=X86; TARGETDIR=x86;; i*86-*-netbsdelf* | i*86-*-knetbsd*-gnu) TARGET=X86; TARGETDIR=x86;; +i*86-*-openbsd*) TARGET=X86; TARGETDIR=x86;; i*86-*-rtems*) TARGET=X86; TARGETDIR=x86;; i*86-*-win32*) TARGET=X86_WIN32; TARGETDIR=x86;; i*86-*-cygwin*) TARGET=X86_WIN32; TARGETDIR=x86;; diff --git a/Modules/_ctypes/libffi/src/powerpc/darwin_closure.S b/Modules/_ctypes/libffi/src/powerpc/darwin_closure.S index 7959838..6d9a364 100644 --- a/Modules/_ctypes/libffi/src/powerpc/darwin_closure.S +++ b/Modules/_ctypes/libffi/src/powerpc/darwin_closure.S @@ -246,7 +246,7 @@ Lfinish: /* END(ffi_closure_ASM) */ .data -.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support +.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms EH_frame1: .set L$set$0,LECIE1-LSCIE1 .long L$set$0 ; Length of Common Information Entry diff --git a/Modules/_ctypes/libffi/src/x86/ffi.c b/Modules/_ctypes/libffi/src/x86/ffi.c index e4d5fc3..7f792b7 100644 --- a/Modules/_ctypes/libffi/src/x86/ffi.c +++ b/Modules/_ctypes/libffi/src/x86/ffi.c @@ -121,7 +121,7 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) switch (cif->rtype->type) { case FFI_TYPE_VOID: -#ifndef X86_WIN32 +#if !defined(X86_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__) case FFI_TYPE_STRUCT: #endif case FFI_TYPE_SINT64: @@ -135,7 +135,7 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) cif->flags = FFI_TYPE_SINT64; break; -#ifdef X86_WIN32 +#if defined(X86_WIN32) || defined(__OpenBSD__) || defined(__FreeBSD__) case FFI_TYPE_STRUCT: if (cif->rtype->size == 1) { diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index cb3d599..336be37 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -38,7 +38,7 @@ static void StgDict_dealloc(StgDictObject *self) { StgDict_clear(self); - PyMem_Free(self->ffi_type.elements); + PyMem_Free(self->ffi_type_pointer.elements); PyDict_Type.tp_dealloc((PyObject *)self); } @@ -49,8 +49,8 @@ StgDict_clone(StgDictObject *dst, StgDictObject *src) int size; StgDict_clear(dst); - PyMem_Free(dst->ffi_type.elements); - dst->ffi_type.elements = NULL; + PyMem_Free(dst->ffi_type_pointer.elements); + dst->ffi_type_pointer.elements = NULL; d = (char *)dst; s = (char *)src; @@ -64,13 +64,15 @@ StgDict_clone(StgDictObject *dst, StgDictObject *src) Py_XINCREF(dst->restype); Py_XINCREF(dst->checker); - if (src->ffi_type.elements == NULL) + if (src->ffi_type_pointer.elements == NULL) return 0; size = sizeof(ffi_type *) * (src->length + 1); - dst->ffi_type.elements = PyMem_Malloc(size); - if (dst->ffi_type.elements == NULL) + dst->ffi_type_pointer.elements = PyMem_Malloc(size); + if (dst->ffi_type_pointer.elements == NULL) return -1; - memcpy(dst->ffi_type.elements, src->ffi_type.elements, size); + memcpy(dst->ffi_type_pointer.elements, + src->ffi_type_pointer.elements, + size); return 0; } @@ -234,8 +236,8 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) stuff is sucessfully finished. */ stgdict->flags |= DICTFLAG_FINAL; /* set final */ - if (stgdict->ffi_type.elements) - PyMem_Free(stgdict->ffi_type.elements); + if (stgdict->ffi_type_pointer.elements) + PyMem_Free(stgdict->ffi_type_pointer.elements); basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base); if (basedict && !use_broken_old_ctypes_semantics) { @@ -243,10 +245,12 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) align = basedict->align; union_size = 0; total_align = align ? align : 1; - stgdict->ffi_type.type = FFI_TYPE_STRUCT; - stgdict->ffi_type.elements = PyMem_Malloc(sizeof(ffi_type *) * (basedict->length + len + 1)); - memset(stgdict->ffi_type.elements, 0, sizeof(ffi_type *) * (basedict->length + len + 1)); - memcpy(stgdict->ffi_type.elements, basedict->ffi_type.elements, + stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; + stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (basedict->length + len + 1)); + memset(stgdict->ffi_type_pointer.elements, 0, + sizeof(ffi_type *) * (basedict->length + len + 1)); + memcpy(stgdict->ffi_type_pointer.elements, + basedict->ffi_type_pointer.elements, sizeof(ffi_type *) * (basedict->length)); ffi_ofs = basedict->length; } else { @@ -255,9 +259,10 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) align = 0; union_size = 0; total_align = 1; - stgdict->ffi_type.type = FFI_TYPE_STRUCT; - stgdict->ffi_type.elements = PyMem_Malloc(sizeof(ffi_type *) * (len + 1)); - memset(stgdict->ffi_type.elements, 0, sizeof(ffi_type *) * (len + 1)); + stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; + stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (len + 1)); + memset(stgdict->ffi_type_pointer.elements, 0, + sizeof(ffi_type *) * (len + 1)); ffi_ofs = 0; } @@ -283,10 +288,10 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) i); return -1; } - stgdict->ffi_type.elements[ffi_ofs + i] = &dict->ffi_type; + stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; dict->flags |= DICTFLAG_FINAL; /* mark field type final */ if (PyTuple_Size(pair) == 3) { /* bits specified */ - switch(dict->ffi_type.type) { + switch(dict->ffi_type_pointer.type) { case FFI_TYPE_UINT8: case FFI_TYPE_UINT16: case FFI_TYPE_UINT32: @@ -357,8 +362,8 @@ StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) /* Adjust the size according to the alignment requirements */ size = ((size + total_align - 1) / total_align) * total_align; - stgdict->ffi_type.alignment = total_align; - stgdict->ffi_type.size = size; + stgdict->ffi_type_pointer.alignment = total_align; + stgdict->ffi_type_pointer.size = size; stgdict->size = size; stgdict->align = total_align; diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c index d5b4cde..2ee4eb9 100644 --- a/Modules/_hotshot.c +++ b/Modules/_hotshot.c @@ -26,7 +26,7 @@ typedef __int64 hs_time; #ifndef HAVE_GETTIMEOFDAY #error "This module requires gettimeofday() on non-Windows platforms!" #endif -#if (defined(PYOS_OS2) && defined(PYCC_GCC)) +#if (defined(PYOS_OS2) && defined(PYCC_GCC)) || defined(__QNX__) #include #else #include @@ -308,7 +308,7 @@ unpack_string(LogReaderObject *self, PyObject **pvalue) if ((err = unpack_packed_int(self, &len, 0))) return err; - buf = malloc(len); + buf = (char *)malloc(len); for (i=0; i < len; i++) { ch = fgetc(self->logfp); buf[i] = ch; @@ -918,7 +918,7 @@ calibrate(void) #endif } #if defined(MS_WINDOWS) || defined(PYOS_OS2) || \ - defined(__VMS) + defined(__VMS) || defined (__QNX__) rusage_diff = -1; #else { @@ -1403,7 +1403,7 @@ get_version_string(void) ++rev; while (rev[i] != ' ' && rev[i] != '\0') ++i; - buffer = malloc(i + 1); + buffer = (char *)malloc(i + 1); if (buffer != NULL) { memmove(buffer, rev, i); buffer[i] = '\0'; diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 17c71e9..d665f83 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -515,6 +515,7 @@ static PyStructSequence_Desc profiler_subentry_desc = { 5 }; +static int initialized; static PyTypeObject StatsEntryType; static PyTypeObject StatsSubEntryType; @@ -857,8 +858,12 @@ init_lsprof(void) return; PyDict_SetItemString(d, "Profiler", (PyObject *)&PyProfiler_Type); - PyStructSequence_InitType(&StatsEntryType, &profiler_entry_desc); - PyStructSequence_InitType(&StatsSubEntryType, &profiler_subentry_desc); + if (!initialized) { + PyStructSequence_InitType(&StatsEntryType, + &profiler_entry_desc); + PyStructSequence_InitType(&StatsSubEntryType, + &profiler_subentry_desc); + } Py_INCREF((PyObject*) &StatsEntryType); Py_INCREF((PyObject*) &StatsSubEntryType); PyModule_AddObject(module, "profiler_entry", @@ -866,4 +871,5 @@ init_lsprof(void) PyModule_AddObject(module, "profiler_subentry", (PyObject*) &StatsSubEntryType); empty_tuple = PyTuple_New(0); + initialized = 1; } diff --git a/Modules/_sqlite/adapters.c b/Modules/_sqlite/adapters.c new file mode 100644 index 0000000..e6fde03 --- /dev/null +++ b/Modules/_sqlite/adapters.c @@ -0,0 +1,40 @@ +/* adapters.c - default adapters + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "util.h" +#include "module.h" +#include "adapters.h" + +/* dummy, will be implemented in a later version */ + +PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs) +{ + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs) +{ + Py_INCREF(Py_None); + return Py_None; +} diff --git a/Modules/_sqlite/adapters.h b/Modules/_sqlite/adapters.h new file mode 100644 index 0000000..d2e8479 --- /dev/null +++ b/Modules/_sqlite/adapters.h @@ -0,0 +1,33 @@ +/* adapters.h - default adapters + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_ADAPTERS_H +#define PYSQLITE_ADAPTERS_H +#include "Python.h" +#include "pythread.h" +#include "sqlite3.h" + +PyObject* adapt_date(PyObject* self, PyObject* args, PyObject* kwargs); +PyObject* adapt_datetime(PyObject* self, PyObject* args, PyObject* kwargs); + +#endif diff --git a/Modules/_sqlite/cache.c b/Modules/_sqlite/cache.c new file mode 100644 index 0000000..d102e97 --- /dev/null +++ b/Modules/_sqlite/cache.c @@ -0,0 +1,362 @@ +/* cache .c - a LRU cache + * + * Copyright (C) 2004-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "cache.h" + +/* only used internally */ +Node* new_node(PyObject* key, PyObject* data) +{ + Node* node; + + node = (Node*) (NodeType.tp_alloc(&NodeType, 0)); + if (!node) { + return NULL; + } + + Py_INCREF(key); + node->key = key; + + Py_INCREF(data); + node->data = data; + + node->prev = NULL; + node->next = NULL; + + return node; +} + +void node_dealloc(Node* self) +{ + Py_DECREF(self->key); + Py_DECREF(self->data); + + self->ob_type->tp_free((PyObject*)self); +} + +int cache_init(Cache* self, PyObject* args, PyObject* kwargs) +{ + PyObject* factory; + int size = 10; + + self->factory = NULL; + + if (!PyArg_ParseTuple(args, "O|i", &factory, &size)) + { + return -1; + } + + if (size < 5) { + size = 5; + } + self->size = size; + self->first = NULL; + self->last = NULL; + + self->mapping = PyDict_New(); + if (!self->mapping) { + return -1; + } + + Py_INCREF(factory); + self->factory = factory; + + self->decref_factory = 1; + + return 0; +} + +void cache_dealloc(Cache* self) +{ + Node* node; + Node* delete_node; + + if (!self->factory) { + /* constructor failed, just get out of here */ + return; + } + + node = self->first; + while (node) { + delete_node = node; + node = node->next; + Py_DECREF(delete_node); + } + + if (self->decref_factory) { + Py_DECREF(self->factory); + } + Py_DECREF(self->mapping); + + self->ob_type->tp_free((PyObject*)self); +} + +PyObject* cache_get(Cache* self, PyObject* args) +{ + PyObject* key = args; + Node* node; + Node* ptr; + PyObject* data; + + node = (Node*)PyDict_GetItem(self->mapping, key); + if (node) { + node->count++; + if (node->prev && node->count > node->prev->count) { + ptr = node->prev; + + while (ptr->prev && node->count > ptr->prev->count) { + ptr = ptr->prev; + } + + if (node->next) { + node->next->prev = node->prev; + } else { + self->last = node->prev; + } + if (node->prev) { + node->prev->next = node->next; + } + if (ptr->prev) { + ptr->prev->next = node; + } else { + self->first = node; + } + + node->next = ptr; + node->prev = ptr->prev; + if (!node->prev) { + self->first = node; + } + ptr->prev = node; + } + } else { + if (PyDict_Size(self->mapping) == self->size) { + if (self->last) { + node = self->last; + + if (PyDict_DelItem(self->mapping, self->last->key) != 0) { + return NULL; + } + + if (node->prev) { + node->prev->next = NULL; + } + self->last = node->prev; + node->prev = NULL; + + Py_DECREF(node); + } + } + + data = PyObject_CallFunction(self->factory, "O", key); + + if (!data) { + return NULL; + } + + node = new_node(key, data); + if (!node) { + return NULL; + } + node->prev = self->last; + + Py_DECREF(data); + + if (PyDict_SetItem(self->mapping, key, (PyObject*)node) != 0) { + Py_DECREF(node); + return NULL; + } + + if (self->last) { + self->last->next = node; + } else { + self->first = node; + } + self->last = node; + } + + Py_INCREF(node->data); + return node->data; +} + +PyObject* cache_display(Cache* self, PyObject* args) +{ + Node* ptr; + PyObject* prevkey; + PyObject* nextkey; + PyObject* fmt_args; + PyObject* template; + PyObject* display_str; + + ptr = self->first; + + while (ptr) { + if (ptr->prev) { + prevkey = ptr->prev->key; + } else { + prevkey = Py_None; + } + Py_INCREF(prevkey); + + if (ptr->next) { + nextkey = ptr->next->key; + } else { + nextkey = Py_None; + } + Py_INCREF(nextkey); + + fmt_args = Py_BuildValue("OOO", prevkey, ptr->key, nextkey); + if (!fmt_args) { + return NULL; + } + template = PyString_FromString("%s <- %s ->%s\n"); + if (!template) { + return NULL; + } + display_str = PyString_Format(template, fmt_args); + Py_DECREF(template); + Py_DECREF(fmt_args); + if (!display_str) { + return NULL; + } + PyObject_Print(display_str, stdout, Py_PRINT_RAW); + Py_DECREF(display_str); + + Py_DECREF(prevkey); + Py_DECREF(nextkey); + + ptr = ptr->next; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef cache_methods[] = { + {"get", (PyCFunction)cache_get, METH_O, + PyDoc_STR("Gets an entry from the cache.")}, + {"display", (PyCFunction)cache_display, METH_NOARGS, + PyDoc_STR("For debugging only.")}, + {NULL, NULL} +}; + +PyTypeObject NodeType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME "Node", /* tp_name */ + sizeof(Node), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)node_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|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* 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 */ + (initproc)0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +PyTypeObject CacheType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".Cache", /* tp_name */ + sizeof(Cache), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)cache_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|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + cache_methods, /* 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 */ + (initproc)cache_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int cache_setup_types(void) +{ + int rc; + + NodeType.tp_new = PyType_GenericNew; + CacheType.tp_new = PyType_GenericNew; + + rc = PyType_Ready(&NodeType); + if (rc < 0) { + return rc; + } + + rc = PyType_Ready(&CacheType); + return rc; +} diff --git a/Modules/_sqlite/cache.h b/Modules/_sqlite/cache.h new file mode 100644 index 0000000..5cc16f3 --- /dev/null +++ b/Modules/_sqlite/cache.h @@ -0,0 +1,61 @@ +/* cache.h - definitions for the LRU cache + * + * Copyright (C) 2004-2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_CACHE_H +#define PYSQLITE_CACHE_H +#include "Python.h" + +typedef struct _Node +{ + PyObject_HEAD + PyObject* key; + PyObject* data; + long count; + struct _Node* prev; + struct _Node* next; +} Node; + +typedef struct +{ + PyObject_HEAD + int size; + PyObject* mapping; + PyObject* factory; + Node* first; + Node* last; + int decref_factory; +} Cache; + +extern PyTypeObject NodeType; +extern PyTypeObject CacheType; + +int node_init(Node* self, PyObject* args, PyObject* kwargs); +void node_dealloc(Node* self); + +int cache_init(Cache* self, PyObject* args, PyObject* kwargs); +void cache_dealloc(Cache* self); +PyObject* cache_get(Cache* self, PyObject* args); + +int cache_setup_types(void); + +#endif diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c new file mode 100644 index 0000000..78aad37 --- /dev/null +++ b/Modules/_sqlite/connection.c @@ -0,0 +1,1082 @@ +/* connection.c - the connection type + * + * Copyright (C) 2004-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "cache.h" +#include "module.h" +#include "connection.h" +#include "statement.h" +#include "cursor.h" +#include "prepare_protocol.h" +#include "util.h" +#include "sqlitecompat.h" + +#include "pythread.h" + +static int connection_set_isolation_level(Connection* self, PyObject* isolation_level); + +int connection_init(Connection* self, PyObject* args, PyObject* kwargs) +{ + static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; + + char* database; + int detect_types = 0; + PyObject* isolation_level = NULL; + PyObject* factory = NULL; + int check_same_thread = 1; + int cached_statements = 100; + double timeout = 5.0; + int rc; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist, + &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) + { + return -1; + } + + self->begin_statement = NULL; + + self->statement_cache = NULL; + + Py_INCREF(Py_None); + self->row_factory = Py_None; + + Py_INCREF(&PyUnicode_Type); + self->text_factory = (PyObject*)&PyUnicode_Type; + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_open(database, &self->db); + Py_END_ALLOW_THREADS + + if (rc != SQLITE_OK) { + _seterror(self->db); + return -1; + } + + if (!isolation_level) { + isolation_level = PyString_FromString(""); + } else { + Py_INCREF(isolation_level); + } + self->isolation_level = NULL; + connection_set_isolation_level(self, isolation_level); + Py_DECREF(isolation_level); + + self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "Oi", self, cached_statements); + if (PyErr_Occurred()) { + return -1; + } + + /* By default, the Cache class INCREFs the factory in its initializer, and + * decrefs it in its deallocator method. Since this would create a circular + * reference here, we're breaking it by decrementing self, and telling the + * cache class to not decref the factory (self) in its deallocator. + */ + self->statement_cache->decref_factory = 0; + Py_DECREF(self); + + self->inTransaction = 0; + self->detect_types = detect_types; + self->timeout = timeout; + (void)sqlite3_busy_timeout(self->db, (int)(timeout*1000)); + + self->thread_ident = PyThread_get_thread_ident(); + self->check_same_thread = check_same_thread; + + self->function_pinboard = PyDict_New(); + if (!self->function_pinboard) { + return -1; + } + + self->collations = PyDict_New(); + if (!self->collations) { + return -1; + } + + self->Warning = Warning; + self->Error = Error; + self->InterfaceError = InterfaceError; + self->DatabaseError = DatabaseError; + self->DataError = DataError; + self->OperationalError = OperationalError; + self->IntegrityError = IntegrityError; + self->InternalError = InternalError; + self->ProgrammingError = ProgrammingError; + self->NotSupportedError = NotSupportedError; + + return 0; +} + +void flush_statement_cache(Connection* self) +{ + Node* node; + Statement* statement; + + node = self->statement_cache->first; + + while (node) { + statement = (Statement*)(node->data); + (void)statement_finalize(statement); + node = node->next; + } + + Py_DECREF(self->statement_cache); + self->statement_cache = (Cache*)PyObject_CallFunction((PyObject*)&CacheType, "O", self); + Py_DECREF(self); + self->statement_cache->decref_factory = 0; +} + +void reset_all_statements(Connection* self) +{ + Node* node; + Statement* statement; + + node = self->statement_cache->first; + + while (node) { + statement = (Statement*)(node->data); + (void)statement_reset(statement); + node = node->next; + } +} + +void connection_dealloc(Connection* self) +{ + Py_XDECREF(self->statement_cache); + + /* Clean up if user has not called .close() explicitly. */ + if (self->db) { + Py_BEGIN_ALLOW_THREADS + sqlite3_close(self->db); + Py_END_ALLOW_THREADS + } + + if (self->begin_statement) { + PyMem_Free(self->begin_statement); + } + Py_XDECREF(self->isolation_level); + Py_XDECREF(self->function_pinboard); + Py_XDECREF(self->row_factory); + Py_XDECREF(self->text_factory); + Py_XDECREF(self->collations); + + self->ob_type->tp_free((PyObject*)self); +} + +PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs) +{ + static char *kwlist[] = {"factory", NULL, NULL}; + PyObject* factory = NULL; + PyObject* cursor; + + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, + &factory)) { + return NULL; + } + + if (!check_thread(self) || !check_connection(self)) { + return NULL; + } + + if (factory == NULL) { + factory = (PyObject*)&CursorType; + } + + cursor = PyObject_CallFunction(factory, "O", self); + + if (cursor && self->row_factory != Py_None) { + Py_XDECREF(((Cursor*)cursor)->row_factory); + Py_INCREF(self->row_factory); + ((Cursor*)cursor)->row_factory = self->row_factory; + } + + return cursor; +} + +PyObject* connection_close(Connection* self, PyObject* args) +{ + int rc; + + if (!check_thread(self)) { + return NULL; + } + + flush_statement_cache(self); + + if (self->db) { + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_close(self->db); + Py_END_ALLOW_THREADS + + if (rc != SQLITE_OK) { + _seterror(self->db); + return NULL; + } else { + self->db = NULL; + } + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* + * Checks if a connection object is usable (i. e. not closed). + * + * 0 => error; 1 => ok + */ +int check_connection(Connection* con) +{ + if (!con->db) { + PyErr_SetString(ProgrammingError, "Cannot operate on a closed database."); + return 0; + } else { + return 1; + } +} + +PyObject* _connection_begin(Connection* self) +{ + int rc; + const char* tail; + sqlite3_stmt* statement; + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_prepare(self->db, self->begin_statement, -1, &statement, &tail); + Py_END_ALLOW_THREADS + + if (rc != SQLITE_OK) { + _seterror(self->db); + goto error; + } + + rc = _sqlite_step_with_busyhandler(statement, self); + if (rc == SQLITE_DONE) { + self->inTransaction = 1; + } else { + _seterror(self->db); + } + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_finalize(statement); + Py_END_ALLOW_THREADS + + if (rc != SQLITE_OK && !PyErr_Occurred()) { + _seterror(self->db); + } + +error: + if (PyErr_Occurred()) { + return NULL; + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyObject* connection_commit(Connection* self, PyObject* args) +{ + int rc; + const char* tail; + sqlite3_stmt* statement; + + if (!check_thread(self) || !check_connection(self)) { + return NULL; + } + + if (self->inTransaction) { + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_prepare(self->db, "COMMIT", -1, &statement, &tail); + Py_END_ALLOW_THREADS + if (rc != SQLITE_OK) { + _seterror(self->db); + goto error; + } + + rc = _sqlite_step_with_busyhandler(statement, self); + if (rc == SQLITE_DONE) { + self->inTransaction = 0; + } else { + _seterror(self->db); + } + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_finalize(statement); + Py_END_ALLOW_THREADS + if (rc != SQLITE_OK && !PyErr_Occurred()) { + _seterror(self->db); + } + + } + +error: + if (PyErr_Occurred()) { + return NULL; + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyObject* connection_rollback(Connection* self, PyObject* args) +{ + int rc; + const char* tail; + sqlite3_stmt* statement; + + if (!check_thread(self) || !check_connection(self)) { + return NULL; + } + + if (self->inTransaction) { + reset_all_statements(self); + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_prepare(self->db, "ROLLBACK", -1, &statement, &tail); + Py_END_ALLOW_THREADS + if (rc != SQLITE_OK) { + _seterror(self->db); + goto error; + } + + rc = _sqlite_step_with_busyhandler(statement, self); + if (rc == SQLITE_DONE) { + self->inTransaction = 0; + } else { + _seterror(self->db); + } + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_finalize(statement); + Py_END_ALLOW_THREADS + if (rc != SQLITE_OK && !PyErr_Occurred()) { + _seterror(self->db); + } + + } + +error: + if (PyErr_Occurred()) { + return NULL; + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +void _set_result(sqlite3_context* context, PyObject* py_val) +{ + long longval; + const char* buffer; + Py_ssize_t buflen; + PyObject* stringval; + + if (PyErr_Occurred()) { + /* Errors in callbacks are ignored, and we return NULL */ + PyErr_Clear(); + sqlite3_result_null(context); + } else if (py_val == Py_None) { + sqlite3_result_null(context); + } else if (PyInt_Check(py_val)) { + longval = PyInt_AsLong(py_val); + /* TODO: investigate what to do with range overflows - long vs. long long */ + sqlite3_result_int64(context, (PY_LONG_LONG)longval); + } else if (PyFloat_Check(py_val)) { + sqlite3_result_double(context, PyFloat_AsDouble(py_val)); + } else if (PyBuffer_Check(py_val)) { + if (PyObject_AsCharBuffer(py_val, &buffer, &buflen) != 0) { + PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); + } + sqlite3_result_blob(context, buffer, buflen, SQLITE_TRANSIENT); + } else if (PyString_Check(py_val)) { + sqlite3_result_text(context, PyString_AsString(py_val), -1, SQLITE_TRANSIENT); + } else if (PyUnicode_Check(py_val)) { + stringval = PyUnicode_AsUTF8String(py_val); + sqlite3_result_text(context, PyString_AsString(stringval), -1, SQLITE_TRANSIENT); + Py_DECREF(stringval); + } else { + /* TODO: raise error */ + } +} + +PyObject* _build_py_params(sqlite3_context *context, int argc, sqlite3_value** argv) +{ + PyObject* args; + int i; + sqlite3_value* cur_value; + PyObject* cur_py_value; + const char* val_str; + PY_LONG_LONG val_int; + Py_ssize_t buflen; + void* raw_buffer; + + args = PyTuple_New(argc); + if (!args) { + return NULL; + } + + for (i = 0; i < argc; i++) { + cur_value = argv[i]; + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + val_int = sqlite3_value_int64(cur_value); + cur_py_value = PyInt_FromLong((long)val_int); + break; + case SQLITE_FLOAT: + cur_py_value = PyFloat_FromDouble(sqlite3_value_double(cur_value)); + break; + case SQLITE_TEXT: + val_str = (const char*)sqlite3_value_text(cur_value); + cur_py_value = PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL); + /* TODO: have a way to show errors here */ + if (!cur_py_value) { + Py_INCREF(Py_None); + cur_py_value = Py_None; + } + break; + case SQLITE_BLOB: + buflen = sqlite3_value_bytes(cur_value); + cur_py_value = PyBuffer_New(buflen); + if (!cur_py_value) { + /* TODO: error */ + } + if (PyObject_AsWriteBuffer(cur_py_value, &raw_buffer, &buflen)) { + /* TODO: error */ + } + memcpy(raw_buffer, sqlite3_value_blob(cur_value), buflen); + break; + case SQLITE_NULL: + default: + Py_INCREF(Py_None); + cur_py_value = Py_None; + } + PyTuple_SetItem(args, i, cur_py_value); + + } + + return args; +} + +void _func_callback(sqlite3_context* context, int argc, sqlite3_value** argv) +{ + PyObject* args; + PyObject* py_func; + PyObject* py_retval; + + + PyGILState_STATE threadstate; + + threadstate = PyGILState_Ensure(); + + py_func = (PyObject*)sqlite3_user_data(context); + + args = _build_py_params(context, argc, argv); + + py_retval = PyObject_CallObject(py_func, args); + Py_DECREF(args); + + _set_result(context, py_retval); + Py_XDECREF(py_retval); + + PyGILState_Release(threadstate); +} + +static void _step_callback(sqlite3_context *context, int argc, sqlite3_value** params) +{ + PyObject* args; + PyObject* function_result; + PyObject* aggregate_class; + PyObject** aggregate_instance; + PyObject* stepmethod; + + PyGILState_STATE threadstate; + + threadstate = PyGILState_Ensure(); + + aggregate_class = (PyObject*)sqlite3_user_data(context); + + aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); + + if (*aggregate_instance == 0) { + *aggregate_instance = PyObject_CallFunction(aggregate_class, ""); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + *aggregate_instance = 0; + PyGILState_Release(threadstate); + return; + } + } + + stepmethod = PyObject_GetAttrString(*aggregate_instance, "step"); + if (!stepmethod) + { + PyGILState_Release(threadstate); + return; + } + + args = _build_py_params(context, argc, params); + + function_result = PyObject_CallObject(stepmethod, args); + Py_DECREF(args); + Py_DECREF(stepmethod); + + if (function_result == NULL) { + PyErr_Clear(); + } else { + Py_DECREF(function_result); + } + + PyGILState_Release(threadstate); +} + +void _final_callback(sqlite3_context* context) +{ + PyObject* args; + PyObject* function_result; + PyObject** aggregate_instance; + PyObject* aggregate_class; + PyObject* finalizemethod; + + PyGILState_STATE threadstate; + + threadstate = PyGILState_Ensure(); + + aggregate_class = (PyObject*)sqlite3_user_data(context); + + aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); + if (!*aggregate_instance) { + /* this branch is executed if there was an exception in the aggregate's + * __init__ */ + + PyGILState_Release(threadstate); + return; + } + + finalizemethod = PyObject_GetAttrString(*aggregate_instance, "finalize"); + + if (!finalizemethod) { + /* + PyErr_SetString(ProgrammingError, "finalize method missing"); + goto error; + */ + Py_INCREF(Py_None); + function_result = Py_None; + } else { + args = PyTuple_New(0); + if (!args) + return; + function_result = PyObject_CallObject(finalizemethod, args); + Py_DECREF(args); + Py_DECREF(finalizemethod); + } + + _set_result(context, function_result); + Py_XDECREF(*aggregate_instance); + Py_XDECREF(function_result); + + PyGILState_Release(threadstate); +} + + +PyObject* connection_create_function(Connection* self, PyObject* args, PyObject* kwargs) +{ + static char *kwlist[] = {"name", "narg", "func", NULL, NULL}; + + PyObject* func; + char* name; + int narg; + int rc; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO", kwlist, + &name, &narg, &func)) + { + return NULL; + } + + rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _func_callback, NULL, NULL); + + PyDict_SetItem(self->function_pinboard, func, Py_None); + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* connection_create_aggregate(Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* aggregate_class; + + int n_arg; + char* name; + static char *kwlist[] = { "name", "n_arg", "aggregate_class", NULL }; + int rc; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO:create_aggregate", + kwlist, &name, &n_arg, &aggregate_class)) { + return NULL; + } + + rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_step_callback, &_final_callback); + if (rc != SQLITE_OK) { + _seterror(self->db); + return NULL; + } else { + PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None); + + Py_INCREF(Py_None); + return Py_None; + } +} + +int check_thread(Connection* self) +{ + if (self->check_same_thread) { + if (PyThread_get_thread_ident() != self->thread_ident) { + PyErr_Format(ProgrammingError, + "SQLite objects created in a thread can only be used in that same thread." + "The object was created in thread id %ld and this is thread id %ld", + self->thread_ident, PyThread_get_thread_ident()); + return 0; + } + + } + + return 1; +} + +static PyObject* connection_get_isolation_level(Connection* self, void* unused) +{ + Py_INCREF(self->isolation_level); + return self->isolation_level; +} + +static PyObject* connection_get_total_changes(Connection* self, void* unused) +{ + if (!check_connection(self)) { + return NULL; + } else { + return Py_BuildValue("i", sqlite3_total_changes(self->db)); + } +} + +static int connection_set_isolation_level(Connection* self, PyObject* isolation_level) +{ + PyObject* empty; + PyObject* res; + PyObject* begin_statement; + + Py_XDECREF(self->isolation_level); + + if (self->begin_statement) { + PyMem_Free(self->begin_statement); + self->begin_statement = NULL; + } + + if (isolation_level == Py_None) { + Py_INCREF(Py_None); + self->isolation_level = Py_None; + + empty = PyTuple_New(0); + if (!empty) { + return -1; + } + res = connection_commit(self, empty); + if (!res) { + return -1; + } + Py_DECREF(empty); + Py_DECREF(res); + + self->inTransaction = 0; + } else { + Py_INCREF(isolation_level); + self->isolation_level = isolation_level; + + begin_statement = PyString_FromString("BEGIN "); + if (!begin_statement) { + return -1; + } + PyString_Concat(&begin_statement, isolation_level); + if (!begin_statement) { + return -1; + } + + self->begin_statement = PyMem_Malloc(PyString_Size(begin_statement) + 2); + if (!self->begin_statement) { + return -1; + } + + strcpy(self->begin_statement, PyString_AsString(begin_statement)); + Py_DECREF(begin_statement); + } + + return 0; +} + +PyObject* connection_call(Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* sql; + Statement* statement; + int rc; + + if (!PyArg_ParseTuple(args, "O", &sql)) { + return NULL; + } + + statement = PyObject_New(Statement, &StatementType); + if (!statement) { + return NULL; + } + + rc = statement_create(statement, self, sql); + + if (rc != SQLITE_OK) { + if (rc == PYSQLITE_TOO_MUCH_SQL) { + PyErr_SetString(Warning, "You can only execute one statement at a time."); + } else if (rc == PYSQLITE_SQL_WRONG_TYPE) { + PyErr_SetString(Warning, "SQL is of wrong type. Must be string or unicode."); + } else { + _seterror(self->db); + } + + Py_DECREF(statement); + statement = 0; + } + + return (PyObject*)statement; +} + +PyObject* connection_execute(Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* cursor = 0; + PyObject* result = 0; + PyObject* method = 0; + + cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); + if (!cursor) { + goto error; + } + + method = PyObject_GetAttrString(cursor, "execute"); + if (!method) { + Py_DECREF(cursor); + cursor = 0; + goto error; + } + + result = PyObject_CallObject(method, args); + if (!result) { + Py_DECREF(cursor); + cursor = 0; + } + +error: + Py_XDECREF(result); + Py_XDECREF(method); + + return cursor; +} + +PyObject* connection_executemany(Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* cursor = 0; + PyObject* result = 0; + PyObject* method = 0; + + cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); + if (!cursor) { + goto error; + } + + method = PyObject_GetAttrString(cursor, "executemany"); + if (!method) { + Py_DECREF(cursor); + cursor = 0; + goto error; + } + + result = PyObject_CallObject(method, args); + if (!result) { + Py_DECREF(cursor); + cursor = 0; + } + +error: + Py_XDECREF(result); + Py_XDECREF(method); + + return cursor; +} + +PyObject* connection_executescript(Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* cursor = 0; + PyObject* result = 0; + PyObject* method = 0; + + cursor = PyObject_CallMethod((PyObject*)self, "cursor", ""); + if (!cursor) { + goto error; + } + + method = PyObject_GetAttrString(cursor, "executescript"); + if (!method) { + Py_DECREF(cursor); + cursor = 0; + goto error; + } + + result = PyObject_CallObject(method, args); + if (!result) { + Py_DECREF(cursor); + cursor = 0; + } + +error: + Py_XDECREF(result); + Py_XDECREF(method); + + return cursor; +} + +/* ------------------------- COLLATION CODE ------------------------ */ + +static int +collation_callback( + void* context, + int text1_length, const void* text1_data, + int text2_length, const void* text2_data) +{ + PyObject* callback = (PyObject*)context; + PyObject* string1 = 0; + PyObject* string2 = 0; + PyGILState_STATE gilstate; + + PyObject* retval = NULL; + int result = 0; + + gilstate = PyGILState_Ensure(); + + if (PyErr_Occurred()) { + goto finally; + } + + string1 = PyString_FromStringAndSize((const char*)text1_data, text1_length); + string2 = PyString_FromStringAndSize((const char*)text2_data, text2_length); + + if (!string1 || !string2) { + goto finally; /* failed to allocate strings */ + } + + retval = PyObject_CallFunctionObjArgs(callback, string1, string2, NULL); + + if (!retval) { + /* execution failed */ + goto finally; + } + + result = PyInt_AsLong(retval); + if (PyErr_Occurred()) { + result = 0; + } + +finally: + Py_XDECREF(string1); + Py_XDECREF(string2); + Py_XDECREF(retval); + + PyGILState_Release(gilstate); + + return result; +} + +static PyObject * +connection_create_collation(Connection* self, PyObject* args) +{ + PyObject* callable; + PyObject* uppercase_name = 0; + PyObject* name; + PyObject* retval; + char* chk; + int rc; + + if (!check_thread(self) || !check_connection(self)) { + goto finally; + } + + if (!PyArg_ParseTuple(args, "O!O:create_collation(name, callback)", &PyString_Type, &name, &callable)) { + goto finally; + } + + uppercase_name = PyObject_CallMethod(name, "upper", ""); + if (!uppercase_name) { + goto finally; + } + + chk = PyString_AsString(uppercase_name); + while (*chk) { + if ((*chk >= '0' && *chk <= '9') + || (*chk >= 'A' && *chk <= 'Z') + || (*chk == '_')) + { + chk++; + } else { + PyErr_SetString(ProgrammingError, "invalid character in collation name"); + goto finally; + } + } + + if (callable != Py_None && !PyCallable_Check(callable)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + goto finally; + } + + if (callable != Py_None) { + PyDict_SetItem(self->collations, uppercase_name, callable); + } else { + PyDict_DelItem(self->collations, uppercase_name); + } + + rc = sqlite3_create_collation(self->db, + PyString_AsString(uppercase_name), + SQLITE_UTF8, + (callable != Py_None) ? callable : NULL, + (callable != Py_None) ? collation_callback : NULL); + if (rc != SQLITE_OK) { + PyDict_DelItem(self->collations, uppercase_name); + _seterror(self->db); + goto finally; + } + +finally: + Py_XDECREF(uppercase_name); + + if (PyErr_Occurred()) { + retval = NULL; + } else { + Py_INCREF(Py_None); + retval = Py_None; + } + + return retval; +} + +static char connection_doc[] = +PyDoc_STR(""); + +static PyGetSetDef connection_getset[] = { + {"isolation_level", (getter)connection_get_isolation_level, (setter)connection_set_isolation_level}, + {"total_changes", (getter)connection_get_total_changes, (setter)0}, + {NULL} +}; + +static PyMethodDef connection_methods[] = { + {"cursor", (PyCFunction)connection_cursor, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("Return a cursor for the connection.")}, + {"close", (PyCFunction)connection_close, METH_NOARGS, + PyDoc_STR("Closes the connection.")}, + {"commit", (PyCFunction)connection_commit, METH_NOARGS, + PyDoc_STR("Commit the current transaction.")}, + {"rollback", (PyCFunction)connection_rollback, METH_NOARGS, + PyDoc_STR("Roll back the current transaction.")}, + {"create_function", (PyCFunction)connection_create_function, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("Creates a new function. Non-standard.")}, + {"create_aggregate", (PyCFunction)connection_create_aggregate, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("Creates a new aggregate. Non-standard.")}, + {"execute", (PyCFunction)connection_execute, METH_VARARGS, + PyDoc_STR("Executes a SQL statement. Non-standard.")}, + {"executemany", (PyCFunction)connection_executemany, METH_VARARGS, + PyDoc_STR("Repeatedly executes a SQL statement. Non-standard.")}, + {"executescript", (PyCFunction)connection_executescript, METH_VARARGS, + PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")}, + {"create_collation", (PyCFunction)connection_create_collation, METH_VARARGS, + PyDoc_STR("Creates a collation function.")}, + {NULL, NULL} +}; + +static struct PyMemberDef connection_members[] = +{ + {"Warning", T_OBJECT, offsetof(Connection, Warning), RO}, + {"Error", T_OBJECT, offsetof(Connection, Error), RO}, + {"InterfaceError", T_OBJECT, offsetof(Connection, InterfaceError), RO}, + {"DatabaseError", T_OBJECT, offsetof(Connection, DatabaseError), RO}, + {"DataError", T_OBJECT, offsetof(Connection, DataError), RO}, + {"OperationalError", T_OBJECT, offsetof(Connection, OperationalError), RO}, + {"IntegrityError", T_OBJECT, offsetof(Connection, IntegrityError), RO}, + {"InternalError", T_OBJECT, offsetof(Connection, InternalError), RO}, + {"ProgrammingError", T_OBJECT, offsetof(Connection, ProgrammingError), RO}, + {"NotSupportedError", T_OBJECT, offsetof(Connection, NotSupportedError), RO}, + {"row_factory", T_OBJECT, offsetof(Connection, row_factory)}, + {"text_factory", T_OBJECT, offsetof(Connection, text_factory)}, + {NULL} +}; + +PyTypeObject ConnectionType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".Connection", /* tp_name */ + sizeof(Connection), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)connection_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 */ + (ternaryfunc)connection_call, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ + connection_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + connection_methods, /* tp_methods */ + connection_members, /* tp_members */ + connection_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)connection_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int connection_setup_types(void) +{ + ConnectionType.tp_new = PyType_GenericNew; + return PyType_Ready(&ConnectionType); +} diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h new file mode 100644 index 0000000..faae6e4 --- /dev/null +++ b/Modules/_sqlite/connection.h @@ -0,0 +1,106 @@ +/* connection.h - definitions for the connection type + * + * Copyright (C) 2004-2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_CONNECTION_H +#define PYSQLITE_CONNECTION_H +#include "Python.h" +#include "pythread.h" +#include "structmember.h" + +#include "cache.h" +#include "module.h" + +#include "sqlite3.h" + +typedef struct +{ + PyObject_HEAD + sqlite3* db; + + int inTransaction; + int detect_types; + + /* the timeout value in seconds for database locks */ + double timeout; + + /* for internal use in the timeout handler: when did the timeout handler + * first get called with count=0? */ + double timeout_started; + + /* None for autocommit, otherwise a PyString with the isolation level */ + PyObject* isolation_level; + + /* NULL for autocommit, otherwise a string with the BEGIN statment; will be + * freed in connection destructor */ + char* begin_statement; + + int check_same_thread; + long thread_ident; + + Cache* statement_cache; + + PyObject* row_factory; + + PyObject* text_factory; + + /* remember references to functions/classes used in + * create_function/create/aggregate, use these as dictionary keys, so we + * can keep the total system refcount constant by clearing that dictionary + * in connection_dealloc */ + PyObject* function_pinboard; + + /* a dictionary of registered collation name => collation callable mappings */ + PyObject* collations; + + /* Exception objects */ + PyObject* Warning; + PyObject* Error; + PyObject* InterfaceError; + PyObject* DatabaseError; + PyObject* DataError; + PyObject* OperationalError; + PyObject* IntegrityError; + PyObject* InternalError; + PyObject* ProgrammingError; + PyObject* NotSupportedError; +} Connection; + +extern PyTypeObject ConnectionType; + +PyObject* connection_alloc(PyTypeObject* type, int aware); +void connection_dealloc(Connection* self); +PyObject* connection_cursor(Connection* self, PyObject* args, PyObject* kwargs); +PyObject* connection_close(Connection* self, PyObject* args); +PyObject* _connection_begin(Connection* self); +PyObject* connection_begin(Connection* self, PyObject* args); +PyObject* connection_commit(Connection* self, PyObject* args); +PyObject* connection_rollback(Connection* self, PyObject* args); +PyObject* connection_new(PyTypeObject* type, PyObject* args, PyObject* kw); +int connection_init(Connection* self, PyObject* args, PyObject* kwargs); + +int check_thread(Connection* self); +int check_connection(Connection* con); + +int connection_setup_types(void); + +#endif diff --git a/Modules/_sqlite/converters.c b/Modules/_sqlite/converters.c new file mode 100644 index 0000000..018063a --- /dev/null +++ b/Modules/_sqlite/converters.c @@ -0,0 +1,40 @@ +/* converters.c - default converters + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "util.h" +#include "module.h" +#include "adapters.h" + +/* dummy, will be implemented in a later version */ + +PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs) +{ + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs) +{ + Py_INCREF(Py_None); + return Py_None; +} diff --git a/Modules/_sqlite/converters.h b/Modules/_sqlite/converters.h new file mode 100644 index 0000000..df3768a --- /dev/null +++ b/Modules/_sqlite/converters.h @@ -0,0 +1,33 @@ +/* converters.h - default converters + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_CONVERTERS_H +#define PYSQLITE_CONVERTERS_H +#include "Python.h" +#include "pythread.h" +#include "sqlite3.h" + +PyObject* convert_date(PyObject* self, PyObject* args, PyObject* kwargs); +PyObject* convert_timestamp(PyObject* self, PyObject* args, PyObject* kwargs); + +#endif diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c new file mode 100644 index 0000000..c6b8c77 --- /dev/null +++ b/Modules/_sqlite/cursor.c @@ -0,0 +1,1027 @@ +/* cursor.c - the cursor type + * + * Copyright (C) 2004-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "cursor.h" +#include "module.h" +#include "util.h" +#include "sqlitecompat.h" + +/* used to decide wether to call PyInt_FromLong or PyLong_FromLongLong */ +#define INT32_MIN (-2147483647 - 1) +#define INT32_MAX 2147483647 + +PyObject* cursor_iternext(Cursor *self); + +static StatementKind detect_statement_type(char* statement) +{ + char buf[20]; + char* src; + char* dst; + + src = statement; + /* skip over whitepace */ + while (*src == '\r' || *src == '\n' || *src == ' ' || *src == '\t') { + src++; + } + + if (*src == 0) + return STATEMENT_INVALID; + + dst = buf; + *dst = 0; + while (isalpha(*src) && dst - buf < sizeof(buf) - 2) { + *dst++ = tolower(*src++); + } + + *dst = 0; + + if (!strcmp(buf, "select")) { + return STATEMENT_SELECT; + } else if (!strcmp(buf, "insert")) { + return STATEMENT_INSERT; + } else if (!strcmp(buf, "update")) { + return STATEMENT_UPDATE; + } else if (!strcmp(buf, "delete")) { + return STATEMENT_DELETE; + } else if (!strcmp(buf, "replace")) { + return STATEMENT_REPLACE; + } else { + return STATEMENT_OTHER; + } +} + +int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs) +{ + Connection* connection; + + if (!PyArg_ParseTuple(args, "O!", &ConnectionType, &connection)) + { + return -1; + } + + Py_INCREF(connection); + self->connection = connection; + self->statement = NULL; + self->next_row = NULL; + + self->row_cast_map = PyList_New(0); + if (!self->row_cast_map) { + return -1; + } + + Py_INCREF(Py_None); + self->description = Py_None; + + Py_INCREF(Py_None); + self->lastrowid= Py_None; + + self->arraysize = 1; + + self->rowcount = PyInt_FromLong(-1L); + if (!self->rowcount) { + return -1; + } + + Py_INCREF(Py_None); + self->row_factory = Py_None; + + if (!check_thread(self->connection)) { + return -1; + } + + return 0; +} + +void cursor_dealloc(Cursor* self) +{ + int rc; + + /* Reset the statement if the user has not closed the cursor */ + if (self->statement) { + rc = statement_reset(self->statement); + Py_DECREF(self->statement); + } + + Py_XDECREF(self->connection); + Py_XDECREF(self->row_cast_map); + Py_XDECREF(self->description); + Py_XDECREF(self->lastrowid); + Py_XDECREF(self->rowcount); + Py_XDECREF(self->row_factory); + Py_XDECREF(self->next_row); + + self->ob_type->tp_free((PyObject*)self); +} + +int build_row_cast_map(Cursor* self) +{ + int i; + const char* type_start = (const char*)-1; + const char* pos; + + const char* colname; + const char* decltype; + PyObject* py_decltype; + PyObject* converter; + PyObject* key; + + if (!self->connection->detect_types) { + return 0; + } + + Py_XDECREF(self->row_cast_map); + self->row_cast_map = PyList_New(0); + + for (i = 0; i < sqlite3_column_count(self->statement->st); i++) { + converter = NULL; + + if (self->connection->detect_types | PARSE_COLNAMES) { + colname = sqlite3_column_name(self->statement->st, i); + + for (pos = colname; *pos != 0; pos++) { + if (*pos == '[') { + type_start = pos + 1; + } else if (*pos == ']' && type_start != (const char*)-1) { + key = PyString_FromStringAndSize(type_start, pos - type_start); + if (!key) { + /* creating a string failed, but it is too complicated + * to propagate the error here, we just assume there is + * no converter and proceed */ + break; + } + + converter = PyDict_GetItem(converters, key); + Py_DECREF(key); + break; + } + + } + } + + if (!converter && self->connection->detect_types | PARSE_DECLTYPES) { + decltype = sqlite3_column_decltype(self->statement->st, i); + if (decltype) { + for (pos = decltype;;pos++) { + if (*pos == ' ' || *pos == 0) { + py_decltype = PyString_FromStringAndSize(decltype, pos - decltype); + if (!py_decltype) { + return -1; + } + break; + } + } + + converter = PyDict_GetItem(converters, py_decltype); + Py_DECREF(py_decltype); + } + } + + if (!converter) { + converter = Py_None; + } + + if (PyList_Append(self->row_cast_map, converter) != 0) { + if (converter != Py_None) { + Py_DECREF(converter); + } + Py_XDECREF(self->row_cast_map); + self->row_cast_map = NULL; + + return -1; + } + } + + return 0; +} + +PyObject* _build_column_name(const char* colname) +{ + const char* pos; + + if (!colname) { + Py_INCREF(Py_None); + return Py_None; + } + + for (pos = colname;; pos++) { + if (*pos == 0 || *pos == ' ') { + return PyString_FromStringAndSize(colname, pos - colname); + } + } +} + +PyObject* unicode_from_string(const char* val_str, int optimize) +{ + const char* check; + int is_ascii = 0; + + if (optimize) { + is_ascii = 1; + + check = val_str; + while (*check) { + if (*check & 0x80) { + is_ascii = 0; + break; + } + + check++; + } + } + + if (is_ascii) { + return PyString_FromString(val_str); + } else { + return PyUnicode_DecodeUTF8(val_str, strlen(val_str), NULL); + } +} + +/* + * Returns a row from the currently active SQLite statement + * + * Precondidition: + * - sqlite3_step() has been called before and it returned SQLITE_ROW. + */ +PyObject* _fetch_one_row(Cursor* self) +{ + int i, numcols; + PyObject* row; + PyObject* item = NULL; + int coltype; + PY_LONG_LONG intval; + PyObject* converter; + PyObject* converted; + Py_ssize_t nbytes; + PyObject* buffer; + void* raw_buffer; + const char* val_str; + char buf[200]; + + Py_BEGIN_ALLOW_THREADS + numcols = sqlite3_data_count(self->statement->st); + Py_END_ALLOW_THREADS + + row = PyTuple_New(numcols); + if (!row) { + return NULL; + } + + for (i = 0; i < numcols; i++) { + if (self->connection->detect_types) { + converter = PyList_GetItem(self->row_cast_map, i); + if (!converter) { + converter = Py_None; + } + } else { + converter = Py_None; + } + + if (converter != Py_None) { + val_str = (const char*)sqlite3_column_text(self->statement->st, i); + if (!val_str) { + Py_INCREF(Py_None); + converted = Py_None; + } else { + item = PyString_FromString(val_str); + if (!item) { + return NULL; + } + converted = PyObject_CallFunction(converter, "O", item); + if (!converted) { + /* TODO: have a way to log these errors */ + Py_INCREF(Py_None); + converted = Py_None; + PyErr_Clear(); + } + Py_DECREF(item); + } + } else { + Py_BEGIN_ALLOW_THREADS + coltype = sqlite3_column_type(self->statement->st, i); + Py_END_ALLOW_THREADS + if (coltype == SQLITE_NULL) { + Py_INCREF(Py_None); + converted = Py_None; + } else if (coltype == SQLITE_INTEGER) { + intval = sqlite3_column_int64(self->statement->st, i); + if (intval < INT32_MIN || intval > INT32_MAX) { + converted = PyLong_FromLongLong(intval); + } else { + converted = PyInt_FromLong((long)intval); + } + } else if (coltype == SQLITE_FLOAT) { + converted = PyFloat_FromDouble(sqlite3_column_double(self->statement->st, i)); + } else if (coltype == SQLITE_TEXT) { + val_str = (const char*)sqlite3_column_text(self->statement->st, i); + if ((self->connection->text_factory == (PyObject*)&PyUnicode_Type) + || (self->connection->text_factory == OptimizedUnicode)) { + + converted = unicode_from_string(val_str, + self->connection->text_factory == OptimizedUnicode ? 1 : 0); + + if (!converted) { + PyOS_snprintf(buf, sizeof(buf) - 1, "Could not decode to UTF-8 column %s with text %s", + sqlite3_column_name(self->statement->st, i), val_str); + PyErr_SetString(OperationalError, buf); + } + } else if (self->connection->text_factory == (PyObject*)&PyString_Type) { + converted = PyString_FromString(val_str); + } else { + converted = PyObject_CallFunction(self->connection->text_factory, "s", val_str); + } + } else { + /* coltype == SQLITE_BLOB */ + nbytes = sqlite3_column_bytes(self->statement->st, i); + buffer = PyBuffer_New(nbytes); + if (!buffer) { + break; + } + if (PyObject_AsWriteBuffer(buffer, &raw_buffer, &nbytes)) { + break; + } + memcpy(raw_buffer, sqlite3_column_blob(self->statement->st, i), nbytes); + converted = buffer; + } + } + + PyTuple_SetItem(row, i, converted); + } + + if (PyErr_Occurred()) { + Py_DECREF(row); + row = NULL; + } + + return row; +} + +PyObject* _query_execute(Cursor* self, int multiple, PyObject* args) +{ + PyObject* operation; + PyObject* operation_bytestr = NULL; + char* operation_cstr; + PyObject* parameters_list = NULL; + PyObject* parameters_iter = NULL; + PyObject* parameters = NULL; + int i; + int rc; + PyObject* func_args; + PyObject* result; + int numcols; + PY_LONG_LONG lastrowid; + int statement_type; + PyObject* descriptor; + PyObject* second_argument = NULL; + long rowcount = 0; + + if (!check_thread(self->connection) || !check_connection(self->connection)) { + return NULL; + } + + Py_XDECREF(self->next_row); + self->next_row = NULL; + + if (multiple) { + /* executemany() */ + if (!PyArg_ParseTuple(args, "OO", &operation, &second_argument)) { + return NULL; + } + + if (!PyString_Check(operation) && !PyUnicode_Check(operation)) { + PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode"); + return NULL; + } + + if (PyIter_Check(second_argument)) { + /* iterator */ + Py_INCREF(second_argument); + parameters_iter = second_argument; + } else { + /* sequence */ + parameters_iter = PyObject_GetIter(second_argument); + if (!parameters_iter) + { + return NULL; + } + } + } else { + /* execute() */ + if (!PyArg_ParseTuple(args, "O|O", &operation, &second_argument)) { + return NULL; + } + + if (!PyString_Check(operation) && !PyUnicode_Check(operation)) { + PyErr_SetString(PyExc_ValueError, "operation parameter must be str or unicode"); + return NULL; + } + + parameters_list = PyList_New(0); + if (!parameters_list) { + return NULL; + } + + if (second_argument == NULL) { + second_argument = PyTuple_New(0); + if (!second_argument) { + goto error; + } + } else { + Py_INCREF(second_argument); + } + if (PyList_Append(parameters_list, second_argument) != 0) { + Py_DECREF(second_argument); + goto error; + } + Py_DECREF(second_argument); + + parameters_iter = PyObject_GetIter(parameters_list); + if (!parameters_iter) { + goto error; + } + } + + if (self->statement != NULL) { + /* There is an active statement */ + rc = statement_reset(self->statement); + } + + if (PyString_Check(operation)) { + operation_cstr = PyString_AsString(operation); + } else { + operation_bytestr = PyUnicode_AsUTF8String(operation); + if (!operation_bytestr) { + goto error; + } + + operation_cstr = PyString_AsString(operation_bytestr); + } + + /* reset description and rowcount */ + Py_DECREF(self->description); + Py_INCREF(Py_None); + self->description = Py_None; + + Py_DECREF(self->rowcount); + self->rowcount = PyInt_FromLong(-1L); + if (!self->rowcount) { + goto error; + } + + statement_type = detect_statement_type(operation_cstr); + if (self->connection->begin_statement) { + switch (statement_type) { + case STATEMENT_UPDATE: + case STATEMENT_DELETE: + case STATEMENT_INSERT: + case STATEMENT_REPLACE: + if (!self->connection->inTransaction) { + result = _connection_begin(self->connection); + if (!result) { + goto error; + } + Py_DECREF(result); + } + break; + case STATEMENT_OTHER: + /* it's a DDL statement or something similar + - we better COMMIT first so it works for all cases */ + if (self->connection->inTransaction) { + func_args = PyTuple_New(0); + if (!func_args) { + goto error; + } + result = connection_commit(self->connection, func_args); + Py_DECREF(func_args); + if (!result) { + goto error; + } + Py_DECREF(result); + } + break; + case STATEMENT_SELECT: + if (multiple) { + PyErr_SetString(ProgrammingError, + "You cannot execute SELECT statements in executemany()."); + goto error; + } + break; + } + } + + func_args = PyTuple_New(1); + if (!func_args) { + goto error; + } + Py_INCREF(operation); + if (PyTuple_SetItem(func_args, 0, operation) != 0) { + goto error; + } + + if (self->statement) { + (void)statement_reset(self->statement); + Py_DECREF(self->statement); + } + + self->statement = (Statement*)cache_get(self->connection->statement_cache, func_args); + Py_DECREF(func_args); + + if (!self->statement) { + goto error; + } + + if (self->statement->in_use) { + Py_DECREF(self->statement); + self->statement = PyObject_New(Statement, &StatementType); + if (!self->statement) { + goto error; + } + rc = statement_create(self->statement, self->connection, operation); + if (rc != SQLITE_OK) { + self->statement = 0; + goto error; + } + } + + statement_reset(self->statement); + statement_mark_dirty(self->statement); + + while (1) { + parameters = PyIter_Next(parameters_iter); + if (!parameters) { + break; + } + + statement_mark_dirty(self->statement); + + statement_bind_parameters(self->statement, parameters); + if (PyErr_Occurred()) { + goto error; + } + + if (build_row_cast_map(self) != 0) { + PyErr_SetString(OperationalError, "Error while building row_cast_map"); + goto error; + } + + rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection); + if (rc != SQLITE_DONE && rc != SQLITE_ROW) { + rc = statement_reset(self->statement); + if (rc == SQLITE_SCHEMA) { + rc = statement_recompile(self->statement, parameters); + if (rc == SQLITE_OK) { + rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection); + } else { + _seterror(self->connection->db); + goto error; + } + } else { + _seterror(self->connection->db); + goto error; + } + } + + if (rc == SQLITE_ROW || (rc == SQLITE_DONE && statement_type == STATEMENT_SELECT)) { + Py_BEGIN_ALLOW_THREADS + numcols = sqlite3_column_count(self->statement->st); + Py_END_ALLOW_THREADS + + if (self->description == Py_None) { + Py_DECREF(self->description); + self->description = PyTuple_New(numcols); + if (!self->description) { + goto error; + } + for (i = 0; i < numcols; i++) { + descriptor = PyTuple_New(7); + if (!descriptor) { + goto error; + } + PyTuple_SetItem(descriptor, 0, _build_column_name(sqlite3_column_name(self->statement->st, i))); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 4, Py_None); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 5, Py_None); + Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 6, Py_None); + PyTuple_SetItem(self->description, i, descriptor); + } + } + } + + if (rc == SQLITE_ROW) { + if (multiple) { + PyErr_SetString(ProgrammingError, "executemany() can only execute DML statements."); + goto error; + } + + self->next_row = _fetch_one_row(self); + } else if (rc == SQLITE_DONE && !multiple) { + statement_reset(self->statement); + Py_DECREF(self->statement); + self->statement = 0; + } + + switch (statement_type) { + case STATEMENT_UPDATE: + case STATEMENT_DELETE: + case STATEMENT_INSERT: + case STATEMENT_REPLACE: + Py_BEGIN_ALLOW_THREADS + rowcount += (long)sqlite3_changes(self->connection->db); + Py_END_ALLOW_THREADS + Py_DECREF(self->rowcount); + self->rowcount = PyInt_FromLong(rowcount); + } + + Py_DECREF(self->lastrowid); + if (statement_type == STATEMENT_INSERT) { + Py_BEGIN_ALLOW_THREADS + lastrowid = sqlite3_last_insert_rowid(self->connection->db); + Py_END_ALLOW_THREADS + self->lastrowid = PyInt_FromLong((long)lastrowid); + } else { + Py_INCREF(Py_None); + self->lastrowid = Py_None; + } + + if (multiple) { + rc = statement_reset(self->statement); + } + Py_XDECREF(parameters); + } + +error: + Py_XDECREF(operation_bytestr); + Py_XDECREF(parameters); + Py_XDECREF(parameters_iter); + Py_XDECREF(parameters_list); + + if (PyErr_Occurred()) { + return NULL; + } else { + Py_INCREF(self); + return (PyObject*)self; + } +} + +PyObject* cursor_execute(Cursor* self, PyObject* args) +{ + return _query_execute(self, 0, args); +} + +PyObject* cursor_executemany(Cursor* self, PyObject* args) +{ + return _query_execute(self, 1, args); +} + +PyObject* cursor_executescript(Cursor* self, PyObject* args) +{ + PyObject* script_obj; + PyObject* script_str = NULL; + const char* script_cstr; + sqlite3_stmt* statement; + int rc; + PyObject* func_args; + PyObject* result; + int statement_completed = 0; + + if (!PyArg_ParseTuple(args, "O", &script_obj)) { + return NULL; + } + + if (!check_thread(self->connection) || !check_connection(self->connection)) { + return NULL; + } + + if (PyString_Check(script_obj)) { + script_cstr = PyString_AsString(script_obj); + } else if (PyUnicode_Check(script_obj)) { + script_str = PyUnicode_AsUTF8String(script_obj); + if (!script_str) { + return NULL; + } + + script_cstr = PyString_AsString(script_str); + } else { + PyErr_SetString(PyExc_ValueError, "script argument must be unicode or string."); + return NULL; + } + + /* commit first */ + func_args = PyTuple_New(0); + if (!func_args) { + goto error; + } + result = connection_commit(self->connection, func_args); + Py_DECREF(func_args); + if (!result) { + goto error; + } + Py_DECREF(result); + + while (1) { + if (!sqlite3_complete(script_cstr)) { + break; + } + statement_completed = 1; + + rc = sqlite3_prepare(self->connection->db, + script_cstr, + -1, + &statement, + &script_cstr); + if (rc != SQLITE_OK) { + _seterror(self->connection->db); + goto error; + } + + /* execute statement, and ignore results of SELECT statements */ + rc = SQLITE_ROW; + while (rc == SQLITE_ROW) { + rc = _sqlite_step_with_busyhandler(statement, self->connection); + } + + if (rc != SQLITE_DONE) { + (void)sqlite3_finalize(statement); + _seterror(self->connection->db); + goto error; + } + + rc = sqlite3_finalize(statement); + if (rc != SQLITE_OK) { + _seterror(self->connection->db); + goto error; + } + } + +error: + Py_XDECREF(script_str); + + if (!statement_completed) { + PyErr_SetString(ProgrammingError, "you did not provide a complete SQL statement"); + } + + if (PyErr_Occurred()) { + return NULL; + } else { + Py_INCREF(self); + return (PyObject*)self; + } +} + +PyObject* cursor_getiter(Cursor *self) +{ + Py_INCREF(self); + return (PyObject*)self; +} + +PyObject* cursor_iternext(Cursor *self) +{ + PyObject* next_row_tuple; + PyObject* next_row; + int rc; + + if (!check_thread(self->connection) || !check_connection(self->connection)) { + return NULL; + } + + if (!self->next_row) { + if (self->statement) { + (void)statement_reset(self->statement); + Py_DECREF(self->statement); + self->statement = NULL; + } + return NULL; + } + + next_row_tuple = self->next_row; + self->next_row = NULL; + + if (self->row_factory != Py_None) { + next_row = PyObject_CallFunction(self->row_factory, "OO", self, next_row_tuple); + Py_DECREF(next_row_tuple); + } else { + next_row = next_row_tuple; + } + + rc = _sqlite_step_with_busyhandler(self->statement->st, self->connection); + if (rc != SQLITE_DONE && rc != SQLITE_ROW) { + Py_DECREF(next_row); + _seterror(self->connection->db); + return NULL; + } + + if (rc == SQLITE_ROW) { + self->next_row = _fetch_one_row(self); + } + + return next_row; +} + +PyObject* cursor_fetchone(Cursor* self, PyObject* args) +{ + PyObject* row; + + row = cursor_iternext(self); + if (!row && !PyErr_Occurred()) { + Py_INCREF(Py_None); + return Py_None; + } + + return row; +} + +PyObject* cursor_fetchmany(Cursor* self, PyObject* args) +{ + PyObject* row; + PyObject* list; + int maxrows = self->arraysize; + int counter = 0; + + if (!PyArg_ParseTuple(args, "|i", &maxrows)) { + return NULL; + } + + list = PyList_New(0); + if (!list) { + return NULL; + } + + /* just make sure we enter the loop */ + row = Py_None; + + while (row) { + row = cursor_iternext(self); + if (row) { + PyList_Append(list, row); + Py_DECREF(row); + } else { + break; + } + + if (++counter == maxrows) { + break; + } + } + + if (PyErr_Occurred()) { + Py_DECREF(list); + return NULL; + } else { + return list; + } +} + +PyObject* cursor_fetchall(Cursor* self, PyObject* args) +{ + PyObject* row; + PyObject* list; + + list = PyList_New(0); + if (!list) { + return NULL; + } + + /* just make sure we enter the loop */ + row = (PyObject*)Py_None; + + while (row) { + row = cursor_iternext(self); + if (row) { + PyList_Append(list, row); + Py_DECREF(row); + } + } + + if (PyErr_Occurred()) { + Py_DECREF(list); + return NULL; + } else { + return list; + } +} + +PyObject* pysqlite_noop(Connection* self, PyObject* args) +{ + /* don't care, return None */ + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* cursor_close(Cursor* self, PyObject* args) +{ + if (!check_thread(self->connection) || !check_connection(self->connection)) { + return NULL; + } + + if (self->statement) { + (void)statement_reset(self->statement); + Py_DECREF(self->statement); + self->statement = 0; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef cursor_methods[] = { + {"execute", (PyCFunction)cursor_execute, METH_VARARGS, + PyDoc_STR("Executes a SQL statement.")}, + {"executemany", (PyCFunction)cursor_executemany, METH_VARARGS, + PyDoc_STR("Repeatedly executes a SQL statement.")}, + {"executescript", (PyCFunction)cursor_executescript, METH_VARARGS, + PyDoc_STR("Executes a multiple SQL statements at once. Non-standard.")}, + {"fetchone", (PyCFunction)cursor_fetchone, METH_NOARGS, + PyDoc_STR("Fetches several rows from the resultset.")}, + {"fetchmany", (PyCFunction)cursor_fetchmany, METH_VARARGS, + PyDoc_STR("Fetches all rows from the resultset.")}, + {"fetchall", (PyCFunction)cursor_fetchall, METH_NOARGS, + PyDoc_STR("Fetches one row from the resultset.")}, + {"close", (PyCFunction)cursor_close, METH_NOARGS, + PyDoc_STR("Closes the cursor.")}, + {"setinputsizes", (PyCFunction)pysqlite_noop, METH_VARARGS, + PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")}, + {"setoutputsize", (PyCFunction)pysqlite_noop, METH_VARARGS, + PyDoc_STR("Required by DB-API. Does nothing in pysqlite.")}, + {NULL, NULL} +}; + +static struct PyMemberDef cursor_members[] = +{ + {"connection", T_OBJECT, offsetof(Cursor, connection), RO}, + {"description", T_OBJECT, offsetof(Cursor, description), RO}, + {"arraysize", T_INT, offsetof(Cursor, arraysize), 0}, + {"lastrowid", T_OBJECT, offsetof(Cursor, lastrowid), RO}, + {"rowcount", T_OBJECT, offsetof(Cursor, rowcount), RO}, + {"row_factory", T_OBJECT, offsetof(Cursor, row_factory), 0}, + {NULL} +}; + +PyTypeObject CursorType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".Cursor", /* tp_name */ + sizeof(Cursor), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)cursor_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|Py_TPFLAGS_HAVE_ITER|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)cursor_getiter, /* tp_iter */ + (iternextfunc)cursor_iternext, /* tp_iternext */ + cursor_methods, /* tp_methods */ + cursor_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)cursor_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int cursor_setup_types(void) +{ + CursorType.tp_new = PyType_GenericNew; + return PyType_Ready(&CursorType); +} diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h new file mode 100644 index 0000000..7f56799 --- /dev/null +++ b/Modules/_sqlite/cursor.h @@ -0,0 +1,71 @@ +/* cursor.h - definitions for the cursor type + * + * Copyright (C) 2004-2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_CURSOR_H +#define PYSQLITE_CURSOR_H +#include "Python.h" + +#include "statement.h" +#include "connection.h" +#include "module.h" + +typedef struct +{ + PyObject_HEAD + Connection* connection; + PyObject* description; + PyObject* row_cast_map; + int arraysize; + PyObject* lastrowid; + PyObject* rowcount; + PyObject* row_factory; + Statement* statement; + + /* the next row to be returned, NULL if no next row available */ + PyObject* next_row; +} Cursor; + +typedef enum { + STATEMENT_INVALID, STATEMENT_INSERT, STATEMENT_DELETE, + STATEMENT_UPDATE, STATEMENT_REPLACE, STATEMENT_SELECT, + STATEMENT_OTHER +} StatementKind; + +extern PyTypeObject CursorType; + +int cursor_init(Cursor* self, PyObject* args, PyObject* kwargs); +void cursor_dealloc(Cursor* self); +PyObject* cursor_execute(Cursor* self, PyObject* args); +PyObject* cursor_executemany(Cursor* self, PyObject* args); +PyObject* cursor_getiter(Cursor *self); +PyObject* cursor_iternext(Cursor *self); +PyObject* cursor_fetchone(Cursor* self, PyObject* args); +PyObject* cursor_fetchmany(Cursor* self, PyObject* args); +PyObject* cursor_fetchall(Cursor* self, PyObject* args); +PyObject* pysqlite_noop(Connection* self, PyObject* args); +PyObject* cursor_close(Cursor* self, PyObject* args); + +int cursor_setup_types(void); + +#define UNKNOWN (-1) +#endif diff --git a/Modules/_sqlite/microprotocols.c b/Modules/_sqlite/microprotocols.c new file mode 100644 index 0000000..4956ac0 --- /dev/null +++ b/Modules/_sqlite/microprotocols.c @@ -0,0 +1,142 @@ +/* microprotocols.c - minimalist and non-validating protocols implementation + * + * Copyright (C) 2003-2004 Federico Di Gregorio + * + * This file is part of psycopg and was adapted for pysqlite. Federico Di + * Gregorio gave the permission to use it within pysqlite under the following + * license: + * + * 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. + */ + +#include +#include + +#include "cursor.h" +#include "microprotocols.h" +#include "prepare_protocol.h" + + +/** the adapters registry **/ + +PyObject *psyco_adapters; + +/* microprotocols_init - initialize the adapters dictionary */ + +int +microprotocols_init(PyObject *dict) +{ + /* create adapters dictionary and put it in module namespace */ + if ((psyco_adapters = PyDict_New()) == NULL) { + return -1; + } + + return PyDict_SetItemString(dict, "adapters", psyco_adapters); +} + + +/* microprotocols_add - add a reverse type-caster to the dictionary */ + +int +microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast) +{ + PyObject* key; + int rc; + + if (proto == NULL) proto = (PyObject*)&SQLitePrepareProtocolType; + + key = Py_BuildValue("(OO)", (PyObject*)type, proto); + if (!key) { + return -1; + } + + rc = PyDict_SetItem(psyco_adapters, key, cast); + Py_DECREF(key); + + return rc; +} + +/* microprotocols_adapt - adapt an object to the built-in protocol */ + +PyObject * +microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) +{ + PyObject *adapter, *key; + + /* we don't check for exact type conformance as specified in PEP 246 + because the SQLitePrepareProtocolType type is abstract and there is no + way to get a quotable object to be its instance */ + + /* look for an adapter in the registry */ + key = Py_BuildValue("(OO)", (PyObject*)obj->ob_type, proto); + if (!key) { + return NULL; + } + adapter = PyDict_GetItem(psyco_adapters, key); + Py_DECREF(key); + if (adapter) { + PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL); + return adapted; + } + + /* try to have the protocol adapt this object*/ + if (PyObject_HasAttrString(proto, "__adapt__")) { + PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj); + if (adapted) { + if (adapted != Py_None) { + return adapted; + } else { + Py_DECREF(adapted); + } + } + + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) + return NULL; + } + + /* and finally try to have the object adapt itself */ + if (PyObject_HasAttrString(obj, "__conform__")) { + PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto); + if (adapted) { + if (adapted != Py_None) { + return adapted; + } else { + Py_DECREF(adapted); + } + } + + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) { + return NULL; + } + } + + /* else set the right exception and return NULL */ + PyErr_SetString(ProgrammingError, "can't adapt"); + return NULL; +} + +/** module-level functions **/ + +PyObject * +psyco_microprotocols_adapt(Cursor *self, PyObject *args) +{ + PyObject *obj, *alt = NULL; + PyObject *proto = (PyObject*)&SQLitePrepareProtocolType; + + if (!PyArg_ParseTuple(args, "O|OO", &obj, &proto, &alt)) return NULL; + return microprotocols_adapt(obj, proto, alt); +} diff --git a/Modules/_sqlite/microprotocols.h b/Modules/_sqlite/microprotocols.h new file mode 100644 index 0000000..d2d9b65 --- /dev/null +++ b/Modules/_sqlite/microprotocols.h @@ -0,0 +1,59 @@ +/* microprotocols.c - definitions for minimalist and non-validating protocols + * + * Copyright (C) 2003-2004 Federico Di Gregorio + * + * This file is part of psycopg and was adapted for pysqlite. Federico Di + * Gregorio gave the permission to use it within pysqlite under the following + * license: + * + * 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. + */ + +#ifndef PSYCOPG_MICROPROTOCOLS_H +#define PSYCOPG_MICROPROTOCOLS_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** adapters registry **/ + +extern PyObject *psyco_adapters; + +/** the names of the three mandatory methods **/ + +#define MICROPROTOCOLS_GETQUOTED_NAME "getquoted" +#define MICROPROTOCOLS_GETSTRING_NAME "getstring" +#define MICROPROTOCOLS_GETBINARY_NAME "getbinary" + +/** exported functions **/ + +/* used by module.c to init the microprotocols system */ +extern int microprotocols_init(PyObject *dict); +extern int microprotocols_add( + PyTypeObject *type, PyObject *proto, PyObject *cast); +extern PyObject *microprotocols_adapt( + PyObject *obj, PyObject *proto, PyObject *alt); + +extern PyObject * + psyco_microprotocols_adapt(Cursor* self, PyObject *args); +#define psyco_microprotocols_adapt_doc \ + "adapt(obj, protocol, alternate) -> adapt obj to given protocol" + +#endif /* !defined(PSYCOPG_MICROPROTOCOLS_H) */ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c new file mode 100644 index 0000000..1537e79 --- /dev/null +++ b/Modules/_sqlite/module.c @@ -0,0 +1,325 @@ +/* module.c - the module itself + * + * Copyright (C) 2004-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "connection.h" +#include "statement.h" +#include "cursor.h" +#include "cache.h" +#include "prepare_protocol.h" +#include "microprotocols.h" +#include "row.h" + +#if SQLITE_VERSION_NUMBER >= 3003003 +#define HAVE_SHARED_CACHE +#endif + +/* static objects at module-level */ + +PyObject* Error, *Warning, *InterfaceError, *DatabaseError, *InternalError, + *OperationalError, *ProgrammingError, *IntegrityError, *DataError, + *NotSupportedError, *OptimizedUnicode; + +PyObject* converters; + +static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* + kwargs) +{ + /* Python seems to have no way of extracting a single keyword-arg at + * C-level, so this code is redundant with the one in connection_init in + * connection.c and must always be copied from there ... */ + + static char *kwlist[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", NULL, NULL}; + char* database; + int detect_types = 0; + PyObject* isolation_level; + PyObject* factory = NULL; + int check_same_thread = 1; + int cached_statements; + double timeout = 5.0; + + PyObject* result; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOi", kwlist, + &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements)) + { + return NULL; + } + + if (factory == NULL) { + factory = (PyObject*)&ConnectionType; + } + + result = PyObject_Call(factory, args, kwargs); + + return result; +} + +static PyObject* module_complete(PyObject* self, PyObject* args, PyObject* + kwargs) +{ + static char *kwlist[] = {"statement", NULL, NULL}; + char* statement; + + PyObject* result; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &statement)) + { + return NULL; + } + + if (sqlite3_complete(statement)) { + result = Py_True; + } else { + result = Py_False; + } + + Py_INCREF(result); + + return result; +} + +#ifdef HAVE_SHARED_CACHE +static PyObject* module_enable_shared_cache(PyObject* self, PyObject* args, PyObject* + kwargs) +{ + static char *kwlist[] = {"do_enable", NULL, NULL}; + int do_enable; + int rc; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &do_enable)) + { + return NULL; + } + + rc = sqlite3_enable_shared_cache(do_enable); + + if (rc != SQLITE_OK) { + PyErr_SetString(OperationalError, "Changing the shared_cache flag failed"); + return NULL; + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SHARED_CACHE */ + +static PyObject* module_register_adapter(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyTypeObject* type; + PyObject* caster; + + if (!PyArg_ParseTuple(args, "OO", &type, &caster)) { + return NULL; + } + + microprotocols_add(type, (PyObject*)&SQLitePrepareProtocolType, caster); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* module_register_converter(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* name; + PyObject* callable; + + if (!PyArg_ParseTuple(args, "OO", &name, &callable)) { + return NULL; + } + + if (PyDict_SetItem(converters, name, callable) != 0) { + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +void converters_init(PyObject* dict) +{ + converters = PyDict_New(); + if (!converters) { + return; + } + + PyDict_SetItemString(dict, "converters", converters); +} + +static PyMethodDef module_methods[] = { + {"connect", (PyCFunction)module_connect, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Creates a connection.")}, + {"complete_statement", (PyCFunction)module_complete, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Checks if a string contains a complete SQL statement.")}, +#ifdef HAVE_SHARED_CACHE + {"enable_shared_cache", (PyCFunction)module_enable_shared_cache, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Enable or disable shared cache mode for the calling thread.")}, +#endif + {"register_adapter", (PyCFunction)module_register_adapter, METH_VARARGS, PyDoc_STR("Registers an adapter with sqlite's adapter registry.")}, + {"register_converter", (PyCFunction)module_register_converter, METH_VARARGS, PyDoc_STR("Registers a converter with sqlite.")}, + {"adapt", (PyCFunction)psyco_microprotocols_adapt, METH_VARARGS, psyco_microprotocols_adapt_doc}, + {NULL, NULL} +}; + +PyMODINIT_FUNC init_sqlite3(void) +{ + PyObject *module, *dict; + PyObject *tmp_obj; + + module = Py_InitModule("_sqlite3", module_methods); + + if (!module || + (row_setup_types() < 0) || + (cursor_setup_types() < 0) || + (connection_setup_types() < 0) || + (cache_setup_types() < 0) || + (statement_setup_types() < 0) || + (prepare_protocol_setup_types() < 0) + ) { + return; + } + + Py_INCREF(&ConnectionType); + PyModule_AddObject(module, "Connection", (PyObject*) &ConnectionType); + Py_INCREF(&CursorType); + PyModule_AddObject(module, "Cursor", (PyObject*) &CursorType); + Py_INCREF(&CacheType); + PyModule_AddObject(module, "Statement", (PyObject*)&StatementType); + Py_INCREF(&StatementType); + PyModule_AddObject(module, "Cache", (PyObject*) &CacheType); + Py_INCREF(&SQLitePrepareProtocolType); + PyModule_AddObject(module, "PrepareProtocol", (PyObject*) &SQLitePrepareProtocolType); + Py_INCREF(&RowType); + PyModule_AddObject(module, "Row", (PyObject*) &RowType); + + if (!(dict = PyModule_GetDict(module))) { + goto error; + } + + /*** Create DB-API Exception hierarchy */ + + if (!(Error = PyErr_NewException(MODULE_NAME ".Error", PyExc_StandardError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "Error", Error); + + if (!(Warning = PyErr_NewException(MODULE_NAME ".Warning", PyExc_StandardError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "Warning", Warning); + + /* Error subclasses */ + + if (!(InterfaceError = PyErr_NewException(MODULE_NAME ".InterfaceError", Error, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "InterfaceError", InterfaceError); + + if (!(DatabaseError = PyErr_NewException(MODULE_NAME ".DatabaseError", Error, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "DatabaseError", DatabaseError); + + /* DatabaseError subclasses */ + + if (!(InternalError = PyErr_NewException(MODULE_NAME ".InternalError", DatabaseError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "InternalError", InternalError); + + if (!(OperationalError = PyErr_NewException(MODULE_NAME ".OperationalError", DatabaseError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "OperationalError", OperationalError); + + if (!(ProgrammingError = PyErr_NewException(MODULE_NAME ".ProgrammingError", DatabaseError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); + + if (!(IntegrityError = PyErr_NewException(MODULE_NAME ".IntegrityError", DatabaseError,NULL))) { + goto error; + } + PyDict_SetItemString(dict, "IntegrityError", IntegrityError); + + if (!(DataError = PyErr_NewException(MODULE_NAME ".DataError", DatabaseError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "DataError", DataError); + + if (!(NotSupportedError = PyErr_NewException(MODULE_NAME ".NotSupportedError", DatabaseError, NULL))) { + goto error; + } + PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError); + + /* We just need "something" unique for OptimizedUnicode. It does not really + * need to be a string subclass. Just anything that can act as a special + * marker for us. So I pulled PyCell_Type out of my magic hat. + */ + Py_INCREF((PyObject*)&PyCell_Type); + OptimizedUnicode = (PyObject*)&PyCell_Type; + PyDict_SetItemString(dict, "OptimizedUnicode", OptimizedUnicode); + + if (!(tmp_obj = PyInt_FromLong(PARSE_DECLTYPES))) { + goto error; + } + PyDict_SetItemString(dict, "PARSE_DECLTYPES", tmp_obj); + + if (!(tmp_obj = PyInt_FromLong(PARSE_COLNAMES))) { + goto error; + } + PyDict_SetItemString(dict, "PARSE_COLNAMES", tmp_obj); + + if (!(tmp_obj = PyString_FromString(PYSQLITE_VERSION))) { + goto error; + } + PyDict_SetItemString(dict, "version", tmp_obj); + + if (!(tmp_obj = PyString_FromString(sqlite3_libversion()))) { + goto error; + } + PyDict_SetItemString(dict, "sqlite_version", tmp_obj); + + /* initialize microprotocols layer */ + microprotocols_init(dict); + + /* initialize the default converters */ + converters_init(dict); + + /* Original comment form _bsddb.c in the Python core. This is also still + * needed nowadays for Python 2.3/2.4. + * + * PyEval_InitThreads is called here due to a quirk in python 1.5 + * - 2.2.1 (at least) according to Russell Williamson : + * The global interepreter lock is not initialized until the first + * thread is created using thread.start_new_thread() or fork() is + * called. that would cause the ALLOW_THREADS here to segfault due + * to a null pointer reference if no threads or child processes + * have been created. This works around that and is a no-op if + * threads have already been initialized. + * (see pybsddb-users mailing list post on 2002-08-07) + */ + PyEval_InitThreads(); + +error: + if (PyErr_Occurred()) + { + PyErr_SetString(PyExc_ImportError, MODULE_NAME ": init failed"); + } +} diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h new file mode 100644 index 0000000..6694735 --- /dev/null +++ b/Modules/_sqlite/module.h @@ -0,0 +1,55 @@ +/* module.h - definitions for the module + * + * Copyright (C) 2004-2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_MODULE_H +#define PYSQLITE_MODULE_H +#include "Python.h" + +#define PYSQLITE_VERSION "2.2.0" + +extern PyObject* Error; +extern PyObject* Warning; +extern PyObject* InterfaceError; +extern PyObject* DatabaseError; +extern PyObject* InternalError; +extern PyObject* OperationalError; +extern PyObject* ProgrammingError; +extern PyObject* IntegrityError; +extern PyObject* DataError; +extern PyObject* NotSupportedError; + +extern PyObject* OptimizedUnicode; + +/* the functions time.time() and time.sleep() */ +extern PyObject* time_time; +extern PyObject* time_sleep; + +/* A dictionary, mapping colum types (INTEGER, VARCHAR, etc.) to converter + * functions, that convert the SQL value to the appropriate Python value. + * The key is uppercase. + */ +extern PyObject* converters; + +#define PARSE_DECLTYPES 1 +#define PARSE_COLNAMES 2 +#endif diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c new file mode 100644 index 0000000..26b663b --- /dev/null +++ b/Modules/_sqlite/prepare_protocol.c @@ -0,0 +1,84 @@ +/* prepare_protocol.c - the protocol for preparing values for SQLite + * + * Copyright (C) 2005-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "prepare_protocol.h" + +int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs) +{ + return 0; +} + +void prepare_protocol_dealloc(SQLitePrepareProtocol* self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +PyTypeObject SQLitePrepareProtocolType= { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".PrepareProtocol", /* tp_name */ + sizeof(SQLitePrepareProtocol), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)prepare_protocol_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 */ + 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 */ + (initproc)prepare_protocol_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int prepare_protocol_setup_types(void) +{ + SQLitePrepareProtocolType.tp_new = PyType_GenericNew; + SQLitePrepareProtocolType.ob_type= &PyType_Type; + return PyType_Ready(&SQLitePrepareProtocolType); +} diff --git a/Modules/_sqlite/prepare_protocol.h b/Modules/_sqlite/prepare_protocol.h new file mode 100644 index 0000000..2fc4f61 --- /dev/null +++ b/Modules/_sqlite/prepare_protocol.h @@ -0,0 +1,41 @@ +/* prepare_protocol.h - the protocol for preparing values for SQLite + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_PREPARE_PROTOCOL_H +#define PYSQLITE_PREPARE_PROTOCOL_H +#include "Python.h" + +typedef struct +{ + PyObject_HEAD +} SQLitePrepareProtocol; + +extern PyTypeObject SQLitePrepareProtocolType; + +int prepare_protocol_init(SQLitePrepareProtocol* self, PyObject* args, PyObject* kwargs); +void prepare_protocol_dealloc(SQLitePrepareProtocol* self); + +int prepare_protocol_setup_types(void); + +#define UNKNOWN (-1) +#endif diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c new file mode 100644 index 0000000..80b6135 --- /dev/null +++ b/Modules/_sqlite/row.c @@ -0,0 +1,202 @@ +/* row.c - an enhanced tuple for database rows + * + * Copyright (C) 2005-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "row.h" +#include "cursor.h" +#include "sqlitecompat.h" + +void row_dealloc(Row* self) +{ + Py_XDECREF(self->data); + Py_XDECREF(self->description); + + self->ob_type->tp_free((PyObject*)self); +} + +int row_init(Row* self, PyObject* args, PyObject* kwargs) +{ + PyObject* data; + Cursor* cursor; + + self->data = 0; + self->description = 0; + + if (!PyArg_ParseTuple(args, "OO", &cursor, &data)) { + return -1; + } + + if (!PyObject_IsInstance((PyObject*)cursor, (PyObject*)&CursorType)) { + PyErr_SetString(PyExc_TypeError, "instance of cursor required for first argument"); + return -1; + } + + if (!PyTuple_Check(data)) { + PyErr_SetString(PyExc_TypeError, "tuple required for second argument"); + return -1; + } + + Py_INCREF(data); + self->data = data; + + Py_INCREF(cursor->description); + self->description = cursor->description; + + return 0; +} + +PyObject* row_subscript(Row* self, PyObject* idx) +{ + long _idx; + char* key; + int nitems, i; + char* compare_key; + + char* p1; + char* p2; + + PyObject* item; + + if (PyInt_Check(idx)) { + _idx = PyInt_AsLong(idx); + item = PyTuple_GetItem(self->data, _idx); + Py_XINCREF(item); + return item; + } else if (PyLong_Check(idx)) { + _idx = PyLong_AsLong(idx); + item = PyTuple_GetItem(self->data, _idx); + Py_XINCREF(item); + return item; + } else if (PyString_Check(idx)) { + key = PyString_AsString(idx); + + nitems = PyTuple_Size(self->description); + + for (i = 0; i < nitems; i++) { + compare_key = PyString_AsString(PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)); + if (!compare_key) { + return NULL; + } + + p1 = key; + p2 = compare_key; + + while (1) { + if ((*p1 == (char)0) || (*p2 == (char)0)) { + break; + } + + if ((*p1 | 0x20) != (*p2 | 0x20)) { + break; + } + + p1++; + p2++; + } + + if ((*p1 == (char)0) && (*p2 == (char)0)) { + /* found item */ + item = PyTuple_GetItem(self->data, i); + Py_INCREF(item); + return item; + } + + } + + PyErr_SetString(PyExc_IndexError, "No item with that key"); + return NULL; + } else if (PySlice_Check(idx)) { + PyErr_SetString(PyExc_ValueError, "slices not implemented, yet"); + return NULL; + } else { + PyErr_SetString(PyExc_IndexError, "Index must be int or string"); + return NULL; + } +} + +Py_ssize_t row_length(Row* self, PyObject* args, PyObject* kwargs) +{ + return PyTuple_GET_SIZE(self->data); +} + +static int row_print(Row* self, FILE *fp, int flags) +{ + return (&PyTuple_Type)->tp_print(self->data, fp, flags); +} + + +PyMappingMethods row_as_mapping = { + /* mp_length */ (lenfunc)row_length, + /* mp_subscript */ (binaryfunc)row_subscript, + /* mp_ass_subscript */ (objobjargproc)0, +}; + + +PyTypeObject RowType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".Row", /* tp_name */ + sizeof(Row), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)row_dealloc, /* tp_dealloc */ + (printfunc)row_print, /* 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|Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* 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 */ + (initproc)row_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int row_setup_types(void) +{ + RowType.tp_new = PyType_GenericNew; + RowType.tp_as_mapping = &row_as_mapping; + return PyType_Ready(&RowType); +} diff --git a/Modules/_sqlite/row.h b/Modules/_sqlite/row.h new file mode 100644 index 0000000..c6e083c --- /dev/null +++ b/Modules/_sqlite/row.h @@ -0,0 +1,39 @@ +/* row.h - an enhanced tuple for database rows + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_ROW_H +#define PYSQLITE_ROW_H +#include "Python.h" + +typedef struct _Row +{ + PyObject_HEAD + PyObject* data; + PyObject* description; +} Row; + +extern PyTypeObject RowType; + +int row_setup_types(void); + +#endif diff --git a/Modules/_sqlite/sqlitecompat.h b/Modules/_sqlite/sqlitecompat.h new file mode 100644 index 0000000..c379825 --- /dev/null +++ b/Modules/_sqlite/sqlitecompat.h @@ -0,0 +1,34 @@ +/* sqlitecompat.h - compatibility macros + * + * Copyright (C) 2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_COMPAT_H +#define PYSQLITE_COMPAT_H + +/* define Py_ssize_t for pre-2.5 versions of Python */ + +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +typedef int (*lenfunc)(PyObject*); +#endif + +#endif diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c new file mode 100644 index 0000000..0c93651 --- /dev/null +++ b/Modules/_sqlite/statement.c @@ -0,0 +1,427 @@ +/* statement.c - the statement type + * + * Copyright (C) 2005-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "statement.h" +#include "cursor.h" +#include "connection.h" +#include "microprotocols.h" +#include "prepare_protocol.h" +#include "sqlitecompat.h" + +/* prototypes */ +int check_remaining_sql(const char* tail); + +typedef enum { + LINECOMMENT_1, + IN_LINECOMMENT, + COMMENTSTART_1, + IN_COMMENT, + COMMENTEND_1, + NORMAL +} parse_remaining_sql_state; + +int statement_create(Statement* self, Connection* connection, PyObject* sql) +{ + const char* tail; + int rc; + PyObject* sql_str; + char* sql_cstr; + + self->st = NULL; + self->in_use = 0; + + if (PyString_Check(sql)) { + sql_str = sql; + Py_INCREF(sql_str); + } else if (PyUnicode_Check(sql)) { + sql_str = PyUnicode_AsUTF8String(sql); + if (!sql_str) { + rc = PYSQLITE_SQL_WRONG_TYPE; + return rc; + } + } else { + rc = PYSQLITE_SQL_WRONG_TYPE; + return rc; + } + + self->sql = sql_str; + + sql_cstr = PyString_AsString(sql_str); + + rc = sqlite3_prepare(connection->db, + sql_cstr, + -1, + &self->st, + &tail); + + self->db = connection->db; + + if (rc == SQLITE_OK && check_remaining_sql(tail)) { + (void)sqlite3_finalize(self->st); + self->st = NULL; + rc = PYSQLITE_TOO_MUCH_SQL; + } + + return rc; +} + +int statement_bind_parameter(Statement* self, int pos, PyObject* parameter) +{ + int rc = SQLITE_OK; + long longval; +#ifdef HAVE_LONG_LONG + PY_LONG_LONG longlongval; +#endif + const char* buffer; + char* string; + Py_ssize_t buflen; + PyObject* stringval; + + if (parameter == Py_None) { + rc = sqlite3_bind_null(self->st, pos); + } else if (PyInt_Check(parameter)) { + longval = PyInt_AsLong(parameter); + rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longval); +#ifdef HAVE_LONG_LONG + } else if (PyLong_Check(parameter)) { + longlongval = PyLong_AsLongLong(parameter); + /* in the overflow error case, longlongval is -1, and an exception is set */ + rc = sqlite3_bind_int64(self->st, pos, (sqlite_int64)longlongval); +#endif + } else if (PyFloat_Check(parameter)) { + rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter)); + } else if (PyBuffer_Check(parameter)) { + if (PyObject_AsCharBuffer(parameter, &buffer, &buflen) == 0) { + rc = sqlite3_bind_blob(self->st, pos, buffer, buflen, SQLITE_TRANSIENT); + } else { + PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer"); + rc = -1; + } + } else if PyString_Check(parameter) { + string = PyString_AsString(parameter); + rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); + } else if PyUnicode_Check(parameter) { + stringval = PyUnicode_AsUTF8String(parameter); + string = PyString_AsString(stringval); + rc = sqlite3_bind_text(self->st, pos, string, -1, SQLITE_TRANSIENT); + Py_DECREF(stringval); + } else { + rc = -1; + } + + return rc; +} + +void statement_bind_parameters(Statement* self, PyObject* parameters) +{ + PyObject* current_param; + PyObject* adapted; + const char* binding_name; + int i; + int rc; + int num_params_needed; + int num_params; + + Py_BEGIN_ALLOW_THREADS + num_params_needed = sqlite3_bind_parameter_count(self->st); + Py_END_ALLOW_THREADS + + if (PyDict_Check(parameters)) { + /* parameters passed as dictionary */ + for (i = 1; i <= num_params_needed; i++) { + Py_BEGIN_ALLOW_THREADS + binding_name = sqlite3_bind_parameter_name(self->st, i); + Py_END_ALLOW_THREADS + if (!binding_name) { + PyErr_Format(ProgrammingError, "Binding %d has no name, but you supplied a dictionary (which has only names).", i); + return; + } + + binding_name++; /* skip first char (the colon) */ + current_param = PyDict_GetItemString(parameters, binding_name); + if (!current_param) { + PyErr_Format(ProgrammingError, "You did not supply a value for binding %d.", i); + return; + } + + Py_INCREF(current_param); + adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); + if (adapted) { + Py_DECREF(current_param); + } else { + PyErr_Clear(); + adapted = current_param; + } + + rc = statement_bind_parameter(self, i, adapted); + Py_DECREF(adapted); + + if (rc != SQLITE_OK) { + PyErr_Format(InterfaceError, "Error binding parameter :%s - probably unsupported type.", binding_name); + return; + } + } + } else { + /* parameters passed as sequence */ + num_params = PySequence_Length(parameters); + if (num_params != num_params_needed) { + PyErr_Format(ProgrammingError, "Incorrect number of bindings supplied. The current statement uses %d, and there are %d supplied.", + num_params_needed, num_params); + return; + } + for (i = 0; i < num_params; i++) { + current_param = PySequence_GetItem(parameters, i); + if (!current_param) { + return; + } + adapted = microprotocols_adapt(current_param, (PyObject*)&SQLitePrepareProtocolType, NULL); + + if (adapted) { + Py_DECREF(current_param); + } else { + PyErr_Clear(); + adapted = current_param; + } + + rc = statement_bind_parameter(self, i + 1, adapted); + Py_DECREF(adapted); + + if (rc != SQLITE_OK) { + PyErr_Format(InterfaceError, "Error binding parameter %d - probably unsupported type.", i); + return; + } + } + } +} + +int statement_recompile(Statement* self, PyObject* params) +{ + const char* tail; + int rc; + char* sql_cstr; + sqlite3_stmt* new_st; + + sql_cstr = PyString_AsString(self->sql); + + rc = sqlite3_prepare(self->db, + sql_cstr, + -1, + &new_st, + &tail); + + if (rc == SQLITE_OK) { + /* The efficient sqlite3_transfer_bindings is only available in SQLite + * version 3.2.2 or later. For older SQLite releases, that might not + * even define SQLITE_VERSION_NUMBER, we do it the manual way. + */ + #ifdef SQLITE_VERSION_NUMBER + #if SQLITE_VERSION_NUMBER >= 3002002 + (void)sqlite3_transfer_bindings(self->st, new_st); + #endif + #else + statement_bind_parameters(self, params); + #endif + + (void)sqlite3_finalize(self->st); + self->st = new_st; + } + + return rc; +} + +int statement_finalize(Statement* self) +{ + int rc; + + rc = SQLITE_OK; + if (self->st) { + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_finalize(self->st); + Py_END_ALLOW_THREADS + self->st = NULL; + } + + self->in_use = 0; + + return rc; +} + +int statement_reset(Statement* self) +{ + int rc; + + rc = SQLITE_OK; + + if (self->in_use && self->st) { + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_reset(self->st); + Py_END_ALLOW_THREADS + + if (rc == SQLITE_OK) { + self->in_use = 0; + } + } + + return rc; +} + +void statement_mark_dirty(Statement* self) +{ + self->in_use = 1; +} + +void statement_dealloc(Statement* self) +{ + int rc; + + if (self->st) { + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_finalize(self->st); + Py_END_ALLOW_THREADS + } + + self->st = NULL; + + Py_XDECREF(self->sql); + + self->ob_type->tp_free((PyObject*)self); +} + +/* + * Checks if there is anything left in an SQL string after SQLite compiled it. + * This is used to check if somebody tried to execute more than one SQL command + * with one execute()/executemany() command, which the DB-API and we don't + * allow. + * + * Returns 1 if there is more left than should be. 0 if ok. + */ +int check_remaining_sql(const char* tail) +{ + const char* pos = tail; + + parse_remaining_sql_state state = NORMAL; + + for (;;) { + switch (*pos) { + case 0: + return 0; + case '-': + if (state == NORMAL) { + state = LINECOMMENT_1; + } else if (state == LINECOMMENT_1) { + state = IN_LINECOMMENT; + } + break; + case ' ': + case '\t': + break; + case '\n': + case 13: + if (state == IN_LINECOMMENT) { + state = NORMAL; + } + break; + case '/': + if (state == NORMAL) { + state = COMMENTSTART_1; + } else if (state == COMMENTEND_1) { + state = NORMAL; + } else if (state == COMMENTSTART_1) { + return 1; + } + break; + case '*': + if (state == NORMAL) { + return 1; + } else if (state == LINECOMMENT_1) { + return 1; + } else if (state == COMMENTSTART_1) { + state = IN_COMMENT; + } else if (state == IN_COMMENT) { + state = COMMENTEND_1; + } + break; + default: + if (state == COMMENTEND_1) { + state = IN_COMMENT; + } else if (state == IN_LINECOMMENT) { + } else if (state == IN_COMMENT) { + } else { + return 1; + } + } + + pos++; + } + + return 0; +} + +PyTypeObject StatementType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MODULE_NAME ".Statement", /* tp_name */ + sizeof(Statement), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)statement_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 */ + 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 */ + (initproc)0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0 /* tp_free */ +}; + +extern int statement_setup_types(void) +{ + StatementType.tp_new = PyType_GenericNew; + return PyType_Ready(&StatementType); +} diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h new file mode 100644 index 0000000..e45a0fc --- /dev/null +++ b/Modules/_sqlite/statement.h @@ -0,0 +1,58 @@ +/* statement.h - definitions for the statement type + * + * Copyright (C) 2005 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_STATEMENT_H +#define PYSQLITE_STATEMENT_H +#include "Python.h" + +#include "connection.h" +#include "sqlite3.h" + +#define PYSQLITE_TOO_MUCH_SQL (-100) +#define PYSQLITE_SQL_WRONG_TYPE (-101) + +typedef struct +{ + PyObject_HEAD + sqlite3* db; + sqlite3_stmt* st; + PyObject* sql; + int in_use; +} Statement; + +extern PyTypeObject StatementType; + +int statement_create(Statement* self, Connection* connection, PyObject* sql); +void statement_dealloc(Statement* self); + +int statement_bind_parameter(Statement* self, int pos, PyObject* parameter); +void statement_bind_parameters(Statement* self, PyObject* parameters); + +int statement_recompile(Statement* self, PyObject* parameters); +int statement_finalize(Statement* self); +int statement_reset(Statement* self); +void statement_mark_dirty(Statement* self); + +int statement_setup_types(void); + +#endif diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c new file mode 100644 index 0000000..33748a6 --- /dev/null +++ b/Modules/_sqlite/util.c @@ -0,0 +1,96 @@ +/* util.c - various utility functions + * + * Copyright (C) 2005-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#include "module.h" +#include "connection.h" + +int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection +) +{ + int rc; + + Py_BEGIN_ALLOW_THREADS + rc = sqlite3_step(statement); + Py_END_ALLOW_THREADS + + return rc; +} + +/** + * Checks the SQLite error code and sets the appropriate DB-API exception. + * Returns the error code (0 means no error occured). + */ +int _seterror(sqlite3* db) +{ + int errorcode; + + errorcode = sqlite3_errcode(db); + + switch (errorcode) + { + case SQLITE_OK: + PyErr_Clear(); + break; + case SQLITE_INTERNAL: + case SQLITE_NOTFOUND: + PyErr_SetString(InternalError, sqlite3_errmsg(db)); + break; + case SQLITE_NOMEM: + (void)PyErr_NoMemory(); + break; + case SQLITE_ERROR: + case SQLITE_PERM: + case SQLITE_ABORT: + case SQLITE_BUSY: + case SQLITE_LOCKED: + case SQLITE_READONLY: + case SQLITE_INTERRUPT: + case SQLITE_IOERR: + case SQLITE_FULL: + case SQLITE_CANTOPEN: + case SQLITE_PROTOCOL: + case SQLITE_EMPTY: + case SQLITE_SCHEMA: + PyErr_SetString(OperationalError, sqlite3_errmsg(db)); + break; + case SQLITE_CORRUPT: + PyErr_SetString(DatabaseError, sqlite3_errmsg(db)); + break; + case SQLITE_TOOBIG: + PyErr_SetString(DataError, sqlite3_errmsg(db)); + break; + case SQLITE_CONSTRAINT: + case SQLITE_MISMATCH: + PyErr_SetString(IntegrityError, sqlite3_errmsg(db)); + break; + case SQLITE_MISUSE: + PyErr_SetString(ProgrammingError, sqlite3_errmsg(db)); + break; + default: + PyErr_SetString(DatabaseError, sqlite3_errmsg(db)); + break; + } + + return errorcode; +} + diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h new file mode 100644 index 0000000..e99a4dd --- /dev/null +++ b/Modules/_sqlite/util.h @@ -0,0 +1,38 @@ +/* util.h - various utility functions + * + * Copyright (C) 2005-2006 Gerhard Häring + * + * This file is part of pysqlite. + * + * 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. + */ + +#ifndef PYSQLITE_UTIL_H +#define PYSQLITE_UTIL_H +#include "Python.h" +#include "pythread.h" +#include "sqlite3.h" +#include "connection.h" + +int _sqlite_step_with_busyhandler(sqlite3_stmt* statement, Connection* connection); + +/** + * Checks the SQLite error code and sets the appropriate DB-API exception. + * Returns the error code (0 means no error occured). + */ +int _seterror(sqlite3* db); +#endif diff --git a/Modules/_sre.c b/Modules/_sre.c index 81223d7..4af08ed 100644 --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -275,7 +275,7 @@ data_stack_grow(SRE_STATE* state, int size) data_stack_dealloc(state); return SRE_ERROR_MEMORY; } - state->data_stack = stack; + state->data_stack = (char *)stack; state->data_stack_size = cursize; } return 0; @@ -335,7 +335,7 @@ SRE_AT(SRE_STATE* state, SRE_CHAR* ptr, SRE_CODE at) { /* check if pointer is at given position */ - int this, that; + int thisp, thatp; switch (at) { @@ -362,57 +362,57 @@ SRE_AT(SRE_STATE* state, SRE_CHAR* ptr, SRE_CODE at) case SRE_AT_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_IS_WORD((int) ptr[0]) : 0; - return this != that; + return thisp != thatp; case SRE_AT_NON_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_IS_WORD((int) ptr[0]) : 0; - return this == that; + return thisp == thatp; case SRE_AT_LOC_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_LOC_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_LOC_IS_WORD((int) ptr[0]) : 0; - return this != that; + return thisp != thatp; case SRE_AT_LOC_NON_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_LOC_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_LOC_IS_WORD((int) ptr[0]) : 0; - return this == that; + return thisp == thatp; #if defined(HAVE_UNICODE) case SRE_AT_UNI_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_UNI_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_UNI_IS_WORD((int) ptr[0]) : 0; - return this != that; + return thisp != thatp; case SRE_AT_UNI_NON_BOUNDARY: if (state->beginning == state->end) return 0; - that = ((void*) ptr > state->beginning) ? + thatp = ((void*) ptr > state->beginning) ? SRE_UNI_IS_WORD((int) ptr[-1]) : 0; - this = ((void*) ptr < state->end) ? + thisp = ((void*) ptr < state->end) ? SRE_UNI_IS_WORD((int) ptr[0]) : 0; - return this == that; + return thisp == thatp; #endif } @@ -516,8 +516,8 @@ LOCAL(int) SRE_COUNT(SRE_STATE* state, SRE_CODE* pattern, int maxcount) { SRE_CODE chr; - SRE_CHAR* ptr = state->ptr; - SRE_CHAR* end = state->end; + SRE_CHAR* ptr = (SRE_CHAR *)state->ptr; + SRE_CHAR* end = (SRE_CHAR *)state->end; int i; /* adjust end */ @@ -803,7 +803,7 @@ typedef struct { LOCAL(int) SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern) { - SRE_CHAR* end = state->end; + SRE_CHAR* end = (SRE_CHAR *)state->end; int alloc_pos, ctx_pos = -1; int i, ret = 0; int jump; @@ -821,7 +821,7 @@ SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern) entrance: - ctx->ptr = state->ptr; + ctx->ptr = (SRE_CHAR *)state->ptr; if (ctx->pattern[0] == SRE_OP_INFO) { /* optimization info block */ @@ -1477,8 +1477,8 @@ exit: LOCAL(int) SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) { - SRE_CHAR* ptr = state->start; - SRE_CHAR* end = state->end; + SRE_CHAR* ptr = (SRE_CHAR *)state->start; + SRE_CHAR* end = (SRE_CHAR *)state->end; int status = 0; int prefix_len = 0; int prefix_skip = 0; @@ -1524,7 +1524,7 @@ SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) /* pattern starts with a known prefix. use the overlap table to skip forward as fast as we possibly can */ int i = 0; - end = state->end; + end = (SRE_CHAR *)state->end; while (ptr < end) { for (;;) { if ((SRE_CODE) ptr[0] != prefix[i]) { @@ -1559,7 +1559,7 @@ SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) /* pattern starts with a literal character. this is used for short prefixes, and if fast search is disabled */ SRE_CODE chr = pattern[1]; - end = state->end; + end = (SRE_CHAR *)state->end; for (;;) { while (ptr < end && (SRE_CODE) ptr[0] != chr) ptr++; @@ -1576,7 +1576,7 @@ SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) } } else if (charset) { /* pattern starts with a character from a known set */ - end = state->end; + end = (SRE_CHAR *)state->end; for (;;) { while (ptr < end && !SRE_CHARSET(charset, ptr[0])) ptr++; @@ -1619,72 +1619,8 @@ SRE_LITERAL_TEMPLATE(SRE_CHAR* ptr, int len) /* factories and destructors */ /* see sre.h for object declarations */ - -static PyTypeObject Pattern_Type; -static PyTypeObject Match_Type; -static PyTypeObject Scanner_Type; - -static PyObject * -_compile(PyObject* self_, PyObject* args) -{ - /* "compile" pattern descriptor to pattern object */ - - PatternObject* self; - int i, n; - - PyObject* pattern; - int flags = 0; - PyObject* code; - int groups = 0; - PyObject* groupindex = NULL; - PyObject* indexgroup = NULL; - if (!PyArg_ParseTuple(args, "OiO!|iOO", &pattern, &flags, - &PyList_Type, &code, &groups, - &groupindex, &indexgroup)) - return NULL; - - n = PyList_GET_SIZE(code); - - self = PyObject_NEW_VAR(PatternObject, &Pattern_Type, n); - if (!self) - return NULL; - - self->codesize = n; - - for (i = 0; i < n; i++) { - PyObject *o = PyList_GET_ITEM(code, i); - unsigned long value = PyInt_Check(o) ? (unsigned long)PyInt_AsLong(o) - : PyLong_AsUnsignedLong(o); - self->code[i] = (SRE_CODE) value; - if ((unsigned long) self->code[i] != value) { - PyErr_SetString(PyExc_OverflowError, - "regular expression code size limit exceeded"); - break; - } - } - - if (PyErr_Occurred()) { - PyObject_DEL(self); - return NULL; - } - - Py_INCREF(pattern); - self->pattern = pattern; - - self->flags = flags; - - self->groups = groups; - - Py_XINCREF(groupindex); - self->groupindex = groupindex; - - Py_XINCREF(indexgroup); - self->indexgroup = indexgroup; - - self->weakreflist = NULL; - - return (PyObject*) self; -} +static PyObject*pattern_new_match(PatternObject*, SRE_STATE*, int); +static PyObject*pattern_scanner(PatternObject*, PyObject*); static PyObject * sre_codesize(PyObject* self, PyObject* args) @@ -1900,98 +1836,6 @@ pattern_error(int status) } } -static PyObject* -pattern_new_match(PatternObject* pattern, SRE_STATE* state, int status) -{ - /* create match object (from state object) */ - - MatchObject* match; - int i, j; - char* base; - int n; - - if (status > 0) { - - /* create match object (with room for extra group marks) */ - match = PyObject_NEW_VAR(MatchObject, &Match_Type, - 2*(pattern->groups+1)); - if (!match) - return NULL; - - Py_INCREF(pattern); - match->pattern = pattern; - - Py_INCREF(state->string); - match->string = state->string; - - match->regs = NULL; - match->groups = pattern->groups+1; - - /* fill in group slices */ - - base = (char*) state->beginning; - n = state->charsize; - - match->mark[0] = ((char*) state->start - base) / n; - match->mark[1] = ((char*) state->ptr - base) / n; - - for (i = j = 0; i < pattern->groups; i++, j+=2) - if (j+1 <= state->lastmark && state->mark[j] && state->mark[j+1]) { - match->mark[j+2] = ((char*) state->mark[j] - base) / n; - match->mark[j+3] = ((char*) state->mark[j+1] - base) / n; - } else - match->mark[j+2] = match->mark[j+3] = -1; /* undefined */ - - match->pos = state->pos; - match->endpos = state->endpos; - - match->lastindex = state->lastindex; - - return (PyObject*) match; - - } else if (status == 0) { - - /* no match */ - Py_INCREF(Py_None); - return Py_None; - - } - - /* internal error */ - pattern_error(status); - return NULL; -} - -static PyObject* -pattern_scanner(PatternObject* pattern, PyObject* args) -{ - /* create search state object */ - - ScannerObject* self; - - PyObject* string; - int start = 0; - int end = INT_MAX; - if (!PyArg_ParseTuple(args, "O|ii:scanner", &string, &start, &end)) - return NULL; - - /* create scanner object */ - self = PyObject_NEW(ScannerObject, &Scanner_Type); - if (!self) - return NULL; - - string = state_init(&self->state, pattern, string, start, end); - if (!string) { - PyObject_DEL(self); - return NULL; - } - - Py_INCREF(pattern); - self->pattern = (PyObject*) pattern; - - return (PyObject*) self; -} - static void pattern_dealloc(PatternObject* self) { @@ -2414,7 +2258,7 @@ error: } static PyObject* -pattern_subx(PatternObject* self, PyObject* template, PyObject* string, +pattern_subx(PatternObject* self, PyObject* ptemplate, PyObject* string, int count, int subn) { SRE_STATE state; @@ -2429,21 +2273,21 @@ pattern_subx(PatternObject* self, PyObject* template, PyObject* string, int i, b, e; int filter_is_callable; - if (PyCallable_Check(template)) { + if (PyCallable_Check(ptemplate)) { /* sub/subn takes either a function or a template */ - filter = template; + filter = ptemplate; Py_INCREF(filter); filter_is_callable = 1; } else { /* if not callable, check if it's a literal string */ int literal; - ptr = getstring(template, &n, &b); + ptr = getstring(ptemplate, &n, &b); if (ptr) { if (b == 1) { - literal = sre_literal_template(ptr, n); + literal = sre_literal_template((unsigned char *)ptr, n); } else { #if defined(HAVE_UNICODE) - literal = sre_uliteral_template(ptr, n); + literal = sre_uliteral_template((Py_UNICODE *)ptr, n); #endif } } else { @@ -2451,14 +2295,14 @@ pattern_subx(PatternObject* self, PyObject* template, PyObject* string, literal = 0; } if (literal) { - filter = template; + filter = ptemplate; Py_INCREF(filter); filter_is_callable = 0; } else { /* not a literal; hand it over to the template compiler */ filter = call( SRE_PY_MODULE, "_subx", - PyTuple_Pack(2, self, template) + PyTuple_Pack(2, self, ptemplate) ); if (!filter) return NULL; @@ -2597,29 +2441,29 @@ error: static PyObject* pattern_sub(PatternObject* self, PyObject* args, PyObject* kw) { - PyObject* template; + PyObject* ptemplate; PyObject* string; int count = 0; static char* kwlist[] = { "repl", "string", "count", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:sub", kwlist, - &template, &string, &count)) + &ptemplate, &string, &count)) return NULL; - return pattern_subx(self, template, string, count, 0); + return pattern_subx(self, ptemplate, string, count, 0); } static PyObject* pattern_subn(PatternObject* self, PyObject* args, PyObject* kw) { - PyObject* template; + PyObject* ptemplate; PyObject* string; int count = 0; static char* kwlist[] = { "repl", "string", "count", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:subn", kwlist, - &template, &string, &count)) + &ptemplate, &string, &count)) return NULL; - return pattern_subx(self, template, string, count, 1); + return pattern_subx(self, ptemplate, string, count, 1); } static PyObject* @@ -2799,6 +2643,68 @@ static PyTypeObject Pattern_Type = { offsetof(PatternObject, weakreflist), /* tp_weaklistoffset */ }; +static PyObject * +_compile(PyObject* self_, PyObject* args) +{ + /* "compile" pattern descriptor to pattern object */ + + PatternObject* self; + int i, n; + + PyObject* pattern; + int flags = 0; + PyObject* code; + int groups = 0; + PyObject* groupindex = NULL; + PyObject* indexgroup = NULL; + if (!PyArg_ParseTuple(args, "OiO!|iOO", &pattern, &flags, + &PyList_Type, &code, &groups, + &groupindex, &indexgroup)) + return NULL; + + n = PyList_GET_SIZE(code); + + self = PyObject_NEW_VAR(PatternObject, &Pattern_Type, n); + if (!self) + return NULL; + + self->codesize = n; + + for (i = 0; i < n; i++) { + PyObject *o = PyList_GET_ITEM(code, i); + unsigned long value = PyInt_Check(o) ? (unsigned long)PyInt_AsLong(o) + : PyLong_AsUnsignedLong(o); + self->code[i] = (SRE_CODE) value; + if ((unsigned long) self->code[i] != value) { + PyErr_SetString(PyExc_OverflowError, + "regular expression code size limit exceeded"); + break; + } + } + + if (PyErr_Occurred()) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = pattern; + + self->flags = flags; + + self->groups = groups; + + Py_XINCREF(groupindex); + self->groupindex = groupindex; + + Py_XINCREF(indexgroup); + self->indexgroup = indexgroup; + + self->weakreflist = NULL; + + return (PyObject*) self; +} + /* -------------------------------------------------------------------- */ /* match methods */ @@ -2868,14 +2774,14 @@ match_getslice(MatchObject* self, PyObject* index, PyObject* def) static PyObject* match_expand(MatchObject* self, PyObject* args) { - PyObject* template; - if (!PyArg_ParseTuple(args, "O:expand", &template)) + PyObject* ptemplate; + if (!PyArg_ParseTuple(args, "O:expand", &ptemplate)) return NULL; /* delegate to Python code */ return call( SRE_PY_MODULE, "_expand", - PyTuple_Pack(3, self->pattern, self, template) + PyTuple_Pack(3, self->pattern, self, ptemplate) ); } @@ -3262,6 +3168,69 @@ static PyTypeObject Match_Type = { (getattrfunc)match_getattr /*tp_getattr*/ }; +static PyObject* +pattern_new_match(PatternObject* pattern, SRE_STATE* state, int status) +{ + /* create match object (from state object) */ + + MatchObject* match; + int i, j; + char* base; + int n; + + if (status > 0) { + + /* create match object (with room for extra group marks) */ + match = PyObject_NEW_VAR(MatchObject, &Match_Type, + 2*(pattern->groups+1)); + if (!match) + return NULL; + + Py_INCREF(pattern); + match->pattern = pattern; + + Py_INCREF(state->string); + match->string = state->string; + + match->regs = NULL; + match->groups = pattern->groups+1; + + /* fill in group slices */ + + base = (char*) state->beginning; + n = state->charsize; + + match->mark[0] = ((char*) state->start - base) / n; + match->mark[1] = ((char*) state->ptr - base) / n; + + for (i = j = 0; i < pattern->groups; i++, j+=2) + if (j+1 <= state->lastmark && state->mark[j] && state->mark[j+1]) { + match->mark[j+2] = ((char*) state->mark[j] - base) / n; + match->mark[j+3] = ((char*) state->mark[j+1] - base) / n; + } else + match->mark[j+2] = match->mark[j+3] = -1; /* undefined */ + + match->pos = state->pos; + match->endpos = state->endpos; + + match->lastindex = state->lastindex; + + return (PyObject*) match; + + } else if (status == 0) { + + /* no match */ + Py_INCREF(Py_None); + return Py_None; + + } + + /* internal error */ + pattern_error(status); + return NULL; +} + + /* -------------------------------------------------------------------- */ /* scanner methods (experimental) */ @@ -3372,6 +3341,36 @@ static PyTypeObject Scanner_Type = { (getattrfunc)scanner_getattr, /*tp_getattr*/ }; +static PyObject* +pattern_scanner(PatternObject* pattern, PyObject* args) +{ + /* create search state object */ + + ScannerObject* self; + + PyObject* string; + int start = 0; + int end = INT_MAX; + if (!PyArg_ParseTuple(args, "O|ii:scanner", &string, &start, &end)) + return NULL; + + /* create scanner object */ + self = PyObject_NEW(ScannerObject, &Scanner_Type); + if (!self) + return NULL; + + string = state_init(&self->state, pattern, string, start, end); + if (!string) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = (PyObject*) pattern; + + return (PyObject*) self; +} + static PyMethodDef _functions[] = { {"compile", _compile, METH_VARARGS}, {"getcodesize", sre_codesize, METH_VARARGS}, diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 5f541f5..4c0da6f 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -55,7 +55,6 @@ typedef struct { SSL_CTX* ctx; SSL* ssl; X509* server_cert; - BIO* sbio; char server[X509_NAME_MAXLEN]; char issuer[X509_NAME_MAXLEN]; @@ -474,15 +473,22 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) if (!(buf = PyString_FromStringAndSize((char *) 0, len))) return NULL; + + /* first check if there are bytes ready to be read */ + Py_BEGIN_ALLOW_THREADS + count = SSL_pending(self->ssl); + Py_END_ALLOW_THREADS - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); - if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySSLErrorObject, "The read operation timed out"); - Py_DECREF(buf); - return NULL; - } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); - return NULL; + if (!count) { + sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + if (sockstate == SOCKET_HAS_TIMED_OUT) { + PyErr_SetString(PySSLErrorObject, "The read operation timed out"); + Py_DECREF(buf); + return NULL; + } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { + PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); + return NULL; + } } do { err = 0; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 6d8ea3c..e8881dc 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -10,7 +10,6 @@ #ifdef WITH_THREAD #include "pythread.h" #endif /* WITH_THREAD */ - static PyObject *TestError; /* set to exception object in init */ /* Raise TestError with test_name + ": " + msg, and return NULL. */ @@ -235,7 +234,7 @@ raise_test_longlong_error(const char* msg) #include "testcapi_long.h" static PyObject * -test_longlong_api(PyObject* self) +test_longlong_api(PyObject* self, PyObject *args) { return TESTNAME(raise_test_longlong_error); } @@ -361,6 +360,15 @@ getargs_l(PyObject *self, PyObject *args) return PyLong_FromLong(value); } +static PyObject * +getargs_n(PyObject *self, PyObject *args) +{ + Py_ssize_t value; + if (!PyArg_ParseTuple(args, "n", &value)) + return NULL; + return PyInt_FromSsize_t(value); +} + #ifdef HAVE_LONG_LONG static PyObject * getargs_L(PyObject *self, PyObject *args) @@ -405,7 +413,7 @@ test_k_code(PyObject *self) PyTuple_SET_ITEM(tuple, 0, num); - value = -1; + value = 0; if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) return NULL; if (value != ULONG_MAX) @@ -424,7 +432,7 @@ test_k_code(PyObject *self) PyTuple_SET_ITEM(tuple, 0, num); - value = -1; + value = 0; if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) return NULL; if (value != (unsigned long)-0x42) @@ -478,6 +486,26 @@ test_u_code(PyObject *self) return Py_None; } +static +PyObject *codec_incrementalencoder(PyObject *self, PyObject *args) +{ + const char *encoding, *errors = NULL; + if (!PyArg_ParseTuple(args, "s|s:test_incrementalencoder", + &encoding, &errors)) + return NULL; + return PyCodec_IncrementalEncoder(encoding, errors); +} + +static +PyObject *codec_incrementaldecoder(PyObject *self, PyObject *args) +{ + const char *encoding, *errors = NULL; + if (!PyArg_ParseTuple(args, "s|s:test_incrementaldecoder", + &encoding, &errors)) + return NULL; + return PyCodec_IncrementalDecoder(encoding, errors); +} + #endif /* Simple test of _PyLong_NumBits and _PyLong_Sign. */ @@ -563,7 +591,17 @@ raise_exception(PyObject *self, PyObject *args) #ifdef WITH_THREAD -void _make_call(void *callable) +/* test_thread_state spawns a thread of its own, and that thread releases + * `thread_done` when it's finished. The driver code has to know when the + * thread finishes, because the thread uses a PyObject (the callable) that + * may go away when the driver finishes. The former lack of this explicit + * synchronization caused rare segfaults, so rare that they were seen only + * on a Mac buildbot (although they were possible on any box). + */ +static PyThread_type_lock thread_done = NULL; + +static void +_make_call(void *callable) { PyObject *rc; PyGILState_STATE s = PyGILState_Ensure(); @@ -572,32 +610,53 @@ void _make_call(void *callable) PyGILState_Release(s); } +/* Same thing, but releases `thread_done` when it returns. This variant + * should be called only from threads spawned by test_thread_state(). + */ +static void +_make_call_from_thread(void *callable) +{ + _make_call(callable); + PyThread_release_lock(thread_done); +} + static PyObject * test_thread_state(PyObject *self, PyObject *args) { PyObject *fn; + if (!PyArg_ParseTuple(args, "O:test_thread_state", &fn)) return NULL; - /* Ensure Python is setup for threading */ + + /* Ensure Python is set up for threading */ PyEval_InitThreads(); - /* Start a new thread for our callback. */ - PyThread_start_new_thread( _make_call, fn); + thread_done = PyThread_allocate_lock(); + if (thread_done == NULL) + return PyErr_NoMemory(); + PyThread_acquire_lock(thread_done, 1); + + /* Start a new thread with our callback. */ + PyThread_start_new_thread(_make_call_from_thread, fn); /* Make the callback with the thread lock held by this thread */ _make_call(fn); /* Do it all again, but this time with the thread-lock released */ Py_BEGIN_ALLOW_THREADS _make_call(fn); + PyThread_acquire_lock(thread_done, 1); /* wait for thread to finish */ Py_END_ALLOW_THREADS + /* And once more with and without a thread - XXX - should use a lock and work out exactly what we are trying - to test + XXX - should use a lock and work out exactly what we are trying + to test */ Py_BEGIN_ALLOW_THREADS - PyThread_start_new_thread( _make_call, fn); + PyThread_start_new_thread(_make_call_from_thread, fn); _make_call(fn); + PyThread_acquire_lock(thread_done, 1); /* wait for thread to finish */ Py_END_ALLOW_THREADS - Py_INCREF(Py_None); - return Py_None; + + PyThread_free_lock(thread_done); + Py_RETURN_NONE; } #endif @@ -611,24 +670,29 @@ static PyMethodDef TestMethods[] = { {"test_k_code", (PyCFunction)test_k_code, METH_NOARGS}, {"test_null_strings", (PyCFunction)test_null_strings, METH_NOARGS}, - {"getargs_b", (PyCFunction)getargs_b, METH_VARARGS}, - {"getargs_B", (PyCFunction)getargs_B, METH_VARARGS}, - {"getargs_H", (PyCFunction)getargs_H, METH_VARARGS}, - {"getargs_I", (PyCFunction)getargs_I, METH_VARARGS}, - {"getargs_k", (PyCFunction)getargs_k, METH_VARARGS}, - {"getargs_i", (PyCFunction)getargs_i, METH_VARARGS}, - {"getargs_l", (PyCFunction)getargs_l, METH_VARARGS}, + {"getargs_b", getargs_b, METH_VARARGS}, + {"getargs_B", getargs_B, METH_VARARGS}, + {"getargs_H", getargs_H, METH_VARARGS}, + {"getargs_I", getargs_I, METH_VARARGS}, + {"getargs_k", getargs_k, METH_VARARGS}, + {"getargs_i", getargs_i, METH_VARARGS}, + {"getargs_l", getargs_l, METH_VARARGS}, + {"getargs_n", getargs_n, METH_VARARGS}, #ifdef HAVE_LONG_LONG - {"getargs_L", (PyCFunction)getargs_L, METH_VARARGS}, - {"getargs_K", (PyCFunction)getargs_K, METH_VARARGS}, - {"test_longlong_api", (PyCFunction)test_longlong_api, METH_NOARGS}, + {"getargs_L", getargs_L, METH_VARARGS}, + {"getargs_K", getargs_K, METH_VARARGS}, + {"test_longlong_api", test_longlong_api, METH_NOARGS}, {"test_L_code", (PyCFunction)test_L_code, METH_NOARGS}, + {"codec_incrementalencoder", + (PyCFunction)codec_incrementalencoder, METH_VARARGS}, + {"codec_incrementaldecoder", + (PyCFunction)codec_incrementaldecoder, METH_VARARGS}, #endif #ifdef Py_USING_UNICODE {"test_u_code", (PyCFunction)test_u_code, METH_NOARGS}, #endif #ifdef WITH_THREAD - {"_test_thread_state", (PyCFunction)test_thread_state, METH_VARARGS}, + {"_test_thread_state", test_thread_state, METH_VARARGS}, #endif {NULL, NULL} /* sentinel */ }; @@ -650,8 +714,10 @@ init_testcapi(void) PyModule_AddObject(m, "ULONG_MAX", PyLong_FromUnsignedLong(ULONG_MAX)); PyModule_AddObject(m, "INT_MIN", PyInt_FromLong(INT_MIN)); PyModule_AddObject(m, "LONG_MIN", PyInt_FromLong(LONG_MIN)); + PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyInt_FromSsize_t(PY_SSIZE_T_MIN)); PyModule_AddObject(m, "INT_MAX", PyInt_FromLong(INT_MAX)); PyModule_AddObject(m, "LONG_MAX", PyInt_FromLong(LONG_MAX)); + PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyInt_FromSsize_t(PY_SSIZE_T_MAX)); TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index e8efaa7..c17b6c6 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -647,7 +647,7 @@ Tkapp_New(char *screenName, char *baseName, char *className, strcpy(argv0, className); if (isupper(Py_CHARMASK(argv0[0]))) - argv0[0] = tolower(argv0[0]); + argv0[0] = tolower(Py_CHARMASK(argv0[0])); Tcl_SetVar(v->interp, "argv0", argv0, TCL_GLOBAL_ONLY); ckfree(argv0); diff --git a/Modules/almodule.c b/Modules/almodule.c index 5254fca..fbeb13a 100644 --- a/Modules/almodule.c +++ b/Modules/almodule.c @@ -1482,7 +1482,8 @@ al_GetParams(PyObject *self, PyObject *args) } if (alGetParams(resource, pvs, npvs) < 0) goto error; - v = PyList_New(npvs); + if (!(v = PyList_New(npvs))) + goto error; for (i = 0; i < npvs; i++) { if (pvs[i].sizeOut < 0) { char buf[32]; @@ -1692,6 +1693,7 @@ al_GetParamInfo(PyObject *self, PyObject *args) if (alGetParamInfo(res, param, &pinfo) < 0) return NULL; v = PyDict_New(); + if (!v) return NULL; item = PyInt_FromLong((long) pinfo.resource); PyDict_SetItemString(v, "resource", item); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 1650ff2..52a7f5e 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -186,7 +186,8 @@ u_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) if (!PyArg_Parse(v, "u#;array item must be unicode character", &p, &len)) return -1; if (len != 1) { - PyErr_SetString(PyExc_TypeError, "array item must be unicode character"); + PyErr_SetString(PyExc_TypeError, + "array item must be unicode character"); return -1; } if (i >= 0) @@ -1163,7 +1164,7 @@ array_reverse(arrayobject *self, PyObject *unused) register char *p, *q; /* little buffer to hold items while swapping */ char tmp[256]; /* 8 is probably enough -- but why skimp */ - assert(itemsize <= sizeof(tmp)); + assert((size_t)itemsize <= sizeof(tmp)); if (self->ob_size > 1) { for (p = self->ob_item, @@ -1673,7 +1674,8 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) } self->ob_size -= slicelength; - self->ob_item = PyMem_REALLOC(self->ob_item, itemsize*self->ob_size); + self->ob_item = (char *)PyMem_REALLOC(self->ob_item, + itemsize*self->ob_size); self->allocated = self->ob_size; return 0; @@ -1865,7 +1867,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (n > 0) { arrayobject *self = (arrayobject *)a; char *item = self->ob_item; - item = PyMem_Realloc(item, n); + item = (char *)PyMem_Realloc(item, n); if (item == NULL) { PyErr_NoMemory(); Py_DECREF(a); @@ -2060,8 +2062,7 @@ arrayiter_dealloc(arrayiterobject *it) static int arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg) { - if (it->ao != NULL) - return visit((PyObject *)(it->ao), arg); + Py_VISIT(it->ao); return 0; } diff --git a/Modules/audioop.c b/Modules/audioop.c index beeacd3..ed70cdf 100644 --- a/Modules/audioop.c +++ b/Modules/audioop.c @@ -15,6 +15,8 @@ typedef unsigned long Py_UInt32; #endif #endif +typedef short PyInt16; + #if defined(__CHAR_UNSIGNED__) #if defined(signed) /* This module currently does not work on systems where only unsigned @@ -22,122 +24,267 @@ typedef unsigned long Py_UInt32; #endif #endif -/* Code shamelessly stolen from sox, +/* Code shamelessly stolen from sox, 12.17.7, g711.c ** (c) Craig Reese, Joe Campbell and Jeff Poskanzer 1989 */ -#define MINLIN -32768 -#define MAXLIN 32767 -#define LINCLIP(x) do { if ( x < MINLIN ) x = MINLIN ; \ - else if ( x > MAXLIN ) x = MAXLIN; \ - } while ( 0 ) - -static unsigned char st_linear_to_ulaw(int sample); - -/* -** This macro converts from ulaw to 16 bit linear, faster. -** -** Jef Poskanzer -** 23 October 1989 -** -** Input: 8 bit ulaw sample -** Output: signed 16 bit linear sample -*/ -#define st_ulaw_to_linear(ulawbyte) ulaw_table[ulawbyte] - -static int ulaw_table[256] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, - -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, - -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, - -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 }; - -/* #define ZEROTRAP */ /* turn on the trap as per the MIL-STD */ +/* From g711.c: + * + * December 30, 1994: + * Functions linear2alaw, linear2ulaw have been updated to correctly + * convert unquantized 16 bit values. + * Tables for direct u- to A-law and A- to u-law conversions have been + * corrected. + * Borge Lindberg, Center for PersonKommunikation, Aalborg University. + * bli@cpk.auc.dk + * + */ #define BIAS 0x84 /* define the add-in bias for 16 bit samples */ #define CLIP 32635 +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static PyInt16 seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF}; +static PyInt16 seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, + 0x3FF, 0x7FF, 0xFFF, 0x1FFF}; + +static PyInt16 +search(PyInt16 val, PyInt16 *table, int size) +{ + int i; + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} +#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc]) +#define st_alaw2linear16(uc) (_st_alaw2linear16[uc]) + +static PyInt16 _st_ulaw2linear16[256] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, + -24956, -23932, -22908, -21884, -20860, -19836, -18812, + -17788, -16764, -15996, -15484, -14972, -14460, -13948, + -13436, -12924, -12412, -11900, -11388, -10876, -10364, + -9852, -9340, -8828, -8316, -7932, -7676, -7420, + -7164, -6908, -6652, -6396, -6140, -5884, -5628, + -5372, -5116, -4860, -4604, -4348, -4092, -3900, + -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, + -1980, -1884, -1820, -1756, -1692, -1628, -1564, + -1500, -1436, -1372, -1308, -1244, -1180, -1116, + -1052, -988, -924, -876, -844, -812, -780, + -748, -716, -684, -652, -620, -588, -556, + -524, -492, -460, -428, -396, -372, -356, + -340, -324, -308, -292, -276, -260, -244, + -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, + -64, -56, -48, -40, -32, -24, -16, + -8, 0, 32124, 31100, 30076, 29052, 28028, + 27004, 25980, 24956, 23932, 22908, 21884, 20860, + 19836, 18812, 17788, 16764, 15996, 15484, 14972, + 14460, 13948, 13436, 12924, 12412, 11900, 11388, + 10876, 10364, 9852, 9340, 8828, 8316, 7932, + 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, + 4092, 3900, 3772, 3644, 3516, 3388, 3260, + 3132, 3004, 2876, 2748, 2620, 2492, 2364, + 2236, 2108, 1980, 1884, 1820, 1756, 1692, + 1628, 1564, 1500, 1436, 1372, 1308, 1244, + 1180, 1116, 1052, 988, 924, 876, 844, + 812, 780, 748, 716, 684, 652, 620, + 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, + 260, 244, 228, 212, 196, 180, 164, + 148, 132, 120, 112, 104, 96, 88, + 80, 72, 64, 56, 48, 40, 32, + 24, 16, 8, 0 +}; + +/* + * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data + * stored in a unsigned char. This function should only be called with + * the data shifted such that it only contains information in the lower + * 14-bits. + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ static unsigned char -st_linear_to_ulaw(int sample) +st_14linear2ulaw(PyInt16 pcm_val) /* 2's complement (14-bit range) */ { - static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if ( sign != 0 ) sample = -sample; /* get magnitude */ - if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[( sample >> 7 ) & 0xFF]; - mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F; - ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa ); -#ifdef ZEROTRAP - if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */ -#endif + PyInt16 mask; + PyInt16 seg; + unsigned char uval; + + /* The original sox code does this in the calling function, not here */ + pcm_val = pcm_val >> 2; + + /* u-law inverts all bits */ + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = -pcm_val; + mask = 0x7F; + } else { + mask = 0xFF; + } + if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */ + pcm_val += (BIAS >> 2); + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_uend, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (unsigned char) (0x7F ^ mask); + else { + uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); + return (uval ^ mask); + } + +} + +static PyInt16 _st_alaw2linear16[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, + -4736, -7552, -7296, -8064, -7808, -6528, -6272, + -7040, -6784, -2752, -2624, -3008, -2880, -2240, + -2112, -2496, -2368, -3776, -3648, -4032, -3904, + -3264, -3136, -3520, -3392, -22016, -20992, -24064, + -23040, -17920, -16896, -19968, -18944, -30208, -29184, + -32256, -31232, -26112, -25088, -28160, -27136, -11008, + -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, + -13568, -344, -328, -376, -360, -280, -264, + -312, -296, -472, -456, -504, -488, -408, + -392, -440, -424, -88, -72, -120, -104, + -24, -8, -56, -40, -216, -200, -248, + -232, -152, -136, -184, -168, -1376, -1312, + -1504, -1440, -1120, -1056, -1248, -1184, -1888, + -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, + -592, -944, -912, -1008, -976, -816, -784, + -880, -848, 5504, 5248, 6016, 5760, 4480, + 4224, 4992, 4736, 7552, 7296, 8064, 7808, + 6528, 6272, 7040, 6784, 2752, 2624, 3008, + 2880, 2240, 2112, 2496, 2368, 3776, 3648, + 4032, 3904, 3264, 3136, 3520, 3392, 22016, + 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, + 27136, 11008, 10496, 12032, 11520, 8960, 8448, + 9984, 9472, 15104, 14592, 16128, 15616, 13056, + 12544, 14080, 13568, 344, 328, 376, 360, + 280, 264, 312, 296, 472, 456, 504, + 488, 408, 392, 440, 424, 88, 72, + 120, 104, 24, 8, 56, 40, 216, + 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, + 1184, 1888, 1824, 2016, 1952, 1632, 1568, + 1760, 1696, 688, 656, 752, 720, 560, + 528, 624, 592, 944, 912, 1008, 976, + 816, 784, 880, 848 +}; - return ulawbyte; +/* + * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data + * stored in a unsigned char. This function should only be called with + * the data shifted such that it only contains information in the lower + * 13-bits. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char +st_linear2alaw(PyInt16 pcm_val) /* 2's complement (13-bit range) */ +{ + PyInt16 mask; + short seg; + unsigned char aval; + + /* The original sox code does this in the calling function, not here */ + pcm_val = pcm_val >> 3; + + /* A-law using even bit inversion */ + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 1; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_aend, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (unsigned char) (0x7F ^ mask); + else { + aval = (unsigned char) seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 1) & QUANT_MASK; + else + aval |= (pcm_val >> seg) & QUANT_MASK; + return (aval ^ mask); + } } /* End of code taken from sox */ /* Intel ADPCM step variation table */ static int indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, }; static int stepsizeTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; #define CHARP(cp, i) ((signed char *)(cp+i)) @@ -151,137 +298,137 @@ static PyObject *AudioopError; static PyObject * audioop_getsample(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0; - int i; - - if ( !PyArg_Parse(args, "(s#ii)", &cp, &len, &size, &i) ) - return 0; - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - if ( i < 0 || i >= len/size ) { - PyErr_SetString(AudioopError, "Index out of range"); - return 0; - } - if ( size == 1 ) val = (int)*CHARP(cp, i); - else if ( size == 2 ) val = (int)*SHORTP(cp, i*2); - else if ( size == 4 ) val = (int)*LONGP(cp, i*4); - return PyInt_FromLong(val); + signed char *cp; + int len, size, val = 0; + int i; + + if ( !PyArg_Parse(args, "(s#ii)", &cp, &len, &size, &i) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + if ( i < 0 || i >= len/size ) { + PyErr_SetString(AudioopError, "Index out of range"); + return 0; + } + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i*2); + else if ( size == 4 ) val = (int)*LONGP(cp, i*4); + return PyInt_FromLong(val); } static PyObject * audioop_max(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0; - int i; - int max = 0; - - if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) - return 0; - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - for ( i=0; i max ) max = val; - } - return PyInt_FromLong(max); + signed char *cp; + int len, size, val = 0; + int i; + int max = 0; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + for ( i=0; i max ) max = val; + } + return PyInt_FromLong(max); } static PyObject * audioop_minmax(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0; - int i; - int min = 0x7fffffff, max = -0x7fffffff; - - if (!PyArg_Parse(args, "(s#i)", &cp, &len, &size)) - return NULL; - if (size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return NULL; - } - for (i = 0; i < len; i += size) { - if (size == 1) val = (int) *CHARP(cp, i); - else if (size == 2) val = (int) *SHORTP(cp, i); - else if (size == 4) val = (int) *LONGP(cp, i); - if (val > max) max = val; - if (val < min) min = val; - } - return Py_BuildValue("(ii)", min, max); + signed char *cp; + int len, size, val = 0; + int i; + int min = 0x7fffffff, max = -0x7fffffff; + + if (!PyArg_Parse(args, "(s#i)", &cp, &len, &size)) + return NULL; + if (size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return NULL; + } + for (i = 0; i < len; i += size) { + if (size == 1) val = (int) *CHARP(cp, i); + else if (size == 2) val = (int) *SHORTP(cp, i); + else if (size == 4) val = (int) *LONGP(cp, i); + if (val > max) max = val; + if (val < min) min = val; + } + return Py_BuildValue("(ii)", min, max); } static PyObject * audioop_avg(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0; - int i; - double avg = 0.0; - - if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) - return 0; - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - for ( i=0; i>= 1; - len2 >>= 1; + short *cp1, *cp2; + int len1, len2; + int j, best_j; + double aj_m1, aj_lm1; + double sum_ri_2, sum_aij_2, sum_aij_ri, result, best_result, factor; + + if ( !PyArg_Parse(args, "(s#s#)", &cp1, &len1, &cp2, &len2) ) + return 0; + if ( len1 & 1 || len2 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + len1 >>= 1; + len2 >>= 1; - if ( len1 < len2 ) { - PyErr_SetString(AudioopError, "First sample should be longer"); - return 0; - } - sum_ri_2 = _sum2(cp2, cp2, len2); - sum_aij_2 = _sum2(cp1, cp1, len2); - sum_aij_ri = _sum2(cp1, cp2, len2); - - result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) / sum_aij_2; - - best_result = result; - best_j = 0; - j = 0; - - for ( j=1; j<=len1-len2; j++) { - aj_m1 = (double)cp1[j-1]; - aj_lm1 = (double)cp1[j+len2-1]; - - sum_aij_2 = sum_aij_2 + aj_lm1*aj_lm1 - aj_m1*aj_m1; - sum_aij_ri = _sum2(cp1+j, cp2, len2); - - result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) - / sum_aij_2; - - if ( result < best_result ) { - best_result = result; - best_j = j; - } - - } - - factor = _sum2(cp1+best_j, cp2, len2) / sum_ri_2; + if ( len1 < len2 ) { + PyErr_SetString(AudioopError, "First sample should be longer"); + return 0; + } + sum_ri_2 = _sum2(cp2, cp2, len2); + sum_aij_2 = _sum2(cp1, cp1, len2); + sum_aij_ri = _sum2(cp1, cp2, len2); + + result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) / sum_aij_2; + + best_result = result; + best_j = 0; + j = 0; + + for ( j=1; j<=len1-len2; j++) { + aj_m1 = (double)cp1[j-1]; + aj_lm1 = (double)cp1[j+len2-1]; + + sum_aij_2 = sum_aij_2 + aj_lm1*aj_lm1 - aj_m1*aj_m1; + sum_aij_ri = _sum2(cp1+j, cp2, len2); + + result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) + / sum_aij_2; + + if ( result < best_result ) { + best_result = result; + best_j = j; + } + + } + + factor = _sum2(cp1+best_j, cp2, len2) / sum_ri_2; - return Py_BuildValue("(if)", best_j, factor); + return Py_BuildValue("(if)", best_j, factor); } /* @@ -377,27 +524,27 @@ audioop_findfit(PyObject *self, PyObject *args) static PyObject * audioop_findfactor(PyObject *self, PyObject *args) { - short *cp1, *cp2; - int len1, len2; - double sum_ri_2, sum_aij_ri, result; - - if ( !PyArg_Parse(args, "(s#s#)", &cp1, &len1, &cp2, &len2) ) - return 0; - if ( len1 & 1 || len2 & 1 ) { - PyErr_SetString(AudioopError, "Strings should be even-sized"); - return 0; - } - if ( len1 != len2 ) { - PyErr_SetString(AudioopError, "Samples should be same size"); - return 0; - } - len2 >>= 1; - sum_ri_2 = _sum2(cp2, cp2, len2); - sum_aij_ri = _sum2(cp1, cp2, len2); - - result = sum_aij_ri / sum_ri_2; - - return PyFloat_FromDouble(result); + short *cp1, *cp2; + int len1, len2; + double sum_ri_2, sum_aij_ri, result; + + if ( !PyArg_Parse(args, "(s#s#)", &cp1, &len1, &cp2, &len2) ) + return 0; + if ( len1 & 1 || len2 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + if ( len1 != len2 ) { + PyErr_SetString(AudioopError, "Samples should be same size"); + return 0; + } + len2 >>= 1; + sum_ri_2 = _sum2(cp2, cp2, len2); + sum_aij_ri = _sum2(cp1, cp2, len2); + + result = sum_aij_ri / sum_ri_2; + + return PyFloat_FromDouble(result); } /* @@ -407,981 +554,1053 @@ audioop_findfactor(PyObject *self, PyObject *args) static PyObject * audioop_findmax(PyObject *self, PyObject *args) { - short *cp1; - int len1, len2; - int j, best_j; - double aj_m1, aj_lm1; - double result, best_result; - - if ( !PyArg_Parse(args, "(s#i)", &cp1, &len1, &len2) ) - return 0; - if ( len1 & 1 ) { - PyErr_SetString(AudioopError, "Strings should be even-sized"); - return 0; - } - len1 >>= 1; + short *cp1; + int len1, len2; + int j, best_j; + double aj_m1, aj_lm1; + double result, best_result; + + if ( !PyArg_Parse(args, "(s#i)", &cp1, &len1, &len2) ) + return 0; + if ( len1 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + len1 >>= 1; - if ( len1 < len2 ) { - PyErr_SetString(AudioopError, "Input sample should be longer"); - return 0; - } + if ( len1 < len2 ) { + PyErr_SetString(AudioopError, "Input sample should be longer"); + return 0; + } - result = _sum2(cp1, cp1, len2); + result = _sum2(cp1, cp1, len2); - best_result = result; - best_j = 0; - j = 0; + best_result = result; + best_j = 0; + j = 0; - for ( j=1; j<=len1-len2; j++) { - aj_m1 = (double)cp1[j-1]; - aj_lm1 = (double)cp1[j+len2-1]; + for ( j=1; j<=len1-len2; j++) { + aj_m1 = (double)cp1[j-1]; + aj_lm1 = (double)cp1[j+len2-1]; - result = result + aj_lm1*aj_lm1 - aj_m1*aj_m1; + result = result + aj_lm1*aj_lm1 - aj_m1*aj_m1; - if ( result > best_result ) { - best_result = result; - best_j = j; - } - - } + if ( result > best_result ) { + best_result = result; + best_j = j; + } + + } - return PyInt_FromLong(best_j); + return PyInt_FromLong(best_j); } static PyObject * audioop_avgpp(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0, prevval = 0, prevextremevalid = 0, - prevextreme = 0; - int i; - double avg = 0.0; - int diff, prevdiff, extremediff, nextreme = 0; - - if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) - return 0; - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - /* Compute first delta value ahead. Also automatically makes us - ** skip the first extreme value - */ - if ( size == 1 ) prevval = (int)*CHARP(cp, 0); - else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0); - else if ( size == 4 ) prevval = (int)*LONGP(cp, 0); - if ( size == 1 ) val = (int)*CHARP(cp, size); - else if ( size == 2 ) val = (int)*SHORTP(cp, size); - else if ( size == 4 ) val = (int)*LONGP(cp, size); - prevdiff = val - prevval; + signed char *cp; + int len, size, val = 0, prevval = 0, prevextremevalid = 0, + prevextreme = 0; + int i; + double avg = 0.0; + int diff, prevdiff, extremediff, nextreme = 0; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + /* Compute first delta value ahead. Also automatically makes us + ** skip the first extreme value + */ + if ( size == 1 ) prevval = (int)*CHARP(cp, 0); + else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0); + else if ( size == 4 ) prevval = (int)*LONGP(cp, 0); + if ( size == 1 ) val = (int)*CHARP(cp, size); + else if ( size == 2 ) val = (int)*SHORTP(cp, size); + else if ( size == 4 ) val = (int)*LONGP(cp, size); + prevdiff = val - prevval; - for ( i=size; i max ) - max = extremediff; - } - prevextremevalid = 1; - prevextreme = prevval; - } - prevval = val; - if ( diff != 0 ) - prevdiff = diff; - } - return PyInt_FromLong(max); + signed char *cp; + int len, size, val = 0, prevval = 0, prevextremevalid = 0, + prevextreme = 0; + int i; + int max = 0; + int diff, prevdiff, extremediff; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + /* Compute first delta value ahead. Also automatically makes us + ** skip the first extreme value + */ + if ( size == 1 ) prevval = (int)*CHARP(cp, 0); + else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0); + else if ( size == 4 ) prevval = (int)*LONGP(cp, 0); + if ( size == 1 ) val = (int)*CHARP(cp, size); + else if ( size == 2 ) val = (int)*SHORTP(cp, size); + else if ( size == 4 ) val = (int)*LONGP(cp, size); + prevdiff = val - prevval; + + for ( i=size; i max ) + max = extremediff; + } + prevextremevalid = 1; + prevextreme = prevval; + } + prevval = val; + if ( diff != 0 ) + prevdiff = diff; + } + return PyInt_FromLong(max); } static PyObject * audioop_cross(PyObject *self, PyObject *args) { - signed char *cp; - int len, size, val = 0; - int i; - int prevval, ncross; - - if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) - return 0; - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - ncross = -1; - prevval = 17; /* Anything <> 0,1 */ - for ( i=0; i> 7; - else if ( size == 2 ) val = ((int)*SHORTP(cp, i)) >> 15; - else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 31; - val = val & 1; - if ( val != prevval ) ncross++; - prevval = val; - } - return PyInt_FromLong(ncross); + signed char *cp; + int len, size, val = 0; + int i; + int prevval, ncross; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + ncross = -1; + prevval = 17; /* Anything <> 0,1 */ + for ( i=0; i> 7; + else if ( size == 2 ) val = ((int)*SHORTP(cp, i)) >> 15; + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 31; + val = val & 1; + if ( val != prevval ) ncross++; + prevval = val; + } + return PyInt_FromLong(ncross); } static PyObject * audioop_mul(PyObject *self, PyObject *args) { - signed char *cp, *ncp; - int len, size, val = 0; - double factor, fval, maxval; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#id)", &cp, &len, &size, &factor ) ) - return 0; + signed char *cp, *ncp; + int len, size, val = 0; + double factor, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#id)", &cp, &len, &size, &factor ) ) + return 0; - if ( size == 1 ) maxval = (double) 0x7f; - else if ( size == 2 ) maxval = (double) 0x7fff; - else if ( size == 4 ) maxval = (double) 0x7fffffff; - else { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = (int)*CHARP(cp, i); - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = (int)*LONGP(cp, i); - fval = (double)val*factor; - if ( fval > maxval ) fval = maxval; - else if ( fval < -maxval ) fval = -maxval; - val = (int)fval; - if ( size == 1 ) *CHARP(ncp, i) = (signed char)val; - else if ( size == 2 ) *SHORTP(ncp, i) = (short)val; - else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)val; - } - return rv; + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + fval = (double)val*factor; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val = (int)fval; + if ( size == 1 ) *CHARP(ncp, i) = (signed char)val; + else if ( size == 2 ) *SHORTP(ncp, i) = (short)val; + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)val; + } + return rv; } static PyObject * audioop_tomono(PyObject *self, PyObject *args) { - signed char *cp, *ncp; - int len, size, val1 = 0, val2 = 0; - double fac1, fac2, fval, maxval; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) - return 0; + signed char *cp, *ncp; + int len, size, val1 = 0, val2 = 0; + double fac1, fac2, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) + return 0; - if ( size == 1 ) maxval = (double) 0x7f; - else if ( size == 2 ) maxval = (double) 0x7fff; - else if ( size == 4 ) maxval = (double) 0x7fffffff; - else { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len/2); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len/2); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len; i += size*2 ) { - if ( size == 1 ) val1 = (int)*CHARP(cp, i); - else if ( size == 2 ) val1 = (int)*SHORTP(cp, i); - else if ( size == 4 ) val1 = (int)*LONGP(cp, i); - if ( size == 1 ) val2 = (int)*CHARP(cp, i+1); - else if ( size == 2 ) val2 = (int)*SHORTP(cp, i+2); - else if ( size == 4 ) val2 = (int)*LONGP(cp, i+4); - fval = (double)val1*fac1 + (double)val2*fac2; - if ( fval > maxval ) fval = maxval; - else if ( fval < -maxval ) fval = -maxval; - val1 = (int)fval; - if ( size == 1 ) *CHARP(ncp, i/2) = (signed char)val1; - else if ( size == 2 ) *SHORTP(ncp, i/2) = (short)val1; - else if ( size == 4 ) *LONGP(ncp, i/2)= (Py_Int32)val1; - } - return rv; + for ( i=0; i < len; i += size*2 ) { + if ( size == 1 ) val1 = (int)*CHARP(cp, i); + else if ( size == 2 ) val1 = (int)*SHORTP(cp, i); + else if ( size == 4 ) val1 = (int)*LONGP(cp, i); + if ( size == 1 ) val2 = (int)*CHARP(cp, i+1); + else if ( size == 2 ) val2 = (int)*SHORTP(cp, i+2); + else if ( size == 4 ) val2 = (int)*LONGP(cp, i+4); + fval = (double)val1*fac1 + (double)val2*fac2; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val1 = (int)fval; + if ( size == 1 ) *CHARP(ncp, i/2) = (signed char)val1; + else if ( size == 2 ) *SHORTP(ncp, i/2) = (short)val1; + else if ( size == 4 ) *LONGP(ncp, i/2)= (Py_Int32)val1; + } + return rv; } static PyObject * audioop_tostereo(PyObject *self, PyObject *args) { - signed char *cp, *ncp; - int len, size, val1, val2, val = 0; - double fac1, fac2, fval, maxval; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) - return 0; + signed char *cp, *ncp; + int len, size, val1, val2, val = 0; + double fac1, fac2, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) + return 0; - if ( size == 1 ) maxval = (double) 0x7f; - else if ( size == 2 ) maxval = (double) 0x7fff; - else if ( size == 4 ) maxval = (double) 0x7fffffff; - else { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len*2); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len*2); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = (int)*CHARP(cp, i); - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = (int)*LONGP(cp, i); - - fval = (double)val*fac1; - if ( fval > maxval ) fval = maxval; - else if ( fval < -maxval ) fval = -maxval; - val1 = (int)fval; - - fval = (double)val*fac2; - if ( fval > maxval ) fval = maxval; - else if ( fval < -maxval ) fval = -maxval; - val2 = (int)fval; - - if ( size == 1 ) *CHARP(ncp, i*2) = (signed char)val1; - else if ( size == 2 ) *SHORTP(ncp, i*2) = (short)val1; - else if ( size == 4 ) *LONGP(ncp, i*2) = (Py_Int32)val1; - - if ( size == 1 ) *CHARP(ncp, i*2+1) = (signed char)val2; - else if ( size == 2 ) *SHORTP(ncp, i*2+2) = (short)val2; - else if ( size == 4 ) *LONGP(ncp, i*2+4) = (Py_Int32)val2; - } - return rv; + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + + fval = (double)val*fac1; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val1 = (int)fval; + + fval = (double)val*fac2; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val2 = (int)fval; + + if ( size == 1 ) *CHARP(ncp, i*2) = (signed char)val1; + else if ( size == 2 ) *SHORTP(ncp, i*2) = (short)val1; + else if ( size == 4 ) *LONGP(ncp, i*2) = (Py_Int32)val1; + + if ( size == 1 ) *CHARP(ncp, i*2+1) = (signed char)val2; + else if ( size == 2 ) *SHORTP(ncp, i*2+2) = (short)val2; + else if ( size == 4 ) *LONGP(ncp, i*2+4) = (Py_Int32)val2; + } + return rv; } static PyObject * audioop_add(PyObject *self, PyObject *args) { - signed char *cp1, *cp2, *ncp; - int len1, len2, size, val1 = 0, val2 = 0, maxval, newval; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#s#i)", - &cp1, &len1, &cp2, &len2, &size ) ) - return 0; - - if ( len1 != len2 ) { - PyErr_SetString(AudioopError, "Lengths should be the same"); - return 0; - } + signed char *cp1, *cp2, *ncp; + int len1, len2, size, val1 = 0, val2 = 0, maxval, newval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#s#i)", + &cp1, &len1, &cp2, &len2, &size ) ) + return 0; + + if ( len1 != len2 ) { + PyErr_SetString(AudioopError, "Lengths should be the same"); + return 0; + } - if ( size == 1 ) maxval = 0x7f; - else if ( size == 2 ) maxval = 0x7fff; - else if ( size == 4 ) maxval = 0x7fffffff; - else { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } - - rv = PyString_FromStringAndSize(NULL, len1); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); - - for ( i=0; i < len1; i += size ) { - if ( size == 1 ) val1 = (int)*CHARP(cp1, i); - else if ( size == 2 ) val1 = (int)*SHORTP(cp1, i); - else if ( size == 4 ) val1 = (int)*LONGP(cp1, i); - - if ( size == 1 ) val2 = (int)*CHARP(cp2, i); - else if ( size == 2 ) val2 = (int)*SHORTP(cp2, i); - else if ( size == 4 ) val2 = (int)*LONGP(cp2, i); - - newval = val1 + val2; - /* truncate in case of overflow */ - if (newval > maxval) newval = maxval; - else if (newval < -maxval) newval = -maxval; - else if (size == 4 && (newval^val1) < 0 && (newval^val2) < 0) - newval = val1 > 0 ? maxval : - maxval; - - if ( size == 1 ) *CHARP(ncp, i) = (signed char)newval; - else if ( size == 2 ) *SHORTP(ncp, i) = (short)newval; - else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)newval; - } - return rv; + if ( size == 1 ) maxval = 0x7f; + else if ( size == 2 ) maxval = 0x7fff; + else if ( size == 4 ) maxval = 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len1); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + for ( i=0; i < len1; i += size ) { + if ( size == 1 ) val1 = (int)*CHARP(cp1, i); + else if ( size == 2 ) val1 = (int)*SHORTP(cp1, i); + else if ( size == 4 ) val1 = (int)*LONGP(cp1, i); + + if ( size == 1 ) val2 = (int)*CHARP(cp2, i); + else if ( size == 2 ) val2 = (int)*SHORTP(cp2, i); + else if ( size == 4 ) val2 = (int)*LONGP(cp2, i); + + newval = val1 + val2; + /* truncate in case of overflow */ + if (newval > maxval) newval = maxval; + else if (newval < -maxval) newval = -maxval; + else if (size == 4 && (newval^val1) < 0 && (newval^val2) < 0) + newval = val1 > 0 ? maxval : - maxval; + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)newval; + else if ( size == 2 ) *SHORTP(ncp, i) = (short)newval; + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)newval; + } + return rv; } static PyObject * audioop_bias(PyObject *self, PyObject *args) { - signed char *cp, *ncp; - int len, size, val = 0; - PyObject *rv; - int i; - int bias; - - if ( !PyArg_Parse(args, "(s#ii)", - &cp, &len, &size , &bias) ) - return 0; - - if ( size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + signed char *cp, *ncp; + int len, size, val = 0; + PyObject *rv; + int i; + int bias; + + if ( !PyArg_Parse(args, "(s#ii)", + &cp, &len, &size , &bias) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = (int)*CHARP(cp, i); - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = (int)*LONGP(cp, i); - - if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val+bias); - else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val+bias); - else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val+bias); - } - return rv; + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val+bias); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val+bias); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val+bias); + } + return rv; } static PyObject * audioop_reverse(PyObject *self, PyObject *args) { - signed char *cp; - unsigned char *ncp; - int len, size, val = 0; - PyObject *rv; - int i, j; - - if ( !PyArg_Parse(args, "(s#i)", - &cp, &len, &size) ) - return 0; - - if ( size != 1 && size != 2 && size != 4 ) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + signed char *cp; + unsigned char *ncp; + int len, size, val = 0; + PyObject *rv; + int i, j; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len); - if ( rv == 0 ) - return 0; - ncp = (unsigned char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; - - j = len - i - size; - - if ( size == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); - else if ( size == 2 ) *SHORTP(ncp, j) = (short)(val); - else if ( size == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); - } - return rv; + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + j = len - i - size; + + if ( size == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); + else if ( size == 2 ) *SHORTP(ncp, j) = (short)(val); + else if ( size == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); + } + return rv; } static PyObject * audioop_lin2lin(PyObject *self, PyObject *args) { - signed char *cp; - unsigned char *ncp; - int len, size, size2, val = 0; - PyObject *rv; - int i, j; - - if ( !PyArg_Parse(args, "(s#ii)", - &cp, &len, &size, &size2) ) - return 0; - - if ( (size != 1 && size != 2 && size != 4) || - (size2 != 1 && size2 != 2 && size2 != 4)) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + signed char *cp; + unsigned char *ncp; + int len, size, size2, val = 0; + PyObject *rv; + int i, j; + + if ( !PyArg_Parse(args, "(s#ii)", + &cp, &len, &size, &size2) ) + return 0; + + if ( (size != 1 && size != 2 && size != 4) || + (size2 != 1 && size2 != 2 && size2 != 4)) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, (len/size)*size2); - if ( rv == 0 ) - return 0; - ncp = (unsigned char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, (len/size)*size2); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); - for ( i=0, j=0; i < len; i += size, j += size2 ) { - if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; - - if ( size2 == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); - else if ( size2 == 2 ) *SHORTP(ncp, j) = (short)(val); - else if ( size2 == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); - } - return rv; + for ( i=0, j=0; i < len; i += size, j += size2 ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + if ( size2 == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); + else if ( size2 == 2 ) *SHORTP(ncp, j) = (short)(val); + else if ( size2 == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); + } + return rv; } static int gcd(int a, int b) { - while (b > 0) { - int tmp = a % b; - a = b; - b = tmp; - } - return a; + while (b > 0) { + int tmp = a % b; + a = b; + b = tmp; + } + return a; } static PyObject * audioop_ratecv(PyObject *self, PyObject *args) { - char *cp, *ncp; - int len, size, nchannels, inrate, outrate, weightA, weightB; - int chan, d, *prev_i, *cur_i, cur_o; - PyObject *state, *samps, *str, *rv = NULL; - int bytes_per_frame; - - weightA = 1; - weightB = 0; - if (!PyArg_ParseTuple(args, "s#iiiiO|ii:ratecv", &cp, &len, &size, &nchannels, - &inrate, &outrate, &state, &weightA, &weightB)) - return NULL; - if (size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return NULL; - } - if (nchannels < 1) { - PyErr_SetString(AudioopError, "# of channels should be >= 1"); - return NULL; - } - bytes_per_frame = size * nchannels; - if (bytes_per_frame / nchannels != size) { - /* This overflow test is rigorously correct because - both multiplicands are >= 1. Use the argument names - from the docs for the error msg. */ - PyErr_SetString(PyExc_OverflowError, - "width * nchannels too big for a C int"); - return NULL; - } - if (weightA < 1 || weightB < 0) { - PyErr_SetString(AudioopError, - "weightA should be >= 1, weightB should be >= 0"); - return NULL; - } - if (len % bytes_per_frame != 0) { - PyErr_SetString(AudioopError, "not a whole number of frames"); - return NULL; - } - if (inrate <= 0 || outrate <= 0) { - PyErr_SetString(AudioopError, "sampling rate not > 0"); - return NULL; - } - /* divide inrate and outrate by their greatest common divisor */ - d = gcd(inrate, outrate); - inrate /= d; - outrate /= d; - - prev_i = (int *) malloc(nchannels * sizeof(int)); - cur_i = (int *) malloc(nchannels * sizeof(int)); - if (prev_i == NULL || cur_i == NULL) { - (void) PyErr_NoMemory(); - goto exit; - } - - len /= bytes_per_frame; /* # of frames */ - - if (state == Py_None) { - d = -outrate; - for (chan = 0; chan < nchannels; chan++) - prev_i[chan] = cur_i[chan] = 0; - } - else { - if (!PyArg_ParseTuple(state, - "iO!;audioop.ratecv: illegal state argument", - &d, &PyTuple_Type, &samps)) - goto exit; - if (PyTuple_Size(samps) != nchannels) { - PyErr_SetString(AudioopError, - "illegal state argument"); - goto exit; - } - for (chan = 0; chan < nchannels; chan++) { - if (!PyArg_ParseTuple(PyTuple_GetItem(samps, chan), - "ii:ratecv",&prev_i[chan],&cur_i[chan])) - goto exit; - } - } - - /* str <- Space for the output buffer. */ - { - /* There are len input frames, so we need (mathematically) - ceiling(len*outrate/inrate) output frames, and each frame - requires bytes_per_frame bytes. Computing this - without spurious overflow is the challenge; we can - settle for a reasonable upper bound, though. */ - int ceiling; /* the number of output frames */ - int nbytes; /* the number of output bytes needed */ - int q = len / inrate; - /* Now len = q * inrate + r exactly (with r = len % inrate), - and this is less than q * inrate + inrate = (q+1)*inrate. - So a reasonable upper bound on len*outrate/inrate is - ((q+1)*inrate)*outrate/inrate = - (q+1)*outrate. - */ - ceiling = (q+1) * outrate; - nbytes = ceiling * bytes_per_frame; - /* See whether anything overflowed; if not, get the space. */ - if (q+1 < 0 || - ceiling / outrate != q+1 || - nbytes / bytes_per_frame != ceiling) - str = NULL; - else - str = PyString_FromStringAndSize(NULL, nbytes); - - if (str == NULL) { - PyErr_SetString(PyExc_MemoryError, - "not enough memory for output buffer"); - goto exit; - } - } - ncp = PyString_AsString(str); - - for (;;) { - while (d < 0) { - if (len == 0) { - samps = PyTuple_New(nchannels); - if (samps == NULL) - goto exit; - for (chan = 0; chan < nchannels; chan++) - PyTuple_SetItem(samps, chan, - Py_BuildValue("(ii)", - prev_i[chan], - cur_i[chan])); - if (PyErr_Occurred()) - goto exit; - /* We have checked before that the length - * of the string fits into int. */ - len = (int)(ncp - PyString_AsString(str)); - if (len == 0) { - /*don't want to resize to zero length*/ - rv = PyString_FromStringAndSize("", 0); - Py_DECREF(str); - str = rv; - } else if (_PyString_Resize(&str, len) < 0) - goto exit; - rv = Py_BuildValue("(O(iO))", str, d, samps); - Py_DECREF(samps); - Py_DECREF(str); - goto exit; /* return rv */ - } - for (chan = 0; chan < nchannels; chan++) { - prev_i[chan] = cur_i[chan]; - if (size == 1) - cur_i[chan] = ((int)*CHARP(cp, 0)) << 8; - else if (size == 2) - cur_i[chan] = (int)*SHORTP(cp, 0); - else if (size == 4) - cur_i[chan] = ((int)*LONGP(cp, 0)) >> 16; - cp += size; - /* implements a simple digital filter */ - cur_i[chan] = - (weightA * cur_i[chan] + - weightB * prev_i[chan]) / - (weightA + weightB); - } - len--; - d += outrate; - } - while (d >= 0) { - for (chan = 0; chan < nchannels; chan++) { - cur_o = (prev_i[chan] * d + - cur_i[chan] * (outrate - d)) / - outrate; - if (size == 1) - *CHARP(ncp, 0) = (signed char)(cur_o >> 8); - else if (size == 2) - *SHORTP(ncp, 0) = (short)(cur_o); - else if (size == 4) - *LONGP(ncp, 0) = (Py_Int32)(cur_o<<16); - ncp += size; - } - d -= inrate; - } - } + char *cp, *ncp; + int len, size, nchannels, inrate, outrate, weightA, weightB; + int chan, d, *prev_i, *cur_i, cur_o; + PyObject *state, *samps, *str, *rv = NULL; + int bytes_per_frame; + + weightA = 1; + weightB = 0; + if (!PyArg_ParseTuple(args, "s#iiiiO|ii:ratecv", &cp, &len, &size, &nchannels, + &inrate, &outrate, &state, &weightA, &weightB)) + return NULL; + if (size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return NULL; + } + if (nchannels < 1) { + PyErr_SetString(AudioopError, "# of channels should be >= 1"); + return NULL; + } + bytes_per_frame = size * nchannels; + if (bytes_per_frame / nchannels != size) { + /* This overflow test is rigorously correct because + both multiplicands are >= 1. Use the argument names + from the docs for the error msg. */ + PyErr_SetString(PyExc_OverflowError, + "width * nchannels too big for a C int"); + return NULL; + } + if (weightA < 1 || weightB < 0) { + PyErr_SetString(AudioopError, + "weightA should be >= 1, weightB should be >= 0"); + return NULL; + } + if (len % bytes_per_frame != 0) { + PyErr_SetString(AudioopError, "not a whole number of frames"); + return NULL; + } + if (inrate <= 0 || outrate <= 0) { + PyErr_SetString(AudioopError, "sampling rate not > 0"); + return NULL; + } + /* divide inrate and outrate by their greatest common divisor */ + d = gcd(inrate, outrate); + inrate /= d; + outrate /= d; + + prev_i = (int *) malloc(nchannels * sizeof(int)); + cur_i = (int *) malloc(nchannels * sizeof(int)); + if (prev_i == NULL || cur_i == NULL) { + (void) PyErr_NoMemory(); + goto exit; + } + + len /= bytes_per_frame; /* # of frames */ + + if (state == Py_None) { + d = -outrate; + for (chan = 0; chan < nchannels; chan++) + prev_i[chan] = cur_i[chan] = 0; + } + else { + if (!PyArg_ParseTuple(state, + "iO!;audioop.ratecv: illegal state argument", + &d, &PyTuple_Type, &samps)) + goto exit; + if (PyTuple_Size(samps) != nchannels) { + PyErr_SetString(AudioopError, + "illegal state argument"); + goto exit; + } + for (chan = 0; chan < nchannels; chan++) { + if (!PyArg_ParseTuple(PyTuple_GetItem(samps, chan), + "ii:ratecv",&prev_i[chan],&cur_i[chan])) + goto exit; + } + } + + /* str <- Space for the output buffer. */ + { + /* There are len input frames, so we need (mathematically) + ceiling(len*outrate/inrate) output frames, and each frame + requires bytes_per_frame bytes. Computing this + without spurious overflow is the challenge; we can + settle for a reasonable upper bound, though. */ + int ceiling; /* the number of output frames */ + int nbytes; /* the number of output bytes needed */ + int q = len / inrate; + /* Now len = q * inrate + r exactly (with r = len % inrate), + and this is less than q * inrate + inrate = (q+1)*inrate. + So a reasonable upper bound on len*outrate/inrate is + ((q+1)*inrate)*outrate/inrate = + (q+1)*outrate. + */ + ceiling = (q+1) * outrate; + nbytes = ceiling * bytes_per_frame; + /* See whether anything overflowed; if not, get the space. */ + if (q+1 < 0 || + ceiling / outrate != q+1 || + nbytes / bytes_per_frame != ceiling) + str = NULL; + else + str = PyString_FromStringAndSize(NULL, nbytes); + + if (str == NULL) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + goto exit; + } + } + ncp = PyString_AsString(str); + + for (;;) { + while (d < 0) { + if (len == 0) { + samps = PyTuple_New(nchannels); + if (samps == NULL) + goto exit; + for (chan = 0; chan < nchannels; chan++) + PyTuple_SetItem(samps, chan, + Py_BuildValue("(ii)", + prev_i[chan], + cur_i[chan])); + if (PyErr_Occurred()) + goto exit; + /* We have checked before that the length + * of the string fits into int. */ + len = (int)(ncp - PyString_AsString(str)); + if (len == 0) { + /*don't want to resize to zero length*/ + rv = PyString_FromStringAndSize("", 0); + Py_DECREF(str); + str = rv; + } else if (_PyString_Resize(&str, len) < 0) + goto exit; + rv = Py_BuildValue("(O(iO))", str, d, samps); + Py_DECREF(samps); + Py_DECREF(str); + goto exit; /* return rv */ + } + for (chan = 0; chan < nchannels; chan++) { + prev_i[chan] = cur_i[chan]; + if (size == 1) + cur_i[chan] = ((int)*CHARP(cp, 0)) << 8; + else if (size == 2) + cur_i[chan] = (int)*SHORTP(cp, 0); + else if (size == 4) + cur_i[chan] = ((int)*LONGP(cp, 0)) >> 16; + cp += size; + /* implements a simple digital filter */ + cur_i[chan] = + (weightA * cur_i[chan] + + weightB * prev_i[chan]) / + (weightA + weightB); + } + len--; + d += outrate; + } + while (d >= 0) { + for (chan = 0; chan < nchannels; chan++) { + cur_o = (prev_i[chan] * d + + cur_i[chan] * (outrate - d)) / + outrate; + if (size == 1) + *CHARP(ncp, 0) = (signed char)(cur_o >> 8); + else if (size == 2) + *SHORTP(ncp, 0) = (short)(cur_o); + else if (size == 4) + *LONGP(ncp, 0) = (Py_Int32)(cur_o<<16); + ncp += size; + } + d -= inrate; + } + } exit: - if (prev_i != NULL) - free(prev_i); - if (cur_i != NULL) - free(cur_i); - return rv; + if (prev_i != NULL) + free(prev_i); + if (cur_i != NULL) + free(cur_i); + return rv; } static PyObject * audioop_lin2ulaw(PyObject *self, PyObject *args) { - signed char *cp; - unsigned char *ncp; - int len, size, val = 0; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#i)", - &cp, &len, &size) ) - return 0; - - if ( size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + signed char *cp; + unsigned char *ncp; + int len, size, val = 0; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len/size); - if ( rv == 0 ) - return 0; - ncp = (unsigned char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len/size); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; - - *ncp++ = st_linear_to_ulaw(val); - } - return rv; + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + *ncp++ = st_14linear2ulaw(val); + } + return rv; } static PyObject * audioop_ulaw2lin(PyObject *self, PyObject *args) { - unsigned char *cp; - unsigned char cval; - signed char *ncp; - int len, size, val; - PyObject *rv; - int i; - - if ( !PyArg_Parse(args, "(s#i)", - &cp, &len, &size) ) - return 0; - - if ( size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + unsigned char *cp; + unsigned char cval; + signed char *ncp; + int len, size, val; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len*size); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + for ( i=0; i < len*size; i += size ) { + cval = *cp++; + val = st_ulaw2linear16(cval); + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val >> 8); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val<<16); + } + return rv; +} + +static PyObject * +audioop_lin2alaw(PyObject *self, PyObject *args) +{ + signed char *cp; + unsigned char *ncp; + int len, size, val = 0; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len/size); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + *ncp++ = st_linear2alaw(val); + } + return rv; +} + +static PyObject * +audioop_alaw2lin(PyObject *self, PyObject *args) +{ + unsigned char *cp; + unsigned char cval; + signed char *ncp; + int len, size, val; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - rv = PyString_FromStringAndSize(NULL, len*size); - if ( rv == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(rv); + rv = PyString_FromStringAndSize(NULL, len*size); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len*size; i += size ) { - cval = *cp++; - val = st_ulaw_to_linear(cval); - - if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val >> 8); - else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val); - else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val<<16); - } - return rv; + for ( i=0; i < len*size; i += size ) { + cval = *cp++; + val = st_alaw2linear16(cval); + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val >> 8); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val<<16); + } + return rv; } static PyObject * audioop_lin2adpcm(PyObject *self, PyObject *args) { - signed char *cp; - signed char *ncp; - int len, size, val = 0, step, valpred, delta, - index, sign, vpdiff, diff; - PyObject *rv, *state, *str; - int i, outputbuffer = 0, bufferstep; - - if ( !PyArg_Parse(args, "(s#iO)", - &cp, &len, &size, &state) ) - return 0; + signed char *cp; + signed char *ncp; + int len, size, val = 0, step, valpred, delta, + index, sign, vpdiff, diff; + PyObject *rv, *state, *str; + int i, outputbuffer = 0, bufferstep; + + if ( !PyArg_Parse(args, "(s#iO)", + &cp, &len, &size, &state) ) + return 0; - if ( size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - str = PyString_FromStringAndSize(NULL, len/(size*2)); - if ( str == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(str); - - /* Decode state, should have (value, step) */ - if ( state == Py_None ) { - /* First time, it seems. Set defaults */ - valpred = 0; - step = 7; - index = 0; - } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) - return 0; - - step = stepsizeTable[index]; - bufferstep = 1; - - for ( i=0; i < len; i += size ) { - if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; - else if ( size == 2 ) val = (int)*SHORTP(cp, i); - else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; - - /* Step 1 - compute difference with previous value */ - diff = val - valpred; - sign = (diff < 0) ? 8 : 0; - if ( sign ) diff = (-diff); - - /* Step 2 - Divide and clamp */ - /* Note: - ** This code *approximately* computes: - ** delta = diff*4/step; - ** vpdiff = (delta+0.5)*step/4; - ** but in shift step bits are dropped. The net result of this - ** is that even if you have fast mul/div hardware you cannot - ** put it to good use since the fixup would be too expensive. - */ - delta = 0; - vpdiff = (step >> 3); - - if ( diff >= step ) { - delta = 4; - diff -= step; - vpdiff += step; - } - step >>= 1; - if ( diff >= step ) { - delta |= 2; - diff -= step; - vpdiff += step; - } - step >>= 1; - if ( diff >= step ) { - delta |= 1; - vpdiff += step; - } - - /* Step 3 - Update previous value */ - if ( sign ) - valpred -= vpdiff; - else - valpred += vpdiff; - - /* Step 4 - Clamp previous value to 16 bits */ - if ( valpred > 32767 ) - valpred = 32767; - else if ( valpred < -32768 ) - valpred = -32768; - - /* Step 5 - Assemble value, update index and step values */ - delta |= sign; - - index += indexTable[delta]; - if ( index < 0 ) index = 0; - if ( index > 88 ) index = 88; - step = stepsizeTable[index]; - - /* Step 6 - Output value */ - if ( bufferstep ) { - outputbuffer = (delta << 4) & 0xf0; - } else { - *ncp++ = (delta & 0x0f) | outputbuffer; - } - bufferstep = !bufferstep; - } - rv = Py_BuildValue("(O(ii))", str, valpred, index); - Py_DECREF(str); - return rv; + str = PyString_FromStringAndSize(NULL, len/(size*2)); + if ( str == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(str); + + /* Decode state, should have (value, step) */ + if ( state == Py_None ) { + /* First time, it seems. Set defaults */ + valpred = 0; + step = 7; + index = 0; + } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) + return 0; + + step = stepsizeTable[index]; + bufferstep = 1; + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + /* Step 1 - compute difference with previous value */ + diff = val - valpred; + sign = (diff < 0) ? 8 : 0; + if ( sign ) diff = (-diff); + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this + ** is that even if you have fast mul/div hardware you cannot + ** put it to good use since the fixup would be too expensive. + */ + delta = 0; + vpdiff = (step >> 3); + + if ( diff >= step ) { + delta = 4; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 2; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 1; + vpdiff += step; + } + + /* Step 3 - Update previous value */ + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 5 - Assemble value, update index and step values */ + delta |= sign; + + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( bufferstep ) { + outputbuffer = (delta << 4) & 0xf0; + } else { + *ncp++ = (delta & 0x0f) | outputbuffer; + } + bufferstep = !bufferstep; + } + rv = Py_BuildValue("(O(ii))", str, valpred, index); + Py_DECREF(str); + return rv; } static PyObject * audioop_adpcm2lin(PyObject *self, PyObject *args) { - signed char *cp; - signed char *ncp; - int len, size, valpred, step, delta, index, sign, vpdiff; - PyObject *rv, *str, *state; - int i, inputbuffer = 0, bufferstep; - - if ( !PyArg_Parse(args, "(s#iO)", - &cp, &len, &size, &state) ) - return 0; - - if ( size != 1 && size != 2 && size != 4) { - PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); - return 0; - } + signed char *cp; + signed char *ncp; + int len, size, valpred, step, delta, index, sign, vpdiff; + PyObject *rv, *str, *state; + int i, inputbuffer = 0, bufferstep; + + if ( !PyArg_Parse(args, "(s#iO)", + &cp, &len, &size, &state) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } - /* Decode state, should have (value, step) */ - if ( state == Py_None ) { - /* First time, it seems. Set defaults */ - valpred = 0; - step = 7; - index = 0; - } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) - return 0; + /* Decode state, should have (value, step) */ + if ( state == Py_None ) { + /* First time, it seems. Set defaults */ + valpred = 0; + step = 7; + index = 0; + } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) + return 0; - str = PyString_FromStringAndSize(NULL, len*size*2); - if ( str == 0 ) - return 0; - ncp = (signed char *)PyString_AsString(str); + str = PyString_FromStringAndSize(NULL, len*size*2); + if ( str == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(str); - step = stepsizeTable[index]; - bufferstep = 0; + step = stepsizeTable[index]; + bufferstep = 0; - for ( i=0; i < len*size*2; i += size ) { - /* Step 1 - get the delta value and compute next index */ - if ( bufferstep ) { - delta = inputbuffer & 0xf; - } else { - inputbuffer = *cp++; - delta = (inputbuffer >> 4) & 0xf; - } - - bufferstep = !bufferstep; - - /* Step 2 - Find new index value (for later) */ - index += indexTable[delta]; - if ( index < 0 ) index = 0; - if ( index > 88 ) index = 88; - - /* Step 3 - Separate sign and magnitude */ - sign = delta & 8; - delta = delta & 7; - - /* Step 4 - Compute difference and new predicted value */ - /* - ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment - ** in adpcm_coder. - */ - vpdiff = step >> 3; - if ( delta & 4 ) vpdiff += step; - if ( delta & 2 ) vpdiff += step>>1; - if ( delta & 1 ) vpdiff += step>>2; - - if ( sign ) - valpred -= vpdiff; - else - valpred += vpdiff; - - /* Step 5 - clamp output value */ - if ( valpred > 32767 ) - valpred = 32767; - else if ( valpred < -32768 ) - valpred = -32768; - - /* Step 6 - Update step value */ - step = stepsizeTable[index]; - - /* Step 6 - Output value */ - if ( size == 1 ) *CHARP(ncp, i) = (signed char)(valpred >> 8); - else if ( size == 2 ) *SHORTP(ncp, i) = (short)(valpred); - else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(valpred<<16); - } - - rv = Py_BuildValue("(O(ii))", str, valpred, index); - Py_DECREF(str); - return rv; + for ( i=0; i < len*size*2; i += size ) { + /* Step 1 - get the delta value and compute next index */ + if ( bufferstep ) { + delta = inputbuffer & 0xf; + } else { + inputbuffer = *cp++; + delta = (inputbuffer >> 4) & 0xf; + } + + bufferstep = !bufferstep; + + /* Step 2 - Find new index value (for later) */ + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + + /* Step 3 - Separate sign and magnitude */ + sign = delta & 8; + delta = delta & 7; + + /* Step 4 - Compute difference and new predicted value */ + /* + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment + ** in adpcm_coder. + */ + vpdiff = step >> 3; + if ( delta & 4 ) vpdiff += step; + if ( delta & 2 ) vpdiff += step>>1; + if ( delta & 1 ) vpdiff += step>>2; + + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 5 - clamp output value */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 6 - Update step value */ + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(valpred >> 8); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(valpred); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(valpred<<16); + } + + rv = Py_BuildValue("(O(ii))", str, valpred, index); + Py_DECREF(str); + return rv; } static PyMethodDef audioop_methods[] = { - { "max", audioop_max, METH_OLDARGS }, - { "minmax", audioop_minmax, METH_OLDARGS }, - { "avg", audioop_avg, METH_OLDARGS }, - { "maxpp", audioop_maxpp, METH_OLDARGS }, - { "avgpp", audioop_avgpp, METH_OLDARGS }, - { "rms", audioop_rms, METH_OLDARGS }, - { "findfit", audioop_findfit, METH_OLDARGS }, - { "findmax", audioop_findmax, METH_OLDARGS }, - { "findfactor", audioop_findfactor, METH_OLDARGS }, - { "cross", audioop_cross, METH_OLDARGS }, - { "mul", audioop_mul, METH_OLDARGS }, - { "add", audioop_add, METH_OLDARGS }, - { "bias", audioop_bias, METH_OLDARGS }, - { "ulaw2lin", audioop_ulaw2lin, METH_OLDARGS }, - { "lin2ulaw", audioop_lin2ulaw, METH_OLDARGS }, - { "lin2lin", audioop_lin2lin, METH_OLDARGS }, - { "adpcm2lin", audioop_adpcm2lin, METH_OLDARGS }, - { "lin2adpcm", audioop_lin2adpcm, METH_OLDARGS }, - { "tomono", audioop_tomono, METH_OLDARGS }, - { "tostereo", audioop_tostereo, METH_OLDARGS }, - { "getsample", audioop_getsample, METH_OLDARGS }, - { "reverse", audioop_reverse, METH_OLDARGS }, - { "ratecv", audioop_ratecv, METH_VARARGS }, - { 0, 0 } + { "max", audioop_max, METH_OLDARGS }, + { "minmax", audioop_minmax, METH_OLDARGS }, + { "avg", audioop_avg, METH_OLDARGS }, + { "maxpp", audioop_maxpp, METH_OLDARGS }, + { "avgpp", audioop_avgpp, METH_OLDARGS }, + { "rms", audioop_rms, METH_OLDARGS }, + { "findfit", audioop_findfit, METH_OLDARGS }, + { "findmax", audioop_findmax, METH_OLDARGS }, + { "findfactor", audioop_findfactor, METH_OLDARGS }, + { "cross", audioop_cross, METH_OLDARGS }, + { "mul", audioop_mul, METH_OLDARGS }, + { "add", audioop_add, METH_OLDARGS }, + { "bias", audioop_bias, METH_OLDARGS }, + { "ulaw2lin", audioop_ulaw2lin, METH_OLDARGS }, + { "lin2ulaw", audioop_lin2ulaw, METH_OLDARGS }, + { "alaw2lin", audioop_alaw2lin, METH_OLDARGS }, + { "lin2alaw", audioop_lin2alaw, METH_OLDARGS }, + { "lin2lin", audioop_lin2lin, METH_OLDARGS }, + { "adpcm2lin", audioop_adpcm2lin, METH_OLDARGS }, + { "lin2adpcm", audioop_lin2adpcm, METH_OLDARGS }, + { "tomono", audioop_tomono, METH_OLDARGS }, + { "tostereo", audioop_tostereo, METH_OLDARGS }, + { "getsample", audioop_getsample, METH_OLDARGS }, + { "reverse", audioop_reverse, METH_OLDARGS }, + { "ratecv", audioop_ratecv, METH_VARARGS }, + { 0, 0 } }; PyMODINIT_FUNC initaudioop(void) { - PyObject *m, *d; - m = Py_InitModule("audioop", audioop_methods); - if (m == NULL) - return; - d = PyModule_GetDict(m); - AudioopError = PyErr_NewException("audioop.error", NULL, NULL); - if (AudioopError != NULL) - PyDict_SetItemString(d,"error",AudioopError); + PyObject *m, *d; + m = Py_InitModule("audioop", audioop_methods); + if (m == NULL) + return; + d = PyModule_GetDict(m); + if (d == NULL) + return; + AudioopError = PyErr_NewException("audioop.error", NULL, NULL); + if (AudioopError != NULL) + PyDict_SetItemString(d,"error",AudioopError); } diff --git a/Modules/cPickle.c b/Modules/cPickle.c index 727dcc9..18df599 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -123,7 +123,7 @@ static PyObject *__class___str, *__getinitargs___str, *__dict___str, *__getstate___str, *__setstate___str, *__name___str, *__reduce___str, *__reduce_ex___str, *write_str, *append_str, - *read_str, *readline_str, *__main___str, *__basicnew___str, + *read_str, *readline_str, *__main___str, *copy_reg_str, *dispatch_table_str; /************************************************************************* @@ -2909,38 +2909,28 @@ Pickler_dealloc(Picklerobject *self) static int Pickler_traverse(Picklerobject *self, visitproc visit, void *arg) { - int err; -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - VISIT(self->write); - VISIT(self->memo); - VISIT(self->fast_memo); - VISIT(self->arg); - VISIT(self->file); - VISIT(self->pers_func); - VISIT(self->inst_pers_func); - VISIT(self->dispatch_table); -#undef VISIT + Py_VISIT(self->write); + Py_VISIT(self->memo); + Py_VISIT(self->fast_memo); + Py_VISIT(self->arg); + Py_VISIT(self->file); + Py_VISIT(self->pers_func); + Py_VISIT(self->inst_pers_func); + Py_VISIT(self->dispatch_table); return 0; } static int Pickler_clear(Picklerobject *self) { -#define CLEAR(SLOT) Py_XDECREF(SLOT); SLOT = NULL; - CLEAR(self->write); - CLEAR(self->memo); - CLEAR(self->fast_memo); - CLEAR(self->arg); - CLEAR(self->file); - CLEAR(self->pers_func); - CLEAR(self->inst_pers_func); - CLEAR(self->dispatch_table); -#undef CLEAR + Py_CLEAR(self->write); + Py_CLEAR(self->memo); + Py_CLEAR(self->fast_memo); + Py_CLEAR(self->arg); + Py_CLEAR(self->file); + Py_CLEAR(self->pers_func); + Py_CLEAR(self->inst_pers_func); + Py_CLEAR(self->dispatch_table); return 0; } @@ -5260,41 +5250,30 @@ Unpickler_dealloc(Unpicklerobject *self) static int Unpickler_traverse(Unpicklerobject *self, visitproc visit, void *arg) { - int err; - -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - VISIT(self->readline); - VISIT(self->read); - VISIT(self->file); - VISIT(self->memo); - VISIT(self->stack); - VISIT(self->pers_func); - VISIT(self->arg); - VISIT(self->last_string); - VISIT(self->find_class); -#undef VISIT + Py_VISIT(self->readline); + Py_VISIT(self->read); + Py_VISIT(self->file); + Py_VISIT(self->memo); + Py_VISIT(self->stack); + Py_VISIT(self->pers_func); + Py_VISIT(self->arg); + Py_VISIT(self->last_string); + Py_VISIT(self->find_class); return 0; } static int Unpickler_clear(Unpicklerobject *self) { -#define CLEAR(SLOT) Py_XDECREF(SLOT); SLOT = NULL - CLEAR(self->readline); - CLEAR(self->read); - CLEAR(self->file); - CLEAR(self->memo); - CLEAR(self->stack); - CLEAR(self->pers_func); - CLEAR(self->arg); - CLEAR(self->last_string); - CLEAR(self->find_class); -#undef CLEAR + Py_CLEAR(self->readline); + Py_CLEAR(self->read); + Py_CLEAR(self->file); + Py_CLEAR(self->memo); + Py_CLEAR(self->stack); + Py_CLEAR(self->pers_func); + Py_CLEAR(self->arg); + Py_CLEAR(self->last_string); + Py_CLEAR(self->find_class); return 0; } @@ -5602,7 +5581,6 @@ init_stuff(PyObject *module_dict) INIT_STR(readline); INIT_STR(copy_reg); INIT_STR(dispatch_table); - INIT_STR(__basicnew__); if (!( copy_reg = PyImport_ImportModule("copy_reg"))) return -1; diff --git a/Modules/cStringIO.c b/Modules/cStringIO.c index bdc9f00..4debb72 100644 --- a/Modules/cStringIO.c +++ b/Modules/cStringIO.c @@ -503,17 +503,17 @@ static PyTypeObject Otype = { 0, /*tp_itemsize*/ /* methods */ (destructor)O_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ + 0, /*tp_print*/ 0, /*tp_getattr */ 0, /*tp_setattr */ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ + 0, /*tp_hash*/ + 0 , /*tp_call*/ + 0, /*tp_str*/ 0, /*tp_getattro */ 0, /*tp_setattro */ 0, /*tp_as_buffer */ @@ -624,17 +624,17 @@ static PyTypeObject Itype = { 0, /*tp_itemsize*/ /* methods */ (destructor)I_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ + 0, /*tp_print*/ 0, /* tp_getattr */ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ diff --git a/Modules/ccpython.cc b/Modules/ccpython.cc deleted file mode 100644 index a6e97ff..0000000 --- a/Modules/ccpython.cc +++ /dev/null @@ -1,11 +0,0 @@ -/* Minimal main program -- everything is loaded from the library */ - -#include "Python.h" - -extern "C" -DL_EXPORT(int) Py_Main( int argc, char *argv[] ); - -int main( int argc, char *argv[] ) -{ - return Py_Main(argc, argv); -} diff --git a/Modules/cjkcodecs/_codecs_cn.c b/Modules/cjkcodecs/_codecs_cn.c index fd048d9..fb51297 100644 --- a/Modules/cjkcodecs/_codecs_cn.c +++ b/Modules/cjkcodecs/_codecs_cn.c @@ -217,11 +217,8 @@ ENCODER(gb18030) break; } - if (utrrange->first == 0) { - PyErr_SetString(PyExc_RuntimeError, - "unicode mapping invalid"); + if (utrrange->first == 0) return 1; - } continue; } diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index f51b6f2..340de18 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -6,6 +6,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "structmember.h" #include "multibytecodec.h" typedef struct { @@ -38,22 +39,14 @@ that encoding errors raise a UnicodeDecodeError. Other possible values\n\ are 'ignore' and 'replace' as well as any other name registerd with\n\ codecs.register_error that is able to handle UnicodeDecodeErrors."); -PyDoc_STRVAR(MultibyteCodec_StreamReader__doc__, -"I.StreamReader(stream[, errors]) -> StreamReader instance"); - -PyDoc_STRVAR(MultibyteCodec_StreamWriter__doc__, -"I.StreamWriter(stream[, errors]) -> StreamWriter instance"); - static char *codeckwarglist[] = {"input", "errors", NULL}; +static char *incnewkwarglist[] = {"errors", NULL}; +static char *incrementalkwarglist[] = {"input", "final", NULL}; static char *streamkwarglist[] = {"stream", "errors", NULL}; static PyObject *multibytecodec_encode(MultibyteCodec *, MultibyteCodec_State *, const Py_UNICODE **, Py_ssize_t, PyObject *, int); -static PyObject *mbstreamreader_create(MultibyteCodec *, - PyObject *, const char *); -static PyObject *mbstreamwriter_create(MultibyteCodec *, - PyObject *, const char *); #define MBENC_RESET MBENC_MAX<<1 /* reset after an encoding session */ @@ -83,7 +76,7 @@ make_tuple(PyObject *object, Py_ssize_t len) } static PyObject * -get_errorcallback(const char *errors) +internal_error_callback(const char *errors) { if (errors == NULL || strcmp(errors, "strict") == 0) return ERROR_STRICT; @@ -91,17 +84,88 @@ get_errorcallback(const char *errors) return ERROR_IGNORE; else if (strcmp(errors, "replace") == 0) return ERROR_REPLACE; + else + return PyString_FromString(errors); +} + +static PyObject * +call_error_callback(PyObject *errors, PyObject *exc) +{ + PyObject *args, *cb, *r; + + assert(PyString_Check(errors)); + cb = PyCodec_LookupError(PyString_AS_STRING(errors)); + if (cb == NULL) + return NULL; + + args = PyTuple_New(1); + if (args == NULL) { + Py_DECREF(cb); + return NULL; + } + + PyTuple_SET_ITEM(args, 0, exc); + Py_INCREF(exc); + + r = PyObject_CallObject(cb, args); + Py_DECREF(args); + Py_DECREF(cb); + return r; +} + +static PyObject * +codecctx_errors_get(MultibyteStatefulCodecContext *self) +{ + const char *errors; + + if (self->errors == ERROR_STRICT) + errors = "strict"; + else if (self->errors == ERROR_IGNORE) + errors = "ignore"; + else if (self->errors == ERROR_REPLACE) + errors = "replace"; else { - return PyCodec_LookupError(errors); + Py_INCREF(self->errors); + return self->errors; + } + + return PyString_FromString(errors); +} + +static int +codecctx_errors_set(MultibyteStatefulCodecContext *self, PyObject *value, + void *closure) +{ + PyObject *cb; + + if (!PyString_Check(value)) { + PyErr_SetString(PyExc_TypeError, "errors must be a string"); + return -1; } + + cb = internal_error_callback(PyString_AS_STRING(value)); + if (cb == NULL) + return -1; + + ERROR_DECREF(self->errors); + self->errors = cb; + return 0; } +/* This getset handlers list is used by all the stateful codec objects */ +static PyGetSetDef codecctx_getsets[] = { + {"errors", (getter)codecctx_errors_get, + (setter)codecctx_errors_set, + PyDoc_STR("how to treat errors")}, + {NULL,} +}; + static int expand_encodebuffer(MultibyteEncodeBuffer *buf, Py_ssize_t esize) { Py_ssize_t orgpos, orgsize; - orgpos = (Py_ssize_t)((char*)buf->outbuf - + orgpos = (Py_ssize_t)((char *)buf->outbuf - PyString_AS_STRING(buf->outobj)); orgsize = PyString_GET_SIZE(buf->outobj); if (_PyString_Resize(&buf->outobj, orgsize + ( @@ -125,8 +189,7 @@ expand_decodebuffer(MultibyteDecodeBuffer *buf, Py_ssize_t esize) { Py_ssize_t orgpos, orgsize; - orgpos = (Py_ssize_t)(buf->outbuf - - PyUnicode_AS_UNICODE(buf->outobj)); + orgpos = (Py_ssize_t)(buf->outbuf - PyUnicode_AS_UNICODE(buf->outobj)); orgsize = PyUnicode_GET_SIZE(buf->outobj); if (PyUnicode_Resize(&buf->outobj, orgsize + ( esize < (orgsize >> 1) ? (orgsize >> 1) | 1 : esize)) == -1) @@ -144,16 +207,21 @@ expand_decodebuffer(MultibyteDecodeBuffer *buf, Py_ssize_t esize) goto errorexit; \ } + +/** + * MultibyteCodec object + */ + static int multibytecodec_encerror(MultibyteCodec *codec, MultibyteCodec_State *state, MultibyteEncodeBuffer *buf, PyObject *errors, Py_ssize_t e) { - PyObject *retobj = NULL, *retstr = NULL, *argsobj, *tobj; + PyObject *retobj = NULL, *retstr = NULL, *tobj; Py_ssize_t retstrsize, newpos; - const char *reason; Py_ssize_t esize, start, end; + const char *reason; if (e > 0) { reason = "illegal multibyte sequence"; @@ -166,7 +234,7 @@ multibytecodec_encerror(MultibyteCodec *codec, return 0; /* retry it */ case MBERR_TOOFEW: reason = "incomplete multibyte sequence"; - esize = (size_t)(buf->inbuf_end - buf->inbuf); + esize = (Py_ssize_t)(buf->inbuf_end - buf->inbuf); break; case MBERR_INTERNAL: PyErr_SetString(PyExc_RuntimeError, @@ -230,21 +298,15 @@ multibytecodec_encerror(MultibyteCodec *codec, goto errorexit; } - argsobj = PyTuple_New(1); - if (argsobj == NULL) - goto errorexit; - - PyTuple_SET_ITEM(argsobj, 0, buf->excobj); - Py_INCREF(buf->excobj); - retobj = PyObject_CallObject(errors, argsobj); - Py_DECREF(argsobj); + retobj = call_error_callback(errors, buf->excobj); if (retobj == NULL) goto errorexit; if (!PyTuple_Check(retobj) || PyTuple_GET_SIZE(retobj) != 2 || !PyUnicode_Check((tobj = PyTuple_GET_ITEM(retobj, 0))) || - !PyInt_Check(PyTuple_GET_ITEM(retobj, 1))) { - PyErr_SetString(PyExc_ValueError, + !(PyInt_Check(PyTuple_GET_ITEM(retobj, 1)) || + PyLong_Check(PyTuple_GET_ITEM(retobj, 1)))) { + PyErr_SetString(PyExc_TypeError, "encoding error handler must return " "(unicode, int) tuple"); goto errorexit; @@ -267,12 +329,13 @@ multibytecodec_encerror(MultibyteCodec *codec, buf->outbuf += retstrsize; newpos = PyInt_AsSsize_t(PyTuple_GET_ITEM(retobj, 1)); - if (newpos < 0) + if (newpos < 0 && !PyErr_Occurred()) newpos += (Py_ssize_t)(buf->inbuf_end - buf->inbuf_top); if (newpos < 0 || buf->inbuf_top + newpos > buf->inbuf_end) { + PyErr_Clear(); PyErr_Format(PyExc_IndexError, - "position %d from error handler out of bounds", - (int)newpos); + "position %zd from error handler out of bounds", + newpos); goto errorexit; } buf->inbuf = buf->inbuf_top + newpos; @@ -293,7 +356,7 @@ multibytecodec_decerror(MultibyteCodec *codec, MultibyteDecodeBuffer *buf, PyObject *errors, Py_ssize_t e) { - PyObject *argsobj, *retobj = NULL, *retuni = NULL; + PyObject *retobj = NULL, *retuni = NULL; Py_ssize_t retunisize, newpos; const char *reason; Py_ssize_t esize, start, end; @@ -309,7 +372,7 @@ multibytecodec_decerror(MultibyteCodec *codec, return 0; /* retry it */ case MBERR_TOOFEW: reason = "incomplete multibyte sequence"; - esize = (size_t)(buf->inbuf_end - buf->inbuf); + esize = (Py_ssize_t)(buf->inbuf_end - buf->inbuf); break; case MBERR_INTERNAL: PyErr_SetString(PyExc_RuntimeError, @@ -354,21 +417,15 @@ multibytecodec_decerror(MultibyteCodec *codec, goto errorexit; } - argsobj = PyTuple_New(1); - if (argsobj == NULL) - goto errorexit; - - PyTuple_SET_ITEM(argsobj, 0, buf->excobj); - Py_INCREF(buf->excobj); - retobj = PyObject_CallObject(errors, argsobj); - Py_DECREF(argsobj); + retobj = call_error_callback(errors, buf->excobj); if (retobj == NULL) goto errorexit; if (!PyTuple_Check(retobj) || PyTuple_GET_SIZE(retobj) != 2 || !PyUnicode_Check((retuni = PyTuple_GET_ITEM(retobj, 0))) || - !PyInt_Check(PyTuple_GET_ITEM(retobj, 1))) { - PyErr_SetString(PyExc_ValueError, + !(PyInt_Check(PyTuple_GET_ITEM(retobj, 1)) || + PyLong_Check(PyTuple_GET_ITEM(retobj, 1)))) { + PyErr_SetString(PyExc_TypeError, "decoding error handler must return " "(unicode, int) tuple"); goto errorexit; @@ -383,12 +440,13 @@ multibytecodec_decerror(MultibyteCodec *codec, } newpos = PyInt_AsSsize_t(PyTuple_GET_ITEM(retobj, 1)); - if (newpos < 0) + if (newpos < 0 && !PyErr_Occurred()) newpos += (Py_ssize_t)(buf->inbuf_end - buf->inbuf_top); if (newpos < 0 || buf->inbuf_top + newpos > buf->inbuf_end) { + PyErr_Clear(); PyErr_Format(PyExc_IndexError, - "position %d from error handler out of bounds", - (int)newpos); + "position %zd from error handler out of bounds", + newpos); goto errorexit; } buf->inbuf = buf->inbuf_top + newpos; @@ -453,7 +511,7 @@ multibytecodec_encode(MultibyteCodec *codec, goto errorexit; } - finalsize = (Py_ssize_t)((char*)buf.outbuf - + finalsize = (Py_ssize_t)((char *)buf.outbuf - PyString_AS_STRING(buf.outobj)); if (finalsize != PyString_GET_SIZE(buf.outobj)) @@ -500,7 +558,7 @@ MultibyteCodec_Encode(MultibyteCodecObject *self, data = PyUnicode_AS_UNICODE(arg); datalen = PyUnicode_GET_SIZE(arg); - errorcb = get_errorcallback(errors); + errorcb = internal_error_callback(errors); if (errorcb == NULL) { Py_XDECREF(ucvt); return NULL; @@ -515,16 +573,12 @@ MultibyteCodec_Encode(MultibyteCodecObject *self, if (r == NULL) goto errorexit; - if (errorcb > ERROR_MAX) { - Py_DECREF(errorcb); - } + ERROR_DECREF(errorcb); Py_XDECREF(ucvt); return make_tuple(r, datalen); errorexit: - if (errorcb > ERROR_MAX) { - Py_DECREF(errorcb); - } + ERROR_DECREF(errorcb); Py_XDECREF(ucvt); return NULL; } @@ -543,18 +597,16 @@ MultibyteCodec_Decode(MultibyteCodecObject *self, codeckwarglist, &data, &datalen, &errors)) return NULL; - errorcb = get_errorcallback(errors); + errorcb = internal_error_callback(errors); if (errorcb == NULL) return NULL; if (datalen == 0) { - if (errorcb > ERROR_MAX) { - Py_DECREF(errorcb); - } + ERROR_DECREF(errorcb); return make_tuple(PyUnicode_FromUnicode(NULL, 0), 0); } - buf.outobj = buf.excobj = NULL; + buf.excobj = NULL; buf.inbuf = buf.inbuf_top = (unsigned char *)data; buf.inbuf_end = buf.inbuf_top + datalen; buf.outobj = PyUnicode_FromUnicode(NULL, datalen); @@ -590,49 +642,17 @@ MultibyteCodec_Decode(MultibyteCodecObject *self, goto errorexit; Py_XDECREF(buf.excobj); - if (errorcb > ERROR_MAX) { - Py_DECREF(errorcb); - } + ERROR_DECREF(errorcb); return make_tuple(buf.outobj, datalen); errorexit: - if (errorcb > ERROR_MAX) { - Py_DECREF(errorcb); - } + ERROR_DECREF(errorcb); Py_XDECREF(buf.excobj); Py_XDECREF(buf.outobj); return NULL; } -static PyObject * -MultibyteCodec_StreamReader(MultibyteCodecObject *self, - PyObject *args, PyObject *kwargs) -{ - PyObject *stream; - char *errors = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|s:StreamReader", - streamkwarglist, &stream, &errors)) - return NULL; - - return mbstreamreader_create(self->codec, stream, errors); -} - -static PyObject * -MultibyteCodec_StreamWriter(MultibyteCodecObject *self, - PyObject *args, PyObject *kwargs) -{ - PyObject *stream; - char *errors = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|s:StreamWriter", - streamkwarglist, &stream, &errors)) - return NULL; - - return mbstreamwriter_create(self->codec, stream, errors); -} - static struct PyMethodDef multibytecodec_methods[] = { {"encode", (PyCFunction)MultibyteCodec_Encode, METH_VARARGS | METH_KEYWORDS, @@ -640,12 +660,6 @@ static struct PyMethodDef multibytecodec_methods[] = { {"decode", (PyCFunction)MultibyteCodec_Decode, METH_VARARGS | METH_KEYWORDS, MultibyteCodec_Decode__doc__}, - {"StreamReader",(PyCFunction)MultibyteCodec_StreamReader, - METH_VARARGS | METH_KEYWORDS, - MultibyteCodec_StreamReader__doc__}, - {"StreamWriter",(PyCFunction)MultibyteCodec_StreamWriter, - METH_VARARGS | METH_KEYWORDS, - MultibyteCodec_StreamWriter__doc__}, {NULL, NULL}, }; @@ -655,8 +669,6 @@ multibytecodec_dealloc(MultibyteCodecObject *self) PyObject_Del(self); } - - static PyTypeObject MultibyteCodec_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ @@ -690,244 +702,748 @@ static PyTypeObject MultibyteCodec_Type = { multibytecodec_methods, /* tp_methods */ }; -static PyObject * -mbstreamreader_iread(MultibyteStreamReaderObject *self, - const char *method, Py_ssize_t sizehint) -{ - MultibyteDecodeBuffer buf; - PyObject *cres; - Py_ssize_t rsize, r, finalsize = 0; - if (sizehint == 0) - return PyUnicode_FromUnicode(NULL, 0); +/** + * Utility functions for stateful codec mechanism + */ - buf.outobj = buf.excobj = NULL; - cres = NULL; +#define STATEFUL_DCTX(o) ((MultibyteStatefulDecoderContext *)(o)) +#define STATEFUL_ECTX(o) ((MultibyteStatefulEncoderContext *)(o)) - for (;;) { - if (sizehint < 0) - cres = PyObject_CallMethod(self->stream, - (char *)method, NULL); - else - cres = PyObject_CallMethod(self->stream, - (char *)method, "i", sizehint); - if (cres == NULL) - goto errorexit; +static PyObject * +encoder_encode_stateful(MultibyteStatefulEncoderContext *ctx, + PyObject *unistr, int final) +{ + PyObject *ucvt, *r = NULL; + Py_UNICODE *inbuf, *inbuf_end, *inbuf_tmp = NULL; + Py_ssize_t datalen, origpending; - if (!PyString_Check(cres)) { + if (PyUnicode_Check(unistr)) + ucvt = NULL; + else { + unistr = ucvt = PyObject_Unicode(unistr); + if (unistr == NULL) + return NULL; + else if (!PyUnicode_Check(unistr)) { PyErr_SetString(PyExc_TypeError, - "stream function returned a " - "non-string object"); - goto errorexit; - } - - if (self->pendingsize > 0) { - PyObject *ctr; - char *ctrdata; - - rsize = PyString_GET_SIZE(cres) + self->pendingsize; - ctr = PyString_FromStringAndSize(NULL, rsize); - if (ctr == NULL) - goto errorexit; - ctrdata = PyString_AS_STRING(ctr); - memcpy(ctrdata, self->pending, self->pendingsize); - memcpy(ctrdata + self->pendingsize, - PyString_AS_STRING(cres), - PyString_GET_SIZE(cres)); - Py_DECREF(cres); - cres = ctr; - self->pendingsize = 0; - } - - rsize = PyString_GET_SIZE(cres); - buf.inbuf = buf.inbuf_top = - (unsigned char *)PyString_AS_STRING(cres); - buf.inbuf_end = buf.inbuf_top + rsize; - if (buf.outobj == NULL) { - buf.outobj = PyUnicode_FromUnicode(NULL, rsize); - if (buf.outobj == NULL) - goto errorexit; - buf.outbuf = PyUnicode_AS_UNICODE(buf.outobj); - buf.outbuf_end = buf.outbuf + - PyUnicode_GET_SIZE(buf.outobj); - } - - r = 0; - if (rsize > 0) - while (buf.inbuf < buf.inbuf_end) { - Py_ssize_t inleft, outleft; - - inleft = (Py_ssize_t)(buf.inbuf_end - - buf.inbuf); - outleft = (Py_ssize_t)(buf.outbuf_end - - buf.outbuf); - - r = self->codec->decode(&self->state, - self->codec->config, - &buf.inbuf, inleft, - &buf.outbuf, outleft); - if (r == 0 || r == MBERR_TOOFEW) - break; - else if (multibytecodec_decerror(self->codec, - &self->state, &buf, - self->errors, r)) - goto errorexit; - } - - if (rsize == 0 || sizehint < 0) { /* end of file */ - if (buf.inbuf < buf.inbuf_end && - multibytecodec_decerror(self->codec, &self->state, - &buf, self->errors, MBERR_TOOFEW)) - goto errorexit; + "couldn't convert the object to unicode."); + Py_DECREF(ucvt); + return NULL; } + } - if (buf.inbuf < buf.inbuf_end) { /* pending sequence exists */ - Py_ssize_t npendings; - - /* we can't assume that pendingsize is still 0 here. - * because this function can be called recursively - * from error callback */ - npendings = (Py_ssize_t)(buf.inbuf_end - buf.inbuf); - if (npendings + self->pendingsize > MAXDECPENDING) { - PyErr_SetString(PyExc_RuntimeError, - "pending buffer overflow"); - goto errorexit; - } - memcpy(self->pending + self->pendingsize, buf.inbuf, - npendings); - self->pendingsize += npendings; - } + datalen = PyUnicode_GET_SIZE(unistr); + origpending = ctx->pendingsize; - finalsize = (Py_ssize_t)(buf.outbuf - - PyUnicode_AS_UNICODE(buf.outobj)); - Py_DECREF(cres); - cres = NULL; + if (origpending > 0) { + inbuf_tmp = PyMem_New(Py_UNICODE, datalen + ctx->pendingsize); + if (inbuf_tmp == NULL) + goto errorexit; + memcpy(inbuf_tmp, ctx->pending, + Py_UNICODE_SIZE * ctx->pendingsize); + memcpy(inbuf_tmp + ctx->pendingsize, + PyUnicode_AS_UNICODE(unistr), + Py_UNICODE_SIZE * datalen); + datalen += ctx->pendingsize; + ctx->pendingsize = 0; + inbuf = inbuf_tmp; + } + else + inbuf = (Py_UNICODE *)PyUnicode_AS_UNICODE(unistr); - if (sizehint < 0 || finalsize != 0 || rsize == 0) - break; + inbuf_end = inbuf + datalen; - sizehint = 1; /* read 1 more byte and retry */ + r = multibytecodec_encode(ctx->codec, &ctx->state, + (const Py_UNICODE **)&inbuf, + datalen, ctx->errors, final ? MBENC_FLUSH : 0); + if (r == NULL) { + /* recover the original pending buffer */ + if (origpending > 0) + memcpy(ctx->pending, inbuf_tmp, + Py_UNICODE_SIZE * origpending); + ctx->pendingsize = origpending; + goto errorexit; } - if (finalsize != PyUnicode_GET_SIZE(buf.outobj)) - if (PyUnicode_Resize(&buf.outobj, finalsize) == -1) + if (inbuf < inbuf_end) { + ctx->pendingsize = (Py_ssize_t)(inbuf_end - inbuf); + if (ctx->pendingsize > MAXENCPENDING) { + /* normal codecs can't reach here */ + ctx->pendingsize = 0; + PyErr_SetString(PyExc_UnicodeError, + "pending buffer overflow"); goto errorexit; + } + memcpy(ctx->pending, inbuf, + ctx->pendingsize * Py_UNICODE_SIZE); + } - Py_XDECREF(cres); - Py_XDECREF(buf.excobj); - return buf.outobj; + if (inbuf_tmp != NULL) + PyMem_Del(inbuf_tmp); + Py_XDECREF(ucvt); + return r; errorexit: - Py_XDECREF(cres); - Py_XDECREF(buf.excobj); - Py_XDECREF(buf.outobj); + if (inbuf_tmp != NULL) + PyMem_Del(inbuf_tmp); + Py_XDECREF(r); + Py_XDECREF(ucvt); return NULL; } -static PyObject * -mbstreamreader_read(MultibyteStreamReaderObject *self, PyObject *args) +static int +decoder_append_pending(MultibyteStatefulDecoderContext *ctx, + MultibyteDecodeBuffer *buf) { - PyObject *sizeobj = NULL; - Py_ssize_t size; + Py_ssize_t npendings; - if (!PyArg_ParseTuple(args, "|O:read", &sizeobj)) - return NULL; + npendings = (Py_ssize_t)(buf->inbuf_end - buf->inbuf); + if (npendings + ctx->pendingsize > MAXDECPENDING) { + PyErr_SetString(PyExc_UnicodeError, "pending buffer overflow"); + return -1; + } + memcpy(ctx->pending + ctx->pendingsize, buf->inbuf, npendings); + ctx->pendingsize += npendings; + return 0; +} - if (sizeobj == Py_None || sizeobj == NULL) - size = -1; - else if (PyInt_Check(sizeobj)) - size = PyInt_AsSsize_t(sizeobj); - else { - PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); - return NULL; +static int +decoder_prepare_buffer(MultibyteDecodeBuffer *buf, const char *data, + Py_ssize_t size) +{ + buf->inbuf = buf->inbuf_top = (const unsigned char *)data; + buf->inbuf_end = buf->inbuf_top + size; + if (buf->outobj == NULL) { /* only if outobj is not allocated yet */ + buf->outobj = PyUnicode_FromUnicode(NULL, size); + if (buf->outobj == NULL) + return -1; + buf->outbuf = PyUnicode_AS_UNICODE(buf->outobj); + buf->outbuf_end = buf->outbuf + + PyUnicode_GET_SIZE(buf->outobj); } - return mbstreamreader_iread(self, "read", size); + return 0; } -static PyObject * -mbstreamreader_readline(MultibyteStreamReaderObject *self, PyObject *args) +static int +decoder_feed_buffer(MultibyteStatefulDecoderContext *ctx, + MultibyteDecodeBuffer *buf) { - PyObject *sizeobj = NULL; - Py_ssize_t size; + while (buf->inbuf < buf->inbuf_end) { + Py_ssize_t inleft, outleft; + int r; - if (!PyArg_ParseTuple(args, "|O:readline", &sizeobj)) - return NULL; + inleft = (Py_ssize_t)(buf->inbuf_end - buf->inbuf); + outleft = (Py_ssize_t)(buf->outbuf_end - buf->outbuf); - if (sizeobj == Py_None || sizeobj == NULL) - size = -1; - else if (PyInt_Check(sizeobj)) - size = PyInt_AsSsize_t(sizeobj); - else { - PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); - return NULL; + r = ctx->codec->decode(&ctx->state, ctx->codec->config, + &buf->inbuf, inleft, &buf->outbuf, outleft); + if (r == 0 || r == MBERR_TOOFEW) + break; + else if (multibytecodec_decerror(ctx->codec, &ctx->state, + buf, ctx->errors, r)) + return -1; } - - return mbstreamreader_iread(self, "readline", size); + return 0; } -static PyObject * -mbstreamreader_readlines(MultibyteStreamReaderObject *self, PyObject *args) -{ - PyObject *sizehintobj = NULL, *r, *sr; - Py_ssize_t sizehint; - if (!PyArg_ParseTuple(args, "|O:readlines", &sizehintobj)) - return NULL; +/** + * MultibyteIncrementalEncoder object + */ - if (sizehintobj == Py_None || sizehintobj == NULL) - sizehint = -1; - else if (PyInt_Check(sizehintobj)) - sizehint = PyInt_AsSsize_t(sizehintobj); - else { - PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); - return NULL; - } +static PyObject * +mbiencoder_encode(MultibyteIncrementalEncoderObject *self, + PyObject *args, PyObject *kwargs) +{ + PyObject *data; + int final = 0; - r = mbstreamreader_iread(self, "read", sizehint); - if (r == NULL) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:encode", + incrementalkwarglist, &data, &final)) return NULL; - sr = PyUnicode_Splitlines(r, 1); - Py_DECREF(r); - return sr; + return encoder_encode_stateful(STATEFUL_ECTX(self), data, final); } static PyObject * -mbstreamreader_reset(MultibyteStreamReaderObject *self) +mbiencoder_reset(MultibyteIncrementalEncoderObject *self) { if (self->codec->decreset != NULL && self->codec->decreset(&self->state, self->codec->config) != 0) return NULL; self->pendingsize = 0; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -static struct PyMethodDef mbstreamreader_methods[] = { - {"read", (PyCFunction)mbstreamreader_read, - METH_VARARGS, NULL}, - {"readline", (PyCFunction)mbstreamreader_readline, - METH_VARARGS, NULL}, - {"readlines", (PyCFunction)mbstreamreader_readlines, - METH_VARARGS, NULL}, +static struct PyMethodDef mbiencoder_methods[] = { + {"encode", (PyCFunction)mbiencoder_encode, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"reset", (PyCFunction)mbiencoder_reset, + METH_NOARGS, NULL}, + {NULL, NULL}, +}; + +static PyObject * +mbiencoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + MultibyteIncrementalEncoderObject *self; + PyObject *codec = NULL; + char *errors = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s:IncrementalEncoder", + incnewkwarglist, &errors)) + return NULL; + + self = (MultibyteIncrementalEncoderObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + codec = PyObject_GetAttrString((PyObject *)type, "codec"); + if (codec == NULL) + goto errorexit; + if (!MultibyteCodec_Check(codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; + } + + self->codec = ((MultibyteCodecObject *)codec)->codec; + self->pendingsize = 0; + self->errors = internal_error_callback(errors); + if (self->errors == NULL) + goto errorexit; + if (self->codec->encinit != NULL && + self->codec->encinit(&self->state, self->codec->config) != 0) + goto errorexit; + + Py_DECREF(codec); + return (PyObject *)self; + +errorexit: + Py_XDECREF(self); + Py_XDECREF(codec); + return NULL; +} + +static int +mbiencoder_traverse(MultibyteIncrementalEncoderObject *self, + visitproc visit, void *arg) +{ + if (ERROR_ISCUSTOM(self->errors)) + Py_VISIT(self->errors); + return 0; +} + +static void +mbiencoder_dealloc(MultibyteIncrementalEncoderObject *self) +{ + PyObject_GC_UnTrack(self); + ERROR_DECREF(self->errors); + self->ob_type->tp_free(self); +} + +static PyTypeObject MultibyteIncrementalEncoder_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "MultibyteIncrementalEncoder", /* tp_name */ + sizeof(MultibyteIncrementalEncoderObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)mbiencoder_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 */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)mbiencoder_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iterext */ + mbiencoder_methods, /* tp_methods */ + 0, /* tp_members */ + codecctx_getsets, /* 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 */ + mbiencoder_new, /* tp_new */ +}; + + +/** + * MultibyteIncrementalDecoder object + */ + +static PyObject * +mbidecoder_decode(MultibyteIncrementalDecoderObject *self, + PyObject *args, PyObject *kwargs) +{ + MultibyteDecodeBuffer buf; + char *data, *wdata; + Py_ssize_t wsize, finalsize = 0, size, origpending; + int final = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "t#|i:decode", + incrementalkwarglist, &data, &size, &final)) + return NULL; + + buf.outobj = buf.excobj = NULL; + origpending = self->pendingsize; + + if (self->pendingsize == 0) { + wsize = size; + wdata = data; + } + else { + wsize = size + self->pendingsize; + wdata = PyMem_Malloc(wsize); + if (wdata == NULL) + goto errorexit; + memcpy(wdata, self->pending, self->pendingsize); + memcpy(wdata + self->pendingsize, data, size); + self->pendingsize = 0; + } + + if (decoder_prepare_buffer(&buf, wdata, wsize) != 0) + goto errorexit; + + if (decoder_feed_buffer(STATEFUL_DCTX(self), &buf)) + goto errorexit; + + if (final && buf.inbuf < buf.inbuf_end) { + if (multibytecodec_decerror(self->codec, &self->state, + &buf, self->errors, MBERR_TOOFEW)) { + /* recover the original pending buffer */ + memcpy(self->pending, wdata, origpending); + self->pendingsize = origpending; + goto errorexit; + } + } + + if (buf.inbuf < buf.inbuf_end) { /* pending sequence still exists */ + if (decoder_append_pending(STATEFUL_DCTX(self), &buf) != 0) + goto errorexit; + } + + finalsize = (Py_ssize_t)(buf.outbuf - PyUnicode_AS_UNICODE(buf.outobj)); + if (finalsize != PyUnicode_GET_SIZE(buf.outobj)) + if (PyUnicode_Resize(&buf.outobj, finalsize) == -1) + goto errorexit; + + if (wdata != data) + PyMem_Del(wdata); + Py_XDECREF(buf.excobj); + return buf.outobj; + +errorexit: + if (wdata != NULL && wdata != data) + PyMem_Del(wdata); + Py_XDECREF(buf.excobj); + Py_XDECREF(buf.outobj); + return NULL; +} + +static PyObject * +mbidecoder_reset(MultibyteIncrementalDecoderObject *self) +{ + if (self->codec->decreset != NULL && + self->codec->decreset(&self->state, self->codec->config) != 0) + return NULL; + self->pendingsize = 0; + + Py_RETURN_NONE; +} + +static struct PyMethodDef mbidecoder_methods[] = { + {"decode", (PyCFunction)mbidecoder_decode, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"reset", (PyCFunction)mbidecoder_reset, + METH_NOARGS, NULL}, + {NULL, NULL}, +}; + +static PyObject * +mbidecoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + MultibyteIncrementalDecoderObject *self; + PyObject *codec = NULL; + char *errors = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s:IncrementalDecoder", + incnewkwarglist, &errors)) + return NULL; + + self = (MultibyteIncrementalDecoderObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + codec = PyObject_GetAttrString((PyObject *)type, "codec"); + if (codec == NULL) + goto errorexit; + if (!MultibyteCodec_Check(codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; + } + + self->codec = ((MultibyteCodecObject *)codec)->codec; + self->pendingsize = 0; + self->errors = internal_error_callback(errors); + if (self->errors == NULL) + goto errorexit; + if (self->codec->decinit != NULL && + self->codec->decinit(&self->state, self->codec->config) != 0) + goto errorexit; + + Py_DECREF(codec); + return (PyObject *)self; + +errorexit: + Py_XDECREF(self); + Py_XDECREF(codec); + return NULL; +} + +static int +mbidecoder_traverse(MultibyteIncrementalDecoderObject *self, + visitproc visit, void *arg) +{ + if (ERROR_ISCUSTOM(self->errors)) + Py_VISIT(self->errors); + return 0; +} + +static void +mbidecoder_dealloc(MultibyteIncrementalDecoderObject *self) +{ + PyObject_GC_UnTrack(self); + ERROR_DECREF(self->errors); + self->ob_type->tp_free(self); +} + +static PyTypeObject MultibyteIncrementalDecoder_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "MultibyteIncrementalDecoder", /* tp_name */ + sizeof(MultibyteIncrementalDecoderObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)mbidecoder_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 */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)mbidecoder_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iterext */ + mbidecoder_methods, /* tp_methods */ + 0, /* tp_members */ + codecctx_getsets, /* 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 */ + mbidecoder_new, /* tp_new */ +}; + + +/** + * MultibyteStreamReader object + */ + +static PyObject * +mbstreamreader_iread(MultibyteStreamReaderObject *self, + const char *method, Py_ssize_t sizehint) +{ + MultibyteDecodeBuffer buf; + PyObject *cres; + Py_ssize_t rsize, finalsize = 0; + + if (sizehint == 0) + return PyUnicode_FromUnicode(NULL, 0); + + buf.outobj = buf.excobj = NULL; + cres = NULL; + + for (;;) { + if (sizehint < 0) + cres = PyObject_CallMethod(self->stream, + (char *)method, NULL); + else + cres = PyObject_CallMethod(self->stream, + (char *)method, "i", sizehint); + if (cres == NULL) + goto errorexit; + + if (!PyString_Check(cres)) { + PyErr_SetString(PyExc_TypeError, + "stream function returned a " + "non-string object"); + goto errorexit; + } + + if (self->pendingsize > 0) { + PyObject *ctr; + char *ctrdata; + + rsize = PyString_GET_SIZE(cres) + self->pendingsize; + ctr = PyString_FromStringAndSize(NULL, rsize); + if (ctr == NULL) + goto errorexit; + ctrdata = PyString_AS_STRING(ctr); + memcpy(ctrdata, self->pending, self->pendingsize); + memcpy(ctrdata + self->pendingsize, + PyString_AS_STRING(cres), + PyString_GET_SIZE(cres)); + Py_DECREF(cres); + cres = ctr; + self->pendingsize = 0; + } + + rsize = PyString_GET_SIZE(cres); + if (decoder_prepare_buffer(&buf, PyString_AS_STRING(cres), + rsize) != 0) + goto errorexit; + + if (rsize > 0 && decoder_feed_buffer( + (MultibyteStatefulDecoderContext *)self, &buf)) + goto errorexit; + + if (rsize == 0 || sizehint < 0) { /* end of file */ + if (buf.inbuf < buf.inbuf_end && + multibytecodec_decerror(self->codec, &self->state, + &buf, self->errors, MBERR_TOOFEW)) + goto errorexit; + } + + if (buf.inbuf < buf.inbuf_end) { /* pending sequence exists */ + if (decoder_append_pending(STATEFUL_DCTX(self), + &buf) != 0) + goto errorexit; + } + + finalsize = (Py_ssize_t)(buf.outbuf - + PyUnicode_AS_UNICODE(buf.outobj)); + Py_DECREF(cres); + cres = NULL; + + if (sizehint < 0 || finalsize != 0 || rsize == 0) + break; + + sizehint = 1; /* read 1 more byte and retry */ + } + + if (finalsize != PyUnicode_GET_SIZE(buf.outobj)) + if (PyUnicode_Resize(&buf.outobj, finalsize) == -1) + goto errorexit; + + Py_XDECREF(cres); + Py_XDECREF(buf.excobj); + return buf.outobj; + +errorexit: + Py_XDECREF(cres); + Py_XDECREF(buf.excobj); + Py_XDECREF(buf.outobj); + return NULL; +} + +static PyObject * +mbstreamreader_read(MultibyteStreamReaderObject *self, PyObject *args) +{ + PyObject *sizeobj = NULL; + Py_ssize_t size; + + if (!PyArg_ParseTuple(args, "|O:read", &sizeobj)) + return NULL; + + if (sizeobj == Py_None || sizeobj == NULL) + size = -1; + else if (PyInt_Check(sizeobj)) + size = PyInt_AsSsize_t(sizeobj); + else { + PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); + return NULL; + } + + return mbstreamreader_iread(self, "read", size); +} + +static PyObject * +mbstreamreader_readline(MultibyteStreamReaderObject *self, PyObject *args) +{ + PyObject *sizeobj = NULL; + Py_ssize_t size; + + if (!PyArg_ParseTuple(args, "|O:readline", &sizeobj)) + return NULL; + + if (sizeobj == Py_None || sizeobj == NULL) + size = -1; + else if (PyInt_Check(sizeobj)) + size = PyInt_AsSsize_t(sizeobj); + else { + PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); + return NULL; + } + + return mbstreamreader_iread(self, "readline", size); +} + +static PyObject * +mbstreamreader_readlines(MultibyteStreamReaderObject *self, PyObject *args) +{ + PyObject *sizehintobj = NULL, *r, *sr; + Py_ssize_t sizehint; + + if (!PyArg_ParseTuple(args, "|O:readlines", &sizehintobj)) + return NULL; + + if (sizehintobj == Py_None || sizehintobj == NULL) + sizehint = -1; + else if (PyInt_Check(sizehintobj)) + sizehint = PyInt_AsSsize_t(sizehintobj); + else { + PyErr_SetString(PyExc_TypeError, "arg 1 must be an integer"); + return NULL; + } + + r = mbstreamreader_iread(self, "read", sizehint); + if (r == NULL) + return NULL; + + sr = PyUnicode_Splitlines(r, 1); + Py_DECREF(r); + return sr; +} + +static PyObject * +mbstreamreader_reset(MultibyteStreamReaderObject *self) +{ + if (self->codec->decreset != NULL && + self->codec->decreset(&self->state, self->codec->config) != 0) + return NULL; + self->pendingsize = 0; + + Py_RETURN_NONE; +} + +static struct PyMethodDef mbstreamreader_methods[] = { + {"read", (PyCFunction)mbstreamreader_read, + METH_VARARGS, NULL}, + {"readline", (PyCFunction)mbstreamreader_readline, + METH_VARARGS, NULL}, + {"readlines", (PyCFunction)mbstreamreader_readlines, + METH_VARARGS, NULL}, {"reset", (PyCFunction)mbstreamreader_reset, METH_NOARGS, NULL}, {NULL, NULL}, }; -static void -mbstreamreader_dealloc(MultibyteStreamReaderObject *self) +static PyMemberDef mbstreamreader_members[] = { + {"stream", T_OBJECT, + offsetof(MultibyteStreamReaderObject, stream), + READONLY, NULL}, + {NULL,} +}; + +static PyObject * +mbstreamreader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (self->errors > ERROR_MAX) { - Py_DECREF(self->errors); + MultibyteStreamReaderObject *self; + PyObject *stream, *codec = NULL; + char *errors = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:StreamReader", + streamkwarglist, &stream, &errors)) + return NULL; + + self = (MultibyteStreamReaderObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + codec = PyObject_GetAttrString((PyObject *)type, "codec"); + if (codec == NULL) + goto errorexit; + if (!MultibyteCodec_Check(codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; } - Py_DECREF(self->stream); - PyObject_Del(self); + + self->codec = ((MultibyteCodecObject *)codec)->codec; + self->stream = stream; + Py_INCREF(stream); + self->pendingsize = 0; + self->errors = internal_error_callback(errors); + if (self->errors == NULL) + goto errorexit; + if (self->codec->decinit != NULL && + self->codec->decinit(&self->state, self->codec->config) != 0) + goto errorexit; + + Py_DECREF(codec); + return (PyObject *)self; + +errorexit: + Py_XDECREF(self); + Py_XDECREF(codec); + return NULL; } +static int +mbstreamreader_traverse(MultibyteStreamReaderObject *self, + visitproc visit, void *arg) +{ + if (ERROR_ISCUSTOM(self->errors)) + Py_VISIT(self->errors); + Py_VISIT(self->stream); + return 0; +} +static void +mbstreamreader_dealloc(MultibyteStreamReaderObject *self) +{ + PyObject_GC_UnTrack(self); + ERROR_DECREF(self->errors); + Py_DECREF(self->stream); + self->ob_type->tp_free(self); +} static PyTypeObject MultibyteStreamReader_Type = { PyObject_HEAD_INIT(NULL) @@ -951,97 +1467,50 @@ static PyTypeObject MultibyteStreamReader_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)mbstreamreader_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iterext */ mbstreamreader_methods, /* tp_methods */ + mbstreamreader_members, /* tp_members */ + codecctx_getsets, /* 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 */ + mbstreamreader_new, /* tp_new */ }; + +/** + * MultibyteStreamWriter object + */ + static int mbstreamwriter_iwrite(MultibyteStreamWriterObject *self, PyObject *unistr) { - PyObject *wr, *ucvt, *r = NULL; - Py_UNICODE *inbuf, *inbuf_end, *inbuf_tmp = NULL; - Py_ssize_t datalen; - - if (PyUnicode_Check(unistr)) - ucvt = NULL; - else { - unistr = ucvt = PyObject_Unicode(unistr); - if (unistr == NULL) - return -1; - else if (!PyUnicode_Check(unistr)) { - PyErr_SetString(PyExc_TypeError, - "couldn't convert the object to unicode."); - Py_DECREF(ucvt); - return -1; - } - } - - datalen = PyUnicode_GET_SIZE(unistr); - if (datalen == 0) { - Py_XDECREF(ucvt); - return 0; - } - - if (self->pendingsize > 0) { - inbuf_tmp = PyMem_New(Py_UNICODE, datalen + self->pendingsize); - if (inbuf_tmp == NULL) - goto errorexit; - memcpy(inbuf_tmp, self->pending, - Py_UNICODE_SIZE * self->pendingsize); - memcpy(inbuf_tmp + self->pendingsize, - PyUnicode_AS_UNICODE(unistr), - Py_UNICODE_SIZE * datalen); - datalen += self->pendingsize; - self->pendingsize = 0; - inbuf = inbuf_tmp; - } - else - inbuf = (Py_UNICODE *)PyUnicode_AS_UNICODE(unistr); - - inbuf_end = inbuf + datalen; + PyObject *str, *wr; - r = multibytecodec_encode(self->codec, &self->state, - (const Py_UNICODE **)&inbuf, datalen, self->errors, 0); - if (r == NULL) - goto errorexit; - - if (inbuf < inbuf_end) { - self->pendingsize = (Py_ssize_t)(inbuf_end - inbuf); - if (self->pendingsize > MAXENCPENDING) { - self->pendingsize = 0; - PyErr_SetString(PyExc_RuntimeError, - "pending buffer overflow"); - goto errorexit; - } - memcpy(self->pending, inbuf, - self->pendingsize * Py_UNICODE_SIZE); - } + str = encoder_encode_stateful(STATEFUL_ECTX(self), unistr, 0); + if (str == NULL) + return -1; - wr = PyObject_CallMethod(self->stream, "write", "O", r); + wr = PyObject_CallMethod(self->stream, "write", "O", str); + Py_DECREF(str); if (wr == NULL) - goto errorexit; + return -1; - if (inbuf_tmp != NULL) - PyMem_Del(inbuf_tmp); - Py_DECREF(r); Py_DECREF(wr); - Py_XDECREF(ucvt); return 0; - -errorexit: - if (inbuf_tmp != NULL) - PyMem_Del(inbuf_tmp); - Py_XDECREF(r); - Py_XDECREF(ucvt); - return -1; } static PyObject * @@ -1054,10 +1523,8 @@ mbstreamwriter_write(MultibyteStreamWriterObject *self, PyObject *args) if (mbstreamwriter_iwrite(self, strobj)) return NULL; - else { - Py_INCREF(Py_None); - return Py_None; - } + else + Py_RETURN_NONE; } static PyObject * @@ -1087,8 +1554,7 @@ mbstreamwriter_writelines(MultibyteStreamWriterObject *self, PyObject *args) return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject * @@ -1119,18 +1585,69 @@ mbstreamwriter_reset(MultibyteStreamWriterObject *self) } Py_DECREF(pwrt); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; +} + +static PyObject * +mbstreamwriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + MultibyteStreamWriterObject *self; + PyObject *stream, *codec = NULL; + char *errors = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:StreamWriter", + streamkwarglist, &stream, &errors)) + return NULL; + + self = (MultibyteStreamWriterObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + codec = PyObject_GetAttrString((PyObject *)type, "codec"); + if (codec == NULL) + goto errorexit; + if (!MultibyteCodec_Check(codec)) { + PyErr_SetString(PyExc_TypeError, "codec is unexpected type"); + goto errorexit; + } + + self->codec = ((MultibyteCodecObject *)codec)->codec; + self->stream = stream; + Py_INCREF(stream); + self->pendingsize = 0; + self->errors = internal_error_callback(errors); + if (self->errors == NULL) + goto errorexit; + if (self->codec->encinit != NULL && + self->codec->encinit(&self->state, self->codec->config) != 0) + goto errorexit; + + Py_DECREF(codec); + return (PyObject *)self; + +errorexit: + Py_XDECREF(self); + Py_XDECREF(codec); + return NULL; +} + +static int +mbstreamwriter_traverse(MultibyteStreamWriterObject *self, + visitproc visit, void *arg) +{ + if (ERROR_ISCUSTOM(self->errors)) + Py_VISIT(self->errors); + Py_VISIT(self->stream); + return 0; } static void mbstreamwriter_dealloc(MultibyteStreamWriterObject *self) { - if (self->errors > ERROR_MAX) { - Py_DECREF(self->errors); - } + PyObject_GC_UnTrack(self); + ERROR_DECREF(self->errors); Py_DECREF(self->stream); - PyObject_Del(self); + self->ob_type->tp_free(self); } static struct PyMethodDef mbstreamwriter_methods[] = { @@ -1143,7 +1660,12 @@ static struct PyMethodDef mbstreamwriter_methods[] = { {NULL, NULL}, }; - +static PyMemberDef mbstreamwriter_members[] = { + {"stream", T_OBJECT, + offsetof(MultibyteStreamWriterObject, stream), + READONLY, NULL}, + {NULL,} +}; static PyTypeObject MultibyteStreamWriter_Type = { PyObject_HEAD_INIT(NULL) @@ -1167,17 +1689,33 @@ static PyTypeObject MultibyteStreamWriter_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)mbstreamwriter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iterext */ mbstreamwriter_methods, /* tp_methods */ + mbstreamwriter_members, /* tp_members */ + codecctx_getsets, /* 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 */ + mbstreamwriter_new, /* tp_new */ }; + +/** + * Exposed factory function + */ + static PyObject * __create_codec(PyObject *ignore, PyObject *arg) { @@ -1201,80 +1739,38 @@ __create_codec(PyObject *ignore, PyObject *arg) return (PyObject *)self; } -static PyObject * -mbstreamreader_create(MultibyteCodec *codec, - PyObject *stream, const char *errors) -{ - MultibyteStreamReaderObject *self; - - self = PyObject_New(MultibyteStreamReaderObject, - &MultibyteStreamReader_Type); - if (self == NULL) - return NULL; - - self->codec = codec; - self->stream = stream; - Py_INCREF(stream); - self->pendingsize = 0; - self->errors = get_errorcallback(errors); - if (self->errors == NULL) - goto errorexit; - if (self->codec->decinit != NULL && - self->codec->decinit(&self->state, self->codec->config) != 0) - goto errorexit; - - return (PyObject *)self; - -errorexit: - Py_XDECREF(self); - return NULL; -} - -static PyObject * -mbstreamwriter_create(MultibyteCodec *codec, - PyObject *stream, const char *errors) -{ - MultibyteStreamWriterObject *self; - - self = PyObject_New(MultibyteStreamWriterObject, - &MultibyteStreamWriter_Type); - if (self == NULL) - return NULL; - - self->codec = codec; - self->stream = stream; - Py_INCREF(stream); - self->pendingsize = 0; - self->errors = get_errorcallback(errors); - if (self->errors == NULL) - goto errorexit; - if (self->codec->encinit != NULL && - self->codec->encinit(&self->state, self->codec->config) != 0) - goto errorexit; - - return (PyObject *)self; - -errorexit: - Py_XDECREF(self); - return NULL; -} - static struct PyMethodDef __methods[] = { {"__create_codec", (PyCFunction)__create_codec, METH_O}, {NULL, NULL}, }; -void +PyMODINIT_FUNC init_multibytecodec(void) { + int i; + PyObject *m; + PyTypeObject *typelist[] = { + &MultibyteIncrementalEncoder_Type, + &MultibyteIncrementalDecoder_Type, + &MultibyteStreamReader_Type, + &MultibyteStreamWriter_Type, + NULL + }; + if (PyType_Ready(&MultibyteCodec_Type) < 0) return; - if (PyType_Ready(&MultibyteStreamReader_Type) < 0) - return; - if (PyType_Ready(&MultibyteStreamWriter_Type) < 0) + + m = Py_InitModule("_multibytecodec", __methods); + if (m == NULL) return; - Py_InitModule("_multibytecodec", __methods); + for (i = 0; typelist[i] != NULL; i++) { + if (PyType_Ready(typelist[i]) < 0) + return; + Py_INCREF(typelist[i]); + PyModule_AddObject(m, typelist[i]->tp_name, + (PyObject *)typelist[i]); + } if (PyErr_Occurred()) Py_FatalError("can't initialize the _multibytecodec module"); diff --git a/Modules/cjkcodecs/multibytecodec.h b/Modules/cjkcodecs/multibytecodec.h index ec49c78..22ea5d4 100644 --- a/Modules/cjkcodecs/multibytecodec.h +++ b/Modules/cjkcodecs/multibytecodec.h @@ -67,24 +67,51 @@ typedef struct { MultibyteCodec *codec; } MultibyteCodecObject; -#define MAXDECPENDING 8 +#define MultibyteCodec_Check(op) ((op)->ob_type == &MultibyteCodec_Type) + +#define _MultibyteStatefulCodec_HEAD \ + PyObject_HEAD \ + MultibyteCodec *codec; \ + MultibyteCodec_State state; \ + PyObject *errors; typedef struct { - PyObject_HEAD - MultibyteCodec *codec; - MultibyteCodec_State state; - unsigned char pending[MAXDECPENDING]; - Py_ssize_t pendingsize; - PyObject *stream, *errors; -} MultibyteStreamReaderObject; + _MultibyteStatefulCodec_HEAD +} MultibyteStatefulCodecContext; #define MAXENCPENDING 2 +#define _MultibyteStatefulEncoder_HEAD \ + _MultibyteStatefulCodec_HEAD \ + Py_UNICODE pending[MAXENCPENDING]; \ + Py_ssize_t pendingsize; typedef struct { - PyObject_HEAD - MultibyteCodec *codec; - MultibyteCodec_State state; - Py_UNICODE pending[MAXENCPENDING]; + _MultibyteStatefulEncoder_HEAD +} MultibyteStatefulEncoderContext; + +#define MAXDECPENDING 8 +#define _MultibyteStatefulDecoder_HEAD \ + _MultibyteStatefulCodec_HEAD \ + unsigned char pending[MAXDECPENDING]; \ Py_ssize_t pendingsize; - PyObject *stream, *errors; +typedef struct { + _MultibyteStatefulDecoder_HEAD +} MultibyteStatefulDecoderContext; + +typedef struct { + _MultibyteStatefulEncoder_HEAD +} MultibyteIncrementalEncoderObject; + +typedef struct { + _MultibyteStatefulDecoder_HEAD +} MultibyteIncrementalDecoderObject; + +typedef struct { + _MultibyteStatefulDecoder_HEAD + PyObject *stream; +} MultibyteStreamReaderObject; + +typedef struct { + _MultibyteStatefulEncoder_HEAD + PyObject *stream; } MultibyteStreamWriterObject; /* positive values for illegal sequences */ @@ -95,7 +122,12 @@ typedef struct { #define ERROR_STRICT (PyObject *)(1) #define ERROR_IGNORE (PyObject *)(2) #define ERROR_REPLACE (PyObject *)(3) -#define ERROR_MAX ERROR_REPLACE +#define ERROR_ISCUSTOM(p) ((p) < ERROR_STRICT || ERROR_REPLACE < (p)) +#define ERROR_DECREF(p) do { \ + if (p != NULL && ERROR_ISCUSTOM(p)) { \ + Py_DECREF(p); \ + } \ +} while (0); #define MBENC_FLUSH 0x0001 /* encode all characters encodable */ #define MBENC_MAX MBENC_FLUSH diff --git a/Modules/collectionsmodule.c b/Modules/collectionsmodule.c index b80ab07..c7e2c85 100644 --- a/Modules/collectionsmodule.c +++ b/Modules/collectionsmodule.c @@ -832,11 +832,11 @@ static PyTypeObject deque_type = { 0, /* tp_itemsize */ /* methods */ (destructor)deque_dealloc, /* tp_dealloc */ - (printfunc)deque_tp_print, /* tp_print */ + deque_tp_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - (reprfunc)deque_repr, /* tp_repr */ + deque_repr, /* tp_repr */ 0, /* tp_as_number */ &deque_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -1236,10 +1236,7 @@ defdict_traverse(PyObject *self, visitproc visit, void *arg) static int defdict_tp_clear(defdictobject *dd) { - if (dd->default_factory != NULL) { - Py_DECREF(dd->default_factory); - dd->default_factory = NULL; - } + Py_CLEAR(dd->default_factory); return PyDict_Type.tp_clear((PyObject *)dd); } @@ -1277,8 +1274,11 @@ a new value when a key is not present, in __getitem__ only.\n\ A defaultdict compares equal to a dict with the same items.\n\ "); +/* See comment in xxsubtype.c */ +#define DEFERRED_ADDRESS(ADDR) 0 + static PyTypeObject defdict_type = { - PyObject_HEAD_INIT(NULL) + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) 0, /* ob_size */ "collections.defaultdict", /* tp_name */ sizeof(defdictobject), /* tp_basicsize */ @@ -1302,7 +1302,7 @@ static PyTypeObject defdict_type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ defdict_doc, /* tp_doc */ - (traverseproc)defdict_traverse, /* tp_traverse */ + defdict_traverse, /* tp_traverse */ (inquiry)defdict_tp_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset*/ @@ -1311,12 +1311,12 @@ static PyTypeObject defdict_type = { defdict_methods, /* tp_methods */ defdict_members, /* tp_members */ 0, /* tp_getset */ - &PyDict_Type, /* tp_base */ + DEFERRED_ADDRESS(&PyDict_Type), /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)defdict_init, /* tp_init */ + defdict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ 0, /* tp_new */ PyObject_GC_Del, /* tp_free */ @@ -1344,6 +1344,7 @@ initcollections(void) Py_INCREF(&deque_type); PyModule_AddObject(m, "deque", (PyObject *)&deque_type); + defdict_type.tp_base = &PyDict_Type; if (PyType_Ready(&defdict_type) < 0) return; Py_INCREF(&defdict_type); diff --git a/Modules/config.c.in b/Modules/config.c.in index 9ec281c..f811991 100644 --- a/Modules/config.c.in +++ b/Modules/config.c.in @@ -17,6 +17,10 @@ redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. #include "Python.h" +#ifdef __cplusplus +extern "C" { +#endif + /* -- ADDMODULE MARKER 1 -- */ @@ -50,3 +54,9 @@ struct _inittab _PyImport_Inittab[] = { /* Sentinel */ {0, 0} }; + + +#ifdef __cplusplus +} +#endif + diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index c1a0cb3..9ae235a 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -1228,8 +1228,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } } assert(zreplacement != NULL); - ptoappend = PyString_AsString(zreplacement); - ntoappend = PyString_Size(zreplacement); + ptoappend = PyString_AS_STRING(zreplacement); + ntoappend = PyString_GET_SIZE(zreplacement); } else if (ch == 'Z') { /* format tzname */ @@ -1257,14 +1257,18 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, Py_DECREF(temp); if (Zreplacement == NULL) goto Done; + if (!PyString_Check(Zreplacement)) { + PyErr_SetString(PyExc_TypeError, "tzname.replace() did not return a string"); + goto Done; + } } else Py_DECREF(temp); } } assert(Zreplacement != NULL); - ptoappend = PyString_AsString(Zreplacement); - ntoappend = PyString_Size(Zreplacement); + ptoappend = PyString_AS_STRING(Zreplacement); + ntoappend = PyString_GET_SIZE(Zreplacement); } else { /* percent followed by neither z nor Z */ @@ -1275,6 +1279,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, /* Append the ntoappend chars starting at ptoappend to * the new format. */ + assert(ptoappend != NULL); assert(ntoappend >= 0); if (ntoappend == 0) continue; @@ -2404,11 +2409,11 @@ static PyObject * date_repr(PyDateTime_Date *self) { char buffer[1028]; - const char *typename; + const char *type_name; - typename = self->ob_type->tp_name; + type_name = self->ob_type->tp_name; PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d)", - typename, + type_name, GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); return PyString_FromString(buffer); @@ -3130,7 +3135,7 @@ static PyObject * time_repr(PyDateTime_Time *self) { char buffer[100]; - const char *typename = self->ob_type->tp_name; + const char *type_name = self->ob_type->tp_name; int h = TIME_GET_HOUR(self); int m = TIME_GET_MINUTE(self); int s = TIME_GET_SECOND(self); @@ -3139,13 +3144,13 @@ time_repr(PyDateTime_Time *self) if (us) PyOS_snprintf(buffer, sizeof(buffer), - "%s(%d, %d, %d, %d)", typename, h, m, s, us); + "%s(%d, %d, %d, %d)", type_name, h, m, s, us); else if (s) PyOS_snprintf(buffer, sizeof(buffer), - "%s(%d, %d, %d)", typename, h, m, s); + "%s(%d, %d, %d)", type_name, h, m, s); else PyOS_snprintf(buffer, sizeof(buffer), - "%s(%d, %d)", typename, h, m); + "%s(%d, %d)", type_name, h, m); result = PyString_FromString(buffer); if (result != NULL && HASTZINFO(self)) result = append_keyword_tzinfo(result, self->tzinfo); @@ -3816,6 +3821,10 @@ datetime_strptime(PyObject *cls, PyObject *args) if (PySequence_Check(obj) && PySequence_Size(obj) >= 6) for (i=0; i < 6; i++) { PyObject *p = PySequence_GetItem(obj, i); + if (p == NULL) { + Py_DECREF(obj); + return NULL; + } if (PyInt_Check(p)) ia[i] = PyInt_AsLong(p); else @@ -4023,13 +4032,13 @@ static PyObject * datetime_repr(PyDateTime_DateTime *self) { char buffer[1000]; - const char *typename = self->ob_type->tp_name; + const char *type_name = self->ob_type->tp_name; PyObject *baserepr; if (DATE_GET_MICROSECOND(self)) { PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d, %d, %d, %d, %d)", - typename, + type_name, GET_YEAR(self), GET_MONTH(self), GET_DAY(self), DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), @@ -4038,7 +4047,7 @@ datetime_repr(PyDateTime_DateTime *self) else if (DATE_GET_SECOND(self)) { PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d, %d, %d, %d)", - typename, + type_name, GET_YEAR(self), GET_MONTH(self), GET_DAY(self), DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self)); @@ -4046,7 +4055,7 @@ datetime_repr(PyDateTime_DateTime *self) else { PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d, %d, %d)", - typename, + type_name, GET_YEAR(self), GET_MONTH(self), GET_DAY(self), DATE_GET_HOUR(self), DATE_GET_MINUTE(self)); } diff --git a/Modules/functionalmodule.c b/Modules/functionalmodule.c index 4b2e9b4..38ef43a 100644 --- a/Modules/functionalmodule.c +++ b/Modules/functionalmodule.c @@ -48,7 +48,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) pto->fn = func; Py_INCREF(func); - pto->args = PyTuple_GetSlice(args, 1, INT_MAX); + pto->args = PyTuple_GetSlice(args, 1, PY_SSIZE_T_MAX); if (pto->args == NULL) { pto->kw = NULL; Py_DECREF(pto); diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 7e3f95a..0176d6f 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -413,8 +413,12 @@ has_finalizer(PyObject *op) assert(delstr != NULL); return _PyInstance_Lookup(op, delstr) != NULL; } - else + else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) return op->ob_type->tp_del != NULL; + else if (PyGen_CheckExact(op)) + return PyGen_NeedsFinalizing((PyGenObject *)op); + else + return 0; } /* Move the objects in unreachable with __del__ methods into `finalizers`. @@ -730,6 +734,8 @@ collect(int generation) PyGC_Head unreachable; /* non-problematic unreachable trash */ PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ PyGC_Head *gc; + static PyObject *tmod = NULL; + double t1 = 0.0; if (delstr == NULL) { delstr = PyString_InternFromString("__del__"); @@ -737,19 +743,29 @@ collect(int generation) Py_FatalError("gc couldn't allocate \"__del__\""); } + if (tmod == NULL) { + tmod = PyImport_ImportModule("time"); + if (tmod == NULL) + PyErr_Clear(); + } + if (debug & DEBUG_STATS) { + if (tmod != NULL) { + PyObject *f = PyObject_CallMethod(tmod, "time", NULL); + if (f == NULL) { + PyErr_Clear(); + } + else { + t1 = PyFloat_AsDouble(f); + Py_DECREF(f); + } + } PySys_WriteStderr("gc: collecting generation %d...\n", generation); PySys_WriteStderr("gc: objects in each generation:"); - for (i = 0; i < NUM_GENERATIONS; i++) { -#ifdef MS_WIN64 - PySys_WriteStderr(" %Id", gc_list_size(GEN_HEAD(i))); -#else - PySys_WriteStderr(" %ld", - Py_SAFE_DOWNCAST(gc_list_size(GEN_HEAD(i)), - Py_ssize_t, long)); -#endif - } + for (i = 0; i < NUM_GENERATIONS; i++) + PySys_WriteStderr(" %" PY_FORMAT_SIZE_T "d", + gc_list_size(GEN_HEAD(i))); PySys_WriteStderr("\n"); } @@ -816,6 +832,17 @@ collect(int generation) if (debug & DEBUG_COLLECTABLE) { debug_cycle("collectable", FROM_GC(gc)); } + if (tmod != NULL && (debug & DEBUG_STATS)) { + PyObject *f = PyObject_CallMethod(tmod, "time", NULL); + if (f == NULL) { + PyErr_Clear(); + } + else { + t1 = PyFloat_AsDouble(f)-t1; + Py_DECREF(f); + PySys_WriteStderr("gc: %.4fs elapsed.\n", t1); + } + } } /* Clear weakrefs and invoke callbacks as necessary. */ @@ -837,21 +864,14 @@ collect(int generation) debug_cycle("uncollectable", FROM_GC(gc)); } if (debug & DEBUG_STATS) { - if (m == 0 && n == 0) { + if (m == 0 && n == 0) PySys_WriteStderr("gc: done.\n"); - } - else { -#ifdef MS_WIN64 + else PySys_WriteStderr( - "gc: done, %Id unreachable, %Id uncollectable.\n", + "gc: done, " + "%" PY_FORMAT_SIZE_T "d unreachable, " + "%" PY_FORMAT_SIZE_T "d uncollectable.\n", n+m, n); -#else - PySys_WriteStderr( - "gc: done, %ld unreachable, %ld uncollectable.\n", - Py_SAFE_DOWNCAST(n+m, Py_ssize_t, long), - Py_SAFE_DOWNCAST(n, Py_ssize_t, long)); -#endif - } } /* Append instances in the uncollectable set to a Python @@ -1050,7 +1070,7 @@ gc_get_count(PyObject *self, PyObject *noargs) static int referrersvisit(PyObject* obj, PyObject *objs) { - int i; + Py_ssize_t i; for (i = 0; i < PyTuple_GET_SIZE(objs); i++) if (PyTuple_GET_ITEM(objs, i) == obj) return 1; @@ -1085,6 +1105,8 @@ gc_get_referrers(PyObject *self, PyObject *args) { int i; PyObject *result = PyList_New(0); + if (!result) return NULL; + for (i = 0; i < NUM_GENERATIONS; i++) { if (!(gc_referrers_for(args, GEN_HEAD(i), result))) { Py_DECREF(result); @@ -1108,7 +1130,7 @@ Return the list of objects that are directly referred to by objs."); static PyObject * gc_get_referents(PyObject *self, PyObject *args) { - int i; + Py_ssize_t i; PyObject *result = PyList_New(0); if (result == NULL) @@ -1288,7 +1310,8 @@ PyObject * _PyObject_GC_Malloc(size_t basicsize) { PyObject *op; - PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize); + PyGC_Head *g = (PyGC_Head *)PyObject_MALLOC( + sizeof(PyGC_Head) + basicsize); if (g == NULL) return PyErr_NoMemory(); g->gc.gc_refs = GC_UNTRACKED; @@ -1330,7 +1353,7 @@ _PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems) { const size_t basicsize = _PyObject_VAR_SIZE(op->ob_type, nitems); PyGC_Head *g = AS_GC(op); - g = PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); + g = (PyGC_Head *)PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); if (g == NULL) return (PyVarObject *)PyErr_NoMemory(); op = (PyVarObject *) FROM_GC(g); diff --git a/Modules/getpath.c b/Modules/getpath.c index 4716d15..8eba730 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -91,6 +91,11 @@ * process to find the installed Python tree. */ +#ifdef __cplusplus + extern "C" { +#endif + + #ifndef VERSION #if defined(__VMS) #define VERSION "2_1" @@ -566,7 +571,7 @@ calculate_path(void) bufsz += strlen(exec_prefix) + 1; /* This is the only malloc call in this file */ - buf = PyMem_Malloc(bufsz); + buf = (char *)PyMem_Malloc(bufsz); if (buf == NULL) { /* We can't exit, so print a warning and limp along */ @@ -681,3 +686,9 @@ Py_GetProgramFullPath(void) calculate_path(); return progpath; } + + +#ifdef __cplusplus +} +#endif + diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index de849c9..12d33dd 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -29,6 +29,7 @@ static PyStructSequence_Desc struct_group_type_desc = { }; +static int initialized; static PyTypeObject StructGrpType; static PyObject * @@ -174,6 +175,8 @@ initgrp(void) if (m == NULL) return; d = PyModule_GetDict(m); - PyStructSequence_InitType(&StructGrpType, &struct_group_type_desc); + if (!initialized) + PyStructSequence_InitType(&StructGrpType, &struct_group_type_desc); PyDict_SetItemString(d, "struct_group", (PyObject *) &StructGrpType); + initialized = 1; } diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 49d241f..94617a9 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -340,7 +340,7 @@ teedataobject_new(PyObject *it) { teedataobject *tdo; - tdo = PyObject_New(teedataobject, &teedataobject_type); + tdo = PyObject_GC_New(teedataobject, &teedataobject_type); if (tdo == NULL) return NULL; @@ -348,6 +348,7 @@ teedataobject_new(PyObject *it) tdo->nextlink = NULL; Py_INCREF(it); tdo->it = it; + PyObject_GC_Track(tdo); return (PyObject *)tdo; } @@ -381,16 +382,34 @@ teedataobject_getitem(teedataobject *tdo, int i) return value; } -static void -teedataobject_dealloc(teedataobject *tdo) +static int +teedataobject_traverse(teedataobject *tdo, visitproc visit, void * arg) { int i; + Py_VISIT(tdo->it); + for (i = 0; i < tdo->numread; i++) + Py_VISIT(tdo->values[i]); + Py_VISIT(tdo->nextlink); + return 0; +} +static int +teedataobject_clear(teedataobject *tdo) +{ + int i; + Py_CLEAR(tdo->it); for (i=0 ; inumread ; i++) - Py_DECREF(tdo->values[i]); - Py_XDECREF(tdo->it); - Py_XDECREF(tdo->nextlink); - PyObject_Del(tdo); + Py_CLEAR(tdo->values[i]); + Py_CLEAR(tdo->nextlink); + return 0; +} + +static void +teedataobject_dealloc(teedataobject *tdo) +{ + PyObject_GC_UnTrack(tdo); + teedataobject_clear(tdo); + PyObject_GC_Del(tdo); } PyDoc_STRVAR(teedataobject_doc, "Data container common to multiple tee objects."); @@ -417,9 +436,26 @@ static PyTypeObject teedataobject_type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ teedataobject_doc, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)teedataobject_traverse, /* tp_traverse */ + (inquiry)teedataobject_clear, /* 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 */ + PyObject_GC_Del, /* tp_free */ }; @@ -443,18 +479,26 @@ tee_next(teeobject *to) return value; } +static int +tee_traverse(teeobject *to, visitproc visit, void *arg) +{ + Py_VISIT((PyObject *)to->dataobj); + return 0; +} + static PyObject * tee_copy(teeobject *to) { teeobject *newto; - newto = PyObject_New(teeobject, &tee_type); + newto = PyObject_GC_New(teeobject, &tee_type); if (newto == NULL) return NULL; Py_INCREF(to->dataobj); newto->dataobj = to->dataobj; newto->index = to->index; newto->weakreflist = NULL; + PyObject_GC_Track(newto); return (PyObject *)newto; } @@ -474,12 +518,13 @@ tee_fromiterable(PyObject *iterable) goto done; } - to = PyObject_New(teeobject, &tee_type); + to = PyObject_GC_New(teeobject, &tee_type); if (to == NULL) goto done; to->dataobj = (teedataobject *)teedataobject_new(it); to->index = 0; to->weakreflist = NULL; + PyObject_GC_Track(to); done: Py_XDECREF(it); return (PyObject *)to; @@ -495,13 +540,21 @@ tee_new(PyTypeObject *type, PyObject *args, PyObject *kw) return tee_fromiterable(iterable); } -static void -tee_dealloc(teeobject *to) +static int +tee_clear(teeobject *to) { if (to->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) to); - Py_XDECREF(to->dataobj); - PyObject_Del(to); + Py_CLEAR(to->dataobj); + return 0; +} + +static void +tee_dealloc(teeobject *to) +{ + PyObject_GC_UnTrack(to); + tee_clear(to); + PyObject_GC_Del(to); } PyDoc_STRVAR(teeobject_doc, @@ -534,10 +587,10 @@ static PyTypeObject tee_type = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ teeobject_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ + (traverseproc)tee_traverse, /* tp_traverse */ + (inquiry)tee_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(teeobject, weakreflist), /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ @@ -553,7 +606,7 @@ static PyTypeObject tee_type = { 0, /* tp_init */ 0, /* tp_alloc */ tee_new, /* tp_new */ - PyObject_Del, /* tp_free */ + PyObject_GC_Del, /* tp_free */ }; static PyObject * diff --git a/Modules/main.c b/Modules/main.c index b3ce16e..7326a27 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -28,6 +28,10 @@ "Type \"help\", \"copyright\", \"credits\" or \"license\" " \ "for more information." +#ifdef __cplusplus +extern "C" { +#endif + /* For Py_GetArgcArgv(); set by main() */ static char **orig_argv; static int orig_argc; @@ -206,7 +210,7 @@ Py_Main(int argc, char **argv) /* -c is the last option; following arguments that look like options are left for the command to interpret. */ - command = malloc(strlen(_PyOS_optarg) + 2); + command = (char *)malloc(strlen(_PyOS_optarg) + 2); if (command == NULL) Py_FatalError( "not enough memory to copy -c argument"); @@ -219,7 +223,7 @@ Py_Main(int argc, char **argv) /* -m is the last option; following arguments that look like options are left for the module to interpret. */ - module = malloc(strlen(_PyOS_optarg) + 2); + module = (char *)malloc(strlen(_PyOS_optarg) + 2); if (module == NULL) Py_FatalError( "not enough memory to copy -m argument"); @@ -509,3 +513,8 @@ Py_GetArgcArgv(int *argc, char ***argv) *argc = orig_argc; *argv = orig_argv; } + +#ifdef __cplusplus +} +#endif + diff --git a/Modules/md5.c b/Modules/md5.c new file mode 100644 index 0000000..c35d96c --- /dev/null +++ b/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@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/Modules/md5.h b/Modules/md5.h index 13628df..5eb6d6c 100644 --- a/Modules/md5.h +++ b/Modules/md5.h @@ -1,62 +1,91 @@ -/* MD5.H - header file for MD5C.C - */ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. 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. -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. + 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: -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. + 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. -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. + L. Peter Deutsch + ghost@aladdin.com -These notices must be retained in any copies of any part of this -documentation and/or software. */ +/* $Id$ */ +/* + 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): -/* ========== include global.h ========== */ -/* GLOBAL.H - RSAREF types and constants + 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. */ -/* POINTER defines a generic pointer type */ -typedef unsigned char *POINTER; - -/* UINT4 defines a four byte word */ -#if SIZEOF_LONG == 4 -typedef unsigned long int UINT4; -#elif SIZEOF_SHORT == 4 -typedef unsigned short int UINT4; -#elif INT_MAX == 2147483647 -typedef unsigned int UINT4; -#else -#error "Can't find a 4-byte integral type" +#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 -/* ========== End global.h; continue md5.h ========== */ +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); -/* MD5 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} MD5_CTX; +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); -/* Rename all exported symbols to avoid conflicts with similarly named - symbols in some systems' standard C libraries... */ +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); -#define MD5Init _Py_MD5Init -#define MD5Update _Py_MD5Update -#define MD5Final _Py_MD5Final +#ifdef __cplusplus +} /* end extern "C" */ +#endif -void MD5Init(MD5_CTX *); -void MD5Update(MD5_CTX *, unsigned char *, unsigned int); -void MD5Final(unsigned char [16], MD5_CTX *); +#endif /* md5_INCLUDED */ diff --git a/Modules/md5c.c b/Modules/md5c.c deleted file mode 100644 index 1b8dfdb..0000000 --- a/Modules/md5c.c +++ /dev/null @@ -1,289 +0,0 @@ -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - */ - -#include "Python.h" -#include "md5.h" - -/* Constants for MD5Transform routine. */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -static void MD5Transform(UINT4[4], unsigned char[64]); - - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void -Encode(unsigned char *output, UINT4 *input, unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} - - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void -Decode(UINT4 *output, unsigned char *input, unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); - } -} - - -static unsigned char PADDING[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 -}; - -/* F, G, H and I are basic MD5 functions. */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - - -/* MD5 initialization. Begins an MD5 operation, writing a new context. */ -void -MD5Init(MD5_CTX *context) -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - - -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ -void -MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputLen) -{ - unsigned int i, index, partLen; - - /* Compute number of bytes mod 64 */ - index = (unsigned int)((context->count[0] >> 3) & 0x3F); - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - index; - - /* Transform as many times as possible. */ - if (inputLen >= partLen) { - memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform(context->state, &input[i]); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - memcpy((POINTER)&context->buffer[index], - (POINTER)&input[i], inputLen-i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - message digest and zeroing the context. - */ -void -MD5Final(unsigned char digest[16], MD5_CTX *context) -{ - unsigned char bits[8]; - unsigned int index, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64. */ - index = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (index < 56) ? (56 - index) : (120 - index); - MD5Update(context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update(context, bits, 8); - - /* Store state in digest */ - Encode(digest, context->state, 16); - - /* Zeroize sensitive information. */ - memset((POINTER)context, 0, sizeof (*context)); -} - - -/* MD5 basic transformation. Transforms state based on block. */ -static void -MD5Transform(UINT4 state[4], unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. */ - memset((POINTER)x, 0, sizeof (x)); -} diff --git a/Modules/md5module.c b/Modules/md5module.c index e12bef8..5e4f116 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -15,7 +15,7 @@ typedef struct { PyObject_HEAD - MD5_CTX md5; /* the context holder */ + md5_state_t md5; /* the context holder */ } md5object; static PyTypeObject MD5type; @@ -31,7 +31,7 @@ newmd5object(void) if (md5p == NULL) return NULL; - MD5Init(&md5p->md5); /* actual initialisation */ + md5_init(&md5p->md5); /* actual initialisation */ return md5p; } @@ -56,7 +56,7 @@ md5_update(md5object *self, PyObject *args) if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) return NULL; - MD5Update(&self->md5, cp, len); + md5_append(&self->md5, cp, len); Py_INCREF(Py_None); return Py_None; @@ -73,12 +73,12 @@ arguments."); static PyObject * md5_digest(md5object *self) { - MD5_CTX mdContext; + md5_state_t mdContext; unsigned char aDigest[16]; /* make a temporary copy, and perform the final */ mdContext = self->md5; - MD5Final(aDigest, &mdContext); + md5_finish(&mdContext, aDigest); return PyString_FromStringAndSize((char *)aDigest, 16); } @@ -94,14 +94,14 @@ including null bytes."); static PyObject * md5_hexdigest(md5object *self) { - MD5_CTX mdContext; + 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; - MD5Final(digest, &mdContext); + md5_finish(&mdContext, digest); /* Make hex version of the digest */ for(i=j=0; i<16; i++) { @@ -272,7 +272,7 @@ MD5_new(PyObject *self, PyObject *args) return NULL; if (cp) - MD5Update(&md5p->md5, cp, len); + md5_append(&md5p->md5, cp, len); return (PyObject *)md5p; } diff --git a/Modules/operator.c b/Modules/operator.c index 24f4e0a..cbce16e 100644 --- a/Modules/operator.c +++ b/Modules/operator.c @@ -354,8 +354,7 @@ itemgetter_dealloc(itemgetterobject *ig) static int itemgetter_traverse(itemgetterobject *ig, visitproc visit, void *arg) { - if (ig->item) - return visit(ig->item, arg); + Py_VISIT(ig->item); return 0; } @@ -493,8 +492,7 @@ attrgetter_dealloc(attrgetterobject *ag) static int attrgetter_traverse(attrgetterobject *ag, visitproc visit, void *arg) { - if (ag->attr) - return visit(ag->attr, arg); + Py_VISIT(ag->attr); return 0; } diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index ce8a0d0..563620c 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -935,24 +935,32 @@ build_namelists (PyObject *module) labels = PyList_New(num_controls); names = PyList_New(num_controls); + if (labels == NULL || names == NULL) + goto error2; for (i = 0; i < num_controls; i++) { s = PyString_FromString(control_labels[i]); if (s == NULL) - return -1; + goto error2; PyList_SET_ITEM(labels, i, s); s = PyString_FromString(control_names[i]); if (s == NULL) - return -1; + goto error2; PyList_SET_ITEM(names, i, s); } if (PyModule_AddObject(module, "control_labels", labels) == -1) - return -1; + goto error2; if (PyModule_AddObject(module, "control_names", names) == -1) - return -1; + goto error1; return 0; + +error2: + Py_XDECREF(labels); +error1: + Py_XDECREF(names); + return -1; } diff --git a/Modules/parsermodule.c b/Modules/parsermodule.c index 83165ba..c9edae6 100644 --- a/Modules/parsermodule.c +++ b/Modules/parsermodule.c @@ -657,9 +657,10 @@ build_node_children(PyObject *tuple, node *root, int *line_num) } } if (!ok) { - PyErr_SetObject(parser_error, - Py_BuildValue("os", elem, - "Illegal node construct.")); + PyObject *err = Py_BuildValue("os", elem, + "Illegal node construct."); + PyErr_SetObject(parser_error, err); + Py_XDECREF(err); Py_XDECREF(elem); return (0); } @@ -700,7 +701,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num) } } len = PyString_GET_SIZE(temp) + 1; - strn = (char *)PyMem_MALLOC(len); + strn = (char *)PyObject_MALLOC(len); if (strn != NULL) (void) memcpy(strn, PyString_AS_STRING(temp), len); Py_DECREF(temp); @@ -710,18 +711,19 @@ build_node_children(PyObject *tuple, node *root, int *line_num) * It has to be one or the other; this is an error. * Throw an exception. */ - PyErr_SetObject(parser_error, - Py_BuildValue("os", elem, "unknown node type.")); + PyObject *err = Py_BuildValue("os", elem, "unknown node type."); + PyErr_SetObject(parser_error, err); + Py_XDECREF(err); Py_XDECREF(elem); return (0); } err = PyNode_AddChild(root, type, strn, *line_num, 0); if (err == E_NOMEM) { - PyMem_DEL(strn); + PyObject_FREE(strn); return (node *) PyErr_NoMemory(); } if (err == E_OVERFLOW) { - PyMem_DEL(strn); + PyObject_FREE(strn); PyErr_SetString(PyExc_ValueError, "unsupported number of child nodes"); return NULL; @@ -740,7 +742,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num) } Py_XDECREF(elem); } - return (root); + return root; } @@ -762,6 +764,7 @@ build_node_tree(PyObject *tuple) tuple = Py_BuildValue("os", tuple, "Illegal syntax-tree; cannot start with terminal symbol."); PyErr_SetObject(parser_error, tuple); + Py_XDECREF(tuple); } else if (ISNONTERMINAL(num)) { /* @@ -784,7 +787,7 @@ build_node_tree(PyObject *tuple) if (res && encoding) { Py_ssize_t len; len = PyString_GET_SIZE(encoding) + 1; - res->n_str = (char *)PyMem_MALLOC(len); + res->n_str = (char *)PyObject_MALLOC(len); if (res->n_str != NULL) (void) memcpy(res->n_str, PyString_AS_STRING(encoding), len); Py_DECREF(encoding); @@ -792,14 +795,16 @@ build_node_tree(PyObject *tuple) } } } - else + else { /* The tuple is illegal -- if the number is neither TERMINAL nor * NONTERMINAL, we can't use it. Not sure the implementation * allows this condition, but the API doesn't preclude it. */ - PyErr_SetObject(parser_error, - Py_BuildValue("os", tuple, - "Illegal component tuple.")); + PyObject *err = Py_BuildValue("os", tuple, + "Illegal component tuple."); + PyErr_SetObject(parser_error, err); + Py_XDECREF(err); + } return (res); } @@ -1321,7 +1326,7 @@ validate_gen_for(node *tree) return res; } -/* list_if: 'if' test [list_iter] +/* list_if: 'if' old_test [list_iter] */ static int validate_list_if(node *tree) @@ -1336,12 +1341,12 @@ validate_list_if(node *tree) if (res) res = (validate_name(CHILD(tree, 0), "if") - && validate_test(CHILD(tree, 1))); + && validate_old_test(CHILD(tree, 1))); return res; } -/* gen_if: 'if' test [gen_iter] +/* gen_if: 'if' old_test [gen_iter] */ static int validate_gen_if(node *tree) @@ -1356,7 +1361,7 @@ validate_gen_if(node *tree) if (res) res = (validate_name(CHILD(tree, 0), "if") - && validate_test(CHILD(tree, 1))); + && validate_old_test(CHILD(tree, 1))); return res; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1fbc353..b51ba5d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -22,6 +22,10 @@ # include #endif /* defined(__VMS) */ +#ifdef __cplusplus +extern "C" { +#endif + PyDoc_STRVAR(posix__doc__, "This module provides access to operating system functionality that is\n\ standardized by the C Standard and the POSIX standard (a thinly\n\ @@ -264,6 +268,12 @@ extern int lstat(const char *, struct stat *); #define WTERMSIG(u_wait) ((u_wait).w_termsig) #endif +#define WAIT_TYPE union wait +#define WAIT_STATUS_INT(s) (s.w_status) + +#else /* !UNION_WAIT */ +#define WAIT_TYPE int +#define WAIT_STATUS_INT(s) (s) #endif /* UNION_WAIT */ /* Don't use the "_r" form if we don't need it (also, won't have a @@ -971,6 +981,7 @@ static PyStructSequence_Desc statvfs_result_desc = { 10 }; +static int initialized; static PyTypeObject StatResultType; static PyTypeObject StatVFSResultType; static newfunc structseq_new; @@ -1839,6 +1850,7 @@ posix_listdir(PyObject *self, PyObject *args) struct dirent *ep; int arg_is_unicode = 1; + errno = 0; if (!PyArg_ParseTuple(args, "U:listdir", &v)) { arg_is_unicode = 0; PyErr_Clear(); @@ -1895,6 +1907,12 @@ posix_listdir(PyObject *self, PyObject *args) } Py_DECREF(v); } + if (errno != 0 && d != NULL) { + /* readdir() returned NULL and set errno */ + closedir(dirp); + Py_DECREF(d); + return posix_error_with_allocated_filename(name); + } closedir(dirp); PyMem_Free(name); @@ -1995,13 +2013,13 @@ posix_mkdir(PyObject *self, PyObject *args) } -#ifdef HAVE_NICE -#if defined(HAVE_BROKEN_NICE) && defined(HAVE_SYS_RESOURCE_H) -#if defined(HAVE_GETPRIORITY) && !defined(PRIO_PROCESS) +/* sys/resource.h is needed for at least: wait3(), wait4(), broken nice. */ +#if defined(HAVE_SYS_RESOURCE_H) #include #endif -#endif + +#ifdef HAVE_NICE PyDoc_STRVAR(posix_nice__doc__, "nice(inc) -> new_priority\n\n\ Decrease the priority of process by inc and return the new priority."); @@ -3088,7 +3106,7 @@ posix_openpty(PyObject *self, PyObject *noargs) #if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) PyOS_sighandler_t sig_saved; #ifdef sun - extern char *ptsname(); + extern char *ptsname(int fildes); #endif #endif @@ -5091,6 +5109,114 @@ posix_setgroups(PyObject *self, PyObject *args) } #endif /* HAVE_SETGROUPS */ +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) +static PyObject * +wait_helper(int pid, int status, struct rusage *ru) +{ + PyObject *result; + static PyObject *struct_rusage; + + if (pid == -1) + return posix_error(); + + if (struct_rusage == NULL) { + PyObject *m = PyImport_ImportModule("resource"); + if (m == NULL) + return NULL; + struct_rusage = PyObject_GetAttrString(m, "struct_rusage"); + Py_DECREF(m); + if (struct_rusage == NULL) + return NULL; + } + + /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */ + result = PyStructSequence_New((PyTypeObject*) struct_rusage); + if (!result) + return NULL; + +#ifndef doubletime +#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001) +#endif + + PyStructSequence_SET_ITEM(result, 0, + PyFloat_FromDouble(doubletime(ru->ru_utime))); + PyStructSequence_SET_ITEM(result, 1, + PyFloat_FromDouble(doubletime(ru->ru_stime))); +#define SET_INT(result, index, value)\ + PyStructSequence_SET_ITEM(result, index, PyInt_FromLong(value)) + SET_INT(result, 2, ru->ru_maxrss); + SET_INT(result, 3, ru->ru_ixrss); + SET_INT(result, 4, ru->ru_idrss); + SET_INT(result, 5, ru->ru_isrss); + SET_INT(result, 6, ru->ru_minflt); + SET_INT(result, 7, ru->ru_majflt); + SET_INT(result, 8, ru->ru_nswap); + SET_INT(result, 9, ru->ru_inblock); + SET_INT(result, 10, ru->ru_oublock); + SET_INT(result, 11, ru->ru_msgsnd); + SET_INT(result, 12, ru->ru_msgrcv); + SET_INT(result, 13, ru->ru_nsignals); + SET_INT(result, 14, ru->ru_nvcsw); + SET_INT(result, 15, ru->ru_nivcsw); +#undef SET_INT + + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + + return Py_BuildValue("iiN", pid, status, result); +} +#endif /* HAVE_WAIT3 || HAVE_WAIT4 */ + +#ifdef HAVE_WAIT3 +PyDoc_STRVAR(posix_wait3__doc__, +"wait3(options) -> (pid, status, rusage)\n\n\ +Wait for completion of a child process."); + +static PyObject * +posix_wait3(PyObject *self, PyObject *args) +{ + int pid, options; + struct rusage ru; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + if (!PyArg_ParseTuple(args, "i:wait3", &options)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + pid = wait3(&status, options, &ru); + Py_END_ALLOW_THREADS + + return wait_helper(pid, WAIT_STATUS_INT(status), &ru); +} +#endif /* HAVE_WAIT3 */ + +#ifdef HAVE_WAIT4 +PyDoc_STRVAR(posix_wait4__doc__, +"wait4(pid, options) -> (pid, status, rusage)\n\n\ +Wait for completion of a given child process."); + +static PyObject * +posix_wait4(PyObject *self, PyObject *args) +{ + int pid, options; + struct rusage ru; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + if (!PyArg_ParseTuple(args, "ii:wait4", &pid, &options)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + pid = wait4(pid, &status, options, &ru); + Py_END_ALLOW_THREADS + + return wait_helper(pid, WAIT_STATUS_INT(status), &ru); +} +#endif /* HAVE_WAIT4 */ + #ifdef HAVE_WAITPID PyDoc_STRVAR(posix_waitpid__doc__, "waitpid(pid, options) -> (pid, status)\n\n\ @@ -5100,14 +5226,8 @@ static PyObject * posix_waitpid(PyObject *self, PyObject *args) { int pid, options; -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) return NULL; @@ -5116,8 +5236,8 @@ posix_waitpid(PyObject *self, PyObject *args) Py_END_ALLOW_THREADS if (pid == -1) return posix_error(); - else - return Py_BuildValue("ii", pid, status_i); + + return Py_BuildValue("ii", pid, WAIT_STATUS_INT(status)); } #elif defined(HAVE_CWAIT) @@ -5140,10 +5260,9 @@ posix_waitpid(PyObject *self, PyObject *args) Py_END_ALLOW_THREADS if (pid == -1) return posix_error(); - else - /* shift the status left a byte so this is more like the - POSIX waitpid */ - return Py_BuildValue("ii", pid, status << 8); + + /* shift the status left a byte so this is more like the POSIX waitpid */ + return Py_BuildValue("ii", pid, status << 8); } #endif /* HAVE_WAITPID || HAVE_CWAIT */ @@ -5156,23 +5275,16 @@ static PyObject * posix_wait(PyObject *self, PyObject *noargs) { int pid; -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - status_i = 0; Py_BEGIN_ALLOW_THREADS pid = wait(&status); Py_END_ALLOW_THREADS if (pid == -1) return posix_error(); - else - return Py_BuildValue("ii", pid, status_i); -#undef status_i + + return Py_BuildValue("ii", pid, WAIT_STATUS_INT(status)); } #endif @@ -5668,9 +5780,24 @@ posix_fdopen(PyObject *self, PyObject *args) "invalid file mode '%s'", mode); return NULL; } - Py_BEGIN_ALLOW_THREADS +#if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H) + if (mode[0] == 'a') { + /* try to make sure the O_APPEND flag is set */ + int flags; + flags = fcntl(fd, F_GETFL); + if (flags != -1) + fcntl(fd, F_SETFL, flags | O_APPEND); + fp = fdopen(fd, mode); + if (fp == NULL && flags != -1) + /* restore old mode if fdopen failed */ + fcntl(fd, F_SETFL, flags); + } else { + fp = fdopen(fd, mode); + } +#else fp = fdopen(fd, mode); +#endif Py_END_ALLOW_THREADS if (fp == NULL) return posix_error(); @@ -5887,7 +6014,7 @@ static PyObject * posix_putenv(PyObject *self, PyObject *args) { char *s1, *s2; - char *new; + char *newenv; PyObject *newstr; size_t len; @@ -5918,9 +6045,9 @@ posix_putenv(PyObject *self, PyObject *args) newstr = PyString_FromStringAndSize(NULL, (int)len - 1); if (newstr == NULL) return PyErr_NoMemory(); - new = PyString_AS_STRING(newstr); - PyOS_snprintf(new, len, "%s=%s", s1, s2); - if (putenv(new)) { + newenv = PyString_AS_STRING(newstr); + PyOS_snprintf(newenv, len, "%s=%s", s1, s2); + if (putenv(newenv)) { Py_DECREF(newstr); posix_error(); return NULL; @@ -6010,22 +6137,13 @@ Return True if the process returning 'status' was dumped to a core file."); static PyObject * posix_WCOREDUMP(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &WAIT_STATUS_INT(status))) return NULL; - } return PyBool_FromLong(WCOREDUMP(status)); -#undef status_i } #endif /* WCOREDUMP */ @@ -6038,22 +6156,13 @@ job control stop."); static PyObject * posix_WIFCONTINUED(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WCONTINUED", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WCONTINUED", &WAIT_STATUS_INT(status))) return NULL; - } return PyBool_FromLong(WIFCONTINUED(status)); -#undef status_i } #endif /* WIFCONTINUED */ @@ -6065,22 +6174,13 @@ Return True if the process returning 'status' was stopped."); static PyObject * posix_WIFSTOPPED(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &WAIT_STATUS_INT(status))) return NULL; - } return PyBool_FromLong(WIFSTOPPED(status)); -#undef status_i } #endif /* WIFSTOPPED */ @@ -6092,22 +6192,13 @@ Return True if the process returning 'status' was terminated by a signal."); static PyObject * posix_WIFSIGNALED(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &WAIT_STATUS_INT(status))) return NULL; - } return PyBool_FromLong(WIFSIGNALED(status)); -#undef status_i } #endif /* WIFSIGNALED */ @@ -6120,22 +6211,13 @@ system call."); static PyObject * posix_WIFEXITED(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WIFEXITED", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WIFEXITED", &WAIT_STATUS_INT(status))) return NULL; - } return PyBool_FromLong(WIFEXITED(status)); -#undef status_i } #endif /* WIFEXITED */ @@ -6147,22 +6229,13 @@ Return the process return code from 'status'."); static PyObject * posix_WEXITSTATUS(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &WAIT_STATUS_INT(status))) return NULL; - } return Py_BuildValue("i", WEXITSTATUS(status)); -#undef status_i } #endif /* WEXITSTATUS */ @@ -6175,22 +6248,13 @@ value."); static PyObject * posix_WTERMSIG(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WTERMSIG", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WTERMSIG", &WAIT_STATUS_INT(status))) return NULL; - } return Py_BuildValue("i", WTERMSIG(status)); -#undef status_i } #endif /* WTERMSIG */ @@ -6203,22 +6267,13 @@ the 'status' value."); static PyObject * posix_WSTOPSIG(PyObject *self, PyObject *args) { -#ifdef UNION_WAIT - union wait status; -#define status_i (status.w_status) -#else - int status; -#define status_i status -#endif - status_i = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; - if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &status_i)) - { + if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &WAIT_STATUS_INT(status))) return NULL; - } return Py_BuildValue("i", WSTOPSIG(status)); -#undef status_i } #endif /* WSTOPSIG */ @@ -6396,15 +6451,16 @@ posix_tmpnam(PyObject *self, PyObject *noargs) name = tmpnam(buffer); #endif if (name == NULL) { - PyErr_SetObject(PyExc_OSError, - Py_BuildValue("is", 0, + PyObject *err = Py_BuildValue("is", 0, #ifdef USE_TMPNAM_R "unexpected NULL from tmpnam_r" #else "unexpected NULL from tmpnam" #endif - )); - return NULL; + ); + PyErr_SetObject(PyExc_OSError, err); + Py_XDECREF(err); + return NULL; } return PyString_FromString(buffer); } @@ -6753,26 +6809,30 @@ posix_confstr(PyObject *self, PyObject *args) { PyObject *result = NULL; int name; - char buffer[64]; + char buffer[256]; if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) { - int len = confstr(name, buffer, sizeof(buffer)); + int len; errno = 0; - if (len == 0) { - if (errno != 0) - posix_error(); - else - result = PyString_FromString(""); + len = confstr(name, buffer, sizeof(buffer)); + if (len == 0) { + if (errno) { + posix_error(); + } + else { + result = Py_None; + Py_INCREF(Py_None); + } } else { - if (len >= sizeof(buffer)) { - result = PyString_FromStringAndSize(NULL, len); + if ((unsigned int)len >= sizeof(buffer)) { + result = PyString_FromStringAndSize(NULL, len-1); if (result != NULL) - confstr(name, PyString_AS_STRING(result), len+1); + confstr(name, PyString_AS_STRING(result), len); } else - result = PyString_FromString(buffer); + result = PyString_FromStringAndSize(buffer, len-1); } } return result; @@ -7423,6 +7483,44 @@ win32_startfile(PyObject *self, PyObject *args) char *filepath; char *operation = NULL; HINSTANCE rc; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyObject *unipath, *woperation = NULL; + if (!PyArg_ParseTuple(args, "U|s:startfile", + &unipath, &operation)) { + PyErr_Clear(); + goto normal; + } + + + if (operation) { + woperation = PyUnicode_DecodeASCII(operation, + strlen(operation), NULL); + if (!woperation) { + PyErr_Clear(); + operation = NULL; + goto normal; + } + } + + Py_BEGIN_ALLOW_THREADS + rc = ShellExecuteW((HWND)0, woperation ? PyUnicode_AS_UNICODE(woperation) : 0, + PyUnicode_AS_UNICODE(unipath), + NULL, NULL, SW_SHOWNORMAL); + Py_END_ALLOW_THREADS + + Py_XDECREF(woperation); + if (rc <= (HINSTANCE)32) { + PyObject *errval = win32_error_unicode("startfile", + PyUnicode_AS_UNICODE(unipath)); + return errval; + } + Py_INCREF(Py_None); + return Py_None; + } +#endif + +normal: if (!PyArg_ParseTuple(args, "et|s:startfile", Py_FileSystemDefaultEncoding, &filepath, &operation)) @@ -7695,6 +7793,12 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_WAIT {"wait", posix_wait, METH_NOARGS, posix_wait__doc__}, #endif /* HAVE_WAIT */ +#ifdef HAVE_WAIT3 + {"wait3", posix_wait3, METH_VARARGS, posix_wait3__doc__}, +#endif /* HAVE_WAIT3 */ +#ifdef HAVE_WAIT4 + {"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__}, +#endif /* HAVE_WAIT4 */ #if defined(HAVE_WAITPID) || defined(HAVE_CWAIT) {"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__}, #endif /* HAVE_WAITPID */ @@ -8142,19 +8246,27 @@ INITFUNC(void) posix_putenv_garbage = PyDict_New(); #endif - stat_result_desc.name = MODNAME ".stat_result"; - stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; - stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; - stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; - PyStructSequence_InitType(&StatResultType, &stat_result_desc); - structseq_new = StatResultType.tp_new; - StatResultType.tp_new = statresult_new; + if (!initialized) { + stat_result_desc.name = MODNAME ".stat_result"; + stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; + PyStructSequence_InitType(&StatResultType, &stat_result_desc); + structseq_new = StatResultType.tp_new; + StatResultType.tp_new = statresult_new; + + statvfs_result_desc.name = MODNAME ".statvfs_result"; + PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc); + } Py_INCREF((PyObject*) &StatResultType); PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); - - statvfs_result_desc.name = MODNAME ".statvfs_result"; - PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc); Py_INCREF((PyObject*) &StatVFSResultType); PyModule_AddObject(m, "statvfs_result", (PyObject*) &StatVFSResultType); + initialized = 1; } + +#ifdef __cplusplus +} +#endif + diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 9e7b864..9e01f48 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -42,6 +42,7 @@ The uid and gid items are integers, all others are strings. An\n\ exception is raised if the entry asked for cannot be found."); +static int initialized; static PyTypeObject StructPwdType; static void @@ -186,9 +187,12 @@ initpwd(void) if (m == NULL) return; - PyStructSequence_InitType(&StructPwdType, &struct_pwd_type_desc); + if (!initialized) + PyStructSequence_InitType(&StructPwdType, + &struct_pwd_type_desc); Py_INCREF((PyObject *) &StructPwdType); PyModule_AddObject(m, "struct_passwd", (PyObject *) &StructPwdType); /* And for b/w compatibility (this was defined by mistake): */ PyModule_AddObject(m, "struct_pwent", (PyObject *) &StructPwdType); + initialized = 1; } diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index e4bf180..fe50e36 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1519,6 +1519,8 @@ xmlparse_getattr(xmlparseobject *self, char *name) if (strcmp(name, "__members__") == 0) { int i; PyObject *rc = PyList_New(0); + if (!rc) + return NULL; for (i = 0; handler_info[i].name != NULL; i++) { PyObject *o = get_handler_name(&handler_info[i]); if (o != NULL) @@ -1652,14 +1654,9 @@ xmlparse_setattr(xmlparseobject *self, char *name, PyObject *v) static int xmlparse_traverse(xmlparseobject *op, visitproc visit, void *arg) { - int i, err; - for (i = 0; handler_info[i].name != NULL; i++) { - if (!op->handlers[i]) - continue; - err = visit(op->handlers[i], arg); - if (err) - return err; - } + int i; + for (i = 0; handler_info[i].name != NULL; i++) + Py_VISIT(op->handlers[i]); return 0; } @@ -1667,8 +1664,7 @@ static int xmlparse_clear(xmlparseobject *op) { clear_handlers(op, 0); - Py_XDECREF(op->intern); - op->intern = 0; + Py_CLEAR(op->intern); return 0; } #endif diff --git a/Modules/regexmodule.c b/Modules/regexmodule.c deleted file mode 100644 index 2fb4198..0000000 --- a/Modules/regexmodule.c +++ /dev/null @@ -1,690 +0,0 @@ -/* -XXX support range parameter on search -XXX support mstop parameter on search -*/ - - -/* Regular expression objects */ -/* This uses Tatu Ylonen's copyleft-free reimplementation of - GNU regular expressions */ - -#include "Python.h" - -#include - -#include "regexpr.h" - -static PyObject *RegexError; /* Exception */ - -typedef struct { - PyObject_HEAD - struct re_pattern_buffer re_patbuf; /* The compiled expression */ - struct re_registers re_regs; /* The registers from the last match */ - char re_fastmap[256]; /* Storage for fastmap */ - PyObject *re_translate; /* String object for translate table */ - PyObject *re_lastok; /* String object last matched/searched */ - PyObject *re_groupindex; /* Group name to index dictionary */ - PyObject *re_givenpat; /* Pattern with symbolic groups */ - PyObject *re_realpat; /* Pattern without symbolic groups */ -} regexobject; - -/* Regex object methods */ - -static void -reg_dealloc(regexobject *re) -{ - if (re->re_patbuf.buffer) - free(re->re_patbuf.buffer); - Py_XDECREF(re->re_translate); - Py_XDECREF(re->re_lastok); - Py_XDECREF(re->re_groupindex); - Py_XDECREF(re->re_givenpat); - Py_XDECREF(re->re_realpat); - PyObject_Del(re); -} - -static PyObject * -makeresult(struct re_registers *regs) -{ - PyObject *v; - int i; - static PyObject *filler = NULL; - - if (filler == NULL) { - filler = Py_BuildValue("(ii)", -1, -1); - if (filler == NULL) - return NULL; - } - v = PyTuple_New(RE_NREGS); - if (v == NULL) - return NULL; - - for (i = 0; i < RE_NREGS; i++) { - int lo = regs->start[i]; - int hi = regs->end[i]; - PyObject *w; - if (lo == -1 && hi == -1) { - w = filler; - Py_INCREF(w); - } - else - w = Py_BuildValue("(ii)", lo, hi); - if (w == NULL || PyTuple_SetItem(v, i, w) < 0) { - Py_DECREF(v); - return NULL; - } - } - return v; -} - -static PyObject * -regobj_match(regexobject *re, PyObject *args) -{ - PyObject *argstring; - char *buffer; - int size; - int offset = 0; - int result; - - if (!PyArg_ParseTuple(args, "O|i:match", &argstring, &offset)) - return NULL; - if (!PyArg_Parse(argstring, "t#", &buffer, &size)) - return NULL; - - if (offset < 0 || offset > size) { - PyErr_SetString(RegexError, "match offset out of range"); - return NULL; - } - Py_XDECREF(re->re_lastok); - re->re_lastok = NULL; - result = _Py_re_match(&re->re_patbuf, (unsigned char *)buffer, size, offset, - &re->re_regs); - if (result < -1) { - /* Serious failure of some sort; if re_match didn't - set an exception, raise a generic error */ - if (!PyErr_Occurred()) - PyErr_SetString(RegexError, "match failure"); - return NULL; - } - if (result >= 0) { - Py_INCREF(argstring); - re->re_lastok = argstring; - } - return PyInt_FromLong((long)result); /* Length of the match or -1 */ -} - -static PyObject * -regobj_search(regexobject *re, PyObject *args) -{ - PyObject *argstring; - char *buffer; - int size; - int offset = 0; - int range; - int result; - - if (!PyArg_ParseTuple(args, "O|i:search", &argstring, &offset)) - return NULL; - if (!PyArg_Parse(argstring, "t#:search", &buffer, &size)) - return NULL; - - if (offset < 0 || offset > size) { - PyErr_SetString(RegexError, "search offset out of range"); - return NULL; - } - /* NB: In Emacs 18.57, the documentation for re_search[_2] and - the implementation don't match: the documentation states that - |range| positions are tried, while the code tries |range|+1 - positions. It seems more productive to believe the code! */ - range = size - offset; - Py_XDECREF(re->re_lastok); - re->re_lastok = NULL; - result = _Py_re_search(&re->re_patbuf, (unsigned char *)buffer, size, offset, range, - &re->re_regs); - if (result < -1) { - /* Serious failure of some sort; if re_match didn't - set an exception, raise a generic error */ - if (!PyErr_Occurred()) - PyErr_SetString(RegexError, "match failure"); - return NULL; - } - if (result >= 0) { - Py_INCREF(argstring); - re->re_lastok = argstring; - } - return PyInt_FromLong((long)result); /* Position of the match or -1 */ -} - -/* get the group from the regex where index can be a string (group name) or - an integer index [0 .. 99] - */ -static PyObject* -group_from_index(regexobject *re, PyObject *index) -{ - int i, a, b; - char *v; - - if (PyString_Check(index)) - if (re->re_groupindex == NULL || - !(index = PyDict_GetItem(re->re_groupindex, index))) - { - PyErr_SetString(RegexError, - "group() group name doesn't exist"); - return NULL; - } - - i = PyInt_AsLong(index); - if (i == -1 && PyErr_Occurred()) - return NULL; - - if (i < 0 || i >= RE_NREGS) { - PyErr_SetString(RegexError, "group() index out of range"); - return NULL; - } - if (re->re_lastok == NULL) { - PyErr_SetString(RegexError, - "group() only valid after successful match/search"); - return NULL; - } - a = re->re_regs.start[i]; - b = re->re_regs.end[i]; - if (a < 0 || b < 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!(v = PyString_AsString(re->re_lastok))) - return NULL; - - return PyString_FromStringAndSize(v+a, b-a); -} - - -static PyObject * -regobj_group(regexobject *re, PyObject *args) -{ - int n = PyTuple_Size(args); - int i; - PyObject *res = NULL; - - if (n < 0) - return NULL; - if (n == 0) { - PyErr_SetString(PyExc_TypeError, "not enough arguments"); - return NULL; - } - if (n == 1) { - /* return value is a single string */ - PyObject *index = PyTuple_GetItem(args, 0); - if (!index) - return NULL; - - return group_from_index(re, index); - } - - /* return value is a tuple */ - if (!(res = PyTuple_New(n))) - return NULL; - - for (i = 0; i < n; i++) { - PyObject *index = PyTuple_GetItem(args, i); - PyObject *group = NULL; - - if (!index) - goto finally; - if (!(group = group_from_index(re, index))) - goto finally; - if (PyTuple_SetItem(res, i, group) < 0) - goto finally; - } - return res; - - finally: - Py_DECREF(res); - return NULL; -} - - -static struct PyMethodDef reg_methods[] = { - {"match", (PyCFunction)regobj_match, METH_VARARGS}, - {"search", (PyCFunction)regobj_search, METH_VARARGS}, - {"group", (PyCFunction)regobj_group, METH_VARARGS}, - {NULL, NULL} /* sentinel */ -}; - - - -static char* members[] = { - "last", "regs", "translate", - "groupindex", "realpat", "givenpat", - NULL -}; - - -static PyObject * -regobj_getattr(regexobject *re, char *name) -{ - if (strcmp(name, "regs") == 0) { - if (re->re_lastok == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return makeresult(&re->re_regs); - } - if (strcmp(name, "last") == 0) { - if (re->re_lastok == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - Py_INCREF(re->re_lastok); - return re->re_lastok; - } - if (strcmp(name, "translate") == 0) { - if (re->re_translate == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - Py_INCREF(re->re_translate); - return re->re_translate; - } - if (strcmp(name, "groupindex") == 0) { - if (re->re_groupindex == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - Py_INCREF(re->re_groupindex); - return re->re_groupindex; - } - if (strcmp(name, "realpat") == 0) { - if (re->re_realpat == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - Py_INCREF(re->re_realpat); - return re->re_realpat; - } - if (strcmp(name, "givenpat") == 0) { - if (re->re_givenpat == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - Py_INCREF(re->re_givenpat); - return re->re_givenpat; - } - if (strcmp(name, "__members__") == 0) { - int i = 0; - PyObject *list = NULL; - - /* okay, so it's unlikely this list will change that often. - still, it's easier to change it in just one place. - */ - while (members[i]) - i++; - if (!(list = PyList_New(i))) - return NULL; - - i = 0; - while (members[i]) { - PyObject* v = PyString_FromString(members[i]); - if (!v || PyList_SetItem(list, i, v) < 0) { - Py_DECREF(list); - return NULL; - } - i++; - } - return list; - } - return Py_FindMethod(reg_methods, (PyObject *)re, name); -} - -static PyTypeObject Regextype = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "regex.regex", /*tp_name*/ - sizeof(regexobject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)reg_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)regobj_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ -}; - -/* reference counting invariants: - pattern: borrowed - translate: borrowed - givenpat: borrowed - groupindex: transferred -*/ -static PyObject * -newregexobject(PyObject *pattern, PyObject *translate, PyObject *givenpat, PyObject *groupindex) -{ - regexobject *re; - char *pat; - int size; - - if (!PyArg_Parse(pattern, "t#", &pat, &size)) - return NULL; - - if (translate != NULL && PyString_Size(translate) != 256) { - PyErr_SetString(RegexError, - "translation table must be 256 bytes"); - return NULL; - } - re = PyObject_New(regexobject, &Regextype); - if (re != NULL) { - char *error; - re->re_patbuf.buffer = NULL; - re->re_patbuf.allocated = 0; - re->re_patbuf.fastmap = (unsigned char *)re->re_fastmap; - if (translate) { - re->re_patbuf.translate = (unsigned char *)PyString_AsString(translate); - if (!re->re_patbuf.translate) - goto finally; - Py_INCREF(translate); - } - else - re->re_patbuf.translate = NULL; - re->re_translate = translate; - re->re_lastok = NULL; - re->re_groupindex = groupindex; - Py_INCREF(pattern); - re->re_realpat = pattern; - Py_INCREF(givenpat); - re->re_givenpat = givenpat; - error = _Py_re_compile_pattern((unsigned char *)pat, size, &re->re_patbuf); - if (error != NULL) { - PyErr_SetString(RegexError, error); - goto finally; - } - } - return (PyObject *)re; - finally: - Py_DECREF(re); - return NULL; -} - -static PyObject * -regex_compile(PyObject *self, PyObject *args) -{ - PyObject *pat = NULL; - PyObject *tran = NULL; - - if (!PyArg_ParseTuple(args, "S|S:compile", &pat, &tran)) - return NULL; - return newregexobject(pat, tran, pat, NULL); -} - -static PyObject * -symcomp(PyObject *pattern, PyObject *gdict) -{ - char *opat, *oend, *o, *n, *g, *v; - int group_count = 0; - int sz; - int escaped = 0; - char name_buf[128]; - PyObject *npattern; - int require_escape = re_syntax & RE_NO_BK_PARENS ? 0 : 1; - - if (!(opat = PyString_AsString(pattern))) - return NULL; - - if ((sz = PyString_Size(pattern)) < 0) - return NULL; - - oend = opat + sz; - o = opat; - - if (oend == opat) { - Py_INCREF(pattern); - return pattern; - } - - if (!(npattern = PyString_FromStringAndSize((char*)NULL, sz)) || - !(n = PyString_AsString(npattern))) - return NULL; - - while (o < oend) { - if (*o == '(' && escaped == require_escape) { - char *backtrack; - escaped = 0; - ++group_count; - *n++ = *o; - if (++o >= oend || *o != '<') - continue; - /* *o == '<' */ - if (o+1 < oend && *(o+1) == '>') - continue; - backtrack = o; - g = name_buf; - for (++o; o < oend;) { - if (*o == '>') { - PyObject *group_name = NULL; - PyObject *group_index = NULL; - *g++ = '\0'; - group_name = PyString_FromString(name_buf); - group_index = PyInt_FromLong(group_count); - if (group_name == NULL || - group_index == NULL || - PyDict_SetItem(gdict, group_name, - group_index) != 0) - { - Py_XDECREF(group_name); - Py_XDECREF(group_index); - Py_XDECREF(npattern); - return NULL; - } - Py_DECREF(group_name); - Py_DECREF(group_index); - ++o; /* eat the '>' */ - break; - } - if (!isalnum(Py_CHARMASK(*o)) && *o != '_') { - o = backtrack; - break; - } - *g++ = *o++; - } - } - else if (*o == '[' && !escaped) { - *n++ = *o; - ++o; /* eat the char following '[' */ - *n++ = *o; - while (o < oend && *o != ']') { - ++o; - *n++ = *o; - } - if (o < oend) - ++o; - } - else if (*o == '\\') { - escaped = 1; - *n++ = *o; - ++o; - } - else { - escaped = 0; - *n++ = *o; - ++o; - } - } - - if (!(v = PyString_AsString(npattern))) { - Py_DECREF(npattern); - return NULL; - } - /* _PyString_Resize() decrements npattern on failure */ - _PyString_Resize(&npattern, n - v); - return npattern; - -} - -static PyObject * -regex_symcomp(PyObject *self, PyObject *args) -{ - PyObject *pattern; - PyObject *tran = NULL; - PyObject *gdict = NULL; - PyObject *npattern; - PyObject *retval = NULL; - - if (!PyArg_ParseTuple(args, "S|S:symcomp", &pattern, &tran)) - return NULL; - - gdict = PyDict_New(); - if (gdict == NULL || (npattern = symcomp(pattern, gdict)) == NULL) { - Py_XDECREF(gdict); - return NULL; - } - retval = newregexobject(npattern, tran, pattern, gdict); - Py_DECREF(npattern); - return retval; -} - - -static PyObject *cache_pat; -static PyObject *cache_prog; - -static int -update_cache(PyObject *pat) -{ - PyObject *tuple = PyTuple_Pack(1, pat); - int status = 0; - - if (!tuple) - return -1; - - if (pat != cache_pat) { - Py_XDECREF(cache_pat); - cache_pat = NULL; - Py_XDECREF(cache_prog); - cache_prog = regex_compile((PyObject *)NULL, tuple); - if (cache_prog == NULL) { - status = -1; - goto finally; - } - cache_pat = pat; - Py_INCREF(cache_pat); - } - finally: - Py_DECREF(tuple); - return status; -} - -static PyObject * -regex_match(PyObject *self, PyObject *args) -{ - PyObject *pat, *string; - PyObject *tuple, *v; - - if (!PyArg_ParseTuple(args, "SS:match", &pat, &string)) - return NULL; - if (update_cache(pat) < 0) - return NULL; - - if (!(tuple = Py_BuildValue("(S)", string))) - return NULL; - v = regobj_match((regexobject *)cache_prog, tuple); - Py_DECREF(tuple); - return v; -} - -static PyObject * -regex_search(PyObject *self, PyObject *args) -{ - PyObject *pat, *string; - PyObject *tuple, *v; - - if (!PyArg_ParseTuple(args, "SS:search", &pat, &string)) - return NULL; - if (update_cache(pat) < 0) - return NULL; - - if (!(tuple = Py_BuildValue("(S)", string))) - return NULL; - v = regobj_search((regexobject *)cache_prog, tuple); - Py_DECREF(tuple); - return v; -} - -static PyObject * -regex_set_syntax(PyObject *self, PyObject *args) -{ - int syntax; - if (!PyArg_ParseTuple(args, "i:set_syntax", &syntax)) - return NULL; - syntax = re_set_syntax(syntax); - /* wipe the global pattern cache */ - Py_XDECREF(cache_pat); - cache_pat = NULL; - Py_XDECREF(cache_prog); - cache_prog = NULL; - return PyInt_FromLong((long)syntax); -} - -static PyObject * -regex_get_syntax(PyObject *self) -{ - return PyInt_FromLong((long)re_syntax); -} - - -static struct PyMethodDef regex_global_methods[] = { - {"compile", regex_compile, METH_VARARGS}, - {"symcomp", regex_symcomp, METH_VARARGS}, - {"match", regex_match, METH_VARARGS}, - {"search", regex_search, METH_VARARGS}, - {"set_syntax", regex_set_syntax, METH_VARARGS}, - {"get_syntax", (PyCFunction)regex_get_syntax, METH_NOARGS}, - {NULL, NULL} /* sentinel */ -}; - -PyMODINIT_FUNC -initregex(void) -{ - PyObject *m, *d, *v; - int i; - char *s; - - /* Initialize object type */ - Regextype.ob_type = &PyType_Type; - - m = Py_InitModule("regex", regex_global_methods); - if (m == NULL) - return; - d = PyModule_GetDict(m); - - if (PyErr_Warn(PyExc_DeprecationWarning, - "the regex module is deprecated; " - "please use the re module") < 0) - return; - - /* Initialize regex.error exception */ - v = RegexError = PyErr_NewException("regex.error", NULL, NULL); - if (v == NULL || PyDict_SetItemString(d, "error", v) != 0) - goto finally; - - /* Initialize regex.casefold constant */ - if (!(v = PyString_FromStringAndSize((char *)NULL, 256))) - goto finally; - - if (!(s = PyString_AsString(v))) - goto finally; - - for (i = 0; i < 256; i++) { - if (isupper(i)) - s[i] = tolower(i); - else - s[i] = i; - } - if (PyDict_SetItemString(d, "casefold", v) < 0) - goto finally; - Py_DECREF(v); - - if (!PyErr_Occurred()) - return; - finally: - /* Nothing */ ; -} diff --git a/Modules/regexpr.c b/Modules/regexpr.c deleted file mode 100644 index e6a5417..0000000 --- a/Modules/regexpr.c +++ /dev/null @@ -1,2094 +0,0 @@ -/* regexpr.c - * - * Author: Tatu Ylonen - * - * Copyright (c) 1991 Tatu Ylonen, Espoo, Finland - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without - * fee, provided that the above copyright notice appear in all copies. - * This software is provided "as is" without express or implied - * warranty. - * - * Created: Thu Sep 26 17:14:05 1991 ylo - * Last modified: Mon Nov 4 17:06:48 1991 ylo - * Ported to Think C: 19 Jan 1992 guido@cwi.nl - * - * This code draws many ideas from the regular expression packages by - * Henry Spencer of the University of Toronto and Richard Stallman of - * the Free Software Foundation. - * - * Emacs-specific code and syntax table code is almost directly borrowed - * from GNU regexp. - * - * Bugs fixed and lots of reorganization by Jeffrey C. Ollie, April - * 1997 Thanks for bug reports and ideas from Andrew Kuchling, Tim - * Peters, Guido van Rossum, Ka-Ping Yee, Sjoerd Mullender, and - * probably one or two others that I'm forgetting. - * - * $Id$ */ - -#include "Python.h" -#include "regexpr.h" - -/* The original code blithely assumed that sizeof(short) == 2. Not - * always true. Original instances of "(short)x" were replaced by - * SHORT(x), where SHORT is #defined below. */ - -#define SHORT(x) ((x) & 0x8000 ? (x) - 0x10000 : (x)) - -/* The stack implementation is taken from an idea by Andrew Kuchling. - * It's a doubly linked list of arrays. The advantages of this over a - * simple linked list are that the number of mallocs required are - * reduced. It also makes it possible to statically allocate enough - * space so that small patterns don't ever need to call malloc. - * - * The advantages over a single array is that is periodically - * realloced when more space is needed is that we avoid ever copying - * the stack. */ - -/* item_t is the basic stack element. Defined as a union of - * structures so that both registers, failure points, and counters can - * be pushed/popped from the stack. There's nothing built into the - * item to keep track of whether a certain stack item is a register, a - * failure point, or a counter. */ - -typedef union item_t -{ - struct - { - int num; - int level; - unsigned char *start; - unsigned char *end; - } reg; - struct - { - int count; - int level; - int phantom; - unsigned char *code; - unsigned char *text; - } fail; - struct - { - int num; - int level; - int count; - } cntr; -} item_t; - -#define STACK_PAGE_SIZE 256 -#define NUM_REGISTERS 256 - -/* A 'page' of stack items. */ - -typedef struct item_page_t -{ - item_t items[STACK_PAGE_SIZE]; - struct item_page_t *prev; - struct item_page_t *next; -} item_page_t; - - -typedef struct match_state -{ - /* The number of registers that have been pushed onto the stack - * since the last failure point. */ - - int count; - - /* Used to control when registers need to be pushed onto the - * stack. */ - - int level; - - /* The number of failure points on the stack. */ - - int point; - - /* Storage for the registers. Each register consists of two - * pointers to characters. So register N is represented as - * start[N] and end[N]. The pointers must be converted to - * offsets from the beginning of the string before returning the - * registers to the calling program. */ - - unsigned char *start[NUM_REGISTERS]; - unsigned char *end[NUM_REGISTERS]; - - /* Keeps track of whether a register has changed recently. */ - - int changed[NUM_REGISTERS]; - - /* Structure to encapsulate the stack. */ - struct - { - /* index into the current page. If index == 0 and you need - * to pop an item, move to the previous page and set index - * = STACK_PAGE_SIZE - 1. Otherwise decrement index to - * push a page. If index == STACK_PAGE_SIZE and you need - * to push a page move to the next page and set index = - * 0. If there is no new next page, allocate a new page - * and link it in. Otherwise, increment index to push a - * page. */ - - int index; - item_page_t *current; /* Pointer to the current page. */ - item_page_t first; /* First page is statically allocated. */ - } stack; -} match_state; - -/* Initialize a state object */ - -/* #define NEW_STATE(state) \ */ -/* memset(&state, 0, (void *)(&state.stack) - (void *)(&state)); \ */ -/* state.stack.current = &state.stack.first; \ */ -/* state.stack.first.prev = NULL; \ */ -/* state.stack.first.next = NULL; \ */ -/* state.stack.index = 0; \ */ -/* state.level = 1 */ - -#define NEW_STATE(state, nregs) \ -{ \ - int i; \ - for (i = 0; i < nregs; i++) \ - { \ - state.start[i] = NULL; \ - state.end[i] = NULL; \ - state.changed[i] = 0; \ - } \ - state.stack.current = &state.stack.first; \ - state.stack.first.prev = NULL; \ - state.stack.first.next = NULL; \ - state.stack.index = 0; \ - state.level = 1; \ - state.count = 0; \ - state.level = 0; \ - state.point = 0; \ -} - -/* Free any memory that might have been malloc'd */ - -#define FREE_STATE(state) \ -while(state.stack.first.next != NULL) \ -{ \ - state.stack.current = state.stack.first.next; \ - state.stack.first.next = state.stack.current->next; \ - free(state.stack.current); \ -} - -/* Discard the top 'count' stack items. */ - -#define STACK_DISCARD(stack, count, on_error) \ -stack.index -= count; \ -while (stack.index < 0) \ -{ \ - if (stack.current->prev == NULL) \ - on_error; \ - stack.current = stack.current->prev; \ - stack.index += STACK_PAGE_SIZE; \ -} - -/* Store a pointer to the previous item on the stack. Used to pop an - * item off of the stack. */ - -#define STACK_PREV(stack, top, on_error) \ -if (stack.index == 0) \ -{ \ - if (stack.current->prev == NULL) \ - on_error; \ - stack.current = stack.current->prev; \ - stack.index = STACK_PAGE_SIZE - 1; \ -} \ -else \ -{ \ - stack.index--; \ -} \ -top = &(stack.current->items[stack.index]) - -/* Store a pointer to the next item on the stack. Used to push an item - * on to the stack. */ - -#define STACK_NEXT(stack, top, on_error) \ -if (stack.index == STACK_PAGE_SIZE) \ -{ \ - if (stack.current->next == NULL) \ - { \ - stack.current->next = (item_page_t *)malloc(sizeof(item_page_t)); \ - if (stack.current->next == NULL) \ - on_error; \ - stack.current->next->prev = stack.current; \ - stack.current->next->next = NULL; \ - } \ - stack.current = stack.current->next; \ - stack.index = 0; \ -} \ -top = &(stack.current->items[stack.index++]) - -/* Store a pointer to the item that is 'count' items back in the - * stack. STACK_BACK(stack, top, 1, on_error) is equivalent to - * STACK_TOP(stack, top, on_error). */ - -#define STACK_BACK(stack, top, count, on_error) \ -{ \ - int index; \ - item_page_t *current; \ - current = stack.current; \ - index = stack.index - (count); \ - while (index < 0) \ - { \ - if (current->prev == NULL) \ - on_error; \ - current = current->prev; \ - index += STACK_PAGE_SIZE; \ - } \ - top = &(current->items[index]); \ -} - -/* Store a pointer to the top item on the stack. Execute the - * 'on_error' code if there are no items on the stack. */ - -#define STACK_TOP(stack, top, on_error) \ -if (stack.index == 0) \ -{ \ - if (stack.current->prev == NULL) \ - on_error; \ - top = &(stack.current->prev->items[STACK_PAGE_SIZE - 1]); \ -} \ -else \ -{ \ - top = &(stack.current->items[stack.index - 1]); \ -} - -/* Test to see if the stack is empty */ - -#define STACK_EMPTY(stack) ((stack.index == 0) && \ - (stack.current->prev == NULL)) - -/* Return the start of register 'reg' */ - -#define GET_REG_START(state, reg) (state.start[reg]) - -/* Return the end of register 'reg' */ - -#define GET_REG_END(state, reg) (state.end[reg]) - -/* Set the start of register 'reg'. If the state of the register needs - * saving, push it on the stack. */ - -#define SET_REG_START(state, reg, text, on_error) \ -if(state.changed[reg] < state.level) \ -{ \ - item_t *item; \ - STACK_NEXT(state.stack, item, on_error); \ - item->reg.num = reg; \ - item->reg.start = state.start[reg]; \ - item->reg.end = state.end[reg]; \ - item->reg.level = state.changed[reg]; \ - state.changed[reg] = state.level; \ - state.count++; \ -} \ -state.start[reg] = text - -/* Set the end of register 'reg'. If the state of the register needs - * saving, push it on the stack. */ - -#define SET_REG_END(state, reg, text, on_error) \ -if(state.changed[reg] < state.level) \ -{ \ - item_t *item; \ - STACK_NEXT(state.stack, item, on_error); \ - item->reg.num = reg; \ - item->reg.start = state.start[reg]; \ - item->reg.end = state.end[reg]; \ - item->reg.level = state.changed[reg]; \ - state.changed[reg] = state.level; \ - state.count++; \ -} \ -state.end[reg] = text - -#define PUSH_FAILURE(state, xcode, xtext, on_error) \ -{ \ - item_t *item; \ - STACK_NEXT(state.stack, item, on_error); \ - item->fail.code = xcode; \ - item->fail.text = xtext; \ - item->fail.count = state.count; \ - item->fail.level = state.level; \ - item->fail.phantom = 0; \ - state.count = 0; \ - state.level++; \ - state.point++; \ -} - -/* Update the last failure point with a new position in the text. */ - -#define UPDATE_FAILURE(state, xtext, on_error) \ -{ \ - item_t *item; \ - STACK_BACK(state.stack, item, state.count + 1, on_error); \ - if (!item->fail.phantom) \ - { \ - item_t *item2; \ - STACK_NEXT(state.stack, item2, on_error); \ - item2->fail.code = item->fail.code; \ - item2->fail.text = xtext; \ - item2->fail.count = state.count; \ - item2->fail.level = state.level; \ - item2->fail.phantom = 1; \ - state.count = 0; \ - state.level++; \ - state.point++; \ - } \ - else \ - { \ - STACK_DISCARD(state.stack, state.count, on_error); \ - STACK_TOP(state.stack, item, on_error); \ - item->fail.text = xtext; \ - state.count = 0; \ - state.level++; \ - } \ -} - -#define POP_FAILURE(state, xcode, xtext, on_empty, on_error) \ -{ \ - item_t *item; \ - do \ - { \ - while(state.count > 0) \ - { \ - STACK_PREV(state.stack, item, on_error); \ - state.start[item->reg.num] = item->reg.start; \ - state.end[item->reg.num] = item->reg.end; \ - state.changed[item->reg.num] = item->reg.level; \ - state.count--; \ - } \ - STACK_PREV(state.stack, item, on_empty); \ - xcode = item->fail.code; \ - xtext = item->fail.text; \ - state.count = item->fail.count; \ - state.level = item->fail.level; \ - state.point--; \ - } \ - while (item->fail.text == NULL); \ -} - -enum regexp_compiled_ops /* opcodes for compiled regexp */ -{ - Cend, /* end of pattern reached */ - Cbol, /* beginning of line */ - Ceol, /* end of line */ - Cset, /* character set. Followed by 32 bytes of set. */ - Cexact, /* followed by a byte to match */ - Canychar, /* matches any character except newline */ - Cstart_memory, /* set register start addr (followed by reg number) */ - Cend_memory, /* set register end addr (followed by reg number) */ - Cmatch_memory, /* match a duplicate of reg contents (regnum follows)*/ - Cjump, /* followed by two bytes (lsb,msb) of displacement. */ - Cstar_jump, /* will change to jump/update_failure_jump at runtime */ - Cfailure_jump, /* jump to addr on failure */ - Cupdate_failure_jump, /* update topmost failure point and jump */ - Cdummy_failure_jump, /* push a dummy failure point and jump */ - Cbegbuf, /* match at beginning of buffer */ - Cendbuf, /* match at end of buffer */ - Cwordbeg, /* match at beginning of word */ - Cwordend, /* match at end of word */ - Cwordbound, /* match if at word boundary */ - Cnotwordbound, /* match if not at word boundary */ - Csyntaxspec, /* matches syntax code (1 byte follows) */ - Cnotsyntaxspec, /* matches if syntax code does not match (1 byte follows) */ - Crepeat1 -}; - -enum regexp_syntax_op /* syntax codes for plain and quoted characters */ -{ - Rend, /* special code for end of regexp */ - Rnormal, /* normal character */ - Ranychar, /* any character except newline */ - Rquote, /* the quote character */ - Rbol, /* match beginning of line */ - Reol, /* match end of line */ - Roptional, /* match preceding expression optionally */ - Rstar, /* match preceding expr zero or more times */ - Rplus, /* match preceding expr one or more times */ - Ror, /* match either of alternatives */ - Ropenpar, /* opening parenthesis */ - Rclosepar, /* closing parenthesis */ - Rmemory, /* match memory register */ - Rextended_memory, /* \vnn to match registers 10-99 */ - Ropenset, /* open set. Internal syntax hard-coded below. */ - /* the following are gnu extensions to "normal" regexp syntax */ - Rbegbuf, /* beginning of buffer */ - Rendbuf, /* end of buffer */ - Rwordchar, /* word character */ - Rnotwordchar, /* not word character */ - Rwordbeg, /* beginning of word */ - Rwordend, /* end of word */ - Rwordbound, /* word bound */ - Rnotwordbound, /* not word bound */ - Rnum_ops -}; - -static int re_compile_initialized = 0; -static int regexp_syntax = 0; -int re_syntax = 0; /* Exported copy of regexp_syntax */ -static unsigned char regexp_plain_ops[256]; -static unsigned char regexp_quoted_ops[256]; -static unsigned char regexp_precedences[Rnum_ops]; -static int regexp_context_indep_ops; -static int regexp_ansi_sequences; - -#define NUM_LEVELS 5 /* number of precedence levels in use */ -#define MAX_NESTING 100 /* max nesting level of operators */ - -#define SYNTAX(ch) re_syntax_table[(unsigned char)(ch)] - -unsigned char re_syntax_table[256]; - -void re_compile_initialize(void) -{ - int a; - - static int syntax_table_inited = 0; - - if (!syntax_table_inited) - { - syntax_table_inited = 1; - memset(re_syntax_table, 0, 256); - for (a = 'a'; a <= 'z'; a++) - re_syntax_table[a] = Sword; - for (a = 'A'; a <= 'Z'; a++) - re_syntax_table[a] = Sword; - for (a = '0'; a <= '9'; a++) - re_syntax_table[a] = Sword | Sdigit | Shexdigit; - for (a = '0'; a <= '7'; a++) - re_syntax_table[a] |= Soctaldigit; - for (a = 'A'; a <= 'F'; a++) - re_syntax_table[a] |= Shexdigit; - for (a = 'a'; a <= 'f'; a++) - re_syntax_table[a] |= Shexdigit; - re_syntax_table['_'] = Sword; - for (a = 9; a <= 13; a++) - re_syntax_table[a] = Swhitespace; - re_syntax_table[' '] = Swhitespace; - } - re_compile_initialized = 1; - for (a = 0; a < 256; a++) - { - regexp_plain_ops[a] = Rnormal; - regexp_quoted_ops[a] = Rnormal; - } - for (a = '0'; a <= '9'; a++) - regexp_quoted_ops[a] = Rmemory; - regexp_plain_ops['\134'] = Rquote; - if (regexp_syntax & RE_NO_BK_PARENS) - { - regexp_plain_ops['('] = Ropenpar; - regexp_plain_ops[')'] = Rclosepar; - } - else - { - regexp_quoted_ops['('] = Ropenpar; - regexp_quoted_ops[')'] = Rclosepar; - } - if (regexp_syntax & RE_NO_BK_VBAR) - regexp_plain_ops['\174'] = Ror; - else - regexp_quoted_ops['\174'] = Ror; - regexp_plain_ops['*'] = Rstar; - if (regexp_syntax & RE_BK_PLUS_QM) - { - regexp_quoted_ops['+'] = Rplus; - regexp_quoted_ops['?'] = Roptional; - } - else - { - regexp_plain_ops['+'] = Rplus; - regexp_plain_ops['?'] = Roptional; - } - if (regexp_syntax & RE_NEWLINE_OR) - regexp_plain_ops['\n'] = Ror; - regexp_plain_ops['\133'] = Ropenset; - regexp_plain_ops['\136'] = Rbol; - regexp_plain_ops['$'] = Reol; - regexp_plain_ops['.'] = Ranychar; - if (!(regexp_syntax & RE_NO_GNU_EXTENSIONS)) - { - regexp_quoted_ops['w'] = Rwordchar; - regexp_quoted_ops['W'] = Rnotwordchar; - regexp_quoted_ops['<'] = Rwordbeg; - regexp_quoted_ops['>'] = Rwordend; - regexp_quoted_ops['b'] = Rwordbound; - regexp_quoted_ops['B'] = Rnotwordbound; - regexp_quoted_ops['`'] = Rbegbuf; - regexp_quoted_ops['\''] = Rendbuf; - } - if (regexp_syntax & RE_ANSI_HEX) - regexp_quoted_ops['v'] = Rextended_memory; - for (a = 0; a < Rnum_ops; a++) - regexp_precedences[a] = 4; - if (regexp_syntax & RE_TIGHT_VBAR) - { - regexp_precedences[Ror] = 3; - regexp_precedences[Rbol] = 2; - regexp_precedences[Reol] = 2; - } - else - { - regexp_precedences[Ror] = 2; - regexp_precedences[Rbol] = 3; - regexp_precedences[Reol] = 3; - } - regexp_precedences[Rclosepar] = 1; - regexp_precedences[Rend] = 0; - regexp_context_indep_ops = (regexp_syntax & RE_CONTEXT_INDEP_OPS) != 0; - regexp_ansi_sequences = (regexp_syntax & RE_ANSI_HEX) != 0; -} - -int re_set_syntax(int syntax) -{ - int ret; - - ret = regexp_syntax; - regexp_syntax = syntax; - re_syntax = syntax; /* Exported copy */ - re_compile_initialize(); - return ret; -} - -static int hex_char_to_decimal(int ch) -{ - if (ch >= '0' && ch <= '9') - return ch - '0'; - if (ch >= 'a' && ch <= 'f') - return ch - 'a' + 10; - if (ch >= 'A' && ch <= 'F') - return ch - 'A' + 10; - return 16; -} - -static void re_compile_fastmap_aux(unsigned char *code, int pos, - unsigned char *visited, - unsigned char *can_be_null, - unsigned char *fastmap) -{ - int a; - int b; - int syntaxcode; - - if (visited[pos]) - return; /* we have already been here */ - visited[pos] = 1; - for (;;) - switch (code[pos++]) { - case Cend: - { - *can_be_null = 1; - return; - } - case Cbol: - case Cbegbuf: - case Cendbuf: - case Cwordbeg: - case Cwordend: - case Cwordbound: - case Cnotwordbound: - { - for (a = 0; a < 256; a++) - fastmap[a] = 1; - break; - } - case Csyntaxspec: - { - syntaxcode = code[pos++]; - for (a = 0; a < 256; a++) - if (SYNTAX(a) & syntaxcode) - fastmap[a] = 1; - return; - } - case Cnotsyntaxspec: - { - syntaxcode = code[pos++]; - for (a = 0; a < 256; a++) - if (!(SYNTAX(a) & syntaxcode) ) - fastmap[a] = 1; - return; - } - case Ceol: - { - fastmap['\n'] = 1; - if (*can_be_null == 0) - *can_be_null = 2; /* can match null, but only at end of buffer*/ - return; - } - case Cset: - { - for (a = 0; a < 256/8; a++) - if (code[pos + a] != 0) - for (b = 0; b < 8; b++) - if (code[pos + a] & (1 << b)) - fastmap[(a << 3) + b] = 1; - pos += 256/8; - return; - } - case Cexact: - { - fastmap[(unsigned char)code[pos]] = 1; - return; - } - case Canychar: - { - for (a = 0; a < 256; a++) - if (a != '\n') - fastmap[a] = 1; - return; - } - case Cstart_memory: - case Cend_memory: - { - pos++; - break; - } - case Cmatch_memory: - { - for (a = 0; a < 256; a++) - fastmap[a] = 1; - *can_be_null = 1; - return; - } - case Cjump: - case Cdummy_failure_jump: - case Cupdate_failure_jump: - case Cstar_jump: - { - a = (unsigned char)code[pos++]; - a |= (unsigned char)code[pos++] << 8; - pos += (int)SHORT(a); - if (visited[pos]) - { - /* argh... the regexp contains empty loops. This is not - good, as this may cause a failure stack overflow when - matching. Oh well. */ - /* this path leads nowhere; pursue other paths. */ - return; - } - visited[pos] = 1; - break; - } - case Cfailure_jump: - { - a = (unsigned char)code[pos++]; - a |= (unsigned char)code[pos++] << 8; - a = pos + (int)SHORT(a); - re_compile_fastmap_aux(code, a, visited, can_be_null, fastmap); - break; - } - case Crepeat1: - { - pos += 2; - break; - } - default: - { - PyErr_SetString(PyExc_SystemError, "Unknown regex opcode: memory corrupted?"); - return; - /*NOTREACHED*/ - } - } -} - -static int re_do_compile_fastmap(unsigned char *buffer, int used, int pos, - unsigned char *can_be_null, - unsigned char *fastmap) -{ - unsigned char small_visited[512], *visited; - - if (used <= sizeof(small_visited)) - visited = small_visited; - else - { - visited = malloc(used); - if (!visited) - return 0; - } - *can_be_null = 0; - memset(fastmap, 0, 256); - memset(visited, 0, used); - re_compile_fastmap_aux(buffer, pos, visited, can_be_null, fastmap); - if (visited != small_visited) - free(visited); - return 1; -} - -void re_compile_fastmap(regexp_t bufp) -{ - if (!bufp->fastmap || bufp->fastmap_accurate) - return; - assert(bufp->used > 0); - if (!re_do_compile_fastmap(bufp->buffer, - bufp->used, - 0, - &bufp->can_be_null, - bufp->fastmap)) - return; - if (PyErr_Occurred()) return; - if (bufp->buffer[0] == Cbol) - bufp->anchor = 1; /* begline */ - else - if (bufp->buffer[0] == Cbegbuf) - bufp->anchor = 2; /* begbuf */ - else - bufp->anchor = 0; /* none */ - bufp->fastmap_accurate = 1; -} - -/* - * star is coded as: - * 1: failure_jump 2 - * ... code for operand of star - * star_jump 1 - * 2: ... code after star - * - * We change the star_jump to update_failure_jump if we can determine - * that it is safe to do so; otherwise we change it to an ordinary - * jump. - * - * plus is coded as - * - * jump 2 - * 1: failure_jump 3 - * 2: ... code for operand of plus - * star_jump 1 - * 3: ... code after plus - * - * For star_jump considerations this is processed identically to star. - * - */ - -static int re_optimize_star_jump(regexp_t bufp, unsigned char *code) -{ - unsigned char map[256]; - unsigned char can_be_null; - unsigned char *p1; - unsigned char *p2; - unsigned char ch; - int a; - int b; - int num_instructions = 0; - - a = (unsigned char)*code++; - a |= (unsigned char)*code++ << 8; - a = (int)SHORT(a); - - p1 = code + a + 3; /* skip the failure_jump */ - /* Check that the jump is within the pattern */ - if (p1buffer || bufp->buffer+bufp->usedbuffer, bufp->used, - (int)(p2 - bufp->buffer), - &can_be_null, map)) - goto make_normal_jump; - - /* If we might introduce a new update point inside the - * loop, we can't optimize because then update_jump would - * update a wrong failure point. Thus we have to be - * quite careful here. - */ - - /* loop until we find something that consumes a character */ - loop_p1: - num_instructions++; - switch (*p1++) - { - case Cbol: - case Ceol: - case Cbegbuf: - case Cendbuf: - case Cwordbeg: - case Cwordend: - case Cwordbound: - case Cnotwordbound: - { - goto loop_p1; - } - case Cstart_memory: - case Cend_memory: - { - p1++; - goto loop_p1; - } - case Cexact: - { - ch = (unsigned char)*p1++; - if (map[(int)ch]) - goto make_normal_jump; - break; - } - case Canychar: - { - for (b = 0; b < 256; b++) - if (b != '\n' && map[b]) - goto make_normal_jump; - break; - } - case Cset: - { - for (b = 0; b < 256; b++) - if ((p1[b >> 3] & (1 << (b & 7))) && map[b]) - goto make_normal_jump; - p1 += 256/8; - break; - } - default: - { - goto make_normal_jump; - } - } - /* now we know that we can't backtrack. */ - while (p1 != p2 - 3) - { - num_instructions++; - switch (*p1++) - { - case Cend: - { - return 0; - } - case Cbol: - case Ceol: - case Canychar: - case Cbegbuf: - case Cendbuf: - case Cwordbeg: - case Cwordend: - case Cwordbound: - case Cnotwordbound: - { - break; - } - case Cset: - { - p1 += 256/8; - break; - } - case Cexact: - case Cstart_memory: - case Cend_memory: - case Cmatch_memory: - case Csyntaxspec: - case Cnotsyntaxspec: - { - p1++; - break; - } - case Cjump: - case Cstar_jump: - case Cfailure_jump: - case Cupdate_failure_jump: - case Cdummy_failure_jump: - { - goto make_normal_jump; - } - default: - { - return 0; - } - } - } - - /* make_update_jump: */ - code -= 3; - a += 3; /* jump to after the Cfailure_jump */ - code[0] = Cupdate_failure_jump; - code[1] = a & 0xff; - code[2] = a >> 8; - if (num_instructions > 1) - return 1; - assert(num_instructions == 1); - /* if the only instruction matches a single character, we can do - * better */ - p1 = code + 3 + a; /* start of sole instruction */ - if (*p1 == Cset || *p1 == Cexact || *p1 == Canychar || - *p1 == Csyntaxspec || *p1 == Cnotsyntaxspec) - code[0] = Crepeat1; - return 1; - - make_normal_jump: - code -= 3; - *code = Cjump; - return 1; -} - -static int re_optimize(regexp_t bufp) -{ - unsigned char *code; - - code = bufp->buffer; - - while(1) - { - switch (*code++) - { - case Cend: - { - return 1; - } - case Canychar: - case Cbol: - case Ceol: - case Cbegbuf: - case Cendbuf: - case Cwordbeg: - case Cwordend: - case Cwordbound: - case Cnotwordbound: - { - break; - } - case Cset: - { - code += 256/8; - break; - } - case Cexact: - case Cstart_memory: - case Cend_memory: - case Cmatch_memory: - case Csyntaxspec: - case Cnotsyntaxspec: - { - code++; - break; - } - case Cstar_jump: - { - if (!re_optimize_star_jump(bufp, code)) - { - return 0; - } - /* fall through */ - } - case Cupdate_failure_jump: - case Cjump: - case Cdummy_failure_jump: - case Cfailure_jump: - case Crepeat1: - { - code += 2; - break; - } - default: - { - return 0; - } - } - } -} - -#define NEXTCHAR(var) \ -{ \ - if (pos >= size) \ - goto ends_prematurely; \ - (var) = regex[pos]; \ - pos++; \ -} - -#define ALLOC(amount) \ -{ \ - if (pattern_offset+(amount) > alloc) \ - { \ - alloc += 256 + (amount); \ - pattern = realloc(pattern, alloc); \ - if (!pattern) \ - goto out_of_memory; \ - } \ -} - -#define STORE(ch) pattern[pattern_offset++] = (ch) - -#define CURRENT_LEVEL_START (starts[starts_base + current_level]) - -#define SET_LEVEL_START starts[starts_base + current_level] = pattern_offset - -#define PUSH_LEVEL_STARTS \ -if (starts_base < (MAX_NESTING-1)*NUM_LEVELS) \ - starts_base += NUM_LEVELS; \ -else \ - goto too_complex \ - -#define POP_LEVEL_STARTS starts_base -= NUM_LEVELS - -#define PUT_ADDR(offset,addr) \ -{ \ - int disp = (addr) - (offset) - 2; \ - pattern[(offset)] = disp & 0xff; \ - pattern[(offset)+1] = (disp>>8) & 0xff; \ -} - -#define INSERT_JUMP(pos,type,addr) \ -{ \ - int a, p = (pos), t = (type), ad = (addr); \ - for (a = pattern_offset - 1; a >= p; a--) \ - pattern[a + 3] = pattern[a]; \ - pattern[p] = t; \ - PUT_ADDR(p+1,ad); \ - pattern_offset += 3; \ -} - -#define SETBIT(buf,offset,bit) (buf)[(offset)+(bit)/8] |= (1<<((bit) & 7)) - -#define SET_FIELDS \ -{ \ - bufp->allocated = alloc; \ - bufp->buffer = pattern; \ - bufp->used = pattern_offset; \ -} - -#define GETHEX(var) \ -{ \ - unsigned char gethex_ch, gethex_value; \ - NEXTCHAR(gethex_ch); \ - gethex_value = hex_char_to_decimal(gethex_ch); \ - if (gethex_value == 16) \ - goto hex_error; \ - NEXTCHAR(gethex_ch); \ - gethex_ch = hex_char_to_decimal(gethex_ch); \ - if (gethex_ch == 16) \ - goto hex_error; \ - (var) = gethex_value * 16 + gethex_ch; \ -} - -#define ANSI_TRANSLATE(ch) \ -{ \ - switch (ch) \ - { \ - case 'a': \ - case 'A': \ - { \ - ch = 7; /* audible bell */ \ - break; \ - } \ - case 'b': \ - case 'B': \ - { \ - ch = 8; /* backspace */ \ - break; \ - } \ - case 'f': \ - case 'F': \ - { \ - ch = 12; /* form feed */ \ - break; \ - } \ - case 'n': \ - case 'N': \ - { \ - ch = 10; /* line feed */ \ - break; \ - } \ - case 'r': \ - case 'R': \ - { \ - ch = 13; /* carriage return */ \ - break; \ - } \ - case 't': \ - case 'T': \ - { \ - ch = 9; /* tab */ \ - break; \ - } \ - case 'v': \ - case 'V': \ - { \ - ch = 11; /* vertical tab */ \ - break; \ - } \ - case 'x': /* hex code */ \ - case 'X': \ - { \ - GETHEX(ch); \ - break; \ - } \ - default: \ - { \ - /* other characters passed through */ \ - if (translate) \ - ch = translate[(unsigned char)ch]; \ - break; \ - } \ - } \ -} - -char *re_compile_pattern(unsigned char *regex, int size, regexp_t bufp) -{ - int a; - int pos; - int op; - int current_level; - int level; - int opcode; - int pattern_offset = 0, alloc; - int starts[NUM_LEVELS * MAX_NESTING]; - int starts_base; - int future_jumps[MAX_NESTING]; - int num_jumps; - unsigned char ch = '\0'; - unsigned char *pattern; - unsigned char *translate; - int next_register; - int paren_depth; - int num_open_registers; - int open_registers[RE_NREGS]; - int beginning_context; - - if (!re_compile_initialized) - re_compile_initialize(); - bufp->used = 0; - bufp->fastmap_accurate = 0; - bufp->uses_registers = 1; - bufp->num_registers = 1; - translate = bufp->translate; - pattern = bufp->buffer; - alloc = bufp->allocated; - if (alloc == 0 || pattern == NULL) - { - alloc = 256; - pattern = malloc(alloc); - if (!pattern) - goto out_of_memory; - } - pattern_offset = 0; - starts_base = 0; - num_jumps = 0; - current_level = 0; - SET_LEVEL_START; - num_open_registers = 0; - next_register = 1; - paren_depth = 0; - beginning_context = 1; - op = -1; - /* we use Rend dummy to ensure that pending jumps are updated - (due to low priority of Rend) before exiting the loop. */ - pos = 0; - while (op != Rend) - { - if (pos >= size) - op = Rend; - else - { - NEXTCHAR(ch); - if (translate) - ch = translate[(unsigned char)ch]; - op = regexp_plain_ops[(unsigned char)ch]; - if (op == Rquote) - { - NEXTCHAR(ch); - op = regexp_quoted_ops[(unsigned char)ch]; - if (op == Rnormal && regexp_ansi_sequences) - ANSI_TRANSLATE(ch); - } - } - level = regexp_precedences[op]; - /* printf("ch='%c' op=%d level=%d current_level=%d - curlevstart=%d\n", ch, op, level, current_level, - CURRENT_LEVEL_START); */ - if (level > current_level) - { - for (current_level++; current_level < level; current_level++) - SET_LEVEL_START; - SET_LEVEL_START; - } - else - if (level < current_level) - { - current_level = level; - for (;num_jumps > 0 && - future_jumps[num_jumps-1] >= CURRENT_LEVEL_START; - num_jumps--) - PUT_ADDR(future_jumps[num_jumps-1], pattern_offset); - } - switch (op) - { - case Rend: - { - break; - } - case Rnormal: - { - normal_char: - opcode = Cexact; - store_opcode_and_arg: /* opcode & ch must be set */ - SET_LEVEL_START; - ALLOC(2); - STORE(opcode); - STORE(ch); - break; - } - case Ranychar: - { - opcode = Canychar; - store_opcode: - SET_LEVEL_START; - ALLOC(1); - STORE(opcode); - break; - } - case Rquote: - { - Py_FatalError("Rquote"); - /*NOTREACHED*/ - } - case Rbol: - { - if (!beginning_context) { - if (regexp_context_indep_ops) - goto op_error; - else - goto normal_char; - } - opcode = Cbol; - goto store_opcode; - } - case Reol: - { - if (!((pos >= size) || - ((regexp_syntax & RE_NO_BK_VBAR) ? - (regex[pos] == '\174') : - (pos+1 < size && regex[pos] == '\134' && - regex[pos+1] == '\174')) || - ((regexp_syntax & RE_NO_BK_PARENS)? - (regex[pos] == ')'): - (pos+1 < size && regex[pos] == '\134' && - regex[pos+1] == ')')))) { - if (regexp_context_indep_ops) - goto op_error; - else - goto normal_char; - } - opcode = Ceol; - goto store_opcode; - /* NOTREACHED */ - break; - } - case Roptional: - { - if (beginning_context) { - if (regexp_context_indep_ops) - goto op_error; - else - goto normal_char; - } - if (CURRENT_LEVEL_START == pattern_offset) - break; /* ignore empty patterns for ? */ - ALLOC(3); - INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, - pattern_offset + 3); - break; - } - case Rstar: - case Rplus: - { - if (beginning_context) { - if (regexp_context_indep_ops) - goto op_error; - else - goto normal_char; - } - if (CURRENT_LEVEL_START == pattern_offset) - break; /* ignore empty patterns for + and * */ - ALLOC(9); - INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, - pattern_offset + 6); - INSERT_JUMP(pattern_offset, Cstar_jump, CURRENT_LEVEL_START); - if (op == Rplus) /* jump over initial failure_jump */ - INSERT_JUMP(CURRENT_LEVEL_START, Cdummy_failure_jump, - CURRENT_LEVEL_START + 6); - break; - } - case Ror: - { - ALLOC(6); - INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, - pattern_offset + 6); - if (num_jumps >= MAX_NESTING) - goto too_complex; - STORE(Cjump); - future_jumps[num_jumps++] = pattern_offset; - STORE(0); - STORE(0); - SET_LEVEL_START; - break; - } - case Ropenpar: - { - SET_LEVEL_START; - if (next_register < RE_NREGS) - { - bufp->uses_registers = 1; - ALLOC(2); - STORE(Cstart_memory); - STORE(next_register); - open_registers[num_open_registers++] = next_register; - bufp->num_registers++; - next_register++; - } - paren_depth++; - PUSH_LEVEL_STARTS; - current_level = 0; - SET_LEVEL_START; - break; - } - case Rclosepar: - { - if (paren_depth <= 0) - goto parenthesis_error; - POP_LEVEL_STARTS; - current_level = regexp_precedences[Ropenpar]; - paren_depth--; - if (paren_depth < num_open_registers) - { - bufp->uses_registers = 1; - ALLOC(2); - STORE(Cend_memory); - num_open_registers--; - STORE(open_registers[num_open_registers]); - } - break; - } - case Rmemory: - { - if (ch == '0') - goto bad_match_register; - assert(ch >= '0' && ch <= '9'); - bufp->uses_registers = 1; - opcode = Cmatch_memory; - ch -= '0'; - goto store_opcode_and_arg; - } - case Rextended_memory: - { - NEXTCHAR(ch); - if (ch < '0' || ch > '9') - goto bad_match_register; - NEXTCHAR(a); - if (a < '0' || a > '9') - goto bad_match_register; - ch = 10 * (a - '0') + ch - '0'; - if (ch == 0 || ch >= RE_NREGS) - goto bad_match_register; - bufp->uses_registers = 1; - opcode = Cmatch_memory; - goto store_opcode_and_arg; - } - case Ropenset: - { - int complement; - int prev; - int offset; - int range; - int firstchar; - - SET_LEVEL_START; - ALLOC(1+256/8); - STORE(Cset); - offset = pattern_offset; - for (a = 0; a < 256/8; a++) - STORE(0); - NEXTCHAR(ch); - if (translate) - ch = translate[(unsigned char)ch]; - if (ch == '\136') - { - complement = 1; - NEXTCHAR(ch); - if (translate) - ch = translate[(unsigned char)ch]; - } - else - complement = 0; - prev = -1; - range = 0; - firstchar = 1; - while (ch != '\135' || firstchar) - { - firstchar = 0; - if (regexp_ansi_sequences && ch == '\134') - { - NEXTCHAR(ch); - ANSI_TRANSLATE(ch); - } - if (range) - { - for (a = prev; a <= (int)ch; a++) - SETBIT(pattern, offset, a); - prev = -1; - range = 0; - } - else - if (prev != -1 && ch == '-') - range = 1; - else - { - SETBIT(pattern, offset, ch); - prev = ch; - } - NEXTCHAR(ch); - if (translate) - ch = translate[(unsigned char)ch]; - } - if (range) - SETBIT(pattern, offset, '-'); - if (complement) - { - for (a = 0; a < 256/8; a++) - pattern[offset+a] ^= 0xff; - } - break; - } - case Rbegbuf: - { - opcode = Cbegbuf; - goto store_opcode; - } - case Rendbuf: - { - opcode = Cendbuf; - goto store_opcode; - } - case Rwordchar: - { - opcode = Csyntaxspec; - ch = Sword; - goto store_opcode_and_arg; - } - case Rnotwordchar: - { - opcode = Cnotsyntaxspec; - ch = Sword; - goto store_opcode_and_arg; - } - case Rwordbeg: - { - opcode = Cwordbeg; - goto store_opcode; - } - case Rwordend: - { - opcode = Cwordend; - goto store_opcode; - } - case Rwordbound: - { - opcode = Cwordbound; - goto store_opcode; - } - case Rnotwordbound: - { - opcode = Cnotwordbound; - goto store_opcode; - } - default: - { - abort(); - } - } - beginning_context = (op == Ropenpar || op == Ror); - } - if (starts_base != 0) - goto parenthesis_error; - assert(num_jumps == 0); - ALLOC(1); - STORE(Cend); - SET_FIELDS; - if(!re_optimize(bufp)) - return "Optimization error"; - return NULL; - - op_error: - SET_FIELDS; - return "Badly placed special character"; - - bad_match_register: - SET_FIELDS; - return "Bad match register number"; - - hex_error: - SET_FIELDS; - return "Bad hexadecimal number"; - - parenthesis_error: - SET_FIELDS; - return "Badly placed parenthesis"; - - out_of_memory: - SET_FIELDS; - return "Out of memory"; - - ends_prematurely: - SET_FIELDS; - return "Regular expression ends prematurely"; - - too_complex: - SET_FIELDS; - return "Regular expression too complex"; -} - -#undef CHARAT -#undef NEXTCHAR -#undef GETHEX -#undef ALLOC -#undef STORE -#undef CURRENT_LEVEL_START -#undef SET_LEVEL_START -#undef PUSH_LEVEL_STARTS -#undef POP_LEVEL_STARTS -#undef PUT_ADDR -#undef INSERT_JUMP -#undef SETBIT -#undef SET_FIELDS - -#define PREFETCH if (text == textend) goto fail - -#define NEXTCHAR(var) \ -PREFETCH; \ -var = (unsigned char)*text++; \ -if (translate) \ - var = translate[var] - -int re_match(regexp_t bufp, unsigned char *string, int size, int pos, - regexp_registers_t old_regs) -{ - unsigned char *code; - unsigned char *translate; - unsigned char *text; - unsigned char *textstart; - unsigned char *textend; - int a; - int b; - int ch; - int reg; - int match_end; - unsigned char *regstart; - unsigned char *regend; - int regsize; - match_state state; - - assert(pos >= 0 && size >= 0); - assert(pos <= size); - - text = string + pos; - textstart = string; - textend = string + size; - - code = bufp->buffer; - - translate = bufp->translate; - - NEW_STATE(state, bufp->num_registers); - - continue_matching: - switch (*code++) - { - case Cend: - { - match_end = text - textstart; - if (old_regs) - { - old_regs->start[0] = pos; - old_regs->end[0] = match_end; - if (!bufp->uses_registers) - { - for (a = 1; a < RE_NREGS; a++) - { - old_regs->start[a] = -1; - old_regs->end[a] = -1; - } - } - else - { - for (a = 1; a < bufp->num_registers; a++) - { - if ((GET_REG_START(state, a) == NULL) || - (GET_REG_END(state, a) == NULL)) - { - old_regs->start[a] = -1; - old_regs->end[a] = -1; - continue; - } - old_regs->start[a] = GET_REG_START(state, a) - textstart; - old_regs->end[a] = GET_REG_END(state, a) - textstart; - } - for (; a < RE_NREGS; a++) - { - old_regs->start[a] = -1; - old_regs->end[a] = -1; - } - } - } - FREE_STATE(state); - return match_end - pos; - } - case Cbol: - { - if (text == textstart || text[-1] == '\n') - goto continue_matching; - goto fail; - } - case Ceol: - { - if (text == textend || *text == '\n') - goto continue_matching; - goto fail; - } - case Cset: - { - NEXTCHAR(ch); - if (code[ch/8] & (1<<(ch & 7))) - { - code += 256/8; - goto continue_matching; - } - goto fail; - } - case Cexact: - { - NEXTCHAR(ch); - if (ch != (unsigned char)*code++) - goto fail; - goto continue_matching; - } - case Canychar: - { - NEXTCHAR(ch); - if (ch == '\n') - goto fail; - goto continue_matching; - } - case Cstart_memory: - { - reg = *code++; - SET_REG_START(state, reg, text, goto error); - goto continue_matching; - } - case Cend_memory: - { - reg = *code++; - SET_REG_END(state, reg, text, goto error); - goto continue_matching; - } - case Cmatch_memory: - { - reg = *code++; - regstart = GET_REG_START(state, reg); - regend = GET_REG_END(state, reg); - if ((regstart == NULL) || (regend == NULL)) - goto fail; /* or should we just match nothing? */ - regsize = regend - regstart; - - if (regsize > (textend - text)) - goto fail; - if(translate) - { - for (; regstart < regend; regstart++, text++) - if (translate[*regstart] != translate[*text]) - goto fail; - } - else - for (; regstart < regend; regstart++, text++) - if (*regstart != *text) - goto fail; - goto continue_matching; - } - case Cupdate_failure_jump: - { - UPDATE_FAILURE(state, text, goto error); - /* fall to next case */ - } - /* treat Cstar_jump just like Cjump if it hasn't been optimized */ - case Cstar_jump: - case Cjump: - { - a = (unsigned char)*code++; - a |= (unsigned char)*code++ << 8; - code += (int)SHORT(a); - if (codebuffer || bufp->buffer+bufp->usedbuffer || bufp->buffer+bufp->used < failuredest) { - PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cdummy_failure_jump failuredest)"); - FREE_STATE(state); - return -2; - } - PUSH_FAILURE(state, failuredest, NULL, goto error); - code += a; - if (codebuffer || bufp->buffer+bufp->used < code) { - PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cdummy_failure_jump code)"); - FREE_STATE(state); - return -2; - } - goto continue_matching; - } - case Cfailure_jump: - { - a = (unsigned char)*code++; - a |= (unsigned char)*code++ << 8; - a = (int)SHORT(a); - if (code+abuffer || bufp->buffer+bufp->used < code+a) { - PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cfailure_jump)"); - FREE_STATE(state); - return -2; - } - PUSH_FAILURE(state, code + a, text, goto error); - goto continue_matching; - } - case Crepeat1: - { - unsigned char *pinst; - a = (unsigned char)*code++; - a |= (unsigned char)*code++ << 8; - a = (int)SHORT(a); - pinst = code + a; - if (pinstbuffer || bufp->buffer+bufp->used */ - } - case Cbegbuf: - { - if (text == textstart) - goto continue_matching; - goto fail; - } - case Cendbuf: - { - if (text == textend) - goto continue_matching; - goto fail; - } - case Cwordbeg: - { - if (text == textend) - goto fail; - if (!(SYNTAX(*text) & Sword)) - goto fail; - if (text == textstart) - goto continue_matching; - if (!(SYNTAX(text[-1]) & Sword)) - goto continue_matching; - goto fail; - } - case Cwordend: - { - if (text == textstart) - goto fail; - if (!(SYNTAX(text[-1]) & Sword)) - goto fail; - if (text == textend) - goto continue_matching; - if (!(SYNTAX(*text) & Sword)) - goto continue_matching; - goto fail; - } - case Cwordbound: - { - /* Note: as in gnu regexp, this also matches at the - * beginning and end of buffer. */ - - if (text == textstart || text == textend) - goto continue_matching; - if ((SYNTAX(text[-1]) & Sword) ^ (SYNTAX(*text) & Sword)) - goto continue_matching; - goto fail; - } - case Cnotwordbound: - { - /* Note: as in gnu regexp, this never matches at the - * beginning and end of buffer. */ - if (text == textstart || text == textend) - goto fail; - if (!((SYNTAX(text[-1]) & Sword) ^ (SYNTAX(*text) & Sword))) - goto continue_matching; - goto fail; - } - case Csyntaxspec: - { - NEXTCHAR(ch); - if (!(SYNTAX(ch) & (unsigned char)*code++)) - goto fail; - goto continue_matching; - } - case Cnotsyntaxspec: - { - NEXTCHAR(ch); - if (SYNTAX(ch) & (unsigned char)*code++) - goto fail; - goto continue_matching; - } - default: - { - FREE_STATE(state); - PyErr_SetString(PyExc_SystemError, "Unknown regex opcode: memory corrupted?"); - return -2; - /*NOTREACHED*/ - } - } - - - -#if 0 /* This line is never reached --Guido */ - abort(); -#endif - /* - *NOTREACHED - */ - - /* Using "break;" in the above switch statement is equivalent to "goto fail;" */ - fail: - POP_FAILURE(state, code, text, goto done_matching, goto error); - goto continue_matching; - - done_matching: -/* if(translated != NULL) */ -/* free(translated); */ - FREE_STATE(state); - return -1; - - error: -/* if (translated != NULL) */ -/* free(translated); */ - FREE_STATE(state); - return -2; -} - - -#undef PREFETCH -#undef NEXTCHAR - -int re_search(regexp_t bufp, unsigned char *string, int size, int pos, - int range, regexp_registers_t regs) -{ - unsigned char *fastmap; - unsigned char *translate; - unsigned char *text; - unsigned char *partstart; - unsigned char *partend; - int dir; - int ret; - unsigned char anchor; - - assert(size >= 0 && pos >= 0); - assert(pos + range >= 0 && pos + range <= size); /* Bugfix by ylo */ - - fastmap = bufp->fastmap; - translate = bufp->translate; - if (fastmap && !bufp->fastmap_accurate) { - re_compile_fastmap(bufp); - if (PyErr_Occurred()) return -2; - } - - anchor = bufp->anchor; - if (bufp->can_be_null == 1) /* can_be_null == 2: can match null at eob */ - fastmap = NULL; - - if (range < 0) - { - dir = -1; - range = -range; - } - else - dir = 1; - - if (anchor == 2) { - if (pos != 0) - return -1; - else - range = 0; - } - - for (; range >= 0; range--, pos += dir) - { - if (fastmap) - { - if (dir == 1) - { /* searching forwards */ - - text = string + pos; - partend = string + size; - partstart = text; - if (translate) - while (text != partend && - !fastmap[(unsigned char) translate[(unsigned char)*text]]) - text++; - else - while (text != partend && !fastmap[(unsigned char)*text]) - text++; - pos += text - partstart; - range -= text - partstart; - if (pos == size && bufp->can_be_null == 0) - return -1; - } - else - { /* searching backwards */ - text = string + pos; - partstart = string + pos - range; - partend = text; - if (translate) - while (text != partstart && - !fastmap[(unsigned char) - translate[(unsigned char)*text]]) - text--; - else - while (text != partstart && - !fastmap[(unsigned char)*text]) - text--; - pos -= partend - text; - range -= partend - text; - } - } - if (anchor == 1) - { /* anchored to begline */ - if (pos > 0 && (string[pos - 1] != '\n')) - continue; - } - assert(pos >= 0 && pos <= size); - ret = re_match(bufp, string, size, pos, regs); - if (ret >= 0) - return pos; - if (ret == -2) - return -2; - } - return -1; -} - -/* -** Local Variables: -** mode: c -** c-file-style: "python" -** End: -*/ diff --git a/Modules/regexpr.h b/Modules/regexpr.h deleted file mode 100644 index 2aee62d..0000000 --- a/Modules/regexpr.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * -*- mode: c-mode; c-file-style: python -*- - */ - -#ifndef Py_REGEXPR_H -#define Py_REGEXPR_H -#ifdef __cplusplus -extern "C" { -#endif - -/* - * regexpr.h - * - * Author: Tatu Ylonen - * - * Copyright (c) 1991 Tatu Ylonen, Espoo, Finland - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies. This - * software is provided "as is" without express or implied warranty. - * - * Created: Thu Sep 26 17:15:36 1991 ylo - * Last modified: Mon Nov 4 15:49:46 1991 ylo - */ - -/* $Id$ */ - -#ifndef REGEXPR_H -#define REGEXPR_H - -#define RE_NREGS 100 /* number of registers available */ - -typedef struct re_pattern_buffer -{ - unsigned char *buffer; /* compiled pattern */ - int allocated; /* allocated size of compiled pattern */ - int used; /* actual length of compiled pattern */ - unsigned char *fastmap; /* fastmap[ch] is true if ch can start pattern */ - unsigned char *translate; /* translation to apply during compilation/matching */ - unsigned char fastmap_accurate; /* true if fastmap is valid */ - unsigned char can_be_null; /* true if can match empty string */ - unsigned char uses_registers; /* registers are used and need to be initialized */ - int num_registers; /* number of registers used */ - unsigned char anchor; /* anchor: 0=none 1=begline 2=begbuf */ -} *regexp_t; - -typedef struct re_registers -{ - int start[RE_NREGS]; /* start offset of region */ - int end[RE_NREGS]; /* end offset of region */ -} *regexp_registers_t; - -/* bit definitions for syntax */ -#define RE_NO_BK_PARENS 1 /* no quoting for parentheses */ -#define RE_NO_BK_VBAR 2 /* no quoting for vertical bar */ -#define RE_BK_PLUS_QM 4 /* quoting needed for + and ? */ -#define RE_TIGHT_VBAR 8 /* | binds tighter than ^ and $ */ -#define RE_NEWLINE_OR 16 /* treat newline as or */ -#define RE_CONTEXT_INDEP_OPS 32 /* ^$?*+ are special in all contexts */ -#define RE_ANSI_HEX 64 /* ansi sequences (\n etc) and \xhh */ -#define RE_NO_GNU_EXTENSIONS 128 /* no gnu extensions */ - -/* definitions for some common regexp styles */ -#define RE_SYNTAX_AWK (RE_NO_BK_PARENS|RE_NO_BK_VBAR|RE_CONTEXT_INDEP_OPS) -#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK|RE_NEWLINE_OR) -#define RE_SYNTAX_GREP (RE_BK_PLUS_QM|RE_NEWLINE_OR) -#define RE_SYNTAX_EMACS 0 - -#define Sword 1 -#define Swhitespace 2 -#define Sdigit 4 -#define Soctaldigit 8 -#define Shexdigit 16 - -/* Rename all exported symbols to avoid conflicts with similarly named - symbols in some systems' standard C libraries... */ - -#define re_syntax _Py_re_syntax -#define re_syntax_table _Py_re_syntax_table -#define re_compile_initialize _Py_re_compile_initialize -#define re_set_syntax _Py_re_set_syntax -#define re_compile_pattern _Py_re_compile_pattern -#define re_match _Py_re_match -#define re_search _Py_re_search -#define re_compile_fastmap _Py_re_compile_fastmap -#define re_comp _Py_re_comp -#define re_exec _Py_re_exec - -#ifdef HAVE_PROTOTYPES - -extern int re_syntax; -/* This is the actual syntax mask. It was added so that Python could do - * syntax-dependent munging of patterns before compilation. */ - -extern unsigned char re_syntax_table[256]; - -void re_compile_initialize(void); - -int re_set_syntax(int syntax); -/* This sets the syntax to use and returns the previous syntax. The - * syntax is specified by a bit mask of the above defined bits. */ - -char *re_compile_pattern(unsigned char *regex, int regex_size, regexp_t compiled); -/* This compiles the regexp (given in regex and length in regex_size). - * This returns NULL if the regexp compiled successfully, and an error - * message if an error was encountered. The buffer field must be - * initialized to a memory area allocated by malloc (or to NULL) before - * use, and the allocated field must be set to its length (or 0 if - * buffer is NULL). Also, the translate field must be set to point to a - * valid translation table, or NULL if it is not used. */ - -int re_match(regexp_t compiled, unsigned char *string, int size, int pos, - regexp_registers_t old_regs); -/* This tries to match the regexp against the string. This returns the - * length of the matched portion, or -1 if the pattern could not be - * matched and -2 if an error (such as failure stack overflow) is - * encountered. */ - -int re_search(regexp_t compiled, unsigned char *string, int size, int startpos, - int range, regexp_registers_t regs); -/* This searches for a substring matching the regexp. This returns the - * first index at which a match is found. range specifies at how many - * positions to try matching; positive values indicate searching - * forwards, and negative values indicate searching backwards. mstop - * specifies the offset beyond which a match must not go. This returns - * -1 if no match is found, and -2 if an error (such as failure stack - * overflow) is encountered. */ - -void re_compile_fastmap(regexp_t compiled); -/* This computes the fastmap for the regexp. For this to have any effect, - * the calling program must have initialized the fastmap field to point - * to an array of 256 characters. */ - -#else /* HAVE_PROTOTYPES */ - -extern int re_syntax; -extern unsigned char re_syntax_table[256]; -void re_compile_initialize(); -int re_set_syntax(); -char *re_compile_pattern(); -int re_match(); -int re_search(); -void re_compile_fastmap(); - -#endif /* HAVE_PROTOTYPES */ - -#endif /* REGEXPR_H */ - - - -#ifdef __cplusplus -} -#endif -#endif /* !Py_REGEXPR_H */ diff --git a/Modules/resource.c b/Modules/resource.c index 7cbd2c9..e73c878 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -55,6 +55,7 @@ static PyStructSequence_Desc struct_rusage_desc = { 16 /* n_in_sequence */ }; +static int initialized; static PyTypeObject StructRUsageType; static PyObject * @@ -244,7 +245,10 @@ initresource(void) } Py_INCREF(ResourceError); PyModule_AddObject(m, "error", ResourceError); - PyStructSequence_InitType(&StructRUsageType, &struct_rusage_desc); + if (!initialized) + PyStructSequence_InitType(&StructRUsageType, + &struct_rusage_desc); + Py_INCREF(&StructRUsageType); PyModule_AddObject(m, "struct_rusage", (PyObject*) &StructRUsageType); @@ -320,4 +324,5 @@ initresource(void) if (v) { PyModule_AddObject(m, "RLIM_INFINITY", v); } + initialized = 1; } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c526d75..c9dd4a3 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -62,6 +62,7 @@ Local naming conventions: */ #include "Python.h" +#include "structmember.h" #undef MAX #define MAX(x, y) ((x) < (y) ? (y) : (x)) @@ -967,7 +968,18 @@ makesockaddr(int sockfd, struct sockaddr *addr, int addrlen, int proto) case AF_UNIX: { struct sockaddr_un *a = (struct sockaddr_un *) addr; - return PyString_FromString(a->sun_path); +#ifdef linux + if (a->sun_path[0] == 0) { /* Linux abstract namespace */ + addrlen -= (sizeof(*a) - sizeof(a->sun_path)); + return PyString_FromStringAndSize(a->sun_path, + addrlen); + } + else +#endif /* linux */ + { + /* regular NULL-terminated string */ + return PyString_FromString(a->sun_path); + } } #endif /* AF_UNIX */ @@ -1097,14 +1109,28 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, addr = (struct sockaddr_un*)&(s->sock_addr).un; if (!PyArg_Parse(args, "t#", &path, &len)) return 0; - if (len > sizeof addr->sun_path) { - PyErr_SetString(socket_error, - "AF_UNIX path too long"); - return 0; +#ifdef linux + if (len > 0 && path[0] == 0) { + /* Linux abstract namespace extension */ + if (len > sizeof addr->sun_path) { + PyErr_SetString(socket_error, + "AF_UNIX path too long"); + return 0; + } + } + else +#endif /* linux */ + { + /* regular NULL-terminated string */ + if (len >= sizeof addr->sun_path) { + PyErr_SetString(socket_error, + "AF_UNIX path too long"); + return 0; + } + addr->sun_path[len] = 0; } addr->sun_family = s->sock_family; memcpy(addr->sun_path, path, len); - addr->sun_path[len] = 0; *addr_ret = (struct sockaddr *) addr; #if defined(PYOS_OS2) *len_ret = sizeof(*addr); @@ -2207,18 +2233,20 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args) Py_BEGIN_ALLOW_THREADS memset(&addrbuf, 0, addrlen); timeout = internal_select(s, 0); - if (!timeout) - n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, + if (!timeout) { #ifndef MS_WINDOWS #if defined(PYOS_OS2) && !defined(PYCC_GCC) - (struct sockaddr *) &addrbuf, &addrlen + n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, + (struct sockaddr *) &addrbuf, &addrlen); #else - (void *) &addrbuf, &addrlen + n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, + (void *) &addrbuf, &addrlen); #endif #else - (struct sockaddr *) &addrbuf, &addrlen + n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, + (struct sockaddr *) &addrbuf, &addrlen); #endif - ); + } Py_END_ALLOW_THREADS if (timeout) { @@ -2502,6 +2530,14 @@ static PyMethodDef sock_methods[] = { {NULL, NULL} /* sentinel */ }; +/* SockObject members */ +static PyMemberDef sock_memberlist[] = { + {"family", T_INT, offsetof(PySocketSockObject, sock_family), READONLY, "the socket family"}, + {"type", T_INT, offsetof(PySocketSockObject, sock_type), READONLY, "the socket type"}, + {"proto", T_INT, offsetof(PySocketSockObject, sock_proto), READONLY, "the socket protocol"}, + {"timeout", T_DOUBLE, offsetof(PySocketSockObject, sock_timeout), READONLY, "the socket timeout"}, + {0}, +}; /* Deallocate a socket object in response to the last Py_DECREF(). First close the file description. */ @@ -2625,7 +2661,7 @@ static PyTypeObject sock_type = { 0, /* tp_iter */ 0, /* tp_iternext */ sock_methods, /* tp_methods */ - 0, /* tp_members */ + sock_memberlist, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ @@ -3159,7 +3195,8 @@ socket_fromfd(PyObject *self, PyObject *args) PyDoc_STRVAR(fromfd_doc, "fromfd(fd, family, type[, proto]) -> socket object\n\ \n\ -Create a socket object from the given file descriptor.\n\ +Create a socket object from a duplicate of the given\n\ +file descriptor.\n\ The remaining arguments are the same as for socket()."); #endif /* NO_DUP */ @@ -4026,7 +4063,12 @@ init_socket(void) /* */ PyModule_AddIntConstant(m, "AF_NETLINK", AF_NETLINK); PyModule_AddIntConstant(m, "NETLINK_ROUTE", NETLINK_ROUTE); +#ifdef NETLINK_SKIP PyModule_AddIntConstant(m, "NETLINK_SKIP", NETLINK_SKIP); +#endif +#ifdef NETLINK_W1 + PyModule_AddIntConstant(m, "NETLINK_W1", NETLINK_W1); +#endif PyModule_AddIntConstant(m, "NETLINK_USERSOCK", NETLINK_USERSOCK); PyModule_AddIntConstant(m, "NETLINK_FIREWALL", NETLINK_FIREWALL); #ifdef NETLINK_TCPDIAG @@ -4038,12 +4080,18 @@ init_socket(void) #ifdef NETLINK_XFRM PyModule_AddIntConstant(m, "NETLINK_XFRM", NETLINK_XFRM); #endif +#ifdef NETLINK_ARPD PyModule_AddIntConstant(m, "NETLINK_ARPD", NETLINK_ARPD); +#endif +#ifdef NETLINK_ROUTE6 PyModule_AddIntConstant(m, "NETLINK_ROUTE6", NETLINK_ROUTE6); +#endif PyModule_AddIntConstant(m, "NETLINK_IP6_FW", NETLINK_IP6_FW); PyModule_AddIntConstant(m, "NETLINK_DNRTMSG", NETLINK_DNRTMSG); +#ifdef NETLINK_TAPBASE PyModule_AddIntConstant(m, "NETLINK_TAPBASE", NETLINK_TAPBASE); #endif +#endif /* AF_NETLINK */ #ifdef AF_ROUTE /* Alias to emulate 4.4BSD */ PyModule_AddIntConstant(m, "AF_ROUTE", AF_ROUTE); diff --git a/Modules/spwdmodule.c b/Modules/spwdmodule.c index 7c618e7..b7bf20e 100644 --- a/Modules/spwdmodule.c +++ b/Modules/spwdmodule.c @@ -52,6 +52,7 @@ static PyStructSequence_Desc struct_spwd_type_desc = { 9, }; +static int initialized; static PyTypeObject StructSpwdType; @@ -173,7 +174,10 @@ initspwd(void) m=Py_InitModule3("spwd", spwd_methods, spwd__doc__); if (m == NULL) return; - PyStructSequence_InitType(&StructSpwdType, &struct_spwd_type_desc); + if (!initialized) + PyStructSequence_InitType(&StructSpwdType, + &struct_spwd_type_desc); Py_INCREF((PyObject *) &StructSpwdType); PyModule_AddObject(m, "struct_spwd", (PyObject *) &StructSpwdType); + initialized = 1; } diff --git a/Modules/stropmodule.c b/Modules/stropmodule.c index cffef3a..8b00fed 100644 --- a/Modules/stropmodule.c +++ b/Modules/stropmodule.c @@ -333,7 +333,7 @@ strop_rfind(PyObject *self, PyObject *args) { char *s, *sub; Py_ssize_t len, n, j; - Py_ssize_t i = 0, last = INT_MAX; + Py_ssize_t i = 0, last = PY_SSIZE_T_MAX; WARN; if (!PyArg_ParseTuple(args, "t#t#|nn:rfind", &s, &len, &sub, &n, &i, &last)) @@ -446,16 +446,16 @@ strop_lower(PyObject *self, PyObject *args) { char *s, *s_new; Py_ssize_t i, n; - PyObject *new; + PyObject *newstr; int changed; WARN; if (PyString_AsStringAndSize(args, &s, &n)) return NULL; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newstr = PyString_FromStringAndSize(NULL, n); + if (newstr == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newstr); changed = 0; for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); @@ -467,11 +467,11 @@ strop_lower(PyObject *self, PyObject *args) s_new++; } if (!changed) { - Py_DECREF(new); + Py_DECREF(newstr); Py_INCREF(args); return args; } - return new; + return newstr; } @@ -485,16 +485,16 @@ strop_upper(PyObject *self, PyObject *args) { char *s, *s_new; Py_ssize_t i, n; - PyObject *new; + PyObject *newstr; int changed; WARN; if (PyString_AsStringAndSize(args, &s, &n)) return NULL; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newstr = PyString_FromStringAndSize(NULL, n); + if (newstr == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newstr); changed = 0; for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); @@ -506,11 +506,11 @@ strop_upper(PyObject *self, PyObject *args) s_new++; } if (!changed) { - Py_DECREF(new); + Py_DECREF(newstr); Py_INCREF(args); return args; } - return new; + return newstr; } @@ -525,16 +525,16 @@ strop_capitalize(PyObject *self, PyObject *args) { char *s, *s_new; Py_ssize_t i, n; - PyObject *new; + PyObject *newstr; int changed; WARN; if (PyString_AsStringAndSize(args, &s, &n)) return NULL; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newstr = PyString_FromStringAndSize(NULL, n); + if (newstr == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newstr); changed = 0; if (0 < n) { int c = Py_CHARMASK(*s++); @@ -555,11 +555,11 @@ strop_capitalize(PyObject *self, PyObject *args) s_new++; } if (!changed) { - Py_DECREF(new); + Py_DECREF(newstr); Py_INCREF(args); return args; } - return new; + return newstr; } @@ -647,7 +647,7 @@ strop_count(PyObject *self, PyObject *args) { char *s, *sub; Py_ssize_t len, n; - Py_ssize_t i = 0, last = INT_MAX; + Py_ssize_t i = 0, last = PY_SSIZE_T_MAX; Py_ssize_t m, r; WARN; @@ -691,16 +691,16 @@ strop_swapcase(PyObject *self, PyObject *args) { char *s, *s_new; Py_ssize_t i, n; - PyObject *new; + PyObject *newstr; int changed; WARN; if (PyString_AsStringAndSize(args, &s, &n)) return NULL; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newstr = PyString_FromStringAndSize(NULL, n); + if (newstr == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newstr); changed = 0; for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); @@ -717,11 +717,11 @@ strop_swapcase(PyObject *self, PyObject *args) s_new++; } if (!changed) { - Py_DECREF(new); + Py_DECREF(newstr); Py_INCREF(args); return args; } - return new; + return newstr; } @@ -942,7 +942,7 @@ strop_translate(PyObject *self, PyObject *args) } table = table1; - inlen = PyString_Size(input_obj); + inlen = PyString_GET_SIZE(input_obj); result = PyString_FromStringAndSize((char *)NULL, inlen); if (result == NULL) return NULL; @@ -1078,7 +1078,7 @@ mymemreplace(const char *str, Py_ssize_t len, /* input string */ /* find length of output string */ nfound = mymemcnt(str, len, pat, pat_len); if (count < 0) - count = INT_MAX; + count = PY_SSIZE_T_MAX; else if (nfound > count) nfound = count; if (nfound == 0) @@ -1141,7 +1141,7 @@ strop_replace(PyObject *self, PyObject *args) char *str, *pat,*sub,*new_s; Py_ssize_t len,pat_len,sub_len,out_len; Py_ssize_t count = -1; - PyObject *new; + PyObject *newstr; WARN; if (!PyArg_ParseTuple(args, "t#t#t#|n:replace", @@ -1165,14 +1165,14 @@ strop_replace(PyObject *self, PyObject *args) } if (out_len == -1) { /* we're returning another reference to the input string */ - new = PyTuple_GetItem(args, 0); - Py_XINCREF(new); + newstr = PyTuple_GetItem(args, 0); + Py_XINCREF(newstr); } else { - new = PyString_FromStringAndSize(new_s, out_len); + newstr = PyString_FromStringAndSize(new_s, out_len); PyMem_FREE(new_s); } - return new; + return newstr; } diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c index 9a6c5d8..83313df 100644 --- a/Modules/threadmodule.c +++ b/Modules/threadmodule.c @@ -22,24 +22,6 @@ typedef struct { PyThread_type_lock lock_lock; } lockobject; -static PyTypeObject Locktype; - -static lockobject * -newlockobject(void) -{ - lockobject *self; - self = PyObject_New(lockobject, &Locktype); - if (self == NULL) - return NULL; - self->lock_lock = PyThread_allocate_lock(); - if (self->lock_lock == NULL) { - PyObject_Del(self); - self = NULL; - PyErr_SetString(ThreadError, "can't allocate lock"); - } - return self; -} - static void lock_dealloc(lockobject *self) { @@ -166,6 +148,22 @@ static PyTypeObject Locktype = { 0, /*tp_repr*/ }; +static lockobject * +newlockobject(void) +{ + lockobject *self; + self = PyObject_New(lockobject, &Locktype); + if (self == NULL) + return NULL; + self->lock_lock = PyThread_allocate_lock(); + if (self->lock_lock == NULL) { + PyObject_Del(self); + self = NULL; + PyErr_SetString(ThreadError, "can't allocate lock"); + } + return self; +} + /* Thread-local objects */ #include "structmember.h" @@ -178,8 +176,6 @@ typedef struct { PyObject *dict; } localobject; -static PyTypeObject localtype; - static PyObject * local_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -315,29 +311,6 @@ _ldict(localobject *self) return ldict; } -static PyObject * -local_getattro(localobject *self, PyObject *name) -{ - PyObject *ldict, *value; - - ldict = _ldict(self); - if (ldict == NULL) - return NULL; - - if (self->ob_type != &localtype) - /* use generic lookup for subtypes */ - return PyObject_GenericGetAttr((PyObject *)self, name); - - /* Optimization: just look in dict ourselves */ - value = PyDict_GetItem(ldict, name); - if (value == NULL) - /* Fall back on generic to get __class__ and __dict__ */ - return PyObject_GenericGetAttr((PyObject *)self, name); - - Py_INCREF(value); - return value; -} - static int local_setattro(localobject *self, PyObject *name, PyObject *v) { @@ -368,6 +341,8 @@ static PyGetSetDef local_getset[] = { {NULL} /* Sentinel */ }; +static PyObject *local_getattro(localobject *, PyObject *); + static PyTypeObject localtype = { PyObject_HEAD_INIT(NULL) /* ob_size */ 0, @@ -375,17 +350,17 @@ static PyTypeObject localtype = { /* tp_basicsize */ sizeof(localobject), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)local_dealloc, - /* tp_print */ (printfunc)0, - /* tp_getattr */ (getattrfunc)0, - /* tp_setattr */ (setattrfunc)0, - /* tp_compare */ (cmpfunc)0, - /* tp_repr */ (reprfunc)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 */ (hashfunc)0, - /* tp_call */ (ternaryfunc)0, - /* tp_str */ (reprfunc)0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ 0, /* tp_getattro */ (getattrofunc)local_getattro, /* tp_setattro */ (setattrofunc)local_setattro, /* tp_as_buffer */ 0, @@ -393,25 +368,47 @@ static PyTypeObject localtype = { /* tp_doc */ "Thread-local data", /* tp_traverse */ (traverseproc)local_traverse, /* tp_clear */ (inquiry)local_clear, - /* tp_richcompare */ (richcmpfunc)0, - /* tp_weaklistoffset */ (long)0, - /* tp_iter */ (getiterfunc)0, - /* tp_iternext */ (iternextfunc)0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ local_getset, /* tp_base */ 0, /* tp_dict */ 0, /* internal use */ - /* tp_descr_get */ (descrgetfunc)0, - /* tp_descr_set */ (descrsetfunc)0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, /* tp_dictoffset */ offsetof(localobject, dict), - /* tp_init */ (initproc)0, - /* tp_alloc */ (allocfunc)0, - /* tp_new */ (newfunc)local_new, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ local_new, /* tp_free */ 0, /* Low-level free-mem routine */ - /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */ + /* tp_is_gc */ 0, /* For PyObject_IS_GC */ }; +static PyObject * +local_getattro(localobject *self, PyObject *name) +{ + PyObject *ldict, *value; + + ldict = _ldict(self); + if (ldict == NULL) + return NULL; + + if (self->ob_type != &localtype) + /* use generic lookup for subtypes */ + return PyObject_GenericGetAttr((PyObject *)self, name); + + /* Optimization: just look in dict ourselves */ + value = PyDict_GetItem(ldict, name); + if (value == NULL) + /* Fall back on generic to get __class__ and __dict__ */ + return PyObject_GenericGetAttr((PyObject *)self, name); + + Py_INCREF(value); + return value; +} /* Module functions */ @@ -560,6 +557,8 @@ thread_PyThread_exit_prog(PyObject *self, PyObject *args) } #endif +static lockobject *newlockobject(void); + static PyObject * thread_PyThread_allocate_lock(PyObject *self) { diff --git a/Modules/timemodule.c b/Modules/timemodule.c index ba93957..08d28a1 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -228,6 +228,7 @@ static PyStructSequence_Desc struct_time_type_desc = { 9, }; +static int initialized; static PyTypeObject StructTimeType; static PyObject * @@ -443,7 +444,7 @@ time_strftime(PyObject *self, PyObject *args) * will be ahead of time... */ for (i = 1024; ; i += i) { - outbuf = malloc(i); + outbuf = (char *)malloc(i); if (outbuf == NULL) { return PyErr_NoMemory(); } @@ -807,9 +808,13 @@ inittime(void) hInterruptEvent = CreateEvent(NULL, TRUE, FALSE, NULL); SetConsoleCtrlHandler( PyCtrlHandler, TRUE); #endif /* MS_WINDOWS */ - PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc); + if (!initialized) { + PyStructSequence_InitType(&StructTimeType, + &struct_time_type_desc); + } Py_INCREF(&StructTimeType); PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); + initialized = 1; } diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 9eda653..297611c 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -446,7 +446,7 @@ unicodedata_decomposition(PyObject *self, PyObject *args) return PyString_FromString(decomp); } -void +static void get_decomp_record(PyObject *self, Py_UCS4 code, int *index, int *prefix, int *count) { if (code >= 0x110000) { @@ -486,8 +486,8 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) Py_UNICODE *i, *end, *o; /* Longest decomposition in Unicode 3.2: U+FDFA */ Py_UNICODE stack[20]; - int space, stackptr, isize; - int index, prefix, count; + Py_ssize_t space, isize; + int index, prefix, count, stackptr; unsigned char prev, cur; stackptr = 0; @@ -508,7 +508,7 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) /* Hangul Decomposition adds three characters in a single step, so we need atleast that much room. */ if (space < 3) { - int newsize = PyString_GET_SIZE(result) + 10; + Py_ssize_t newsize = PyString_GET_SIZE(result) + 10; space += 10; if (PyUnicode_Resize(&result, newsize) == -1) return NULL; @@ -759,7 +759,7 @@ _gethash(const char *s, int len, int scale) unsigned long h = 0; unsigned long ix; for (i = 0; i < len; i++) { - h = (h * scale) + (unsigned char) toupper(s[i]); + h = (h * scale) + (unsigned char) toupper(Py_CHARMASK(s[i])); ix = h & 0xff000000; if (ix) h = (h ^ ((ix>>24) & 0xff)) & 0x00ffffff; @@ -906,7 +906,7 @@ _cmpname(PyObject *self, int code, const char* name, int namelen) if (!_getucname(self, code, buffer, sizeof(buffer))) return 0; for (i = 0; i < namelen; i++) { - if (toupper(name[i]) != buffer[i]) + if (toupper(Py_CHARMASK(name[i])) != buffer[i]) return 0; } return buffer[namelen] == '\0'; diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c index ffbc72b..88ce6c5 100644 --- a/Modules/xxsubtype.c +++ b/Modules/xxsubtype.c @@ -79,8 +79,6 @@ static PyMethodDef spamlist_methods[] = { {NULL, NULL}, }; -static PyTypeObject spamlist_type; - static int spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds) { @@ -179,8 +177,6 @@ static PyMethodDef spamdict_methods[] = { {NULL, NULL}, }; -static PyTypeObject spamdict_type; - static int spamdict_init(spamdictobject *self, PyObject *args, PyObject *kwds) { diff --git a/Modules/zipimport.c b/Modules/zipimport.c index 637dc48..d59ebd8 100644 --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -40,7 +40,6 @@ struct _zipimporter { PyObject *files; /* dict with file info {path: toc_entry} */ }; -static PyTypeObject ZipImporter_Type; static PyObject *ZipImportError; static PyObject *zip_directory_cache = NULL; @@ -171,13 +170,7 @@ static int zipimporter_traverse(PyObject *obj, visitproc visit, void *arg) { ZipImporter *self = (ZipImporter *)obj; - int err; - - if (self->files != NULL) { - err = visit(self->files, arg); - if (err) - return err; - } + Py_VISIT(self->files); return 0; } @@ -958,7 +951,7 @@ normalize_line_endings(PyObject *source) PyObject *fixed_source; /* one char extra for trailing \n and one for terminating \0 */ - buf = PyMem_Malloc(PyString_Size(source) + 2); + buf = (char *)PyMem_Malloc(PyString_Size(source) + 2); if (buf == NULL) { PyErr_SetString(PyExc_MemoryError, "zipimport: no memory to allocate " diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 725755d..35b8c32 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -654,7 +654,9 @@ PyZlib_flush(compobject *self, PyObject *args) } PyDoc_STRVAR(decomp_flush__doc__, -"flush() -- Return a string containing any remaining decompressed data.\n" +"flush( [length] ) -- Return a string containing any remaining\n" +"decompressed data. length, if given, is the initial size of the\n" +"output buffer.\n" "\n" "The decompressor object can no longer be used after this call."); diff --git a/Objects/abstract.c b/Objects/abstract.c index c755654..13a9473 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -10,6 +10,7 @@ #define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) + /* Shorthands to return certain errors */ static PyObject * @@ -940,8 +941,9 @@ PyNumber_Index(PyObject *item) value = nb->nb_index(item); } else { - PyErr_SetString(PyExc_IndexError, - "object cannot be interpreted as an index"); + PyErr_Format(PyExc_TypeError, + "'%.200s' object cannot be interpreted " + "as an index", item->ob_type->tp_name); } return value; } @@ -1245,24 +1247,6 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i) return type_error("unindexable object"); } -static PyObject * -sliceobj_from_intint(Py_ssize_t i, Py_ssize_t j) -{ - PyObject *start, *end, *slice; - start = PyInt_FromLong((long)i); - if (!start) - return NULL; - end = PyInt_FromLong((long)j); - if (!end) { - Py_DECREF(start); - return NULL; - } - slice = PySlice_New(start, end, NULL); - Py_DECREF(start); - Py_DECREF(end); - return slice; -} - PyObject * PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) { @@ -1287,7 +1271,7 @@ PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) return m->sq_slice(s, i1, i2); } else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_subscript) { PyObject *res; - PyObject *slice = sliceobj_from_intint(i1, i2); + PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) return NULL; res = mp->mp_subscript(s, slice); @@ -1379,7 +1363,7 @@ PySequence_SetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2, PyObject *o) return m->sq_ass_slice(s, i1, i2, o); } else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_ass_subscript) { int res; - PyObject *slice = sliceobj_from_intint(i1, i2); + PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) return -1; res = mp->mp_ass_subscript(s, slice, o); @@ -1815,11 +1799,37 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) return NULL; } +static PyObject* +call_function_tail(PyObject *callable, PyObject *args) +{ + PyObject *retval; + + if (args == NULL) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) { + Py_DECREF(args); + return NULL; + } + PyTuple_SET_ITEM(a, 0, args); + args = a; + } + retval = PyObject_Call(callable, args, NULL); + + Py_DECREF(args); + + return retval; +} + PyObject * PyObject_CallFunction(PyObject *callable, char *format, ...) { va_list va; - PyObject *args, *retval; + PyObject *args; if (callable == NULL) return null_error(); @@ -1832,31 +1842,34 @@ PyObject_CallFunction(PyObject *callable, char *format, ...) else args = PyTuple_New(0); - if (args == NULL) - return NULL; + return call_function_tail(callable, args); +} - if (!PyTuple_Check(args)) { - PyObject *a; +PyObject * +_PyObject_CallFunction_SizeT(PyObject *callable, char *format, ...) +{ + va_list va; + PyObject *args; - a = PyTuple_New(1); - if (a == NULL) - return NULL; - if (PyTuple_SetItem(a, 0, args) < 0) - return NULL; - args = a; - } - retval = PyObject_Call(callable, args, NULL); + if (callable == NULL) + return null_error(); - Py_DECREF(args); + if (format && *format) { + va_start(va, format); + args = _Py_VaBuildValue_SizeT(format, va); + va_end(va); + } + else + args = PyTuple_New(0); - return retval; + return call_function_tail(callable, args); } PyObject * PyObject_CallMethod(PyObject *o, char *name, char *format, ...) { va_list va; - PyObject *args = NULL; + PyObject *args; PyObject *func = NULL; PyObject *retval = NULL; @@ -1882,24 +1895,49 @@ PyObject_CallMethod(PyObject *o, char *name, char *format, ...) else args = PyTuple_New(0); - if (!args) - goto exit; + retval = call_function_tail(func, args); - if (!PyTuple_Check(args)) { - PyObject *a; + exit: + /* args gets consumed in call_function_tail */ + Py_XDECREF(func); - a = PyTuple_New(1); - if (a == NULL) - goto exit; - if (PyTuple_SetItem(a, 0, args) < 0) - goto exit; - args = a; + return retval; +} + +PyObject * +_PyObject_CallMethod_SizeT(PyObject *o, char *name, char *format, ...) +{ + va_list va; + PyObject *args; + PyObject *func = NULL; + PyObject *retval = NULL; + + if (o == NULL || name == NULL) + return null_error(); + + func = PyObject_GetAttrString(o, name); + if (func == NULL) { + PyErr_SetString(PyExc_AttributeError, name); + return 0; } - retval = PyObject_Call(func, args, NULL); + if (!PyCallable_Check(func)) { + type_error("call of non-callable attribute"); + goto exit; + } + + if (format && *format) { + va_start(va, format); + args = _Py_VaBuildValue_SizeT(format, va); + va_end(va); + } + else + args = PyTuple_New(0); + + retval = call_function_tail(func, args); exit: - Py_XDECREF(args); + /* args gets consumed in call_function_tail */ Py_XDECREF(func); return retval; diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 05784e5..79be184 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -103,42 +103,42 @@ The class bool is a subclass of the class int, and cannot be subclassed."); /* Arithmetic methods -- only so we can override &, |, ^. */ static PyNumberMethods bool_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_nonzero */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - (binaryfunc)bool_and, /* nb_and */ - (binaryfunc)bool_xor, /* nb_xor */ - (binaryfunc)bool_or, /* nb_or */ - 0, /* nb_coerce */ - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ - 0, /* nb_oct */ - 0, /* nb_hex */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - 0, /* nb_floor_divide */ - 0, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + 0, /* nb_nonzero */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + bool_and, /* nb_and */ + bool_xor, /* nb_xor */ + bool_or, /* nb_or */ + 0, /* nb_coerce */ + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ + 0, /* nb_oct */ + 0, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ }; /* The type object for bool. Note that this cannot be subclassed! */ diff --git a/Objects/bufferobject.c b/Objects/bufferobject.c index eff06aa..d2597b9 100644 --- a/Objects/bufferobject.c +++ b/Objects/bufferobject.c @@ -169,7 +169,7 @@ PyBuffer_New(Py_ssize_t size) } /* XXX: check for overflow in multiply */ /* Inline PyObject_New */ - o = PyObject_MALLOC(sizeof(*b) + size); + o = (PyObject *)PyObject_MALLOC(sizeof(*b) + size); if ( o == NULL ) return PyErr_NoMemory(); b = (PyBufferObject *) PyObject_INIT(o, &PyBuffer_Type); @@ -305,7 +305,7 @@ buffer_str(PyBufferObject *self) Py_ssize_t size; if (!get_buf(self, &ptr, &size)) return NULL; - return PyString_FromStringAndSize(ptr, size); + return PyString_FromStringAndSize((const char *)ptr, size); } /* Sequence methods */ diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 3b87093..da48dea 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -73,19 +73,29 @@ cell_repr(PyCellObject *op) static int cell_traverse(PyCellObject *op, visitproc visit, void *arg) { - if (op->ob_ref) - return visit(op->ob_ref, arg); + Py_VISIT(op->ob_ref); return 0; } static int cell_clear(PyCellObject *op) { - Py_XDECREF(op->ob_ref); - op->ob_ref = NULL; + Py_CLEAR(op->ob_ref); return 0; } +static PyObject * +cell_get_contents(PyCellObject *op, void *closure) +{ + Py_XINCREF(op->ob_ref); + return op->ob_ref; +} + +static PyGetSetDef cell_getsetlist[] = { + {"cell_contents", (getter)cell_get_contents, NULL}, + {NULL} /* sentinel */ +}; + PyTypeObject PyCell_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -111,4 +121,11 @@ PyTypeObject PyCell_Type = { 0, /* tp_doc */ (traverseproc)cell_traverse, /* tp_traverse */ (inquiry)cell_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + cell_getsetlist, /* tp_getset */ }; diff --git a/Objects/classobject.c b/Objects/classobject.c index 93acb50..594de11 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -208,7 +208,7 @@ class_getattr(register PyClassObject *op, PyObject *name) { register PyObject *v; register char *sname = PyString_AsString(name); - PyClassObject *class; + PyClassObject *klass; descrgetfunc f; if (sname[0] == '_' && sname[1] == '_') { @@ -234,7 +234,7 @@ class_getattr(register PyClassObject *op, PyObject *name) return v; } } - v = class_lookup(op, name, &class); + v = class_lookup(op, name, &klass); if (v == NULL) { PyErr_Format(PyExc_AttributeError, "class %.50s has no attribute '%.400s'", @@ -388,15 +388,15 @@ class_str(PyClassObject *op) Py_INCREF(name); return name; } - m = PyString_Size(mod); - n = PyString_Size(name); + m = PyString_GET_SIZE(mod); + n = PyString_GET_SIZE(name); res = PyString_FromStringAndSize((char *)NULL, m+1+n); if (res != NULL) { - char *s = PyString_AsString(res); - memcpy(s, PyString_AsString(mod), m); + char *s = PyString_AS_STRING(res); + memcpy(s, PyString_AS_STRING(mod), m); s += m; *s++ = '.'; - memcpy(s, PyString_AsString(name), n); + memcpy(s, PyString_AS_STRING(name), n); } return res; } @@ -404,37 +404,12 @@ class_str(PyClassObject *op) static int class_traverse(PyClassObject *o, visitproc visit, void *arg) { - int err; - if (o->cl_bases) { - err = visit(o->cl_bases, arg); - if (err) - return err; - } - if (o->cl_dict) { - err = visit(o->cl_dict, arg); - if (err) - return err; - } - if (o->cl_name) { - err = visit(o->cl_name, arg); - if (err) - return err; - } - if (o->cl_getattr) { - err = visit(o->cl_getattr, arg); - if (err) - return err; - } - if (o->cl_setattr) { - err = visit(o->cl_setattr, arg); - if (err) - return err; - } - if (o->cl_delattr) { - err = visit(o->cl_delattr, arg); - if (err) - return err; - } + Py_VISIT(o->cl_bases); + Py_VISIT(o->cl_dict); + Py_VISIT(o->cl_name); + Py_VISIT(o->cl_getattr); + Py_VISIT(o->cl_setattr); + Py_VISIT(o->cl_delattr); return 0; } @@ -481,23 +456,23 @@ PyTypeObject PyClass_Type = { }; int -PyClass_IsSubclass(PyObject *class, PyObject *base) +PyClass_IsSubclass(PyObject *klass, PyObject *base) { Py_ssize_t i, n; PyClassObject *cp; - if (class == base) + if (klass == base) return 1; if (PyTuple_Check(base)) { n = PyTuple_GET_SIZE(base); for (i = 0; i < n; i++) { - if (PyClass_IsSubclass(class, PyTuple_GET_ITEM(base, i))) + if (PyClass_IsSubclass(klass, PyTuple_GET_ITEM(base, i))) return 1; } return 0; } - if (class == NULL || !PyClass_Check(class)) + if (klass == NULL || !PyClass_Check(klass)) return 0; - cp = (PyClassObject *)class; + cp = (PyClassObject *)klass; n = PyTuple_Size(cp->cl_bases); for (i = 0; i < n; i++) { if (PyClass_IsSubclass(PyTuple_GetItem(cp->cl_bases, i), base)) @@ -719,7 +694,7 @@ static PyObject * instance_getattr2(register PyInstanceObject *inst, PyObject *name) { register PyObject *v; - PyClassObject *class; + PyClassObject *klass; descrgetfunc f; v = PyDict_GetItem(inst->in_dict, name); @@ -727,7 +702,7 @@ instance_getattr2(register PyInstanceObject *inst, PyObject *name) Py_INCREF(v); return v; } - v = class_lookup(inst->in_class, name, &class); + v = class_lookup(inst->in_class, name, &klass); if (v != NULL) { Py_INCREF(v); f = TP_DESCR_GET(v->ob_type); @@ -767,7 +742,7 @@ PyObject * _PyInstance_Lookup(PyObject *pinst, PyObject *name) { PyObject *v; - PyClassObject *class; + PyClassObject *klass; PyInstanceObject *inst; /* pinst cast to the right type */ assert(PyInstance_Check(pinst)); @@ -777,7 +752,7 @@ _PyInstance_Lookup(PyObject *pinst, PyObject *name) v = PyDict_GetItem(inst->in_dict, name); if (v == NULL) - v = class_lookup(inst->in_class, name, &class); + v = class_lookup(inst->in_class, name, &klass); return v; } @@ -979,17 +954,8 @@ instance_hash(PyInstanceObject *inst) static int instance_traverse(PyInstanceObject *o, visitproc visit, void *arg) { - int err; - if (o->in_class) { - err = visit((PyObject *)(o->in_class), arg); - if (err) - return err; - } - if (o->in_dict) { - err = visit(o->in_dict, arg); - if (err) - return err; - } + Py_VISIT(o->in_class); + Py_VISIT(o->in_dict); return 0; } @@ -1128,27 +1094,6 @@ instance_item(PyInstanceObject *inst, Py_ssize_t i) } static PyObject * -sliceobj_from_intint(Py_ssize_t i, Py_ssize_t j) -{ - PyObject *start, *end, *res; - - start = PyInt_FromLong((long)i); - if (!start) - return NULL; - - end = PyInt_FromLong((long)j); - if (!end) { - Py_DECREF(start); - return NULL; - } - res = PySlice_New(start, end, NULL); - Py_DECREF(start); - Py_DECREF(end); - return res; -} - - -static PyObject * instance_slice(PyInstanceObject *inst, Py_ssize_t i, Py_ssize_t j) { PyObject *func, *arg, *res; @@ -1168,7 +1113,7 @@ instance_slice(PyInstanceObject *inst, Py_ssize_t i, Py_ssize_t j) func = instance_getattr(inst, getitemstr); if (func == NULL) return NULL; - arg = Py_BuildValue("(N)", sliceobj_from_intint(i, j)); + arg = Py_BuildValue("(N)", _PySlice_FromIndices(i, j)); } else arg = Py_BuildValue("(nn)", i, j); @@ -1239,7 +1184,7 @@ instance_ass_slice(PyInstanceObject *inst, Py_ssize_t i, Py_ssize_t j, PyObject return -1; arg = Py_BuildValue("(N)", - sliceobj_from_intint(i, j)); + _PySlice_FromIndices(i, j)); } else arg = Py_BuildValue("(nn)", i, j); } @@ -1260,7 +1205,7 @@ instance_ass_slice(PyInstanceObject *inst, Py_ssize_t i, Py_ssize_t j, PyObject return -1; arg = Py_BuildValue("(NO)", - sliceobj_from_intint(i, j), value); + _PySlice_FromIndices(i, j), value); } else arg = Py_BuildValue("(nnO)", i, j, value); } @@ -2049,43 +1994,43 @@ instance_call(PyObject *func, PyObject *arg, PyObject *kw) static PyNumberMethods instance_as_number = { - (binaryfunc)instance_add, /* nb_add */ - (binaryfunc)instance_sub, /* nb_subtract */ - (binaryfunc)instance_mul, /* nb_multiply */ - (binaryfunc)instance_mod, /* nb_remainder */ - (binaryfunc)instance_divmod, /* nb_divmod */ - (ternaryfunc)instance_pow, /* nb_power */ - (unaryfunc)instance_neg, /* nb_negative */ - (unaryfunc)instance_pos, /* nb_positive */ - (unaryfunc)instance_abs, /* nb_absolute */ - (inquiry)instance_nonzero, /* nb_nonzero */ - (unaryfunc)instance_invert, /* nb_invert */ - (binaryfunc)instance_lshift, /* nb_lshift */ - (binaryfunc)instance_rshift, /* nb_rshift */ - (binaryfunc)instance_and, /* nb_and */ - (binaryfunc)instance_xor, /* nb_xor */ - (binaryfunc)instance_or, /* nb_or */ - (coercion)instance_coerce, /* nb_coerce */ - (unaryfunc)instance_int, /* nb_int */ - (unaryfunc)instance_long, /* nb_long */ - (unaryfunc)instance_float, /* nb_float */ - (unaryfunc)instance_oct, /* nb_oct */ - (unaryfunc)instance_hex, /* nb_hex */ - (binaryfunc)instance_iadd, /* nb_inplace_add */ - (binaryfunc)instance_isub, /* nb_inplace_subtract */ - (binaryfunc)instance_imul, /* nb_inplace_multiply */ - (binaryfunc)instance_imod, /* nb_inplace_remainder */ - (ternaryfunc)instance_ipow, /* nb_inplace_power */ - (binaryfunc)instance_ilshift, /* nb_inplace_lshift */ - (binaryfunc)instance_irshift, /* nb_inplace_rshift */ - (binaryfunc)instance_iand, /* nb_inplace_and */ - (binaryfunc)instance_ixor, /* nb_inplace_xor */ - (binaryfunc)instance_ior, /* nb_inplace_or */ - (binaryfunc)instance_floordiv, /* nb_floor_divide */ - (binaryfunc)instance_truediv, /* nb_true_divide */ - (binaryfunc)instance_ifloordiv, /* nb_inplace_floor_divide */ - (binaryfunc)instance_itruediv, /* nb_inplace_true_divide */ - (lenfunc)instance_index, /* nb_index */ + instance_add, /* nb_add */ + instance_sub, /* nb_subtract */ + instance_mul, /* nb_multiply */ + instance_mod, /* nb_remainder */ + instance_divmod, /* nb_divmod */ + instance_pow, /* nb_power */ + (unaryfunc)instance_neg, /* nb_negative */ + (unaryfunc)instance_pos, /* nb_positive */ + (unaryfunc)instance_abs, /* nb_absolute */ + (inquiry)instance_nonzero, /* nb_nonzero */ + (unaryfunc)instance_invert, /* nb_invert */ + instance_lshift, /* nb_lshift */ + instance_rshift, /* nb_rshift */ + instance_and, /* nb_and */ + instance_xor, /* nb_xor */ + instance_or, /* nb_or */ + instance_coerce, /* nb_coerce */ + (unaryfunc)instance_int, /* nb_int */ + (unaryfunc)instance_long, /* nb_long */ + (unaryfunc)instance_float, /* nb_float */ + (unaryfunc)instance_oct, /* nb_oct */ + (unaryfunc)instance_hex, /* nb_hex */ + instance_iadd, /* nb_inplace_add */ + instance_isub, /* nb_inplace_subtract */ + instance_imul, /* nb_inplace_multiply */ + instance_imod, /* nb_inplace_remainder */ + instance_ipow, /* nb_inplace_power */ + instance_ilshift, /* nb_inplace_lshift */ + instance_irshift, /* nb_inplace_rshift */ + instance_iand, /* nb_inplace_and */ + instance_ixor, /* nb_inplace_xor */ + instance_ior, /* nb_inplace_or */ + instance_floordiv, /* nb_floor_divide */ + instance_truediv, /* nb_true_divide */ + instance_ifloordiv, /* nb_inplace_floor_divide */ + instance_itruediv, /* nb_inplace_true_divide */ + (lenfunc)instance_index, /* nb_index */ }; PyTypeObject PyInstance_Type = { @@ -2140,7 +2085,7 @@ PyTypeObject PyInstance_Type = { static PyMethodObject *free_list; PyObject * -PyMethod_New(PyObject *func, PyObject *self, PyObject *class) +PyMethod_New(PyObject *func, PyObject *self, PyObject *klass) { register PyMethodObject *im; if (!PyCallable_Check(func)) { @@ -2162,8 +2107,8 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *class) im->im_func = func; Py_XINCREF(self); im->im_self = self; - Py_XINCREF(class); - im->im_class = class; + Py_XINCREF(klass); + im->im_class = klass; _PyObject_GC_TRACK(im); return (PyObject *)im; } @@ -2365,35 +2310,22 @@ instancemethod_hash(PyMethodObject *a) static int instancemethod_traverse(PyMethodObject *im, visitproc visit, void *arg) { - int err; - if (im->im_func) { - err = visit(im->im_func, arg); - if (err) - return err; - } - if (im->im_self) { - err = visit(im->im_self, arg); - if (err) - return err; - } - if (im->im_class) { - err = visit(im->im_class, arg); - if (err) - return err; - } + Py_VISIT(im->im_func); + Py_VISIT(im->im_self); + Py_VISIT(im->im_class); return 0; } static void -getclassname(PyObject *class, char *buf, int bufsize) +getclassname(PyObject *klass, char *buf, int bufsize) { PyObject *name; assert(bufsize > 1); strcpy(buf, "?"); /* Default outcome */ - if (class == NULL) + if (klass == NULL) return; - name = PyObject_GetAttrString(class, "__name__"); + name = PyObject_GetAttrString(klass, "__name__"); if (name == NULL) { /* This function cannot return an exception */ PyErr_Clear(); @@ -2409,7 +2341,7 @@ getclassname(PyObject *class, char *buf, int bufsize) static void getinstclassname(PyObject *inst, char *buf, int bufsize) { - PyObject *class; + PyObject *klass; if (inst == NULL) { assert(bufsize > 0 && (size_t)bufsize > strlen("nothing")); @@ -2417,22 +2349,22 @@ getinstclassname(PyObject *inst, char *buf, int bufsize) return; } - class = PyObject_GetAttrString(inst, "__class__"); - if (class == NULL) { + klass = PyObject_GetAttrString(inst, "__class__"); + if (klass == NULL) { /* This function cannot return an exception */ PyErr_Clear(); - class = (PyObject *)(inst->ob_type); - Py_INCREF(class); + klass = (PyObject *)(inst->ob_type); + Py_INCREF(klass); } - getclassname(class, buf, bufsize); - Py_XDECREF(class); + getclassname(klass, buf, bufsize); + Py_XDECREF(klass); } static PyObject * instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self = PyMethod_GET_SELF(func); - PyObject *class = PyMethod_GET_CLASS(func); + PyObject *klass = PyMethod_GET_CLASS(func); PyObject *result; func = PyMethod_GET_FUNCTION(func); @@ -2445,14 +2377,14 @@ instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) if (self == NULL) ok = 0; else { - ok = PyObject_IsInstance(self, class); + ok = PyObject_IsInstance(self, klass); if (ok < 0) return NULL; } if (!ok) { char clsbuf[256]; char instbuf[256]; - getclassname(class, clsbuf, sizeof(clsbuf)); + getclassname(klass, clsbuf, sizeof(clsbuf)); getinstclassname(self, instbuf, sizeof(instbuf)); PyErr_Format(PyExc_TypeError, "unbound method %s%s must be called with " @@ -2531,7 +2463,7 @@ PyTypeObject PyMethod_Type = { (hashfunc)instancemethod_hash, /* tp_hash */ instancemethod_call, /* tp_call */ 0, /* tp_str */ - (getattrofunc)instancemethod_getattro, /* tp_getattro */ + instancemethod_getattro, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ diff --git a/Objects/cobject.c b/Objects/cobject.c index f764a1d..b2cae9a 100644 --- a/Objects/cobject.c +++ b/Objects/cobject.c @@ -136,25 +136,26 @@ mechanism to link to one another."); PyTypeObject PyCObject_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, /*ob_size*/ - "PyCObject", /*tp_name*/ - sizeof(PyCObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ + 0, /*ob_size*/ + "PyCObject", /*tp_name*/ + sizeof(PyCObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ /* methods */ - (destructor)PyCObject_dealloc, /*tp_dealloc*/ - (printfunc)0, /*tp_print*/ - (getattrfunc)0, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - (cmpfunc)0, /*tp_compare*/ - (reprfunc)0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - (hashfunc)0, /*tp_hash*/ - (ternaryfunc)0, /*tp_call*/ - (reprfunc)0, /*tp_str*/ - - /* Space for future expansion */ - 0L,0L,0L,0L, - PyCObject_Type__doc__ /* Documentation string */ + (destructor)PyCObject_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*/ + 0, /*tp_flags*/ + PyCObject_Type__doc__ /*tp_doc*/ }; diff --git a/Objects/codeobject.c b/Objects/codeobject.c index f832911..8ae2399 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -451,3 +451,136 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) } return line; } + +/* + Check whether the current instruction is at the start of a line. + + */ + + /* The theory of SET_LINENO-less tracing. + + In a nutshell, we use the co_lnotab field of the code object + to tell when execution has moved onto a different line. + + As mentioned above, the basic idea is so set things up so + that + + *instr_lb <= frame->f_lasti < *instr_ub + + is true so long as execution does not change lines. + + This is all fairly simple. Digging the information out of + co_lnotab takes some work, but is conceptually clear. + + Somewhat harder to explain is why we don't *always* call the + line trace function when the above test fails. + + Consider this code: + + 1: def f(a): + 2: if a: + 3: print 1 + 4: else: + 5: print 2 + + which compiles to this: + + 2 0 LOAD_FAST 0 (a) + 3 JUMP_IF_FALSE 9 (to 15) + 6 POP_TOP + + 3 7 LOAD_CONST 1 (1) + 10 PRINT_ITEM + 11 PRINT_NEWLINE + 12 JUMP_FORWARD 6 (to 21) + >> 15 POP_TOP + + 5 16 LOAD_CONST 2 (2) + 19 PRINT_ITEM + 20 PRINT_NEWLINE + >> 21 LOAD_CONST 0 (None) + 24 RETURN_VALUE + + If 'a' is false, execution will jump to instruction at offset + 15 and the co_lnotab will claim that execution has moved to + line 3. This is at best misleading. In this case we could + associate the POP_TOP with line 4, but that doesn't make + sense in all cases (I think). + + What we do is only call the line trace function if the co_lnotab + indicates we have jumped to the *start* of a line, i.e. if the + current instruction offset matches the offset given for the + start of a line by the co_lnotab. + + This also takes care of the situation where 'a' is true. + Execution will jump from instruction offset 12 to offset 21. + Then the co_lnotab would imply that execution has moved to line + 5, which is again misleading. + + Why do we set f_lineno when tracing? Well, consider the code + above when 'a' is true. If stepping through this with 'n' in + pdb, you would stop at line 1 with a "call" type event, then + line events on lines 2 and 3, then a "return" type event -- but + you would be shown line 5 during this event. This is a change + from the behaviour in 2.2 and before, and I've found it + confusing in practice. By setting and using f_lineno when + tracing, one can report a line number different from that + suggested by f_lasti on this one occasion where it's desirable. + */ + + +int +PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds) +{ + int size, addr, line; + unsigned char* p; + + p = (unsigned char*)PyString_AS_STRING(co->co_lnotab); + size = PyString_GET_SIZE(co->co_lnotab) / 2; + + addr = 0; + line = co->co_firstlineno; + assert(line > 0); + + /* possible optimization: if f->f_lasti == instr_ub + (likely to be a common case) then we already know + instr_lb -- if we stored the matching value of p + somwhere we could skip the first while loop. */ + + /* see comments in compile.c for the description of + co_lnotab. A point to remember: increments to p + should come in pairs -- although we don't care about + the line increments here, treating them as byte + increments gets confusing, to say the least. */ + + while (size > 0) { + if (addr + *p > lasti) + break; + addr += *p++; + if (*p) + bounds->ap_lower = addr; + line += *p++; + --size; + } + + /* If lasti and addr don't match exactly, we don't want to + change the lineno slot on the frame or execute a trace + function. Return -1 instead. + */ + if (addr != lasti) + line = -1; + + if (size > 0) { + while (--size >= 0) { + addr += *p++; + if (*p++) + break; + } + bounds->ap_upper = addr; + } + else { + bounds->ap_upper = INT_MAX; + } + + return line; +} diff --git a/Objects/complexobject.c b/Objects/complexobject.c index f0915dd..c6021e9 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -667,7 +667,7 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(v)) { - if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { + if (PyUnicode_GET_SIZE(v) >= (Py_ssize_t)sizeof(s_buffer)) { PyErr_SetString(PyExc_ValueError, "complex() literal too large to convert"); return NULL; @@ -940,10 +940,10 @@ static PyNumberMethods complex_as_number = { 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ - (coercion)complex_coerce, /* nb_coerce */ - (unaryfunc)complex_int, /* nb_int */ - (unaryfunc)complex_long, /* nb_long */ - (unaryfunc)complex_float, /* nb_float */ + complex_coerce, /* nb_coerce */ + complex_int, /* nb_int */ + complex_long, /* nb_long */ + complex_float, /* nb_float */ 0, /* nb_oct */ 0, /* nb_hex */ 0, /* nb_inplace_add */ @@ -968,7 +968,7 @@ PyTypeObject PyComplex_Type = { "complex", sizeof(PyComplexObject), 0, - (destructor)complex_dealloc, /* tp_dealloc */ + complex_dealloc, /* tp_dealloc */ (printfunc)complex_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 9494062..561ba4a5 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -377,13 +377,7 @@ static int descr_traverse(PyObject *self, visitproc visit, void *arg) { PyDescrObject *descr = (PyDescrObject *)self; - int err; - - if (descr->d_type) { - err = visit((PyObject *)(descr->d_type), arg); - if (err) - return err; - } + Py_VISIT(descr->d_type); return 0; } @@ -480,7 +474,7 @@ static PyTypeObject PyMemberDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ @@ -518,7 +512,7 @@ static PyTypeObject PyGetSetDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ + 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ @@ -814,13 +808,7 @@ static int proxy_traverse(PyObject *self, visitproc visit, void *arg) { proxyobject *pp = (proxyobject *)self; - int err; - - if (pp->dict) { - err = visit(pp->dict, arg); - if (err) - return err; - } + Py_VISIT(pp->dict); return 0; } @@ -999,18 +987,8 @@ static int wrapper_traverse(PyObject *self, visitproc visit, void *arg) { wrapperobject *wp = (wrapperobject *)self; - int err; - - if (wp->descr) { - err = visit((PyObject *)(wp->descr), arg); - if (err) - return err; - } - if (wp->self) { - err = visit(wp->self, arg); - if (err) - return err; - } + Py_VISIT(wp->descr); + Py_VISIT(wp->self); return 0; } @@ -1237,20 +1215,10 @@ static int property_traverse(PyObject *self, visitproc visit, void *arg) { propertyobject *pp = (propertyobject *)self; - int err; - -#define VISIT(SLOT) \ - if (pp->SLOT) { \ - err = visit((PyObject *)(pp->SLOT), arg); \ - if (err) \ - return err; \ - } - - VISIT(prop_get); - VISIT(prop_set); - VISIT(prop_del); - VISIT(prop_doc); - + Py_VISIT(pp->prop_get); + Py_VISIT(pp->prop_set); + Py_VISIT(pp->prop_del); + Py_VISIT(pp->prop_doc); return 0; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0eccdbb..f5799ee 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -115,6 +115,14 @@ equally good collision statistics, needed less code & used less memory. /* Object used as dummy key to fill deleted entries */ static PyObject *dummy = NULL; /* Initialized by first call to newdictobject() */ +#ifdef Py_REF_DEBUG +PyObject * +_PyDict_Dummy(void) +{ + return dummy; +} +#endif + /* forward declarations */ static dictentry * lookdict_string(dictobject *mp, PyObject *key, long hash); @@ -1724,17 +1732,12 @@ static int dict_traverse(PyObject *op, visitproc visit, void *arg) { Py_ssize_t i = 0; - int err; PyObject *pk; PyObject *pv; while (PyDict_Next(op, &i, &pk, &pv)) { - err = visit(pk, arg); - if (err) - return err; - err = visit(pv, arg); - if (err) - return err; + Py_VISIT(pk); + Py_VISIT(pv); } return 0; } @@ -1880,16 +1883,16 @@ PyDict_Contains(PyObject *op, PyObject *key) /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)PyDict_Contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + PyDict_Contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ }; static PyObject * @@ -1966,8 +1969,8 @@ PyTypeObject PyDict_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ dictionary_doc, /* tp_doc */ - (traverseproc)dict_traverse, /* tp_traverse */ - (inquiry)dict_tp_clear, /* tp_clear */ + dict_traverse, /* tp_traverse */ + dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ @@ -1980,7 +1983,7 @@ PyTypeObject PyDict_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)dict_init, /* tp_init */ + dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 4811239..a8f43e0 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -9,8 +9,6 @@ typedef struct { PyObject* en_result; /* result tuple */ } enumobject; -PyTypeObject PyEnum_Type; - static PyObject * enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -51,18 +49,8 @@ enum_dealloc(enumobject *en) static int enum_traverse(enumobject *en, visitproc visit, void *arg) { - int err; - - if (en->en_sit) { - err = visit(en->en_sit, arg); - if (err) - return err; - } - if (en->en_result) { - err = visit(en->en_result, arg); - if (err) - return err; - } + Py_VISIT(en->en_sit); + Py_VISIT(en->en_result); return 0; } @@ -207,8 +195,7 @@ reversed_dealloc(reversedobject *ro) static int reversed_traverse(reversedobject *ro, visitproc visit, void *arg) { - if (ro->seq) - return visit((PyObject *)(ro->seq), arg); + Py_VISIT(ro->seq); return 0; } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index f96ee7b..632ab04 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -48,6 +48,10 @@ #define NEWLINE_LF 2 /* \n newline seen */ #define NEWLINE_CRLF 4 /* \r\n newline seen */ +#ifdef __cplusplus +extern "C" { +#endif + FILE * PyFile_AsFile(PyObject *f) { @@ -313,7 +317,8 @@ PyFile_SetBufSize(PyObject *f, int bufsize) PyMem_Free(file->f_setbuf); file->f_setbuf = NULL; } else { - file->f_setbuf = PyMem_Realloc(file->f_setbuf, bufsize); + file->f_setbuf = (char *)PyMem_Realloc(file->f_setbuf, + bufsize); } #ifdef HAVE_SETVBUF setvbuf(file->f_fp, file->f_setbuf, type, bufsize); @@ -818,7 +823,7 @@ file_read(PyFileObject *f, PyObject *args) buffersize = new_buffersize(f, (size_t)0); else buffersize = bytesrequested; - if (buffersize > INT_MAX) { + if (buffersize > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "requested number of bytes is more than a Python string can hold"); return NULL; @@ -1093,7 +1098,7 @@ getline_via_fgets(FILE *fp) assert(*(pvend-1) == '\0'); increment = total_v_size >> 2; /* mild exponential growth */ total_v_size += increment; - if (total_v_size > INT_MAX) { + if (total_v_size > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "line is longer than a Python string can hold"); Py_DECREF(v); @@ -1204,7 +1209,7 @@ get_line(PyFileObject *f, int n) used_v_size = total_v_size; increment = total_v_size >> 2; /* mild exponential growth */ total_v_size += increment; - if (total_v_size > INT_MAX) { + if (total_v_size > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "line is longer than a Python string can hold"); Py_DECREF(v); @@ -1391,12 +1396,12 @@ file_readlines(PyFileObject *f, PyObject *args) goto cleanup; } totalread += nread; - p = memchr(buffer+nfilled, '\n', nread); + p = (char *)memchr(buffer+nfilled, '\n', nread); if (p == NULL) { /* Need a larger buffer to fit this line */ nfilled += nread; buffersize *= 2; - if (buffersize > INT_MAX) { + if (buffersize > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "line is longer than a Python string can hold"); goto error; @@ -1431,7 +1436,7 @@ file_readlines(PyFileObject *f, PyObject *args) if (err != 0) goto error; q = p; - p = memchr(q, '\n', end-q); + p = (char *)memchr(q, '\n', end-q); } while (p != NULL); /* Move the remaining incomplete line to the start */ nfilled = end-q; @@ -1790,7 +1795,7 @@ drop_readahead(PyFileObject *f) /* Make sure that file has a readahead buffer with at least one byte (unless at EOF) and no more than bufsize. Returns negative value on - error */ + error, will set MemoryError if bufsize bytes cannot be allocated. */ static int readahead(PyFileObject *f, int bufsize) { @@ -1802,7 +1807,8 @@ readahead(PyFileObject *f, int bufsize) else drop_readahead(f); } - if ((f->f_buf = PyMem_Malloc(bufsize)) == NULL) { + if ((f->f_buf = (char *)PyMem_Malloc(bufsize)) == NULL) { + PyErr_NoMemory(); return -1; } Py_BEGIN_ALLOW_THREADS @@ -1844,7 +1850,7 @@ readahead_get_line_skip(PyFileObject *f, int skip, int bufsize) if (len == 0) return (PyStringObject *) PyString_FromStringAndSize(NULL, skip); - bufptr = memchr(f->f_bufptr, '\n', len); + bufptr = (char *)memchr(f->f_bufptr, '\n', len); if (bufptr != NULL) { bufptr++; /* Count the '\n' */ len = bufptr - f->f_bufptr; @@ -2056,7 +2062,7 @@ PyTypeObject PyFile_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)file_init, /* tp_init */ + file_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ file_new, /* tp_new */ PyObject_Del, /* tp_free */ @@ -2432,3 +2438,8 @@ Py_UniversalNewlineFread(char *buf, size_t n, f->f_skipnextlf = skipnextlf; return dst - buf; } + +#ifdef __cplusplus +} +#endif + diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 20ed86e..8708690 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -97,7 +97,7 @@ PyFloat_FromString(PyObject *v, char **pend) } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(v)) { - if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { + if (PyUnicode_GET_SIZE(v) >= (Py_ssize_t)sizeof(s_buffer)) { PyErr_SetString(PyExc_ValueError, "Unicode float() literal too long to convert"); return NULL; @@ -940,21 +940,21 @@ float_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *tmp, *new; + PyObject *tmp, *newobj; assert(PyType_IsSubtype(type, &PyFloat_Type)); tmp = float_new(&PyFloat_Type, args, kwds); if (tmp == NULL) return NULL; assert(PyFloat_CheckExact(tmp)); - new = type->tp_alloc(type, 0); - if (new == NULL) { + newobj = type->tp_alloc(type, 0); + if (newobj == NULL) { Py_DECREF(tmp); return NULL; } - ((PyFloatObject *)new)->ob_fval = ((PyFloatObject *)tmp)->ob_fval; + ((PyFloatObject *)newobj)->ob_fval = ((PyFloatObject *)tmp)->ob_fval; Py_DECREF(tmp); - return new; + return newobj; } static PyObject * @@ -1106,12 +1106,12 @@ Convert a string or number to a floating point number, if possible."); static PyNumberMethods float_as_number = { - (binaryfunc)float_add, /*nb_add*/ - (binaryfunc)float_sub, /*nb_subtract*/ - (binaryfunc)float_mul, /*nb_multiply*/ - (binaryfunc)float_rem, /*nb_remainder*/ - (binaryfunc)float_divmod, /*nb_divmod*/ - (ternaryfunc)float_pow, /*nb_power*/ + float_add, /*nb_add*/ + float_sub, /*nb_subtract*/ + float_mul, /*nb_multiply*/ + float_rem, /*nb_remainder*/ + float_divmod, /*nb_divmod*/ + float_pow, /*nb_power*/ (unaryfunc)float_neg, /*nb_negative*/ (unaryfunc)float_pos, /*nb_positive*/ (unaryfunc)float_abs, /*nb_absolute*/ @@ -1122,10 +1122,10 @@ static PyNumberMethods float_as_number = { 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ - (coercion)float_coerce, /*nb_coerce*/ - (unaryfunc)float_int, /*nb_int*/ - (unaryfunc)float_long, /*nb_long*/ - (unaryfunc)float_float, /*nb_float*/ + float_coerce, /*nb_coerce*/ + float_int, /*nb_int*/ + float_long, /*nb_long*/ + float_float, /*nb_float*/ 0, /* nb_oct */ 0, /* nb_hex */ 0, /* nb_inplace_add */ @@ -1170,7 +1170,7 @@ PyTypeObject PyFloat_Type = { float_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - (richcmpfunc)float_richcompare, /* tp_richcompare */ + float_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6e3f297..9aabc7a 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1,4 +1,3 @@ - /* Frame object implementation */ #include "Python.h" @@ -333,7 +332,7 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure) Py_XINCREF(v); f->f_trace = v; - + if (v != NULL) f->f_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); @@ -399,7 +398,7 @@ frame_dealloc(PyFrameObject *f) for (p = f->f_valuestack; p < f->f_stacktop; p++) Py_XDECREF(*p); } - + Py_XDECREF(f->f_back); Py_DECREF(f->f_code); Py_DECREF(f->f_builtins); @@ -423,30 +422,28 @@ static int frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { PyObject **fastlocals, **p; - int i, err, slots; -#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;} - - VISIT(f->f_back); - VISIT(f->f_code); - VISIT(f->f_builtins); - VISIT(f->f_globals); - VISIT(f->f_locals); - VISIT(f->f_trace); - VISIT(f->f_exc_type); - VISIT(f->f_exc_value); - VISIT(f->f_exc_traceback); + int i, slots; + + Py_VISIT(f->f_back); + Py_VISIT(f->f_code); + Py_VISIT(f->f_builtins); + Py_VISIT(f->f_globals); + Py_VISIT(f->f_locals); + Py_VISIT(f->f_trace); + Py_VISIT(f->f_exc_type); + Py_VISIT(f->f_exc_value); + Py_VISIT(f->f_exc_traceback); /* locals */ slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; fastlocals = f->f_localsplus; - for (i = slots; --i >= 0; ++fastlocals) { - VISIT(*fastlocals); - } + for (i = slots; --i >= 0; ++fastlocals) + Py_VISIT(*fastlocals); /* stack */ if (f->f_stacktop != NULL) { for (p = f->f_valuestack; p < f->f_stacktop; p++) - VISIT(*p); + Py_VISIT(*p); } return 0; } @@ -454,37 +451,32 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) static void frame_clear(PyFrameObject *f) { - PyObject **fastlocals, **p; + PyObject **fastlocals, **p, **oldtop; int i, slots; - Py_XDECREF(f->f_exc_type); - f->f_exc_type = NULL; + /* Before anything else, make sure that this frame is clearly marked + * as being defunct! Else, e.g., a generator reachable from this + * frame may also point to this frame, believe itself to still be + * active, and try cleaning up this frame again. + */ + oldtop = f->f_stacktop; + f->f_stacktop = NULL; - Py_XDECREF(f->f_exc_value); - f->f_exc_value = NULL; - - Py_XDECREF(f->f_exc_traceback); - f->f_exc_traceback = NULL; - - Py_XDECREF(f->f_trace); - f->f_trace = NULL; + Py_CLEAR(f->f_exc_type); + Py_CLEAR(f->f_exc_value); + Py_CLEAR(f->f_exc_traceback); + Py_CLEAR(f->f_trace); /* locals */ slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; fastlocals = f->f_localsplus; - for (i = slots; --i >= 0; ++fastlocals) { - if (*fastlocals != NULL) { - Py_XDECREF(*fastlocals); - *fastlocals = NULL; - } - } + for (i = slots; --i >= 0; ++fastlocals) + Py_CLEAR(*fastlocals); /* stack */ - if (f->f_stacktop != NULL) { - for (p = f->f_valuestack; p < f->f_stacktop; p++) { - Py_XDECREF(*p); - *p = NULL; - } + if (oldtop != NULL) { + for (p = f->f_valuestack; p < oldtop; p++) + Py_CLEAR(*p); } } @@ -534,7 +526,7 @@ int _PyFrame_Init() } PyFrameObject * -PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, +PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyObject *locals) { PyFrameObject *back = tstate->frame; @@ -563,10 +555,10 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, builtins = NULL; } if (builtins == NULL) { - /* No builtins! Make up a minimal one + /* No builtins! Make up a minimal one Give them 'None', at least. */ builtins = PyDict_New(); - if (builtins == NULL || + if (builtins == NULL || PyDict_SetItemString( builtins, "None", Py_None) < 0) return NULL; @@ -611,7 +603,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, Py_INCREF(globals); f->f_globals = globals; /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */ - if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == + if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == (CO_NEWLOCALS | CO_OPTIMIZED)) locals = NULL; /* PyFrame_FastToLocals() will set. */ else if (code->co_flags & CO_NEWLOCALS) { @@ -749,7 +741,7 @@ PyFrame_FastToLocals(PyFrameObject *f) return; PyErr_Fetch(&error_type, &error_value, &error_traceback); fast = f->f_localsplus; - j = PyTuple_Size(map); + j = PyTuple_GET_SIZE(map); if (j > f->f_nlocals) j = f->f_nlocals; if (f->f_nlocals) @@ -759,10 +751,10 @@ PyFrame_FastToLocals(PyFrameObject *f) && PyTuple_Check(f->f_code->co_freevars))) { return; } - map_to_dict(f->f_code->co_cellvars, + map_to_dict(f->f_code->co_cellvars, PyTuple_GET_SIZE(f->f_code->co_cellvars), locals, fast + f->f_nlocals, 1); - map_to_dict(f->f_code->co_freevars, + map_to_dict(f->f_code->co_freevars, PyTuple_GET_SIZE(f->f_code->co_freevars), locals, fast + f->f_nlocals + f->f_ncells, 1); } @@ -787,7 +779,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) return; PyErr_Fetch(&error_type, &error_value, &error_traceback); fast = f->f_localsplus; - j = PyTuple_Size(map); + j = PyTuple_GET_SIZE(map); if (j > f->f_nlocals) j = f->f_nlocals; if (f->f_nlocals) @@ -796,12 +788,12 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) if (!(PyTuple_Check(f->f_code->co_cellvars) && PyTuple_Check(f->f_code->co_freevars))) return; - dict_to_map(f->f_code->co_cellvars, + dict_to_map(f->f_code->co_cellvars, PyTuple_GET_SIZE(f->f_code->co_cellvars), locals, fast + f->f_nlocals, 1, clear); - dict_to_map(f->f_code->co_freevars, + dict_to_map(f->f_code->co_freevars, PyTuple_GET_SIZE(f->f_code->co_freevars), - locals, fast + f->f_nlocals + f->f_ncells, 1, + locals, fast + f->f_nlocals + f->f_ncells, 1, clear); } PyErr_Restore(error_type, error_value, error_traceback); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 00ae2eb..59cb519 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -466,47 +466,14 @@ func_repr(PyFunctionObject *op) static int func_traverse(PyFunctionObject *f, visitproc visit, void *arg) { - int err; - if (f->func_code) { - err = visit(f->func_code, arg); - if (err) - return err; - } - if (f->func_globals) { - err = visit(f->func_globals, arg); - if (err) - return err; - } - if (f->func_module) { - err = visit(f->func_module, arg); - if (err) - return err; - } - if (f->func_defaults) { - err = visit(f->func_defaults, arg); - if (err) - return err; - } - if (f->func_doc) { - err = visit(f->func_doc, arg); - if (err) - return err; - } - if (f->func_name) { - err = visit(f->func_name, arg); - if (err) - return err; - } - if (f->func_dict) { - err = visit(f->func_dict, arg); - if (err) - return err; - } - if (f->func_closure) { - err = visit(f->func_closure, arg); - if (err) - return err; - } + Py_VISIT(f->func_code); + Py_VISIT(f->func_globals); + Py_VISIT(f->func_module); + Py_VISIT(f->func_defaults); + Py_VISIT(f->func_doc); + Py_VISIT(f->func_name); + Py_VISIT(f->func_dict); + Py_VISIT(f->func_closure); return 0; } @@ -647,17 +614,14 @@ cm_dealloc(classmethod *cm) static int cm_traverse(classmethod *cm, visitproc visit, void *arg) { - if (!cm->cm_callable) - return 0; - return visit(cm->cm_callable, arg); + Py_VISIT(cm->cm_callable); + return 0; } static int cm_clear(classmethod *cm) { - Py_XDECREF(cm->cm_callable); - cm->cm_callable = NULL; - + Py_CLEAR(cm->cm_callable); return 0; } @@ -808,9 +772,8 @@ sm_dealloc(staticmethod *sm) static int sm_traverse(staticmethod *sm, visitproc visit, void *arg) { - if (!sm->sm_callable) - return 0; - return visit(sm->sm_callable, arg); + Py_VISIT(sm->sm_callable); + return 0; } static int diff --git a/Objects/genobject.c b/Objects/genobject.c index 3f6ef85..15e53dd 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -5,11 +5,13 @@ #include "genobject.h" #include "ceval.h" #include "structmember.h" +#include "opcode.h" static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { - return visit((PyObject *)gen->gi_frame, arg); + Py_VISIT((PyObject *)gen->gi_frame); + return 0; } static void @@ -20,12 +22,11 @@ gen_dealloc(PyGenObject *gen) _PyObject_GC_UNTRACK(gen); if (gen->gi_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) gen); - + PyObject_ClearWeakRefs(self); _PyObject_GC_TRACK(self); - if (gen->gi_frame->f_stacktop!=NULL) { + if (gen->gi_frame != NULL && gen->gi_frame->f_stacktop != NULL) { /* Generator is paused, so we need to close */ gen->ob_type->tp_del(self); if (self->ob_refcnt > 0) @@ -33,7 +34,7 @@ gen_dealloc(PyGenObject *gen) } _PyObject_GC_UNTRACK(self); - Py_XDECREF(gen->gi_frame); + Py_CLEAR(gen->gi_frame); PyObject_GC_Del(gen); } @@ -50,16 +51,18 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) "generator already executing"); return NULL; } - if ((PyObject *)f == Py_None || f->f_stacktop == NULL) { + if (f==NULL || f->f_stacktop == NULL) { /* Only set exception if called from send() */ - if (arg && !exc) PyErr_SetNone(PyExc_StopIteration); + if (arg && !exc) + PyErr_SetNone(PyExc_StopIteration); return NULL; } if (f->f_lasti == -1) { if (arg && arg != Py_None) { PyErr_SetString(PyExc_TypeError, - "can't send non-None value to a just-started generator"); + "can't send non-None value to a " + "just-started generator"); return NULL; } } else { @@ -91,21 +94,22 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) Py_DECREF(result); result = NULL; /* Set exception if not called by gen_iternext() */ - if (arg) PyErr_SetNone(PyExc_StopIteration); + if (arg) + PyErr_SetNone(PyExc_StopIteration); } if (!result || f->f_stacktop == NULL) { /* generator can't be rerun, so release the frame */ Py_DECREF(f); - gen->gi_frame = (PyFrameObject *)Py_None; - Py_INCREF(Py_None); + gen->gi_frame = NULL; } return result; } PyDoc_STRVAR(send_doc, -"send(arg) -> send 'arg' into generator, return next yielded value or raise StopIteration."); +"send(arg) -> send 'arg' into generator,\n\ +return next yielded value or raise StopIteration."); static PyObject * gen_send(PyGenObject *gen, PyObject *arg) @@ -125,11 +129,11 @@ gen_close(PyGenObject *gen, PyObject *args) if (retval) { Py_DECREF(retval); PyErr_SetString(PyExc_RuntimeError, - "generator ignored GeneratorExit"); + "generator ignored GeneratorExit"); return NULL; } - if ( PyErr_ExceptionMatches(PyExc_StopIteration) - || PyErr_ExceptionMatches(PyExc_GeneratorExit) ) + if (PyErr_ExceptionMatches(PyExc_StopIteration) + || PyErr_ExceptionMatches(PyExc_GeneratorExit)) { PyErr_Clear(); /* ignore these errors */ Py_INCREF(Py_None); @@ -145,7 +149,7 @@ gen_del(PyObject *self) PyObject *error_type, *error_value, *error_traceback; PyGenObject *gen = (PyGenObject *)self; - if ((PyObject *)gen->gi_frame == Py_None || gen->gi_frame->f_stacktop==NULL) + if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) /* Generator isn't paused, so no need to close */ return; @@ -156,10 +160,10 @@ gen_del(PyObject *self) /* Save the current exception, if any. */ PyErr_Fetch(&error_type, &error_value, &error_traceback); - res = gen_close((PyGenObject *)self, NULL); + res = gen_close(gen, NULL); if (res == NULL) - PyErr_WriteUnraisable((PyObject *)self); + PyErr_WriteUnraisable(self); else Py_DECREF(res); @@ -181,7 +185,7 @@ gen_del(PyObject *self) _Py_NewReference(self); self->ob_refcnt = refcnt; } - assert(!PyType_IS_GC(self->ob_type) || + assert(PyType_IS_GC(self->ob_type) && _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so @@ -202,10 +206,11 @@ gen_del(PyObject *self) PyDoc_STRVAR(throw_doc, -"throw(typ[,val[,tb]]) -> raise exception in generator, return next yielded value or raise StopIteration."); +"throw(typ[,val[,tb]]) -> raise exception in generator,\n\ +return next yielded value or raise StopIteration."); static PyObject * -gen_throw(PyGenObject *gen, PyObject *args) +gen_throw(PyGenObject *gen, PyObject *args) { PyObject *typ; PyObject *tb = NULL; @@ -216,10 +221,8 @@ gen_throw(PyGenObject *gen, PyObject *args) /* First, check the traceback argument, replacing None with NULL. */ - if (tb == Py_None) { - Py_DECREF(tb); + if (tb == Py_None) tb = NULL; - } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback object"); @@ -249,7 +252,10 @@ gen_throw(PyGenObject *gen, PyObject *args) Py_INCREF(typ); } } - else { + + /* Allow raising builtin string exceptions */ + + else if (!PyString_CheckExact(typ)) { /* Not something you can raise. throw() fails. */ PyErr_Format(PyExc_TypeError, "exceptions must be classes, or instances, not %s", @@ -257,7 +263,7 @@ gen_throw(PyGenObject *gen, PyObject *args) goto failed_throw; } - PyErr_Restore(typ,val,tb); + PyErr_Restore(typ, val, tb); return gen_send_ex(gen, Py_None, 1); failed_throw: @@ -324,7 +330,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ - + 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ @@ -355,3 +361,23 @@ PyGen_New(PyFrameObject *f) _PyObject_GC_TRACK(gen); return (PyObject *)gen; } + +int +PyGen_NeedsFinalizing(PyGenObject *gen) +{ + int i; + PyFrameObject *f = gen->gi_frame; + + if (f == NULL || f->f_stacktop == NULL || f->f_iblock <= 0) + return 0; /* no frame or empty blockstack == no finalization */ + + /* Any block type besides a loop requires cleanup. */ + i = f->f_iblock; + while (--i >= 0) { + if (f->f_blockstack[i].b_type != SETUP_LOOP) + return 1; + } + + /* No blocks except loops, it's safe to skip finalization. */ + return 0; +} diff --git a/Objects/intobject.c b/Objects/intobject.c index c734840..fb3221f 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -255,18 +255,18 @@ PyInt_AsUnsignedLongMask(register PyObject *op) if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || nb->nb_int == NULL) { PyErr_SetString(PyExc_TypeError, "an integer is required"); - return -1; + return (unsigned long)-1; } io = (PyIntObject*) (*nb->nb_int) (op); if (io == NULL) - return -1; + return (unsigned long)-1; if (!PyInt_Check(io)) { if (PyLong_Check(io)) { val = PyLong_AsUnsignedLongMask((PyObject *)io); Py_DECREF(io); if (PyErr_Occurred()) - return -1; + return (unsigned long)-1; return val; } else @@ -274,7 +274,7 @@ PyInt_AsUnsignedLongMask(register PyObject *op) Py_DECREF(io); PyErr_SetString(PyExc_TypeError, "nb_int should return int object"); - return -1; + return (unsigned long)-1; } } @@ -300,18 +300,18 @@ PyInt_AsUnsignedLongLongMask(register PyObject *op) if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || nb->nb_int == NULL) { PyErr_SetString(PyExc_TypeError, "an integer is required"); - return -1; + return (unsigned PY_LONG_LONG)-1; } io = (PyIntObject*) (*nb->nb_int) (op); if (io == NULL) - return -1; + return (unsigned PY_LONG_LONG)-1; if (!PyInt_Check(io)) { if (PyLong_Check(io)) { val = PyLong_AsUnsignedLongLongMask((PyObject *)io); Py_DECREF(io); if (PyErr_Occurred()) - return -1; + return (unsigned PY_LONG_LONG)-1; return val; } else @@ -319,7 +319,7 @@ PyInt_AsUnsignedLongLongMask(register PyObject *op) Py_DECREF(io); PyErr_SetString(PyExc_TypeError, "nb_int should return int object"); - return -1; + return (unsigned PY_LONG_LONG)-1; } } @@ -335,7 +335,8 @@ PyInt_FromString(char *s, char **pend, int base) { char *end; long x; - char buffer[256]; /* For errors */ + Py_ssize_t slen; + PyObject *sobj, *srepr; if ((base != 0 && base < 2) || base > 36) { PyErr_SetString(PyExc_ValueError, @@ -359,9 +360,18 @@ PyInt_FromString(char *s, char **pend, int base) end++; if (*end != '\0') { bad: - PyOS_snprintf(buffer, sizeof(buffer), - "invalid literal for int(): %.200s", s); - PyErr_SetString(PyExc_ValueError, buffer); + slen = strlen(s) < 200 ? strlen(s) : 200; + sobj = PyString_FromStringAndSize(s, slen); + if (sobj == NULL) + return NULL; + srepr = PyObject_Repr(sobj); + Py_DECREF(sobj); + if (srepr == NULL) + return NULL; + PyErr_Format(PyExc_ValueError, + "invalid literal for int() with base %d: %s", + base, PyString_AS_STRING(srepr)); + Py_DECREF(srepr); return NULL; } else if (errno != 0) @@ -376,7 +386,7 @@ PyObject * PyInt_FromUnicode(Py_UNICODE *s, Py_ssize_t length, int base) { PyObject *result; - char *buffer = PyMem_MALLOC(length+1); + char *buffer = (char *)PyMem_MALLOC(length+1); if (buffer == NULL) return NULL; @@ -961,7 +971,7 @@ int_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *tmp, *new; + PyObject *tmp, *newobj; long ival; assert(PyType_IsSubtype(type, &PyInt_Type)); @@ -978,14 +988,14 @@ int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ival = ((PyIntObject *)tmp)->ob_ival; } - new = type->tp_alloc(type, 0); - if (new == NULL) { + newobj = type->tp_alloc(type, 0); + if (newobj == NULL) { Py_DECREF(tmp); return NULL; } - ((PyIntObject *)new)->ob_ival = ival; + ((PyIntObject *)newobj)->ob_ival = ival; Py_DECREF(tmp); - return new; + return newobj; } static PyObject * @@ -1046,7 +1056,7 @@ static PyNumberMethods int_as_number = { int_true_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ - (lenfunc)PyInt_AsSsize_t, /* nb_index */ + PyInt_AsSsize_t, /* nb_index */ }; PyTypeObject PyInt_Type = { @@ -1119,6 +1129,7 @@ PyInt_Fini(void) PyIntObject *p; PyIntBlock *list, *next; int i; + unsigned int ctr; int bc, bf; /* block count, number of freed blocks */ int irem, isum; /* remaining unfreed ints per block, total */ @@ -1141,9 +1152,9 @@ PyInt_Fini(void) while (list != NULL) { bc++; irem = 0; - for (i = 0, p = &list->objects[0]; - i < N_INTOBJECTS; - i++, p++) { + for (ctr = 0, p = &list->objects[0]; + ctr < N_INTOBJECTS; + ctr++, p++) { if (PyInt_CheckExact(p) && p->ob_refcnt != 0) irem++; } @@ -1151,9 +1162,9 @@ PyInt_Fini(void) if (irem) { list->next = block_list; block_list = list; - for (i = 0, p = &list->objects[0]; - i < N_INTOBJECTS; - i++, p++) { + for (ctr = 0, p = &list->objects[0]; + ctr < N_INTOBJECTS; + ctr++, p++) { if (!PyInt_CheckExact(p) || p->ob_refcnt == 0) { p->ob_type = (struct _typeobject *) @@ -1194,9 +1205,9 @@ PyInt_Fini(void) if (Py_VerboseFlag > 1) { list = block_list; while (list != NULL) { - for (i = 0, p = &list->objects[0]; - i < N_INTOBJECTS; - i++, p++) { + for (ctr = 0, p = &list->objects[0]; + ctr < N_INTOBJECTS; + ctr++, p++) { if (PyInt_CheckExact(p) && p->ob_refcnt != 0) /* XXX(twouters) cast refcount to long until %zd is universally diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 51f551b..cf839f4 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -38,9 +38,8 @@ iter_dealloc(seqiterobject *it) static int iter_traverse(seqiterobject *it, visitproc visit, void *arg) { - if (it->it_seq == NULL) - return 0; - return visit(it->it_seq, arg); + Py_VISIT(it->it_seq); + return 0; } static PyObject * @@ -123,7 +122,7 @@ PyTypeObject PySeqIter_Type = { 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)iter_iternext, /* tp_iternext */ + iter_iternext, /* tp_iternext */ seqiter_methods, /* tp_methods */ 0, /* tp_members */ }; @@ -162,11 +161,8 @@ calliter_dealloc(calliterobject *it) static int calliter_traverse(calliterobject *it, visitproc visit, void *arg) { - int err; - if (it->it_callable != NULL && (err = visit(it->it_callable, arg))) - return err; - if (it->it_sentinel != NULL && (err = visit(it->it_sentinel, arg))) - return err; + Py_VISIT(it->it_callable); + Py_VISIT(it->it_sentinel); return 0; } diff --git a/Objects/listobject.c b/Objects/listobject.c index 966d659..105df4c 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -181,7 +181,7 @@ ins1(PyListObject *self, Py_ssize_t where, PyObject *v) PyErr_BadInternalCall(); return -1; } - if (n == INT_MAX) { + if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to list"); return -1; @@ -221,7 +221,7 @@ app1(PyListObject *self, PyObject *v) Py_ssize_t n = PyList_GET_SIZE(self); assert (v != NULL); - if (n == INT_MAX) { + if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to list"); return -1; @@ -1805,28 +1805,11 @@ typedef struct { PyObject *value; } sortwrapperobject; -static PyTypeObject sortwrapper_type; - +PyDoc_STRVAR(sortwrapper_doc, "Object wrapper with a custom sort key."); static PyObject * -sortwrapper_richcompare(sortwrapperobject *a, sortwrapperobject *b, int op) -{ - if (!PyObject_TypeCheck(b, &sortwrapper_type)) { - PyErr_SetString(PyExc_TypeError, - "expected a sortwrapperobject"); - return NULL; - } - return PyObject_RichCompare(a->key, b->key, op); -} - +sortwrapper_richcompare(sortwrapperobject *, sortwrapperobject *, int); static void -sortwrapper_dealloc(sortwrapperobject *so) -{ - Py_XDECREF(so->key); - Py_XDECREF(so->value); - PyObject_Del(so); -} - -PyDoc_STRVAR(sortwrapper_doc, "Object wrapper with a custom sort key."); +sortwrapper_dealloc(sortwrapperobject *); static PyTypeObject sortwrapper_type = { PyObject_HEAD_INIT(&PyType_Type) @@ -1858,6 +1841,26 @@ static PyTypeObject sortwrapper_type = { (richcmpfunc)sortwrapper_richcompare, /* tp_richcompare */ }; + +static PyObject * +sortwrapper_richcompare(sortwrapperobject *a, sortwrapperobject *b, int op) +{ + if (!PyObject_TypeCheck(b, &sortwrapper_type)) { + PyErr_SetString(PyExc_TypeError, + "expected a sortwrapperobject"); + return NULL; + } + return PyObject_RichCompare(a->key, b->key, op); +} + +static void +sortwrapper_dealloc(sortwrapperobject *so) +{ + Py_XDECREF(so->key); + Py_XDECREF(so->value); + PyObject_Del(so); +} + /* Returns a new reference to a sortwrapper. Consumes the references to the two underlying objects. */ @@ -2271,16 +2274,9 @@ static int list_traverse(PyListObject *o, visitproc visit, void *arg) { Py_ssize_t i; - PyObject *x; - - for (i = o->ob_size; --i >= 0; ) { - x = o->ob_item[i]; - if (x != NULL) { - int err = visit(x, arg); - if (err) - return err; - } - } + + for (i = o->ob_size; --i >= 0; ) + Py_VISIT(o->ob_item[i]); return 0; } @@ -2698,7 +2694,53 @@ typedef struct { PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ } listiterobject; -PyTypeObject PyListIter_Type; +static PyObject *list_iter(PyObject *); +static void listiter_dealloc(listiterobject *); +static int listiter_traverse(listiterobject *, visitproc, void *); +static PyObject *listiter_next(listiterobject *); +static PyObject *listiter_len(listiterobject *); + +PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); + +static PyMethodDef listiter_methods[] = { + {"__length_hint__", (PyCFunction)listiter_len, METH_NOARGS, length_hint_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyTypeObject PyListIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "listiterator", /* tp_name */ + sizeof(listiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)listiter_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 */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)listiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)listiter_next, /* tp_iternext */ + listiter_methods, /* tp_methods */ + 0, /* tp_members */ +}; + static PyObject * list_iter(PyObject *seq) @@ -2730,9 +2772,8 @@ listiter_dealloc(listiterobject *it) static int listiter_traverse(listiterobject *it, visitproc visit, void *arg) { - if (it->it_seq == NULL) - return 0; - return visit((PyObject *)it->it_seq, arg); + Py_VISIT(it->it_seq); + return 0; } static PyObject * @@ -2770,29 +2811,40 @@ listiter_len(listiterobject *it) } return PyInt_FromLong(0); } +/*********************** List Reverse Iterator **************************/ -PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); +typedef struct { + PyObject_HEAD + Py_ssize_t it_index; + PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ +} listreviterobject; -static PyMethodDef listiter_methods[] = { - {"__length_hint__", (PyCFunction)listiter_len, METH_NOARGS, length_hint_doc}, - {NULL, NULL} /* sentinel */ +static PyObject *list_reversed(PyListObject *, PyObject *); +static void listreviter_dealloc(listreviterobject *); +static int listreviter_traverse(listreviterobject *, visitproc, void *); +static PyObject *listreviter_next(listreviterobject *); +static Py_ssize_t listreviter_len(listreviterobject *); + +static PySequenceMethods listreviter_as_sequence = { + (lenfunc)listreviter_len, /* sq_length */ + 0, /* sq_concat */ }; -PyTypeObject PyListIter_Type = { +PyTypeObject PyListRevIter_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ - "listiterator", /* tp_name */ - sizeof(listiterobject), /* tp_basicsize */ + "listreverseiterator", /* tp_name */ + sizeof(listreviterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)listiter_dealloc, /* tp_dealloc */ + (destructor)listreviter_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 */ + &listreviter_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ @@ -2802,26 +2854,15 @@ PyTypeObject PyListIter_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - (traverseproc)listiter_traverse, /* tp_traverse */ + (traverseproc)listreviter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)listiter_next, /* tp_iternext */ - listiter_methods, /* tp_methods */ - 0, /* tp_members */ + (iternextfunc)listreviter_next, /* tp_iternext */ + 0, }; -/*********************** List Reverse Iterator **************************/ - -typedef struct { - PyObject_HEAD - Py_ssize_t it_index; - PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ -} listreviterobject; - -PyTypeObject PyListRevIter_Type; - static PyObject * list_reversed(PyListObject *seq, PyObject *unused) { @@ -2849,9 +2890,8 @@ listreviter_dealloc(listreviterobject *it) static int listreviter_traverse(listreviterobject *it, visitproc visit, void *arg) { - if (it->it_seq == NULL) - return 0; - return visit((PyObject *)it->it_seq, arg); + Py_VISIT(it->it_seq); + return 0; } static PyObject * @@ -2884,40 +2924,3 @@ listreviter_len(listreviterobject *it) return len; } -static PySequenceMethods listreviter_as_sequence = { - (lenfunc)listreviter_len, /* sq_length */ - 0, /* sq_concat */ -}; - -PyTypeObject PyListRevIter_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ - "listreverseiterator", /* tp_name */ - sizeof(listreviterobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)listreviter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &listreviter_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ - 0, /* tp_doc */ - (traverseproc)listreviter_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)listreviter_next, /* tp_iternext */ - 0, -}; diff --git a/Objects/longobject.c b/Objects/longobject.c index 7c5ebc4..3073923 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -281,7 +281,7 @@ _long_as_ssize_t(PyObject *vv) { if (sign > 0) return PY_SSIZE_T_MAX; else - return -PY_SSIZE_T_MAX-1; + return PY_SSIZE_T_MIN; } /* Get a Py_ssize_t from a long int object. @@ -301,7 +301,7 @@ _PyLong_AsSsize_t(PyObject *vv) /* Get a Py_ssize_t from a long int object. Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX, and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1. - Return 0 on error, 1 on success. + On error, return -1 with an exception set. */ static Py_ssize_t @@ -419,7 +419,7 @@ _PyLong_NumBits(PyObject *vv) digit msd = v->ob_digit[ndigits - 1]; result = (ndigits - 1) * SHIFT; - if (result / SHIFT != ndigits - 1) + if (result / SHIFT != (size_t)(ndigits - 1)) goto Overflow; do { ++result; @@ -771,6 +771,8 @@ PyObject * PyLong_FromVoidPtr(void *p) { #if SIZEOF_VOID_P <= SIZEOF_LONG + if ((long)p < 0) + return PyLong_FromUnsignedLong((unsigned long)p); return PyInt_FromLong((long)p); #else @@ -783,7 +785,7 @@ PyLong_FromVoidPtr(void *p) /* optimize null pointers */ if (p == NULL) return PyInt_FromLong(0); - return PyLong_FromLongLong((PY_LONG_LONG)p); + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)p); #endif /* SIZEOF_VOID_P <= SIZEOF_LONG */ } @@ -802,8 +804,10 @@ PyLong_AsVoidPtr(PyObject *vv) if (PyInt_Check(vv)) x = PyInt_AS_LONG(vv); - else + else if (PyLong_Check(vv) && _PyLong_Sign(vv) < 0) x = PyLong_AsLong(vv); + else + x = PyLong_AsUnsignedLong(vv); #else #ifndef HAVE_LONG_LONG @@ -816,8 +820,10 @@ PyLong_AsVoidPtr(PyObject *vv) if (PyInt_Check(vv)) x = PyInt_AS_LONG(vv); - else + else if (PyLong_Check(vv) && _PyLong_Sign(vv) < 0) x = PyLong_AsLongLong(vv); + else + x = PyLong_AsUnsignedLongLong(vv); #endif /* SIZEOF_VOID_P <= SIZEOF_LONG */ @@ -947,7 +953,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv) if (vv == NULL || !PyLong_Check(vv)) { PyErr_BadInternalCall(); - return -1; + return (unsigned PY_LONG_LONG)-1; } res = _PyLong_AsByteArray( @@ -1394,6 +1400,8 @@ PyLong_FromString(char *str, char **pend, int base) int sign = 1; char *start, *orig_str = str; PyLongObject *z; + PyObject *strobj, *strrepr; + Py_ssize_t slen; if ((base != 0 && base < 2) || base > 36) { PyErr_SetString(PyExc_ValueError, @@ -1459,9 +1467,19 @@ PyLong_FromString(char *str, char **pend, int base) return (PyObject *) z; onError: - PyErr_Format(PyExc_ValueError, - "invalid literal for long(): %.200s", orig_str); Py_XDECREF(z); + slen = strlen(orig_str) < 200 ? strlen(orig_str) : 200; + strobj = PyString_FromStringAndSize(orig_str, slen); + if (strobj == NULL) + return NULL; + strrepr = PyObject_Repr(strobj); + Py_DECREF(strobj); + if (strrepr == NULL) + return NULL; + PyErr_Format(PyExc_ValueError, + "invalid literal for long() with base %d: %s", + base, PyString_AS_STRING(strrepr)); + Py_DECREF(strrepr); return NULL; } @@ -1470,7 +1488,7 @@ PyObject * PyLong_FromUnicode(Py_UNICODE *u, Py_ssize_t length, int base) { PyObject *result; - char *buffer = PyMem_MALLOC(length+1); + char *buffer = (char *)PyMem_MALLOC(length+1); if (buffer == NULL) return NULL; @@ -3066,7 +3084,7 @@ long_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyLongObject *tmp, *new; + PyLongObject *tmp, *newobj; Py_ssize_t i, n; assert(PyType_IsSubtype(type, &PyLong_Type)); @@ -3077,17 +3095,17 @@ long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) n = tmp->ob_size; if (n < 0) n = -n; - new = (PyLongObject *)type->tp_alloc(type, n); - if (new == NULL) { + newobj = (PyLongObject *)type->tp_alloc(type, n); + if (newobj == NULL) { Py_DECREF(tmp); return NULL; } - assert(PyLong_Check(new)); - new->ob_size = tmp->ob_size; + assert(PyLong_Check(newobj)); + newobj->ob_size = tmp->ob_size; for (i = 0; i < n; i++) - new->ob_digit[i] = tmp->ob_digit[i]; + newobj->ob_digit[i] = tmp->ob_digit[i]; Py_DECREF(tmp); - return (PyObject *)new; + return (PyObject *)newobj; } static PyObject * @@ -3114,25 +3132,25 @@ static PyNumberMethods long_as_number = { (binaryfunc) long_add, /*nb_add*/ (binaryfunc) long_sub, /*nb_subtract*/ (binaryfunc) long_mul, /*nb_multiply*/ - (binaryfunc) long_mod, /*nb_remainder*/ - (binaryfunc) long_divmod, /*nb_divmod*/ - (ternaryfunc) long_pow, /*nb_power*/ + long_mod, /*nb_remainder*/ + long_divmod, /*nb_divmod*/ + long_pow, /*nb_power*/ (unaryfunc) long_neg, /*nb_negative*/ (unaryfunc) long_pos, /*tp_positive*/ (unaryfunc) long_abs, /*tp_absolute*/ (inquiry) long_nonzero, /*tp_nonzero*/ (unaryfunc) long_invert, /*nb_invert*/ - (binaryfunc) long_lshift, /*nb_lshift*/ + long_lshift, /*nb_lshift*/ (binaryfunc) long_rshift, /*nb_rshift*/ - (binaryfunc) long_and, /*nb_and*/ - (binaryfunc) long_xor, /*nb_xor*/ - (binaryfunc) long_or, /*nb_or*/ - (coercion) long_coerce, /*nb_coerce*/ - (unaryfunc) long_int, /*nb_int*/ - (unaryfunc) long_long, /*nb_long*/ - (unaryfunc) long_float, /*nb_float*/ - (unaryfunc) long_oct, /*nb_oct*/ - (unaryfunc) long_hex, /*nb_hex*/ + long_and, /*nb_and*/ + long_xor, /*nb_xor*/ + long_or, /*nb_or*/ + long_coerce, /*nb_coerce*/ + long_int, /*nb_int*/ + long_long, /*nb_long*/ + long_float, /*nb_float*/ + long_oct, /*nb_oct*/ + long_hex, /*nb_hex*/ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ @@ -3143,11 +3161,11 @@ static PyNumberMethods long_as_number = { 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ - (binaryfunc)long_div, /* nb_floor_divide */ + long_div, /* nb_floor_divide */ long_true_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ - (lenfunc)long_index, /* nb_index */ + long_index, /* nb_index */ }; PyTypeObject PyLong_Type = { @@ -3156,18 +3174,18 @@ PyTypeObject PyLong_Type = { "long", /* tp_name */ sizeof(PyLongObject) - sizeof(digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ - (destructor)long_dealloc, /* tp_dealloc */ + long_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)long_compare, /* tp_compare */ - (reprfunc)long_repr, /* tp_repr */ + long_repr, /* tp_repr */ &long_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)long_hash, /* tp_hash */ 0, /* tp_call */ - (reprfunc)long_str, /* tp_str */ + long_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 8e3bf86..ecc9a0a 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -149,17 +149,8 @@ meth_get__name__(PyCFunctionObject *m, void *closure) static int meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) { - int err; - if (m->m_self != NULL) { - err = visit(m->m_self, arg); - if (err) - return err; - } - if (m->m_module != NULL) { - err = visit(m->m_module, arg); - if (err) - return err; - } + Py_VISIT(m->m_self); + Py_VISIT(m->m_module); return 0; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 8124968..e454fcf 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -204,8 +204,7 @@ module_repr(PyModuleObject *m) static int module_traverse(PyModuleObject *m, visitproc visit, void *arg) { - if (m->md_dict != NULL) - return visit(m->md_dict, arg); + Py_VISIT(m->md_dict); return 0; } diff --git a/Objects/object.c b/Objects/object.c index 9b6a30a..a75c14e 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -3,9 +3,30 @@ #include "Python.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifdef Py_REF_DEBUG Py_ssize_t _Py_RefTotal; -#endif + +Py_ssize_t +_Py_GetRefTotal(void) +{ + PyObject *o; + Py_ssize_t total = _Py_RefTotal; + /* ignore the references to the dummy object of the dicts and sets + because they are not reliable and not useful (now that the + hash table code is well-tested) */ + o = _PyDict_Dummy(); + if (o != NULL) + total -= o->ob_refcnt; + o = _PySet_Dummy(); + if (o != NULL) + total -= o->ob_refcnt; + return total; +} +#endif /* Py_REF_DEBUG */ int Py_DivisionWarningFlag; @@ -53,23 +74,30 @@ _Py_AddToAllObjects(PyObject *op, int force) #ifdef COUNT_ALLOCS static PyTypeObject *type_list; +/* All types are added to type_list, at least when + they get one object created. That makes them + immortal, which unfortunately contributes to + garbage itself. If unlist_types_without_objects + is set, they will be removed from the type_list + once the last object is deallocated. */ +int unlist_types_without_objects; extern int tuple_zero_allocs, fast_tuple_allocs; extern int quick_int_allocs, quick_neg_int_allocs; extern int null_strings, one_strings; void -dump_counts(void) +dump_counts(FILE* f) { PyTypeObject *tp; for (tp = type_list; tp; tp = tp->tp_next) - fprintf(stderr, "%s alloc'd: %d, freed: %d, max in use: %d\n", + fprintf(f, "%s alloc'd: %d, freed: %d, max in use: %d\n", tp->tp_name, tp->tp_allocs, tp->tp_frees, tp->tp_maxalloc); - fprintf(stderr, "fast tuple allocs: %d, empty: %d\n", + fprintf(f, "fast tuple allocs: %d, empty: %d\n", fast_tuple_allocs, tuple_zero_allocs); - fprintf(stderr, "fast int allocs: pos: %d, neg: %d\n", + fprintf(f, "fast int allocs: pos: %d, neg: %d\n", quick_int_allocs, quick_neg_int_allocs); - fprintf(stderr, "null strings: %d, 1-strings: %d\n", + fprintf(f, "null strings: %d, 1-strings: %d\n", null_strings, one_strings); } @@ -103,10 +131,12 @@ get_counts(void) void inc_count(PyTypeObject *tp) { - if (tp->tp_allocs == 0) { + if (tp->tp_next == NULL && tp->tp_prev == NULL) { /* first time; insert in linked list */ if (tp->tp_next != NULL) /* sanity check */ Py_FatalError("XXX inc_count sanity check"); + if (type_list) + type_list->tp_prev = tp; tp->tp_next = type_list; /* Note that as of Python 2.2, heap-allocated type objects * can go away, but this code requires that they stay alive @@ -129,6 +159,24 @@ inc_count(PyTypeObject *tp) if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc) tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees; } + +void dec_count(PyTypeObject *tp) +{ + tp->tp_frees++; + if (unlist_types_without_objects && + tp->tp_allocs == tp->tp_frees) { + /* unlink the type from type_list */ + if (tp->tp_prev) + tp->tp_prev->tp_next = tp->tp_next; + else + type_list = tp->tp_next; + if (tp->tp_next) + tp->tp_next->tp_prev = tp->tp_prev; + tp->tp_next = tp->tp_prev = NULL; + Py_DECREF(tp); + } +} + #endif #ifdef Py_REF_DEBUG @@ -138,11 +186,10 @@ _Py_NegativeRefcount(const char *fname, int lineno, PyObject *op) { char buf[300]; - /* XXX(twouters) cast refcount to long until %zd is universally - available */ PyOS_snprintf(buf, sizeof(buf), - "%s:%i object at %p has negative ref count %ld", - fname, lineno, op, (long)op->ob_refcnt); + "%s:%i object at %p has negative ref count " + "%" PY_FORMAT_SIZE_T "d", + fname, lineno, op, op->ob_refcnt); Py_FatalError(buf); } @@ -317,7 +364,7 @@ PyObject_Repr(PyObject *v) #ifdef Py_USING_UNICODE if (PyUnicode_Check(res)) { PyObject* str; - str = PyUnicode_AsUnicodeEscapeString(res); + str = PyUnicode_AsEncodedString(res, NULL, NULL); Py_DECREF(res); if (str) res = str; @@ -1775,12 +1822,12 @@ static PyTypeObject PyNone_Type = { "NoneType", 0, 0, - (destructor)none_dealloc, /*tp_dealloc*/ /*never called*/ + none_dealloc, /*tp_dealloc*/ /*never called*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - (reprfunc)none_repr, /*tp_repr*/ + none_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -1806,12 +1853,12 @@ static PyTypeObject PyNotImplemented_Type = { "NotImplementedType", 0, 0, - (destructor)none_dealloc, /*tp_dealloc*/ /*never called*/ + none_dealloc, /*tp_dealloc*/ /*never called*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - (reprfunc)NotImplemented_repr, /*tp_repr*/ + NotImplemented_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -1901,9 +1948,7 @@ _Py_PrintReferences(FILE *fp) PyObject *op; fprintf(fp, "Remaining objects:\n"); for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) { - /* XXX(twouters) cast refcount to long until %zd is - universally available */ - fprintf(fp, "%p [%ld] ", op, (long)op->ob_refcnt); + fprintf(fp, "%p [%" PY_FORMAT_SIZE_T "d] ", op, op->ob_refcnt); if (PyObject_Print(op, fp, 0) != 0) PyErr_Clear(); putc('\n', fp); @@ -1919,10 +1964,8 @@ _Py_PrintReferenceAddresses(FILE *fp) PyObject *op; fprintf(fp, "Remaining object addresses:\n"); for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) - /* XXX(twouters) cast refcount to long until %zd is - universally available */ - fprintf(fp, "%p [%ld] %s\n", op, (long)op->ob_refcnt, - op->ob_type->tp_name); + fprintf(fp, "%p [%" PY_FORMAT_SIZE_T "d] %s\n", op, + op->ob_refcnt, op->ob_type->tp_name); } PyObject * @@ -2100,3 +2143,8 @@ _PyTrash_destroy_chain(void) --_PyTrash_delete_nesting; } } + +#ifdef __cplusplus +} +#endif + diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 870f93c..a393cbc 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -529,7 +529,7 @@ new_arena(void) nbytes = numarenas * sizeof(*arenas); if (nbytes / sizeof(*arenas) != numarenas) return NULL; /* overflow */ - arenaobj = realloc(arenas, nbytes); + arenaobj = (struct arena_object *)realloc(arenas, nbytes); if (arenaobj == NULL) return NULL; arenas = arenaobj; diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index a9c0b55..c48bee0 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -104,13 +104,6 @@ range_item(rangeobject *r, Py_ssize_t i) static Py_ssize_t range_length(rangeobject *r) { -#if LONG_MAX != INT_MAX /* XXX ssize_t_max */ - if (r->len > INT_MAX) { - PyErr_SetString(PyExc_ValueError, - "xrange object size cannot be reported"); - return -1; - } -#endif return (Py_ssize_t)(r->len); } @@ -157,44 +150,44 @@ static PyMethodDef range_methods[] = { PyTypeObject PyRange_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, /* Number of items for varobject */ - "xrange", /* Name of this type */ - sizeof(rangeobject), /* Basic object size */ - 0, /* Item size for varobject */ - (destructor)PyObject_Del, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)range_repr, /* tp_repr */ - 0, /* tp_as_number */ - &range_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - range_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)range_iter, /* tp_iter */ - 0, /* tp_iternext */ - range_methods, /* 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 */ - range_new, /* tp_new */ + 0, /* Number of items for varobject */ + "xrange", /* Name of this type */ + sizeof(rangeobject), /* Basic object size */ + 0, /* Item size for varobject */ + (destructor)PyObject_Del, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)range_repr, /* tp_repr */ + 0, /* tp_as_number */ + &range_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + range_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + range_iter, /* tp_iter */ + 0, /* tp_iternext */ + range_methods, /* 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 */ + range_new, /* tp_new */ }; /*********************** Xrange Iterator **************************/ @@ -207,53 +200,6 @@ typedef struct { long len; } rangeiterobject; -static PyTypeObject Pyrangeiter_Type; - -static PyObject * -range_iter(PyObject *seq) -{ - rangeiterobject *it; - - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; - } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); - if (it == NULL) - return NULL; - it->index = 0; - it->start = ((rangeobject *)seq)->start; - it->step = ((rangeobject *)seq)->step; - it->len = ((rangeobject *)seq)->len; - return (PyObject *)it; -} - -static PyObject * -range_reverse(PyObject *seq) -{ - rangeiterobject *it; - long start, step, len; - - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; - } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); - if (it == NULL) - return NULL; - - start = ((rangeobject *)seq)->start; - step = ((rangeobject *)seq)->step; - len = ((rangeobject *)seq)->len; - - it->index = 0; - it->start = start + (len-1) * step; - it->step = -step; - it->len = len; - - return (PyObject *)it; -} - static PyObject * rangeiter_next(rangeiterobject *r) { @@ -308,3 +254,48 @@ static PyTypeObject Pyrangeiter_Type = { rangeiter_methods, /* tp_methods */ 0, }; + +static PyObject * +range_iter(PyObject *seq) +{ + rangeiterobject *it; + + if (!PyRange_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + if (it == NULL) + return NULL; + it->index = 0; + it->start = ((rangeobject *)seq)->start; + it->step = ((rangeobject *)seq)->step; + it->len = ((rangeobject *)seq)->len; + return (PyObject *)it; +} + +static PyObject * +range_reverse(PyObject *seq) +{ + rangeiterobject *it; + long start, step, len; + + if (!PyRange_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + if (it == NULL) + return NULL; + + start = ((rangeobject *)seq)->start; + step = ((rangeobject *)seq)->step; + len = ((rangeobject *)seq)->len; + + it->index = 0; + it->start = start + (len-1) * step; + it->step = -step; + it->len = len; + + return (PyObject *)it; +} diff --git a/Objects/setobject.c b/Objects/setobject.c index 89d574f..26a232b 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -3,7 +3,7 @@ Written and maintained by Raymond D. Hettinger Derived from Lib/sets.py and Objects/dictobject.c. - Copyright (c) 2003-5 Python Software Foundation. + Copyright (c) 2003-6 Python Software Foundation. All rights reserved. */ @@ -16,6 +16,14 @@ /* Object used as dummy key to fill deleted entries */ static PyObject *dummy = NULL; /* Initialized by first call to make_new_set() */ +#ifdef Py_REF_DEBUG +PyObject * +_PySet_Dummy(void) +{ + return dummy; +} +#endif + #define INIT_NONZERO_SET_SLOTS(so) do { \ (so)->table = (so)->smalltable; \ (so)->mask = PySet_MINSIZE - 1; \ @@ -445,7 +453,7 @@ set_clear_internal(PySetObject *so) } #ifdef Py_DEBUG else - assert(entry->key == NULL || entry->key == dummy); + assert(entry->key == NULL); #endif } @@ -719,8 +727,6 @@ set_nohash(PyObject *self) /***** Set iterator type ***********************************************/ -static PyTypeObject PySetIter_Type; /* Forward */ - typedef struct { PyObject_HEAD PySetObject *si_set; /* Set to NULL when iterator is exhausted */ @@ -729,20 +735,6 @@ typedef struct { long len; } setiterobject; -static PyObject * -set_iter(PySetObject *so) -{ - setiterobject *si = PyObject_New(setiterobject, &PySetIter_Type); - if (si == NULL) - return NULL; - Py_INCREF(so); - si->si_set = so; - si->si_used = so->used; - si->si_pos = 0; - si->len = so->used; - return (PyObject *)si; -} - static void setiter_dealloc(setiterobject *si) { @@ -838,6 +830,20 @@ static PyTypeObject PySetIter_Type = { 0, }; +static PyObject * +set_iter(PySetObject *so) +{ + setiterobject *si = PyObject_New(setiterobject, &PySetIter_Type); + if (si == NULL) + return NULL; + Py_INCREF(so); + si->si_set = so; + si->si_used = so->used; + si->si_pos = 0; + si->len = so->used; + return (PyObject *)si; +} + static int set_update_internal(PySetObject *so, PyObject *other) { @@ -972,8 +978,8 @@ PySet_Fini(void) so = free_sets[num_free_sets]; PyObject_GC_Del(so); } - Py_XDECREF(dummy); - Py_XDECREF(emptyfrozenset); + Py_CLEAR(dummy); + Py_CLEAR(emptyfrozenset); } static PyObject * @@ -1531,7 +1537,7 @@ set_richcompare(PySetObject *v, PyObject *w, int op) } static int -set_nocmp(PyObject *self) +set_nocmp(PyObject *self, PyObject *other) { PyErr_SetString(PyExc_TypeError, "cannot compare sets using cmp()"); return -1; @@ -1688,7 +1694,7 @@ set_init(PySetObject *self, PyObject *args, PyObject *kwds) } static PySequenceMethods set_as_sequence = { - (lenfunc)set_len, /* sq_length */ + set_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ @@ -1802,7 +1808,7 @@ PyTypeObject PySet_Type = { (printfunc)set_tp_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)set_nocmp, /* tp_compare */ + set_nocmp, /* tp_compare */ (reprfunc)set_repr, /* tp_repr */ &set_as_number, /* tp_as_number */ &set_as_sequence, /* tp_as_sequence */ @@ -1896,7 +1902,7 @@ PyTypeObject PyFrozenSet_Type = { (printfunc)set_tp_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)set_nocmp, /* tp_compare */ + set_nocmp, /* tp_compare */ (reprfunc)set_repr, /* tp_repr */ &frozenset_as_number, /* tp_as_number */ &set_as_sequence, /* tp_as_sequence */ @@ -1966,6 +1972,16 @@ PySet_Size(PyObject *anyset) } int +PySet_Clear(PyObject *set) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return -1; + } + return set_clear_internal((PySetObject *)set); +} + +int PySet_Contains(PyObject *anyset, PyObject *key) { if (!PyAnySet_Check(anyset)) { @@ -1995,6 +2011,21 @@ PySet_Add(PyObject *set, PyObject *key) return set_add_key((PySetObject *)set, key); } +int +_PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry) +{ + setentry *entry_ptr; + + if (!PyAnySet_Check(set)) { + PyErr_BadInternalCall(); + return -1; + } + if (set_next((PySetObject *)set, pos, &entry_ptr) == 0) + return 0; + *entry = entry_ptr->key; + return 1; +} + PyObject * PySet_Pop(PyObject *set) { @@ -2005,6 +2036,15 @@ PySet_Pop(PyObject *set) return set_pop((PySetObject *)set); } +int +_PySet_Update(PyObject *set, PyObject *iterable) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return -1; + } + return set_update_internal((PySetObject *)set, iterable); +} #ifdef Py_DEBUG @@ -2021,7 +2061,11 @@ PySet_Pop(PyObject *set) static PyObject * test_c_api(PySetObject *so) { - PyObject *elem, *dup, *t, *f, *ob = (PyObject *)so; + int count; + char *s; + Py_ssize_t i; + PyObject *elem, *dup, *t, *f, *dup2; + PyObject *ob = (PyObject *)so; /* Verify preconditions and exercise type/size checks */ assert(PyAnySet_Check(ob)); @@ -2052,6 +2096,35 @@ test_c_api(PySetObject *so) assert(PySet_Discard(ob, elem) == 0); assert(PySet_GET_SIZE(ob) == 2); + /* Exercise clear */ + dup2 = PySet_New(dup); + assert(PySet_Clear(dup2) == 0); + assert(PySet_Size(dup2) == 0); + Py_DECREF(dup2); + + /* Raise SystemError on clear or update of frozen set */ + f = PyFrozenSet_New(dup); + assertRaises(PySet_Clear(f) == -1, PyExc_SystemError); + assertRaises(_PySet_Update(f, dup) == -1, PyExc_SystemError); + Py_DECREF(f); + + /* Exercise direct iteration */ + i = 0, count = 0; + while (_PySet_Next((PyObject *)dup, &i, &elem)) { + s = PyString_AsString(elem); + assert(s && (s[0] == 'a' || s[0] == 'b' || s[0] == 'c')); + count++; + } + assert(count == 3); + + /* Exercise updates */ + dup2 = PySet_New(NULL); + assert(_PySet_Update(dup2, dup) == 0); + assert(PySet_Size(dup2) == 3); + assert(_PySet_Update(dup2, dup) == 0); + assert(PySet_Size(dup2) == 3); + Py_DECREF(dup2); + /* Raise SystemError when self argument is not a set or frozenset. */ t = PyTuple_New(0); assertRaises(PySet_Size(t) == -1, PyExc_SystemError); diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 3b37dbb..271a9ad 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -24,26 +24,26 @@ ellipsis_repr(PyObject *op) static PyTypeObject PyEllipsis_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ - "ellipsis", /* tp_name */ - 0, /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /*never called*/ /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)ellipsis_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* ob_size */ + "ellipsis", /* tp_name */ + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /*never called*/ /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + ellipsis_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ }; PyObject _Py_EllipsisObject = { @@ -79,6 +79,25 @@ PySlice_New(PyObject *start, PyObject *stop, PyObject *step) return (PyObject *) obj; } +PyObject * +_PySlice_FromIndices(Py_ssize_t istart, Py_ssize_t istop) +{ + PyObject *start, *end, *slice; + start = PyInt_FromSsize_t(istart); + if (!start) + return NULL; + end = PyInt_FromSsize_t(istop); + if (!end) { + Py_DECREF(start); + return NULL; + } + + slice = PySlice_New(start, end, NULL); + Py_DECREF(start); + Py_DECREF(end); + return slice; +} + int PySlice_GetIndices(PySliceObject *r, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) @@ -87,21 +106,21 @@ PySlice_GetIndices(PySliceObject *r, Py_ssize_t length, if (r->step == Py_None) { *step = 1; } else { - if (!PyInt_Check(r->step)) return -1; - *step = PyInt_AsLong(r->step); + if (!PyInt_Check(r->step) && !PyLong_Check(r->step)) return -1; + *step = PyInt_AsSsize_t(r->step); } if (r->start == Py_None) { *start = *step < 0 ? length-1 : 0; } else { - if (!PyInt_Check(r->start)) return -1; - *start = PyInt_AsLong(r->start); + if (!PyInt_Check(r->start) && !PyLong_Check(r->step)) return -1; + *start = PyInt_AsSsize_t(r->start); if (*start < 0) *start += length; } if (r->stop == Py_None) { *stop = *step < 0 ? -1 : length; } else { - if (!PyInt_Check(r->stop)) return -1; - *stop = PyInt_AsLong(r->stop); + if (!PyInt_Check(r->stop) && !PyLong_Check(r->step)) return -1; + *stop = PyInt_AsSsize_t(r->stop); if (*stop < 0) *stop += length; } if (*stop > length) return -1; @@ -233,7 +252,7 @@ slice_indices(PySliceObject* self, PyObject* len) { Py_ssize_t ilen, start, stop, step, slicelength; - ilen = PyInt_AsLong(len); + ilen = PyInt_AsSsize_t(len); if (ilen == -1 && PyErr_Occurred()) { return NULL; @@ -244,7 +263,7 @@ slice_indices(PySliceObject* self, PyObject* len) return NULL; } - return Py_BuildValue("(iii)", start, stop, step); + return Py_BuildValue("(nnn)", start, stop, step); } PyDoc_STRVAR(slice_indices_doc, diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 32aacf5..32e825e 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -1,5 +1,6 @@ /* String object implementation */ +#define PY_SSIZE_T_CLEAN #include "Python.h" #include @@ -16,7 +17,7 @@ static PyStringObject *nullstring; When the interned string reaches a refcnt of 0 the string deallocation function will delete the reference from this dictionary. - Another way to look at this is that to say that the actual reference + Another way to look at this is that to say that the actual reference count of a string is: s->ob_refcnt + (s->ob_sstate?2:0) */ static PyObject *interned; @@ -105,7 +106,7 @@ PyString_FromString(const char *str) assert(str != NULL); size = strlen(str); - if (size > INT_MAX) { + if (size > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; @@ -183,7 +184,7 @@ PyString_FromFormatV(const char *format, va_list vargs) ++f; /* likewise for %zd */ if (*f == 'z' && *(f+1) == 'd') - ++f; + ++f; switch (*f) { case 'c': @@ -273,18 +274,9 @@ PyString_FromFormatV(const char *format, va_list vargs) case 'd': if (longflag) sprintf(s, "%ld", va_arg(vargs, long)); - else if (size_tflag) { - /* Instead of checking whether the C - library supports %zd, handle the - common cases. */ - #if SIZEOF_SIZE_T == SIZEOF_LONG - sprintf(s, "%ld", va_arg(vargs, long)); - #elif defined(MS_WINDOWS) - sprintf(s, "%Id", va_arg(vargs, size_t)); - #else - #error Cannot print size_t values - #endif - } + else if (size_tflag) + sprintf(s, "%" PY_FORMAT_SIZE_T "u", + va_arg(vargs, size_t)); else sprintf(s, "%d", va_arg(vargs, int)); s += strlen(s); @@ -578,8 +570,9 @@ PyObject *PyString_DecodeEscape(const char *s, if (!w) goto failed; /* Append bytes to output buffer. */ - r = PyString_AsString(w); - rn = PyString_Size(w); + assert(PyString_Check(w)); + r = PyString_AS_STRING(w); + rn = PyString_GET_SIZE(w); memcpy(p, r, rn); p += rn; Py_DECREF(w); @@ -622,7 +615,7 @@ PyObject *PyString_DecodeEscape(const char *s, *p++ = c; break; case 'x': - if (isxdigit(Py_CHARMASK(s[0])) + if (isxdigit(Py_CHARMASK(s[0])) && isxdigit(Py_CHARMASK(s[1]))) { unsigned int x = 0; c = Py_CHARMASK(*s); @@ -646,7 +639,7 @@ PyObject *PyString_DecodeEscape(const char *s, break; } if (!errors || strcmp(errors, "strict") == 0) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "invalid \\x escape"); goto failed; } @@ -753,7 +746,7 @@ PyString_AsStringAndSize(register PyObject *obj, *s = PyString_AS_STRING(obj); if (len != NULL) *len = PyString_GET_SIZE(obj); - else if (strlen(*s) != PyString_GET_SIZE(obj)) { + else if (strlen(*s) != (size_t)PyString_GET_SIZE(obj)) { PyErr_SetString(PyExc_TypeError, "expected string without null bytes"); return -1; @@ -822,7 +815,7 @@ PyString_Repr(PyObject *obj, int smartquotes) register PyStringObject* op = (PyStringObject*) obj; size_t newsize = 2 + 4 * op->ob_size; PyObject *v; - if (newsize > INT_MAX) { + if (newsize > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "string is too large to make repr"); } @@ -838,7 +831,7 @@ PyString_Repr(PyObject *obj, int smartquotes) /* figure out which quote to use; single is preferred */ quote = '\''; - if (smartquotes && + if (smartquotes && memchr(op->ob_sval, '\'', op->ob_size) && !memchr(op->ob_sval, '"', op->ob_size)) quote = '"'; @@ -1003,7 +996,7 @@ string_repeat(register PyStringObject *a, register Py_ssize_t n) /* String slice a[i:j] consists of characters a[i] ... a[j-1] */ static PyObject * -string_slice(register PyStringObject *a, register Py_ssize_t i, +string_slice(register PyStringObject *a, register Py_ssize_t i, register Py_ssize_t j) /* j -- may be negative! */ { @@ -1047,7 +1040,7 @@ string_contains(PyObject *a, PyObject *el) if (len_sub == 0) return 1; - /* last points to one char beyond the start of the rightmost + /* last points to one char beyond the start of the rightmost substring. When sob_type->tp_name); + i, item->ob_type->tp_name); Py_DECREF(seq); return NULL; } sz += PyString_GET_SIZE(item); if (i != 0) sz += seplen; - if (sz < old_sz || sz > INT_MAX) { + if (sz < old_sz || sz > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "join() is too long for a Python string"); Py_DECREF(seq); @@ -1754,7 +1747,7 @@ string_find_internal(PyStringObject *self, PyObject *args, int dir) { const char *s = PyString_AS_STRING(self), *sub; Py_ssize_t len = PyString_GET_SIZE(self); - Py_ssize_t n, i = 0, last = INT_MAX; + Py_ssize_t n, i = 0, last = PY_SSIZE_T_MAX; PyObject *subobj; /* XXX ssize_t i */ @@ -1960,17 +1953,14 @@ do_argstrip(PyStringObject *self, int striptype, PyObject *args) return res; } #endif - else { - PyErr_Format(PyExc_TypeError, + PyErr_Format(PyExc_TypeError, #ifdef Py_USING_UNICODE - "%s arg must be None, str or unicode", + "%s arg must be None, str or unicode", #else - "%s arg must be None or str", + "%s arg must be None or str", #endif - STRIPNAME(striptype)); - return NULL; - } - return do_xstrip(self, striptype, sep); + STRIPNAME(striptype)); + return NULL; } return do_strip(self, striptype); @@ -2039,12 +2029,12 @@ string_lower(PyStringObject *self) { char *s = PyString_AS_STRING(self), *s_new; Py_ssize_t i, n = PyString_GET_SIZE(self); - PyObject *new; + PyObject *newobj; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newobj = PyString_FromStringAndSize(NULL, n); + if (newobj == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newobj); for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); if (isupper(c)) { @@ -2053,7 +2043,7 @@ string_lower(PyStringObject *self) *s_new = c; s_new++; } - return new; + return newobj; } @@ -2067,12 +2057,12 @@ string_upper(PyStringObject *self) { char *s = PyString_AS_STRING(self), *s_new; Py_ssize_t i, n = PyString_GET_SIZE(self); - PyObject *new; + PyObject *newobj; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newobj = PyString_FromStringAndSize(NULL, n); + if (newobj == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newobj); for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); if (islower(c)) { @@ -2081,7 +2071,7 @@ string_upper(PyStringObject *self) *s_new = c; s_new++; } - return new; + return newobj; } @@ -2097,12 +2087,12 @@ string_title(PyStringObject *self) char *s = PyString_AS_STRING(self), *s_new; Py_ssize_t i, n = PyString_GET_SIZE(self); int previous_is_cased = 0; - PyObject *new; + PyObject *newobj; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newobj = PyString_FromStringAndSize(NULL, n); + if (newobj == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newobj); for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); if (islower(c)) { @@ -2117,7 +2107,7 @@ string_title(PyStringObject *self) previous_is_cased = 0; *s_new++ = c; } - return new; + return newobj; } PyDoc_STRVAR(capitalize__doc__, @@ -2131,12 +2121,12 @@ string_capitalize(PyStringObject *self) { char *s = PyString_AS_STRING(self), *s_new; Py_ssize_t i, n = PyString_GET_SIZE(self); - PyObject *new; + PyObject *newobj; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newobj = PyString_FromStringAndSize(NULL, n); + if (newobj == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newobj); if (0 < n) { int c = Py_CHARMASK(*s++); if (islower(c)) @@ -2153,7 +2143,7 @@ string_capitalize(PyStringObject *self) *s_new = c; s_new++; } - return new; + return newobj; } @@ -2169,7 +2159,7 @@ string_count(PyStringObject *self, PyObject *args) { const char *s = PyString_AS_STRING(self), *sub, *t; Py_ssize_t len = PyString_GET_SIZE(self), n; - Py_ssize_t i = 0, last = INT_MAX; + Py_ssize_t i = 0, last = PY_SSIZE_T_MAX; Py_ssize_t m, r; PyObject *subobj; @@ -2210,7 +2200,7 @@ string_count(PyStringObject *self, PyObject *args) } if (i >= m) break; - t = memchr(s+i, sub[0], m-i); + t = (const char *)memchr(s+i, sub[0], m-i); if (t == NULL) break; i = t - s; @@ -2229,12 +2219,12 @@ string_swapcase(PyStringObject *self) { char *s = PyString_AS_STRING(self), *s_new; Py_ssize_t i, n = PyString_GET_SIZE(self); - PyObject *new; + PyObject *newobj; - new = PyString_FromStringAndSize(NULL, n); - if (new == NULL) + newobj = PyString_FromStringAndSize(NULL, n); + if (newobj == NULL) return NULL; - s_new = PyString_AsString(new); + s_new = PyString_AsString(newobj); for (i = 0; i < n; i++) { int c = Py_CHARMASK(*s++); if (islower(c)) { @@ -2247,7 +2237,7 @@ string_swapcase(PyStringObject *self) *s_new = c; s_new++; } - return new; + return newobj; } @@ -2323,12 +2313,12 @@ string_translate(PyStringObject *self, PyObject *args) } table = table1; - inlen = PyString_Size(input_obj); + inlen = PyString_GET_SIZE(input_obj); result = PyString_FromStringAndSize((char *)NULL, inlen); if (result == NULL) return NULL; output_start = output = PyString_AsString(result); - input = PyString_AsString(input_obj); + input = PyString_AS_STRING(input_obj); if (dellen == 0) { /* If no deletions are required, use faster code */ @@ -2457,7 +2447,7 @@ mymemreplace(const char *str, Py_ssize_t len, /* input string */ /* find length of output string */ nfound = (pat_len > 0) ? mymemcnt(str, len, pat, pat_len) : len + 1; if (count < 0) - count = INT_MAX; + count = PY_SSIZE_T_MAX; else if (nfound > count) nfound = count; if (nfound == 0) @@ -2534,11 +2524,11 @@ string_replace(PyStringObject *self, PyObject *args) char *new_s; const Py_ssize_t len = PyString_GET_SIZE(self); Py_ssize_t sub_len, repl_len, out_len; - int count = -1; - PyObject *new; + Py_ssize_t count = -1; + PyObject *newobj; PyObject *subobj, *replobj; - if (!PyArg_ParseTuple(args, "OO|i:replace", + if (!PyArg_ParseTuple(args, "OO|n:replace", &subobj, &replobj, &count)) return NULL; @@ -2574,20 +2564,20 @@ string_replace(PyStringObject *self, PyObject *args) if (out_len == -1) { if (PyString_CheckExact(self)) { /* we're returning another reference to self */ - new = (PyObject*)self; - Py_INCREF(new); + newobj = (PyObject*)self; + Py_INCREF(newobj); } else { - new = PyString_FromStringAndSize(str, len); - if (new == NULL) + newobj = PyString_FromStringAndSize(str, len); + if (newobj == NULL) return NULL; } } else { - new = PyString_FromStringAndSize(new_s, out_len); + newobj = PyString_FromStringAndSize(new_s, out_len); PyMem_FREE(new_s); } - return new; + return newobj; } @@ -2606,7 +2596,7 @@ string_startswith(PyStringObject *self, PyObject *args) const char* prefix; Py_ssize_t plen; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *subobj; if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj, @@ -2657,7 +2647,7 @@ string_endswith(PyStringObject *self, PyObject *args) const char* suffix; Py_ssize_t slen; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *subobj; if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj, @@ -2711,7 +2701,7 @@ string_encode(PyStringObject *self, PyObject *args) char *encoding = NULL; char *errors = NULL; PyObject *v; - + if (!PyArg_ParseTuple(args, "|ss:encode", &encoding, &errors)) return NULL; v = PyString_AsEncodedObject((PyObject *)self, encoding, errors); @@ -2748,7 +2738,7 @@ string_decode(PyStringObject *self, PyObject *args) char *encoding = NULL; char *errors = NULL; PyObject *v; - + if (!PyArg_ParseTuple(args, "|ss:decode", &encoding, &errors)) return NULL; v = PyString_AsDecodedObject((PyObject *)self, encoding, errors); @@ -2870,10 +2860,10 @@ PyDoc_STRVAR(ljust__doc__, static PyObject * string_ljust(PyStringObject *self, PyObject *args) { - int width; + Py_ssize_t width; char fillchar = ' '; - if (!PyArg_ParseTuple(args, "i|c:ljust", &width, &fillchar)) + if (!PyArg_ParseTuple(args, "n|c:ljust", &width, &fillchar)) return NULL; if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { @@ -2894,10 +2884,10 @@ PyDoc_STRVAR(rjust__doc__, static PyObject * string_rjust(PyStringObject *self, PyObject *args) { - int width; + Py_ssize_t width; char fillchar = ' '; - if (!PyArg_ParseTuple(args, "i|c:rjust", &width, &fillchar)) + if (!PyArg_ParseTuple(args, "n|c:rjust", &width, &fillchar)) return NULL; if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { @@ -2919,10 +2909,10 @@ static PyObject * string_center(PyStringObject *self, PyObject *args) { Py_ssize_t marg, left; - long width; + Py_ssize_t width; char fillchar = ' '; - if (!PyArg_ParseTuple(args, "l|c:center", &width, &fillchar)) + if (!PyArg_ParseTuple(args, "n|c:center", &width, &fillchar)) return NULL; if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { @@ -2948,9 +2938,9 @@ string_zfill(PyStringObject *self, PyObject *args) Py_ssize_t fill; PyObject *s; char *p; + Py_ssize_t width; - long width; - if (!PyArg_ParseTuple(args, "l:zfill", &width)) + if (!PyArg_ParseTuple(args, "n:zfill", &width)) return NULL; if (PyString_GET_SIZE(self) >= width) { @@ -3467,22 +3457,22 @@ PyTypeObject PyString_Type = { "str", sizeof(PyStringObject), sizeof(char), - (destructor)string_dealloc, /* tp_dealloc */ + string_dealloc, /* tp_dealloc */ (printfunc)string_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - (reprfunc)string_repr, /* tp_repr */ + string_repr, /* tp_repr */ &string_as_number, /* tp_as_number */ &string_as_sequence, /* tp_as_sequence */ &string_as_mapping, /* tp_as_mapping */ (hashfunc)string_hash, /* tp_hash */ 0, /* tp_call */ - (reprfunc)string_str, /* tp_str */ + string_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &string_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ string_doc, /* tp_doc */ 0, /* tp_traverse */ @@ -3635,7 +3625,7 @@ formatfloat(char *buf, size_t buflen, int flags, len = 1 + 50 + 1 + prec = 52 + prec If prec=0 the effective precision is 1 (the leading digit is - always given), therefore increase the length by one. + always given), therefore increase the length by one. */ if ((type == 'g' && buflen <= (size_t)10 + (size_t)prec) || @@ -3711,7 +3701,7 @@ _PyString_FormatLong(PyObject *val, int flags, int prec, int type, } buf = PyString_AsString(result); llen = PyString_Size(result); - if (llen > INT_MAX) { + if (llen > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_ValueError, "string too large in _PyString_FormatLong"); return NULL; } @@ -4439,7 +4429,7 @@ void _Py_ReleaseInternedStrings(void) detector, interned strings are not forcibly deallocated; rather, we give them their stolen references back, and then clear and DECREF the interned dict. */ - + fprintf(stderr, "releasing interned strings\n"); n = PyList_GET_SIZE(keys); for (i = 0; i < n; i++) { diff --git a/Objects/structseq.c b/Objects/structseq.c index 218d0b4..e074810 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -315,7 +315,7 @@ static PyTypeObject _struct_sequence_template = { 0, /* tp_as_number */ &structseq_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)structseq_hash, /* tp_hash */ + structseq_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ @@ -349,6 +349,14 @@ PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc) PyMemberDef* members; int n_members, n_unnamed_members, i, k; +#ifdef Py_TRACE_REFS + /* if the type object was chained, unchain it first + before overwriting its storage */ + if (type->_ob_next) { + _Py_ForgetReference((PyObject*)type); + } +#endif + n_unnamed_members = 0; for (i = 0; desc->fields[i].name != NULL; ++i) if (desc->fields[i].name == PyStructSequence_UnnamedField) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 384b355..2161ab9 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -438,16 +438,9 @@ static int tupletraverse(PyTupleObject *o, visitproc visit, void *arg) { Py_ssize_t i; - PyObject *x; - - for (i = o->ob_size; --i >= 0; ) { - x = o->ob_item[i]; - if (x != NULL) { - int err = visit(x, arg); - if (err) - return err; - } - } + + for (i = o->ob_size; --i >= 0; ) + Py_VISIT(o->ob_item[i]); return 0; } @@ -547,7 +540,7 @@ tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * tuple_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *tmp, *new, *item; + PyObject *tmp, *newobj, *item; Py_ssize_t i, n; assert(PyType_IsSubtype(type, &PyTuple_Type)); @@ -555,16 +548,16 @@ tuple_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (tmp == NULL) return NULL; assert(PyTuple_Check(tmp)); - new = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp)); - if (new == NULL) + newobj = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp)); + if (newobj == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_GET_ITEM(tmp, i); Py_INCREF(item); - PyTuple_SET_ITEM(new, i, item); + PyTuple_SET_ITEM(newobj, i, item); } Py_DECREF(tmp); - return new; + return newobj; } PyDoc_STRVAR(tuple_doc, @@ -615,6 +608,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item) } else { result = PyTuple_New(slicelength); + if (!result) return NULL; src = self->ob_item; dest = ((PyTupleObject *)result)->ob_item; @@ -790,27 +784,6 @@ typedef struct { PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */ } tupleiterobject; -PyTypeObject PyTupleIter_Type; - -static PyObject * -tuple_iter(PyObject *seq) -{ - tupleiterobject *it; - - if (!PyTuple_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; - } - it = PyObject_GC_New(tupleiterobject, &PyTupleIter_Type); - if (it == NULL) - return NULL; - it->it_index = 0; - Py_INCREF(seq); - it->it_seq = (PyTupleObject *)seq; - _PyObject_GC_TRACK(it); - return (PyObject *)it; -} - static void tupleiter_dealloc(tupleiterobject *it) { @@ -822,9 +795,8 @@ tupleiter_dealloc(tupleiterobject *it) static int tupleiter_traverse(tupleiterobject *it, visitproc visit, void *arg) { - if (it->it_seq == NULL) - return 0; - return visit((PyObject *)it->it_seq, arg); + Py_VISIT(it->it_seq); + return 0; } static PyObject * @@ -900,3 +872,22 @@ PyTypeObject PyTupleIter_Type = { tupleiter_methods, /* tp_methods */ 0, }; + +static PyObject * +tuple_iter(PyObject *seq) +{ + tupleiterobject *it; + + if (!PyTuple_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(tupleiterobject, &PyTupleIter_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyTupleObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c02f060..4caf538 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -453,7 +453,7 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) if (PyType_IS_GC(type)) obj = _PyObject_GC_Malloc(size); else - obj = PyObject_MALLOC(size); + obj = (PyObject *)PyObject_MALLOC(size); if (obj == NULL) return PyErr_NoMemory(); @@ -525,21 +525,15 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg) if (type->tp_dictoffset != base->tp_dictoffset) { PyObject **dictptr = _PyObject_GetDictPtr(self); - if (dictptr && *dictptr) { - int err = visit(*dictptr, arg); - if (err) - return err; - } + if (dictptr && *dictptr) + Py_VISIT(*dictptr); } - if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) /* For a heaptype, the instances count as references to the type. Traverse the type so the collector can find cycles involving this link. */ - int err = visit((PyObject *)type, arg); - if (err) - return err; - } + Py_VISIT(type); if (basetraverse) return basetraverse(self, visit, arg); @@ -559,8 +553,8 @@ clear_slots(PyTypeObject *type, PyObject *self) char *addr = (char *)self + mp->offset; PyObject *obj = *(PyObject **)addr; if (obj != NULL) { - Py_DECREF(obj); *(PyObject **)addr = NULL; + Py_DECREF(obj); } } } @@ -1106,14 +1100,17 @@ set_mro_error(PyObject *to_merge, int *remain) char buf[1000]; PyObject *k, *v; PyObject *set = PyDict_New(); + if (!set) return; to_merge_size = PyList_GET_SIZE(to_merge); for (i = 0; i < to_merge_size; i++) { PyObject *L = PyList_GET_ITEM(to_merge, i); if (remain[i] < PyList_GET_SIZE(L)) { PyObject *c = PyList_GET_ITEM(L, remain[i]); - if (PyDict_SetItem(set, c, Py_None) < 0) + if (PyDict_SetItem(set, c, Py_None) < 0) { + Py_DECREF(set); return; + } } } n = PyDict_Size(set); @@ -1121,12 +1118,12 @@ set_mro_error(PyObject *to_merge, int *remain) off = PyOS_snprintf(buf, sizeof(buf), "Cannot create a \ consistent method resolution\norder (MRO) for bases"); i = 0; - while (PyDict_Next(set, &i, &k, &v) && off < sizeof(buf)) { + while (PyDict_Next(set, &i, &k, &v) && (size_t)off < sizeof(buf)) { PyObject *name = class_name(k); off += PyOS_snprintf(buf + off, sizeof(buf) - off, " %s", name ? PyString_AS_STRING(name) : "?"); Py_XDECREF(name); - if (--n && off+1 < sizeof(buf)) { + if (--n && (size_t)(off+1) < sizeof(buf)) { buf[off++] = ','; buf[off] = '\0'; } @@ -1147,7 +1144,7 @@ pmerge(PyObject *acc, PyObject* to_merge) { remain[i] is the index of the next base in to_merge[i] that is not included in acc. */ - remain = PyMem_MALLOC(SIZEOF_INT*to_merge_size); + remain = (int *)PyMem_MALLOC(SIZEOF_INT*to_merge_size); if (remain == NULL) return -1; for (i = 0; i < to_merge_size; i++) @@ -1893,7 +1890,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) PyObject *doc = PyDict_GetItemString(dict, "__doc__"); if (doc != NULL && PyString_Check(doc)) { const size_t n = (size_t)PyString_GET_SIZE(doc); - char *tp_doc = PyObject_MALLOC(n+1); + char *tp_doc = (char *)PyObject_MALLOC(n+1); if (tp_doc == NULL) { Py_DECREF(type); return NULL; @@ -2195,51 +2192,31 @@ PyDoc_STRVAR(type_doc, static int type_traverse(PyTypeObject *type, visitproc visit, void *arg) { - int err; - /* Because of type_is_gc(), the collector only calls this for heaptypes. */ assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - - VISIT(type->tp_dict); - VISIT(type->tp_cache); - VISIT(type->tp_mro); - VISIT(type->tp_bases); - VISIT(type->tp_base); + Py_VISIT(type->tp_dict); + Py_VISIT(type->tp_cache); + Py_VISIT(type->tp_mro); + Py_VISIT(type->tp_bases); + Py_VISIT(type->tp_base); /* There's no need to visit type->tp_subclasses or ((PyHeapTypeObject *)type)->ht_slots, because they can't be involved in cycles; tp_subclasses is a list of weak references, and slots is a tuple of strings. */ -#undef VISIT - return 0; } static int type_clear(PyTypeObject *type) { - PyObject *tmp; - /* Because of type_is_gc(), the collector only calls this for heaptypes. */ assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); -#define CLEAR(SLOT) \ - if (SLOT) { \ - tmp = (PyObject *)(SLOT); \ - SLOT = NULL; \ - Py_DECREF(tmp); \ - } - /* The only field we need to clear is tp_mro, which is part of a hard cycle (its first element is the class itself) that won't be broken otherwise (it's a tuple and tuples don't have a @@ -2265,9 +2242,7 @@ type_clear(PyTypeObject *type) A tuple of strings can't be part of a cycle. */ - CLEAR(type->tp_mro); - -#undef CLEAR + Py_CLEAR(type->tp_mro); return 0; } @@ -2443,23 +2418,23 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) } static int -compatible_for_assignment(PyTypeObject* old, PyTypeObject* new, char* attr) +compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, char* attr) { PyTypeObject *newbase, *oldbase; - if (new->tp_dealloc != old->tp_dealloc || - new->tp_free != old->tp_free) + if (newto->tp_dealloc != oldto->tp_dealloc || + newto->tp_free != oldto->tp_free) { PyErr_Format(PyExc_TypeError, "%s assignment: " "'%s' deallocator differs from '%s'", attr, - new->tp_name, - old->tp_name); + newto->tp_name, + oldto->tp_name); return 0; } - newbase = new; - oldbase = old; + newbase = newto; + oldbase = oldto; while (equiv_structs(newbase, newbase->tp_base)) newbase = newbase->tp_base; while (equiv_structs(oldbase, oldbase->tp_base)) @@ -2471,8 +2446,8 @@ compatible_for_assignment(PyTypeObject* old, PyTypeObject* new, char* attr) "%s assignment: " "'%s' object layout differs from '%s'", attr, - new->tp_name, - old->tp_name); + newto->tp_name, + oldto->tp_name); return 0; } @@ -2482,8 +2457,8 @@ compatible_for_assignment(PyTypeObject* old, PyTypeObject* new, char* attr) static int object_set_class(PyObject *self, PyObject *value, void *closure) { - PyTypeObject *old = self->ob_type; - PyTypeObject *new; + PyTypeObject *oldto = self->ob_type; + PyTypeObject *newto; if (value == NULL) { PyErr_SetString(PyExc_TypeError, @@ -2496,18 +2471,18 @@ object_set_class(PyObject *self, PyObject *value, void *closure) value->ob_type->tp_name); return -1; } - new = (PyTypeObject *)value; - if (!(new->tp_flags & Py_TPFLAGS_HEAPTYPE) || - !(old->tp_flags & Py_TPFLAGS_HEAPTYPE)) + newto = (PyTypeObject *)value; + if (!(newto->tp_flags & Py_TPFLAGS_HEAPTYPE) || + !(oldto->tp_flags & Py_TPFLAGS_HEAPTYPE)) { PyErr_Format(PyExc_TypeError, "__class__ assignment: only for heap types"); return -1; } - if (compatible_for_assignment(new, old, "__class__")) { - Py_INCREF(new); - self->ob_type = new; - Py_DECREF(old); + if (compatible_for_assignment(newto, oldto, "__class__")) { + Py_INCREF(newto); + self->ob_type = newto; + Py_DECREF(oldto); return 0; } else { @@ -2785,7 +2760,7 @@ PyTypeObject PyBaseObject_Type = { "object", /* tp_name */ sizeof(PyObject), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)object_dealloc, /* tp_dealloc */ + object_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -3326,7 +3301,7 @@ add_subclass(PyTypeObject *base, PyTypeObject *type) { Py_ssize_t i; int result; - PyObject *list, *ref, *new; + PyObject *list, *ref, *newobj; list = base->tp_subclasses; if (list == NULL) { @@ -3335,16 +3310,16 @@ add_subclass(PyTypeObject *base, PyTypeObject *type) return -1; } assert(PyList_Check(list)); - new = PyWeakref_NewRef((PyObject *)type, NULL); + newobj = PyWeakref_NewRef((PyObject *)type, NULL); i = PyList_GET_SIZE(list); while (--i >= 0) { ref = PyList_GET_ITEM(list, i); assert(PyWeakref_CheckRef(ref)); if (PyWeakref_GET_OBJECT(ref) == Py_None) - return PyList_SetItem(list, i, new); + return PyList_SetItem(list, i, newobj); } - result = PyList_Append(list, new); - Py_DECREF(new); + result = PyList_Append(list, newobj); + Py_DECREF(newobj); return result; } @@ -3536,12 +3511,16 @@ wrap_unaryfunc(PyObject *self, PyObject *args, void *wrapped) } static PyObject * -wrap_ssizeargfunc(PyObject *self, PyObject *args, void *wrapped) +wrap_indexargfunc(PyObject *self, PyObject *args, void *wrapped) { ssizeargfunc func = (ssizeargfunc)wrapped; + PyObject* o; Py_ssize_t i; - if (!PyArg_ParseTuple(args, "n", &i)) + if (!PyArg_UnpackTuple(args, "", 1, 1, &o)) + return NULL; + i = PyNumber_Index(o); + if (i == -1 && PyErr_Occurred()) return NULL; return (*func)(self, i); } @@ -3551,7 +3530,7 @@ getindex(PyObject *self, PyObject *arg) { Py_ssize_t i; - i = PyInt_AsSsize_t(arg); + i = PyNumber_Index(arg); if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) { @@ -4359,36 +4338,21 @@ slot_nb_nonzero(PyObject *self) static Py_ssize_t slot_nb_index(PyObject *self) { - PyObject *func, *args; static PyObject *index_str; - Py_ssize_t result = -1; + PyObject *temp = call_method(self, "__index__", &index_str, "()"); + Py_ssize_t result; - func = lookup_maybe(self, "__index__", &index_str); - if (func == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, - "object cannot be interpreted as an index"); - } + if (temp == NULL) return -1; - } - args = PyTuple_New(0); - if (args != NULL) { - PyObject *temp = PyObject_Call(func, args, NULL); - Py_DECREF(args); - if (temp != NULL) { - if (PyInt_Check(temp) || PyLong_Check(temp)) { - result = - temp->ob_type->tp_as_number->nb_index(temp); - } - else { - PyErr_SetString(PyExc_TypeError, - "__index__ must return an int or a long"); - result = -1; - } - Py_DECREF(temp); - } + if (PyInt_CheckExact(temp) || PyLong_CheckExact(temp)) { + result = temp->ob_type->tp_as_number->nb_index(temp); } - Py_DECREF(func); + else { + PyErr_SetString(PyExc_TypeError, + "__index__ must return an int or a long"); + result = -1; + } + Py_DECREF(temp); return result; } @@ -5018,9 +4982,9 @@ static slotdef slotdefs[] = { test_descr.notimplemented() */ SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"), - SQSLOT("__mul__", sq_repeat, NULL, wrap_ssizeargfunc, + SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, "x.__mul__(n) <==> x*n"), - SQSLOT("__rmul__", sq_repeat, NULL, wrap_ssizeargfunc, + SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, "x.__rmul__(n) <==> n*x"), SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"), @@ -5046,7 +5010,7 @@ static slotdef slotdefs[] = { SQSLOT("__iadd__", sq_inplace_concat, NULL, wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), SQSLOT("__imul__", sq_inplace_repeat, NULL, - wrap_ssizeargfunc, "x.__imul__(y) <==> x*=y"), + wrap_indexargfunc, "x.__imul__(y) <==> x*=y"), MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc, "x.__len__() <==> len(x)"), @@ -5211,21 +5175,21 @@ slotptr(PyTypeObject *type, int ioffset) /* Note: this depends on the order of the members of PyHeapTypeObject! */ assert(offset >= 0); - assert(offset < offsetof(PyHeapTypeObject, as_buffer)); - if (offset >= offsetof(PyHeapTypeObject, as_sequence)) { - ptr = (void *)type->tp_as_sequence; + assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer)); + if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) { + ptr = (char *)type->tp_as_sequence; offset -= offsetof(PyHeapTypeObject, as_sequence); } - else if (offset >= offsetof(PyHeapTypeObject, as_mapping)) { - ptr = (void *)type->tp_as_mapping; + else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_mapping)) { + ptr = (char *)type->tp_as_mapping; offset -= offsetof(PyHeapTypeObject, as_mapping); } - else if (offset >= offsetof(PyHeapTypeObject, as_number)) { - ptr = (void *)type->tp_as_number; + else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_number)) { + ptr = (char *)type->tp_as_number; offset -= offsetof(PyHeapTypeObject, as_number); } else { - ptr = (void *)type; + ptr = (char *)type; } if (ptr != NULL) ptr += offset; @@ -5743,7 +5707,7 @@ static PyObject * super_descr_get(PyObject *self, PyObject *obj, PyObject *type) { superobject *su = (superobject *)self; - superobject *new; + superobject *newobj; if (obj == NULL || obj == Py_None || su->obj != NULL) { /* Not binding to an object, or already bound */ @@ -5760,16 +5724,16 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type) PyTypeObject *obj_type = supercheck(su->type, obj); if (obj_type == NULL) return NULL; - new = (superobject *)PySuper_Type.tp_new(&PySuper_Type, + newobj = (superobject *)PySuper_Type.tp_new(&PySuper_Type, NULL, NULL); - if (new == NULL) + if (newobj == NULL) return NULL; Py_INCREF(su->type); Py_INCREF(obj); - new->type = su->type; - new->obj = obj; - new->obj_type = obj_type; - return (PyObject *)new; + newobj->type = su->type; + newobj->obj = obj; + newobj->obj_type = obj_type; + return (PyObject *)newobj; } } @@ -5811,20 +5775,10 @@ static int super_traverse(PyObject *self, visitproc visit, void *arg) { superobject *su = (superobject *)self; - int err; - -#define VISIT(SLOT) \ - if (SLOT) { \ - err = visit((PyObject *)(SLOT), arg); \ - if (err) \ - return err; \ - } - - VISIT(su->obj); - VISIT(su->type); - VISIT(su->obj_type); -#undef VISIT + Py_VISIT(su->obj); + Py_VISIT(su->type); + Py_VISIT(su->obj_type); return 0; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 7fbce14..e62c774 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -36,6 +36,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define PY_SSIZE_T_CLEAN #include "Python.h" #include "unicodeobject.h" @@ -83,6 +84,11 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifdef __cplusplus +extern "C" { +#endif + /* Free list for Unicode objects */ static PyUnicodeObject *unicode_freelist; static int unicode_freelist_size; @@ -130,14 +136,9 @@ int unicode_resize(register PyUnicodeObject *unicode, /* Resizing shared object (unicode_empty or single character objects) in-place is not allowed. Use PyUnicode_Resize() instead ! */ - if (unicode == unicode_empty || - (unicode->length == 1 && - /* MvL said unicode->str[] may be signed. Python generally assumes - * an int contains at least 32 bits, and we don't use more than - * 32 bits even in a UCS4 build, so casting to unsigned int should - * be correct. - */ - (unsigned int)unicode->str[0] < 256U && + if (unicode == unicode_empty || + (unicode->length == 1 && + unicode->str[0] < 256U && unicode_latin1[unicode->str[0]] == unicode)) { PyErr_SetString(PyExc_SystemError, "can't resize shared unicode objects"); @@ -149,13 +150,12 @@ int unicode_resize(register PyUnicodeObject *unicode, oldstr = unicode->str; PyMem_RESIZE(unicode->str, Py_UNICODE, length + 1); if (!unicode->str) { - unicode->str = oldstr; + unicode->str = (Py_UNICODE *)oldstr; PyErr_NoMemory(); return -1; } unicode->str[length] = 0; - assert(length < INT_MAX); - unicode->length = (int)length; + unicode->length = length; reset: /* Reset the object caches */ @@ -226,8 +226,7 @@ PyUnicodeObject *_PyUnicode_New(Py_ssize_t length) */ unicode->str[0] = 0; unicode->str[length] = 0; - assert(lengthlength = (int)length; + unicode->length = length; unicode->hash = -1; unicode->defenc = NULL; return unicode; @@ -368,7 +367,7 @@ PyObject *PyUnicode_FromWideChar(register const wchar_t *w, #else { register Py_UNICODE *u; - register int i; + register Py_ssize_t i; u = PyUnicode_AS_UNICODE(unicode); for (i = size; i > 0; i--) *u++ = *w++; @@ -396,7 +395,7 @@ Py_ssize_t PyUnicode_AsWideChar(PyUnicodeObject *unicode, #else { register Py_UNICODE *u; - register int i; + register Py_ssize_t i; u = PyUnicode_AS_UNICODE(unicode); for (i = size; i > 0; i--) *w++ = *u++; @@ -1358,7 +1357,7 @@ PyUnicode_EncodeUTF8(const Py_UNICODE *s, PyObject *v; /* result string object */ char *p; /* next free byte in output buffer */ Py_ssize_t nallocated; /* number of result bytes allocated */ - int nneeded; /* number of result bytes needed */ + Py_ssize_t nneeded; /* number of result bytes needed */ char stackbuf[MAX_SHORT_UNICHARS * 4]; assert(s != NULL); @@ -1427,13 +1426,13 @@ encodeUCS4: if (v == NULL) { /* This was stack allocated. */ - nneeded = Py_SAFE_DOWNCAST(p - stackbuf, long, int); + nneeded = p - stackbuf; assert(nneeded <= nallocated); v = PyString_FromStringAndSize(stackbuf, nneeded); } else { /* Cut back to size actually needed. */ - nneeded = Py_SAFE_DOWNCAST(p - PyString_AS_STRING(v), long, int); + nneeded = p - PyString_AS_STRING(v); assert(nneeded <= nallocated); _PyString_Resize(&v, nneeded); } @@ -1884,7 +1883,7 @@ PyObject *PyUnicode_DecodeUnicodeEscape(const char *s, Py_DECREF(m); if (api == NULL) goto ucnhashError; - ucnhash_CAPI = PyCObject_AsVoidPtr(api); + ucnhash_CAPI = (_PyUnicode_Name_CAPI *)PyCObject_AsVoidPtr(api); Py_DECREF(api); if (ucnhash_CAPI == NULL) goto ucnhashError; @@ -1934,7 +1933,7 @@ PyObject *PyUnicode_DecodeUnicodeEscape(const char *s, nextByte: ; } - if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v))) < 0) + if (_PyUnicode_Resize(&v, p - PyUnicode_AS_UNICODE(v)) < 0) goto onError; Py_XDECREF(errorHandler); Py_XDECREF(exc); @@ -2003,7 +2002,7 @@ PyObject *unicodeescape_string(const Py_UNICODE *s, #ifdef Py_UNICODE_WIDE /* Map 21-bit characters to '\U00xxxxxx' */ else if (ch >= 0x10000) { - int offset = p - PyString_AS_STRING(repr); + Py_ssize_t offset = p - PyString_AS_STRING(repr); /* Resize the string if necessary */ if (offset + 12 > PyString_GET_SIZE(repr)) { @@ -2205,7 +2204,7 @@ PyObject *PyUnicode_DecodeRawUnicodeEscape(const char *s, nextByte: ; } - if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v))) < 0) + if (_PyUnicode_Resize(&v, p - PyUnicode_AS_UNICODE(v)) < 0) goto onError; Py_XDECREF(errorHandler); Py_XDECREF(exc); @@ -2348,7 +2347,7 @@ PyObject *_PyUnicode_DecodeUnicodeInternal(const char *s, } } - if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v))) < 0) + if (_PyUnicode_Resize(&v, p - PyUnicode_AS_UNICODE(v)) < 0) goto onError; Py_XDECREF(errorHandler); Py_XDECREF(exc); @@ -2499,8 +2498,8 @@ static PyObject *unicode_encode_ucs1(const Py_UNICODE *p, /* current output position */ Py_ssize_t respos = 0; Py_ssize_t ressize; - char *encoding = (limit == 256) ? "latin-1" : "ascii"; - char *reason = (limit == 256) ? "ordinal not in range(256)" : "ordinal not in range(128)"; + const char *encoding = (limit == 256) ? "latin-1" : "ascii"; + const char *reason = (limit == 256) ? "ordinal not in range(256)" : "ordinal not in range(128)"; PyObject *errorHandler = NULL; PyObject *exc = NULL; /* the following variable is used for caching string comparisons @@ -2723,7 +2722,7 @@ PyObject *PyUnicode_DecodeASCII(const char *s, } } if (p - PyUnicode_AS_UNICODE(v) < PyString_GET_SIZE(v)) - if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v))) < 0) + if (_PyUnicode_Resize(&v, p - PyUnicode_AS_UNICODE(v)) < 0) goto onError; Py_XDECREF(errorHandler); Py_XDECREF(exc); @@ -2982,7 +2981,7 @@ PyObject *PyUnicode_DecodeCharmap(const char *s, } } if (p - PyUnicode_AS_UNICODE(v) < PyUnicode_GET_SIZE(v)) - if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v))) < 0) + if (_PyUnicode_Resize(&v, p - PyUnicode_AS_UNICODE(v)) < 0) goto onError; Py_XDECREF(errorHandler); Py_XDECREF(exc); @@ -3336,9 +3335,9 @@ static PyObject *unicode_translate_call_errorhandler(const char *errors, Py_ssize_t startpos, Py_ssize_t endpos, Py_ssize_t *newpos) { - static char *argparse = "O!i;translating error handler must return (unicode, int) tuple"; + static char *argparse = "O!n;translating error handler must return (unicode, int) tuple"; - int i_newpos; + Py_ssize_t i_newpos; PyObject *restuple; PyObject *resunicode; @@ -3798,7 +3797,7 @@ Py_ssize_t count(PyUnicodeObject *self, Py_ssize_t end, PyUnicodeObject *substring) { - int count = 0; + Py_ssize_t count = 0; if (start < 0) start += self->length; @@ -4157,7 +4156,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) PyObject *fseq; /* PySequence_Fast(seq) */ Py_ssize_t seqlen; /* len(fseq) -- number of items in sequence */ PyObject *item; - int i; + Py_ssize_t i; fseq = PySequence_Fast(seq, ""); if (fseq == NULL) { @@ -4206,7 +4205,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) } /* Get space. */ - res = _PyUnicode_New((int)res_alloc); + res = _PyUnicode_New(res_alloc); if (res == NULL) goto onError; res_p = PyUnicode_AS_UNICODE(res); @@ -4220,7 +4219,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) /* Convert item to Unicode. */ if (! PyUnicode_Check(item) && ! PyString_Check(item)) { PyErr_Format(PyExc_TypeError, - "sequence item %i: expected string or Unicode," + "sequence item %zd: expected string or Unicode," " %.80s found", i, item->ob_type->tp_name); goto onError; @@ -4236,11 +4235,11 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) /* Make sure we have enough space for the separator and the item. */ itemlen = PyUnicode_GET_SIZE(item); new_res_used = res_used + itemlen; - if (new_res_used < res_used || new_res_used > INT_MAX) + if (new_res_used < res_used || new_res_used > PY_SSIZE_T_MAX) goto Overflow; if (i < seqlen - 1) { new_res_used += seplen; - if (new_res_used < res_used || new_res_used > INT_MAX) + if (new_res_used < res_used || new_res_used > PY_SSIZE_T_MAX) goto Overflow; } if (new_res_used > res_alloc) { @@ -4248,10 +4247,10 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) do { size_t oldsize = res_alloc; res_alloc += res_alloc; - if (res_alloc < oldsize || res_alloc > INT_MAX) + if (res_alloc < oldsize || res_alloc > PY_SSIZE_T_MAX) goto Overflow; } while (new_res_used > res_alloc); - if (_PyUnicode_Resize(&res, (int)res_alloc) < 0) { + if (_PyUnicode_Resize(&res, res_alloc) < 0) { Py_DECREF(item); goto onError; } @@ -4259,10 +4258,10 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) } /* Copy item, and maybe the separator. */ - Py_UNICODE_COPY(res_p, PyUnicode_AS_UNICODE(item), (int)itemlen); + Py_UNICODE_COPY(res_p, PyUnicode_AS_UNICODE(item), itemlen); res_p += itemlen; if (i < seqlen - 1) { - Py_UNICODE_COPY(res_p, sep, (int)seplen); + Py_UNICODE_COPY(res_p, sep, seplen); res_p += seplen; } Py_DECREF(item); @@ -4272,7 +4271,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq) /* Shrink res to match the used area; this probably can't fail, * but it's cheap to check. */ - if (_PyUnicode_Resize(&res, (int)res_used) < 0) + if (_PyUnicode_Resize(&res, res_used) < 0) goto onError; Done: @@ -4605,7 +4604,7 @@ PyObject *split(PyUnicodeObject *self, PyObject *list; if (maxcount < 0) - maxcount = INT_MAX; + maxcount = PY_SSIZE_T_MAX; list = PyList_New(0); if (!list) @@ -4634,7 +4633,7 @@ PyObject *rsplit(PyUnicodeObject *self, PyObject *list; if (maxcount < 0) - maxcount = INT_MAX; + maxcount = PY_SSIZE_T_MAX; list = PyList_New(0); if (!list) @@ -4664,10 +4663,10 @@ PyObject *replace(PyUnicodeObject *self, PyUnicodeObject *u; if (maxcount < 0) - maxcount = INT_MAX; + maxcount = PY_SSIZE_T_MAX; if (str1->length == 1 && str2->length == 1) { - int i; + Py_ssize_t i; /* replace characters */ if (!findchar(self->str, self->length, str1->str[0]) && @@ -5088,7 +5087,7 @@ unicode_count(PyUnicodeObject *self, PyObject *args) { PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *result; if (!PyArg_ParseTuple(args, "O|O&O&:count", &substring, @@ -5265,7 +5264,7 @@ unicode_find(PyUnicodeObject *self, PyObject *args) { PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *result; if (!PyArg_ParseTuple(args, "O|O&O&:find", &substring, @@ -5331,7 +5330,7 @@ unicode_index(PyUnicodeObject *self, PyObject *args) Py_ssize_t result; PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; if (!PyArg_ParseTuple(args, "O|O&O&:index", &substring, _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) @@ -5669,10 +5668,10 @@ done using the specified fill character (default is a space)."); static PyObject * unicode_ljust(PyUnicodeObject *self, PyObject *args) { - int width; + Py_ssize_t width; Py_UNICODE fillchar = ' '; - if (!PyArg_ParseTuple(args, "i|O&:ljust", &width, convert_uc, &fillchar)) + if (!PyArg_ParseTuple(args, "n|O&:ljust", &width, convert_uc, &fillchar)) return NULL; if (self->length >= width && PyUnicode_CheckExact(self)) { @@ -5996,7 +5995,7 @@ unicode_rfind(PyUnicodeObject *self, PyObject *args) { PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *result; if (!PyArg_ParseTuple(args, "O|O&O&:rfind", &substring, @@ -6024,7 +6023,7 @@ unicode_rindex(PyUnicodeObject *self, PyObject *args) Py_ssize_t result; PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; if (!PyArg_ParseTuple(args, "O|O&O&:rindex", &substring, _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) @@ -6053,10 +6052,10 @@ done using the specified fill character (default is a space)."); static PyObject * unicode_rjust(PyUnicodeObject *self, PyObject *args) { - int width; + Py_ssize_t width; Py_UNICODE fillchar = ' '; - if (!PyArg_ParseTuple(args, "i|O&:rjust", &width, convert_uc, &fillchar)) + if (!PyArg_ParseTuple(args, "n|O&:rjust", &width, convert_uc, &fillchar)) return NULL; if (self->length >= width && PyUnicode_CheckExact(self)) { @@ -6318,7 +6317,7 @@ unicode_startswith(PyUnicodeObject *self, { PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *result; if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &substring, @@ -6349,7 +6348,7 @@ unicode_endswith(PyUnicodeObject *self, { PyUnicodeObject *substring; Py_ssize_t start = 0; - Py_ssize_t end = INT_MAX; + Py_ssize_t end = PY_SSIZE_T_MAX; PyObject *result; if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &substring, @@ -6450,13 +6449,13 @@ static PyNumberMethods unicode_as_number = { static PySequenceMethods unicode_as_sequence = { (lenfunc) unicode_length, /* sq_length */ - (binaryfunc) PyUnicode_Concat, /* sq_concat */ + PyUnicode_Concat, /* sq_concat */ (ssizeargfunc) unicode_repeat, /* sq_repeat */ (ssizeargfunc) unicode_getitem, /* sq_item */ (ssizessizeargfunc) unicode_slice, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ - (objobjproc)PyUnicode_Contains, /*sq_contains*/ + PyUnicode_Contains, /* sq_contains */ }; #define HASINDEX(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_HAVE_INDEX) @@ -6487,7 +6486,8 @@ unicode_subscript(PyUnicodeObject* self, PyObject* item) return PyUnicode_FromUnicode(NULL, 0); } else { source_buf = PyUnicode_AS_UNICODE((PyObject*)self); - result_buf = PyMem_MALLOC(slicelength*sizeof(Py_UNICODE)); + result_buf = (Py_UNICODE *)PyMem_MALLOC(slicelength* + sizeof(Py_UNICODE)); if (result_buf == NULL) return PyErr_NoMemory(); @@ -7336,7 +7336,7 @@ PyTypeObject PyUnicode_Type = { 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc) unicode_compare, /* tp_compare */ - (reprfunc) unicode_repr, /* tp_repr */ + unicode_repr, /* tp_repr */ &unicode_as_number, /* tp_as_number */ &unicode_as_sequence, /* tp_as_sequence */ &unicode_as_mapping, /* tp_as_mapping */ @@ -7416,6 +7416,11 @@ _PyUnicode_Fini(void) unicode_freelist_size = 0; } +#ifdef __cplusplus +} +#endif + + /* Local variables: c-basic-offset: 4 diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 39595ae..3f2c261 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -109,8 +109,7 @@ weakref_dealloc(PyObject *self) static int gc_traverse(PyWeakReference *self, visitproc visit, void *arg) { - if (self->wr_callback != NULL) - return visit(self->wr_callback, arg); + Py_VISIT(self->wr_callback); return 0; } @@ -367,7 +366,7 @@ _PyWeakref_RefType = { 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ - (initproc)weakref___init__, /*tp_init*/ + weakref___init__, /*tp_init*/ PyType_GenericAlloc, /*tp_alloc*/ weakref___new__, /*tp_new*/ PyObject_GC_Del, /*tp_free*/ @@ -586,38 +585,38 @@ proxy_iternext(PyWeakReference *proxy) static PyNumberMethods proxy_as_number = { - (binaryfunc)proxy_add, /*nb_add*/ - (binaryfunc)proxy_sub, /*nb_subtract*/ - (binaryfunc)proxy_mul, /*nb_multiply*/ - (binaryfunc)proxy_mod, /*nb_remainder*/ - (binaryfunc)proxy_divmod, /*nb_divmod*/ - (ternaryfunc)proxy_pow, /*nb_power*/ - (unaryfunc)proxy_neg, /*nb_negative*/ - (unaryfunc)proxy_pos, /*nb_positive*/ - (unaryfunc)proxy_abs, /*nb_absolute*/ - (inquiry)proxy_nonzero, /*nb_nonzero*/ - (unaryfunc)proxy_invert, /*nb_invert*/ - (binaryfunc)proxy_lshift, /*nb_lshift*/ - (binaryfunc)proxy_rshift, /*nb_rshift*/ - (binaryfunc)proxy_and, /*nb_and*/ - (binaryfunc)proxy_xor, /*nb_xor*/ - (binaryfunc)proxy_or, /*nb_or*/ - (coercion)0, /*nb_coerce*/ - (unaryfunc)proxy_int, /*nb_int*/ - (unaryfunc)proxy_long, /*nb_long*/ - (unaryfunc)proxy_float, /*nb_float*/ - (unaryfunc)0, /*nb_oct*/ - (unaryfunc)0, /*nb_hex*/ - (binaryfunc)proxy_iadd, /*nb_inplace_add*/ - (binaryfunc)proxy_isub, /*nb_inplace_subtract*/ - (binaryfunc)proxy_imul, /*nb_inplace_multiply*/ - (binaryfunc)proxy_imod, /*nb_inplace_remainder*/ - (ternaryfunc)proxy_ipow, /*nb_inplace_power*/ - (binaryfunc)proxy_ilshift, /*nb_inplace_lshift*/ - (binaryfunc)proxy_irshift, /*nb_inplace_rshift*/ - (binaryfunc)proxy_iand, /*nb_inplace_and*/ - (binaryfunc)proxy_ixor, /*nb_inplace_xor*/ - (binaryfunc)proxy_ior, /*nb_inplace_or*/ + proxy_add, /*nb_add*/ + proxy_sub, /*nb_subtract*/ + proxy_mul, /*nb_multiply*/ + proxy_mod, /*nb_remainder*/ + proxy_divmod, /*nb_divmod*/ + proxy_pow, /*nb_power*/ + proxy_neg, /*nb_negative*/ + proxy_pos, /*nb_positive*/ + proxy_abs, /*nb_absolute*/ + (inquiry)proxy_nonzero, /*nb_nonzero*/ + proxy_invert, /*nb_invert*/ + proxy_lshift, /*nb_lshift*/ + proxy_rshift, /*nb_rshift*/ + proxy_and, /*nb_and*/ + proxy_xor, /*nb_xor*/ + proxy_or, /*nb_or*/ + 0, /*nb_coerce*/ + proxy_int, /*nb_int*/ + proxy_long, /*nb_long*/ + proxy_float, /*nb_float*/ + 0, /*nb_oct*/ + 0, /*nb_hex*/ + proxy_iadd, /*nb_inplace_add*/ + proxy_isub, /*nb_inplace_subtract*/ + proxy_imul, /*nb_inplace_multiply*/ + proxy_imod, /*nb_inplace_remainder*/ + proxy_ipow, /*nb_inplace_power*/ + proxy_ilshift, /*nb_inplace_lshift*/ + proxy_irshift, /*nb_inplace_rshift*/ + proxy_iand, /*nb_inplace_and*/ + proxy_ixor, /*nb_inplace_xor*/ + proxy_ior, /*nb_inplace_or*/ }; static PySequenceMethods proxy_as_sequence = { @@ -632,8 +631,8 @@ static PySequenceMethods proxy_as_sequence = { }; static PyMappingMethods proxy_as_mapping = { - (lenfunc)proxy_length, /*mp_length*/ - (binaryfunc)proxy_getitem, /*mp_subscript*/ + (lenfunc)proxy_length, /*mp_length*/ + proxy_getitem, /*mp_subscript*/ (objobjargproc)proxy_setitem, /*mp_ass_subscript*/ }; @@ -651,14 +650,14 @@ _PyWeakref_ProxyType = { 0, /* tp_getattr */ 0, /* tp_setattr */ proxy_compare, /* tp_compare */ - (unaryfunc)proxy_repr, /* tp_repr */ + (reprfunc)proxy_repr, /* tp_repr */ &proxy_as_number, /* tp_as_number */ &proxy_as_sequence, /* tp_as_sequence */ &proxy_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (unaryfunc)proxy_str, /* tp_str */ - (getattrofunc)proxy_getattr, /* tp_getattro */ + 0, /* tp_call */ + proxy_str, /* tp_str */ + proxy_getattr, /* tp_getattro */ (setattrofunc)proxy_setattr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC @@ -691,9 +690,9 @@ _PyWeakref_CallableProxyType = { &proxy_as_sequence, /* tp_as_sequence */ &proxy_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)proxy_call, /* tp_call */ - (unaryfunc)proxy_str, /* tp_str */ - (getattrofunc)proxy_getattr, /* tp_getattro */ + proxy_call, /* tp_call */ + proxy_str, /* tp_str */ + proxy_getattr, /* tp_getattro */ (setattrofunc)proxy_setattr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC diff --git a/PC/VC6/pythoncore.dsp b/PC/VC6/pythoncore.dsp index 04a1224..cf3200c 100644 --- a/PC/VC6/pythoncore.dsp +++ b/PC/VC6/pythoncore.dsp @@ -535,14 +535,6 @@ SOURCE=..\..\Objects\rangeobject.c # End Source File # Begin Source File -SOURCE=..\..\Modules\regexmodule.c -# End Source File -# Begin Source File - -SOURCE=..\..\Modules\regexpr.c -# End Source File -# Begin Source File - SOURCE=..\..\Modules\rgbimgmodule.c # End Source File # Begin Source File diff --git a/PC/_subprocess.c b/PC/_subprocess.c index 1ca4ece..78ceb7d 100644 --- a/PC/_subprocess.c +++ b/PC/_subprocess.c @@ -104,7 +104,7 @@ sp_handle_dealloc(sp_handle_object* self) { if (self->handle != INVALID_HANDLE_VALUE) CloseHandle(self->handle); - PyMem_DEL(self); + PyObject_FREE(self); } static PyMethodDef sp_handle_methods[] = { diff --git a/PC/config.c b/PC/config.c index ee62dc5..75e1a3c 100644 --- a/PC/config.c +++ b/PC/config.c @@ -20,7 +20,6 @@ extern void initmath(void); extern void init_md5(void); extern void initnt(void); extern void initoperator(void); -extern void initregex(void); #ifndef MS_WIN64 extern void initrgbimg(void); #endif @@ -95,7 +94,6 @@ struct _inittab _PyImport_Inittab[] = { {"_md5", init_md5}, {"nt", initnt}, /* Use the NT os functions, not posix */ {"operator", initoperator}, - {"regex", initregex}, #ifndef MS_WIN64 {"rgbimg", initrgbimg}, #endif diff --git a/PC/dllbase_nt.txt b/PC/dllbase_nt.txt index 944ef95..c06e497 100644 --- a/PC/dllbase_nt.txt +++ b/PC/dllbase_nt.txt @@ -33,6 +33,7 @@ More standard extensions 1D100000 - 1e000000 - bZ2 1D170000 - 1D180000 - datetime 1D180000 - 1D190000 pyd removed in 2.4 - _csv 1D190000 - 1D1A0000 pyd removed in 2.4 + - _ctypes 1D1A0000 - 1D1B0000 Other extension modules - win32api 1e200000 - 1e220000 diff --git a/PC/os2emx/Makefile b/PC/os2emx/Makefile index 847fa67..762bfdb 100644 --- a/PC/os2emx/Makefile +++ b/PC/os2emx/Makefile @@ -304,8 +304,6 @@ SRC.MODULES= $(addprefix $(TOP), \ Modules/md5module.c \ Modules/operator.c \ Modules/_randommodule.c \ - Modules/regexmodule.c \ - Modules/regexpr.c \ Modules/rgbimgmodule.c \ Modules/shamodule.c \ Modules/_sre.c \ diff --git a/PC/os2emx/config.c b/PC/os2emx/config.c index 5ee4343..40c2cdc 100644 --- a/PC/os2emx/config.c +++ b/PC/os2emx/config.c @@ -64,7 +64,6 @@ extern void inititertools(); extern void initmath(); extern void initmd5(); extern void initoperator(); -extern void initregex(); extern void initrgbimg(); extern void initsha(); extern void initstrop(); @@ -128,7 +127,6 @@ struct _inittab _PyImport_Inittab[] = { {"math", initmath}, {"md5", initmd5}, {"operator", initoperator}, - {"regex", initregex}, {"rgbimg", initrgbimg}, {"sha", initsha}, {"strop", initstrop}, diff --git a/PC/os2emx/python24.def b/PC/os2emx/python24.def index 4f78914..534dff8 100644 --- a/PC/os2emx/python24.def +++ b/PC/os2emx/python24.def @@ -1134,19 +1134,6 @@ EXPORTS ; From python24_s.lib(_randommodule) ; "init_random" -; From python24_s.lib(regexmodule) -; "initregex" - -; From python24_s.lib(regexpr) -; "_Py_re_syntax_table" -; "_Py_re_compile_initialize" -; "_Py_re_compile_pattern" -; "_Py_re_match" -; "_Py_re_search" -; "_Py_re_set_syntax" -; "_Py_re_compile_fastmap" -; "_Py_re_syntax" - ; From python24_s.lib(rgbimgmodule) ; "initrgbimg" diff --git a/PC/os2vacpp/config.c b/PC/os2vacpp/config.c index 7512de5..9bb5752 100644 --- a/PC/os2vacpp/config.c +++ b/PC/os2vacpp/config.c @@ -27,7 +27,6 @@ extern void initnt(void); extern void initos2(void); extern void initoperator(void); extern void initposix(void); -extern void initregex(void); extern void initrgbimg(void); extern void initsignal(void); extern void initselect(void); @@ -70,7 +69,6 @@ struct _inittab _PyImport_Inittab[] = { #endif #endif {"operator", initoperator}, - {"regex", initregex}, // {"rgbimg", initrgbimg}, {"signal", initsignal}, #ifdef USE_SOCKET diff --git a/PC/os2vacpp/makefile b/PC/os2vacpp/makefile index 994ac49..f34047f 100644 --- a/PC/os2vacpp/makefile +++ b/PC/os2vacpp/makefile @@ -948,34 +948,6 @@ readline.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\class $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h -regexmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ - $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ - pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ - $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ - $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ - $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ - $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ - $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ - $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ - $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ - $(PY_INCLUDE)\rangeobject.h $(PY_MODULES)\regexpr.h $(PY_INCLUDE)\sliceobject.h \ - $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ - $(PY_INCLUDE)\tupleobject.h - -regexpr.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ - $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ - pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ - $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ - $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ - $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ - $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ - $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ - $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ - $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ - $(PY_INCLUDE)\rangeobject.h $(PY_MODULES)\regexpr.h $(PY_INCLUDE)\sliceobject.h \ - $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ - $(PY_INCLUDE)\tupleobject.h - resource.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\time.h $(PY_INCLUDE)\ceval.h \ $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ diff --git a/PC/os2vacpp/makefile.omk b/PC/os2vacpp/makefile.omk index 0d11b6a..9582338 100644 --- a/PC/os2vacpp/makefile.omk +++ b/PC/os2vacpp/makefile.omk @@ -699,30 +699,6 @@ readline.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ traceback.h tupleobject.h -regexmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ - pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ - import.h intobject.h intrcheck.h listobject.h longobject.h \ - methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ - object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ - pythonrun.h rangeobject.h regexpr.h sliceobject.h stringobject.h \ - sysmodule.h traceback.h tupleobject.h - -regexpr.obj: abstract.h ceval.h classobject.h cobject.h \ - complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ - funcobject.h import.h intobject.h intrcheck.h listobject.h \ - longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ - myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ - pystate.h python.h pythonrun.h rangeobject.h regexpr.h \ - sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h - -reopmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ - pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ - import.h intobject.h intrcheck.h listobject.h longobject.h \ - methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ - object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ - pythonrun.h rangeobject.h regexpr.h sliceobject.h stringobject.h \ - sysmodule.h traceback.h tupleobject.h - resource.obj: abstract.h c:\mptn\include\sys\time.h ceval.h classobject.h \ cobject.h complexobject.h pyconfig.h dictobject.h fileobject.h \ floatobject.h funcobject.h import.h intobject.h intrcheck.h \ diff --git a/PC/os2vacpp/python.def b/PC/os2vacpp/python.def index bc73fac..79d05b0 100644 --- a/PC/os2vacpp/python.def +++ b/PC/os2vacpp/python.def @@ -464,12 +464,6 @@ EXPORTS ; _Py_mergebitset ; _Py_meta_grammar ; _Py_newbitset - _Py_re_compile_fastmap - _Py_re_compile_initialize - _Py_re_compile_pattern - _Py_re_match - _Py_re_search - _Py_re_set_syntax ; _Py_samebitset PyBuffer_Type PyBuffer_FromObject diff --git a/PC/testpy.py b/PC/testpy.py index f8746a3..78ad63c 100644 --- a/PC/testpy.py +++ b/PC/testpy.py @@ -5,23 +5,23 @@ import sys # change this module too. try: - import string + import os except: - print """Could not import the standard "string" module. + print """Could not import the standard "os" module. Please check your PYTHONPATH environment variable.""" sys.exit(1) try: - import regex_syntax + import symbol except: - print """Could not import the standard "regex_syntax" module. If this is + print """Could not import the standard "symbol" module. If this is a PC, you should add the dos_8x3 directory to your PYTHONPATH.""" sys.exit(1) import os for dir in sys.path: - file = os.path.join(dir, "string.py") + file = os.path.join(dir, "os.py") if os.path.isfile(file): test = os.path.join(dir, "test") if os.path.isdir(test): diff --git a/PC/tix.diff b/PC/tix.diff deleted file mode 100644 index 93a271f..0000000 --- a/PC/tix.diff +++ /dev/null @@ -1,108 +0,0 @@ -diff -ur tix-8.1.4/win/common.mak tix-8.1.4.new/win/common.mak ---- tix-8.1.4/win/common.mak 2002-12-11 07:19:42.000000000 +0100 -+++ tix-8.1.4.new/win/common.mak 2004-08-03 21:45:09.859375000 +0200 -@@ -18,10 +18,10 @@ - # support files - # - #---------------------------------------------------------------------- --TCL_VER = 8.3 -+TCL_VER = 8.4 - ITCL_VER = - --INSTALLDIR = C:\progra~1\tcl -+INSTALLDIR = ..\..\tcltk - - !IFNDEF TIX_DEBUG - NODEBUG = 1 -@@ -61,7 +61,7 @@ - !IF "$(TCL_VER)" == "8.4" - TCLMAJOR=8 - TCLMINOR=4 --TCLPATCH=1 -+TCLPATCH=7 - TMPDIR = tk$(TCL_VER) - !ENDIF - -@@ -176,14 +176,14 @@ - $(TMPDIR)\tixWinWm.obj - - RMDIR = $(TCLDIR)\win\rmd.bat --MKDIR = $(TCLDIR)\win\mkd.bat -+MKDIR = mkdir - RM = del - - install: install-binaries install-libraries - - install-binaries: $(TCLSH) -- $(MKDIR) "$(BIN_INSTALL_DIR)" -- $(MKDIR) "$(LIB_INSTALL_DIR)" -+ -$(MKDIR) "$(BIN_INSTALL_DIR)" -+ -$(MKDIR) "$(LIB_INSTALL_DIR)" - @echo installing $(TIXDLL) - @copy "$(TIXDLL)" "$(BIN_INSTALL_DIR)" - @copy "$(TIXLIB)" "$(LIB_INSTALL_DIR)" -diff -ur tix-8.1.4/win/makefile.vc tix-8.1.4.new/win/makefile.vc ---- tix-8.1.4/win/makefile.vc 2002-12-02 04:02:54.000000000 +0100 -+++ tix-8.1.4.new/win/makefile.vc 2004-08-03 21:42:07.953125000 +0200 -@@ -54,12 +54,11 @@ - DBGX = d - !ENDIF - --cc32 = "$(TOOLS32)\bin\cl.exe" --rc32 = "$(TOOLS32_rc)\bin\rc.exe" --link32 = "$(TOOLS32)\bin\link.exe" --include32 = -I"$(TOOLS32)\include" -+cc32 = "cl.exe" -+rc32 = "rc.exe" -+link32 = "link.exe" - --TIX_INCLUDES = $(include32) \ -+TIX_INCLUDES = \ - -I$(ROOT)\win -I$(ROOT)\generic \ - -I$(TKDIR)\generic -I$(TKDIR)\win -I$(TKDIR)\xlib \ - -I$(TCLDIR)\generic $(ITCL_CFLAGS) -@@ -171,7 +170,7 @@ - # - cvarsdll = -D_X86_=1 -DWIN32 -D_WIN32 -D_MT -D_DLL - cflagsdll = $(cvarsdll) -c -W3 -nologo -Fp$(TMPDIR)\ -YX -MD \ -- -Oti -Gs -GD -+ -Oti -Gs -Gd - - ###################################################################### - # Project specific targets -@@ -181,7 +180,6 @@ - - $(DUMPEXTS): $(WINDIR)\winDumpExts.c - $(cc32) $(CON_CFLAGS) -Fo$(TMPDIR)\ /c $? -- set LIB="$(TOOLS32)\lib" - $(link32) $(ldebug) $(conlflags) $(guilibs) -out:$@ \ - $(TMPDIR)\winDumpExts.obj - -@@ -193,7 +191,6 @@ - # (ToDo) $(TIXDLL) doesn't have resources to define its icon, etc. - # - $(TIXDLL): $(TIXOBJS) $(TMPDIR)\tixvc.def -- set LIB="$(TOOLS32)\lib" - $(link32) $(ldebug) $(dlllflags) -def:$(TMPDIR)\tixvc.def \ - $(TKLIBDIR)\$(TKLIB) $(TCLLIBDIR)\$(TCLLIB) $(guilibsdll) \ - $(ITCL_LIBS) -out:$@ @<< -@@ -202,7 +199,6 @@ - - - $(TIXWISH): $(WISHOBJS) $(TIXOBJS) $(TIXLIB) $(TMPDIR)\tixwish.res -- set LIB="$(TOOLS32)\lib" - $(link32) $(ldebug) $(guilflags) \ - $(WISHOBJS) $(TMPDIR)\tixwish.res $(TIXLIB) \ - $(TKLIBDIR)\$(TKLIB) $(TCLLIBDIR)\$(TCLLIB) $(guilibsdll) \ -diff -ur tix-8.1.4/win/tk8.4/pkgIndex.tcl tix-8.1.4.new/win/tk8.4/pkgIndex.tcl ---- tix-8.1.4/win/tk8.4/pkgIndex.tcl 2002-12-15 04:21:54.000000000 +0100 -+++ tix-8.1.4.new/win/tk8.4/pkgIndex.tcl 2004-08-31 08:38:43.921875000 +0200 -@@ -15,7 +15,7 @@ - # We look in the ../../bin directory (an installed Tcl) - lappend dirs ../../bin - # We look in the ../../DLLs directory (an installed Python) --lappend dirs ../../Dlls -+lappend dirs [file join [file dirname [info nameofexe]] DLLs] - # If not, this pkgIndex.tcl will probably fail. - - diff --git a/PCbuild/_ctypes.vcproj b/PCbuild/_ctypes.vcproj index 76518e6..4990c9e 100644 --- a/PCbuild/_ctypes.vcproj +++ b/PCbuild/_ctypes.vcproj @@ -33,12 +33,14 @@ Name="VCCustomBuildTool"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PCbuild/_ssl.mak b/PCbuild/_ssl.mak index c150d65..2c47c6b 100644 --- a/PCbuild/_ssl.mak +++ b/PCbuild/_ssl.mak @@ -2,17 +2,17 @@ !IFDEF DEBUG MODULE=_ssl_d.pyd TEMP_DIR=x86-temp-debug/_ssl -CFLAGS=/Od /Zi /MDd /LDd /DDEBUG /D_DEBUG +CFLAGS=/Od /Zi /MDd /LDd /DDEBUG /D_DEBUG /DWIN32 SSL_LIB_DIR=$(SSL_DIR)/out32.dbg !ELSE MODULE=_ssl.pyd TEMP_DIR=x86-temp-release/_ssl -CFLAGS=/Ox /MD /LD +CFLAGS=/Ox /MD /LD /DWIN32 SSL_LIB_DIR=$(SSL_DIR)/out32 !ENDIF INCLUDES=-I ../Include -I ../PC -I $(SSL_DIR)/inc32 -LIBS=gdi32.lib wsock32.lib /libpath:$(SSL_LIB_DIR) libeay32.lib ssleay32.lib +LIBS=gdi32.lib wsock32.lib user32.lib advapi32.lib /libpath:$(SSL_LIB_DIR) libeay32.lib ssleay32.lib SOURCE=../Modules/_ssl.c $(SSL_LIB_DIR)/libeay32.lib $(SSL_LIB_DIR)/ssleay32.lib diff --git a/PCbuild/db.build b/PCbuild/db.build new file mode 100644 index 0000000..6a87f74 --- /dev/null +++ b/PCbuild/db.build @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/PCbuild/make_buildinfo.c b/PCbuild/make_buildinfo.c index 9d2f9f0..4cebf45 100644 --- a/PCbuild/make_buildinfo.c +++ b/PCbuild/make_buildinfo.c @@ -27,6 +27,9 @@ int make_buildinfo2() DWORD type, size; if (_stat(".svn", &st) < 0) return 0; + /* Allow suppression of subwcrev.exe invocation if a no_subwcrev file is present. */ + if (_stat("no_subwcrev", &st) == 0) + return 0; if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\TortoiseSVN", &hTortoise) != ERROR_SUCCESS && RegOpenKey(HKEY_CURRENT_USER, "Software\\TortoiseSVN", &hTortoise) != ERROR_SUCCESS) /* Tortoise not installed */ diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 88d8c45..20d3ecf 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -96,6 +96,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_ctypes", "_ctypes.vcproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_ctypes_test", "_ctypes_test.vcproj", "{8CF334D9-4F82-42EB-97AF-83592C5AFD2F}" ProjectSection(ProjectDependencies) = postProject + {F22F40F4-D318-40DC-96B3-88DC81CE0894} = {F22F40F4-D318-40DC-96B3-88DC81CE0894} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_sqlite3", "_sqlite3.vcproj", "{2FF0A312-22F9-4C34-B070-842916DE27A9}" + ProjectSection(ProjectDependencies) = postProject + {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} = {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} EndProjectSection EndProject Global @@ -260,6 +266,14 @@ Global {8CF334D9-4F82-42EB-97AF-83592C5AFD2F}.Release.Build.0 = Release|Win32 {8CF334D9-4F82-42EB-97AF-83592C5AFD2F}.ReleaseAMD64.ActiveCfg = ReleaseAMD64|Win32 {8CF334D9-4F82-42EB-97AF-83592C5AFD2F}.ReleaseItanium.ActiveCfg = ReleaseItanium|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.Debug.ActiveCfg = Debug|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.Debug.Build.0 = Debug|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.Release.ActiveCfg = Release|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.Release.Build.0 = Release|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.ReleaseAMD64.ActiveCfg = ReleaseAMD64|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.ReleaseAMD64.Build.0 = ReleaseAMD64|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.ReleaseItanium.ActiveCfg = ReleaseItanium|Win32 + {2FF0A312-22F9-4C34-B070-842916DE27A9}.ReleaseItanium.Build.0 = ReleaseItanium|Win32 EndGlobalSection GlobalSection(SolutionItems) = postSolution ..\Modules\getbuildinfo.c = ..\Modules\getbuildinfo.c diff --git a/PCbuild/python.build b/PCbuild/python.build new file mode 100644 index 0000000..61bbe89 --- /dev/null +++ b/PCbuild/python.build @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index a1bb0ed..3bd740f 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="7.10" Name="pythoncore" - ProjectGUID="{7AFA1F0B-A8A1-455A-A832-BF263404BBEF}" + ProjectGUID="{CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}" RootNamespace="pythoncore" SccProjectName="pythoncore" SccLocalPath=".."> @@ -623,7 +623,7 @@ RelativePath="..\Modules\mathmodule.c"> + RelativePath="..\Modules\md5.c"> @@ -707,12 +707,6 @@ RelativePath="..\Objects\rangeobject.c"> - - - - All Programs -> Microsoft Visual Studio .NET 2003 -> Visual Studio .NET Tools -> Visual Studio .NET 2003 Command Prompt" to get a shell window with the correct environment settings - cd dist\tcl8.4.7\win + cd dist\tcl8.4.12\win nmake -f makefile.vc nmake -f makefile.vc INSTALLDIR=..\..\tcltk install @@ -99,9 +93,9 @@ _tkinter Build Tk -------- - cd dist\tk8.4.7\win - nmake -f makefile.vc TCLDIR=..\..\tcl8.4.7 - nmake -f makefile.vc TCLDIR=..\..\tcl8.4.7 INSTALLDIR=..\..\tcltk install + cd dist\tk8.4.12\win + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.12 + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.12 INSTALLDIR=..\..\tcltk install XXX Should we compile with OPTS=threads? @@ -109,7 +103,7 @@ _tkinter XXX directory. Is all of that really needed for Python use of Tcl/Tk? Optional: run tests, via - nmake -f makefile.vc TCLDIR=..\..\tcl8.4.7 test + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.12 test On WinXP Pro, wholly up to date as of 30-Aug-2004: all.tcl: Total 8420 Passed 6826 Skipped 1581 Failed 13 @@ -118,12 +112,9 @@ _tkinter Built Tix --------- - Download from http://prdownloads.sourceforge.net/tix/tix-8.1.4.tar.gz - cd dist\tix-8.1.4 - [cygwin]patch -p1 < ..\..\python\PC\tix.diff - cd win - nmake -f makefile.vc - nmake -f makefile.vc install + cd dist\tix-8.4.0\win + nmake -f python.mak + nmake -f python.mak install bz2 Python wrapper for the libbz2 compression library. Homepage @@ -223,23 +214,24 @@ _bsddb target ("Release IA64" for Itanium, "Release AMD64" for AMD64), e.g. devenv db-4.4.20\build_win32\Berkeley_DB.sln /build "Release AMD64" /project db_static /useenv +_sqlite3 + Python wrapper for SQLite library. + + Get the source code through + + svn export http://svn.python.org/projects/external/sqlite-source-3.3.4 + + To use the extension module in a Python build tree, copy sqlite3.dll into + the PCbuild folder. _ssl Python wrapper for the secure sockets library. - Get the latest source code for OpenSSL from - http://www.openssl.org + Get the source code through - You (probably) don't want the "engine" code. For example, get - openssl-0.9.7d.tar.gz - not - openssl-engine-0.9.7d.tar.gz - - (see #1233049 for using 0.9.8). - Unpack into the "dist" directory, retaining the folder name from - the archive - for example, the latest stable OpenSSL will install as - dist/openssl-0.9.7d + svn export http://svn.python.org/projects/external/openssl-0.9.8a + Alternatively, get the latest version from http://www.openssl.org. You can (theoretically) use any version of OpenSSL you like - the build process will automatically select the latest version. @@ -281,6 +273,143 @@ The build process for the ReleaseAMD64 configuration is very similar to the Itanium configuration; make sure you use the latest version of vsextcomp. +Building Python Using the free MS Toolkit Compiler +-------------------------------------------------- + +The build process for Visual C++ can be used almost unchanged with the free MS +Toolkit Compiler. This provides a way of building Python using freely +available software. + +Requirements + + To build Python, the following tools are required: + + * The Visual C++ Toolkit Compiler + from http://msdn.microsoft.com/visualc/vctoolkit2003/ + * A recent Platform SDK + from http://www.microsoft.com/downloads/details.aspx?FamilyID=484269e2-3b89-47e3-8eb7-1f2be6d7123a + * The .NET 1.1 SDK + from http://www.microsoft.com/downloads/details.aspx?FamilyID=9b3a2ca6-3647-4070-9f41-a333c6b9181d + + [Does anyone have better URLs for the last 2 of these?] + + The toolkit compiler is needed as it is an optimising compiler (the + compiler supplied with the .NET SDK is a non-optimising version). The + platform SDK is needed to provide the Windows header files and libraries + (the Windows 2003 Server SP1 edition, typical install, is known to work - + other configurations or versions are probably fine as well). The .NET 1.1 + SDK is needed because it contains a version of msvcrt.dll which links to + the msvcr71.dll CRT. Note that the .NET 2.0 SDK is NOT acceptable, as it + references msvcr80.dll. + + All of the above items should be installed as normal. + + If you intend to build the openssl (needed for the _ssl extension) you + will need the C runtime sources installed as part of the platform SDK. + + In addition, you will need Nant, available from + http://nant.sourceforge.net. The 0.85 release candidate 3 version is known + to work. This is the latest released version at the time of writing. Later + "nightly build" versions are known NOT to work - it is not clear at + present whether future released versions will work. + +Setting up the environment + + Start a platform SDK "build environment window" from the start menu. The + "Windows XP 32-bit retail" version is known to work. + + Add the following directories to your PATH: + * The toolkit compiler directory + * The SDK "Win64" binaries directory + * The Nant directory + Add to your INCLUDE environment variable: + * The toolkit compiler INCLUDE directory + Add to your LIB environment variable: + * The toolkit compiler LIB directory + * The .NET SDK Visual Studio 2003 VC7\lib directory + + The following commands should set things up as you need them: + + rem Set these values according to where you installed the software + set TOOLKIT=C:\Program Files\Microsoft Visual C++ Toolkit 2003 + set SDK=C:\Program Files\Microsoft Platform SDK + set NET=C:\Program Files\Microsoft Visual Studio .NET 2003 + set NANT=C:\Utils\Nant + + set PATH=%TOOLKIT%\bin;%PATH%;%SDK%\Bin\win64;%NANT%\bin + set INCLUDE=%TOOLKIT%\include;%INCLUDE% + set LIB=%TOOLKIT%\lib;%NET%\VC7\lib;%LIB% + + The "win64" directory from the SDK is added to supply executables such as + "cvtres" and "lib", which are not available elsewhere. The versions in the + "win64" directory are 32-bit programs, so they are fine to use here. + + That's it. To build Python (the core only, no binary extensions which + depend on external libraries) you just need to issue the command + + nant -buildfile:python.build all + + from within the PCBuild directory. + +Extension modules + + To build those extension modules which require external libraries + (_tkinter, bz2, _bsddb, _sqlite3, _ssl) you can follow the instructions + for the Visual Studio build above, with a few minor modifications. These + instructions have only been tested using the sources in the Python + subversion repository - building from original sources should work, but + has not been tested. + + For each extension module you wish to build, you should remove the + associated include line from the excludeprojects section of pc.build. + + The changes required are: + + _tkinter + The tix makefile (tix-8.4.0\win\makefile.vc) must be modified to + remove references to TOOLS32. The relevant lines should be changed to + read: + cc32 = cl.exe + link32 = link.exe + include32 = + The remainder of the build instructions will work as given. + + bz2 + No changes are needed + + _bsddb + The file db.build should be copied from the Python PCBuild directory + to the directory db-4.4.20\build_win32. + + The file db_static.vcproj in db-4.4.20\build_win32 should be edited to + remove the string "$(SolutionDir)" - this occurs in 2 places, only + relevant for 64-bit builds. (The edit is required as otherwise, nant + wants to read the solution file, which is not in a suitable form). + + The bsddb library can then be build with the command + nant -buildfile:db.build all + run from the db-4.4.20\build_win32 directory. + + _sqlite3 + No changes are needed. However, in order for the tests to succeed, a + copy of sqlite3.dll must be downloaded, and placed alongside + python.exe. + + _ssl + The documented build process works as written. However, it needs a + copy of the file setargv.obj, which is not supplied in the platform + SDK. However, the sources are available (in the crt source code). To + build setargv.obj, proceed as follows: + + Copy setargv.c, cruntime.h and internal.h from %SDK%\src\crt to a + temporary directory. + Compile using "cl /c /I. /MD /D_CRTBLD setargv.c" + Copy the resulting setargv.obj to somewhere on your LIB environment + (%SDK%\lib is a reasonable place). + + With setargv.obj in place, the standard build process should work + fine. + YOUR OWN EXTENSION DLLs ----------------------- If you want to create your own extension module DLL, there's an example diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 4397d89..00de381 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -98,8 +98,11 @@ module Python version "$Revision$" comprehension = (expr target, expr iter, expr* ifs) -- not sure what to call the first argument for raise and except - - excepthandler = (expr? type, expr? name, stmt* body) + -- TODO(jhylton): Figure out if there is a better way to handle + -- lineno and col_offset fields, particularly when + -- ast is exposed to Python. + excepthandler = (expr? type, expr? name, stmt* body, int lineno, + int col_offset) arguments = (expr* args, identifier? vararg, identifier? kwarg, expr* defaults) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index ad2209d..b6d9830 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -155,8 +155,10 @@ class StructVisitor(EmitVisitor): type = sum.types[i] enum.append("%s_kind=%d" % (type.name, i + 1)) + emit("enum _%(name)s_kind {" + ", ".join(enum) + "};") + emit("struct _%(name)s {") - emit("enum { " + ", ".join(enum) + " } kind;", depth + 1) + emit("enum _%(name)s_kind kind;", depth + 1) emit("union {", depth + 1) for t in sum.types: self.visit(t, depth + 2) @@ -186,7 +188,10 @@ class StructVisitor(EmitVisitor): ctype = get_c_type(field.type) name = field.name if field.seq: - self.emit("asdl_seq *%(name)s;" % locals(), depth) + if field.type.value in ('cmpop',): + self.emit("asdl_int_seq *%(name)s;" % locals(), depth) + else: + self.emit("asdl_seq *%(name)s;" % locals(), depth) else: self.emit("%(ctype)s %(name)s;" % locals(), depth) @@ -232,7 +237,10 @@ class PrototypeVisitor(EmitVisitor): name = f.name # XXX should extend get_c_type() to handle this if f.seq: - ctype = "asdl_seq *" + if f.type.value in ('cmpop',): + ctype = "asdl_int_seq *" + else: + ctype = "asdl_seq *" else: ctype = get_c_type(f.type) args.append((ctype, name, f.opt or f.seq)) @@ -276,7 +284,7 @@ class FunctionVisitor(PrototypeVisitor): emit("%s p;" % ctype, 1) for argtype, argname, opt in args: # XXX hack alert: false is allowed for a bool - if not opt and not argtype == "bool": + if not opt and not (argtype == "bool" or argtype == "int"): emit("if (!%s) {" % argname, 1) emit("PyErr_SetString(PyExc_ValueError,", 2) msg = "field %s is required for %s" % (argname, name) @@ -413,10 +421,10 @@ static PyTypeObject* make_type(char *type, PyTypeObject* base, char**fields, int static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) { - int i; + int i, result; PyObject *s, *l = PyList_New(num_fields); if (!l) return 0; - for(i=0; i < num_fields; i++) { + for(i = 0; i < num_fields; i++) { s = PyString_FromString(attrs[i]); if (!s) { Py_DECREF(l); @@ -424,7 +432,9 @@ static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) } PyList_SET_ITEM(l, i, s); } - return PyObject_SetAttrString((PyObject*)type, "_attributes", l) >=0; + result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0; + Py_DECREF(l); + return result; } static PyObject* ast2obj_list(asdl_seq *seq, PyObject* (*func)(void*)) @@ -465,9 +475,9 @@ static PyObject* ast2obj_int(bool b) } """, 0, reflow=False) - self.emit("static int initialized;", 0) self.emit("static int init_types(void)",0) self.emit("{", 0) + self.emit("static int initialized;", 1) self.emit("if (initialized) return 1;", 1) self.emit('AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0);', 1) for dfn in mod.dfns: @@ -543,7 +553,7 @@ class ASTModuleVisitor(PickleVisitor): self.addObj(cons.name) def addObj(self, name): - self.emit('if(PyDict_SetItemString(d, "%s", (PyObject*)%s_type) < 0) return;' % (name, name), 1) + self.emit('if (PyDict_SetItemString(d, "%s", (PyObject*)%s_type) < 0) return;' % (name, name), 1) _SPECIALIZED_SEQUENCES = ('stmt', 'expr') @@ -677,8 +687,8 @@ class ObjVisitor(PickleVisitor): self.emit("if (!value) goto failed;", depth+1) self.emit("for(i = 0; i < n; i++)", depth+1) # This cannot fail, so no need for error handling - self.emit("PyList_SET_ITEM(value, i, ast2obj_%s((%s_ty)asdl_seq_GET(%s, i)));" % - (field.type, field.type, value), depth+2, reflow=False) + self.emit("PyList_SET_ITEM(value, i, ast2obj_cmpop((cmpop_ty)asdl_seq_GET(%s, i)));" % value, + depth+2, reflow=False) self.emit("}", depth) else: self.emit("value = ast2obj_list(%s, ast2obj_%s);" % (value, field.type), depth) @@ -716,39 +726,35 @@ def main(srcfile): sys.exit(1) if INC_DIR: p = "%s/%s-ast.h" % (INC_DIR, mod.name) - else: - p = "%s-ast.h" % mod.name - f = open(p, "wb") - print >> f, auto_gen_msg - print >> f, '#include "asdl.h"\n' - c = ChainOfVisitors(TypeDefVisitor(f), - StructVisitor(f), - PrototypeVisitor(f), - ) - c.visit(mod) - print >>f, "PyObject* PyAST_mod2obj(mod_ty t);" - f.close() + f = open(p, "wb") + print >> f, auto_gen_msg + print >> f, '#include "asdl.h"\n' + c = ChainOfVisitors(TypeDefVisitor(f), + StructVisitor(f), + PrototypeVisitor(f), + ) + c.visit(mod) + print >>f, "PyObject* PyAST_mod2obj(mod_ty t);" + f.close() if SRC_DIR: - p = "%s/%s-ast.c" % (SRC_DIR, mod.name) - else: - p = "%s-ast.c" % mod.name - f = open(p, "wb") - print >> f, auto_gen_msg - print >> f, '#include "Python.h"' - print >> f, '#include "%s-ast.h"' % mod.name - print >> f - print >>f, "static PyTypeObject* AST_type;" - v = ChainOfVisitors( - PyTypesDeclareVisitor(f), - PyTypesVisitor(f), - FunctionVisitor(f), - ObjVisitor(f), - ASTModuleVisitor(f), - PartingShots(f), - ) - v.visit(mod) - f.close() + p = os.path.join(SRC_DIR, str(mod.name) + "-ast.c") + f = open(p, "wb") + print >> f, auto_gen_msg + print >> f, '#include "Python.h"' + print >> f, '#include "%s-ast.h"' % mod.name + print >> f + print >>f, "static PyTypeObject* AST_type;" + v = ChainOfVisitors( + PyTypesDeclareVisitor(f), + PyTypesVisitor(f), + FunctionVisitor(f), + ObjVisitor(f), + ASTModuleVisitor(f), + PartingShots(f), + ) + v.visit(mod) + f.close() if __name__ == "__main__": import sys @@ -757,6 +763,9 @@ if __name__ == "__main__": INC_DIR = '' SRC_DIR = '' opts, args = getopt.getopt(sys.argv[1:], "h:c:") + if len(opts) != 1: + print "Must specify exactly one output file" + sys.exit(1) for o, v in opts: if o == '-h': INC_DIR = v @@ -764,4 +773,5 @@ if __name__ == "__main__": SRC_DIR = v if len(args) != 1: print "Must specify single input file" + sys.exit(1) main(args[0]) diff --git a/Parser/bitset.c b/Parser/bitset.c index 3834e19..b5543b8 100644 --- a/Parser/bitset.c +++ b/Parser/bitset.c @@ -8,7 +8,7 @@ bitset newbitset(int nbits) { int nbytes = NBYTES(nbits); - bitset ss = PyMem_NEW(BYTE, nbytes); + bitset ss = (char *)PyObject_MALLOC(sizeof(BYTE) * nbytes); if (ss == NULL) Py_FatalError("no mem for bitset"); @@ -22,7 +22,7 @@ newbitset(int nbits) void delbitset(bitset ss) { - PyMem_DEL(ss); + PyObject_FREE(ss); } int diff --git a/Parser/firstsets.c b/Parser/firstsets.c index 0f4e09d..00467b3 100644 --- a/Parser/firstsets.c +++ b/Parser/firstsets.c @@ -59,7 +59,7 @@ calcfirstset(grammar *g, dfa *d) nbits = g->g_ll.ll_nlabels; result = newbitset(nbits); - sym = PyMem_NEW(int, 1); + sym = (int *)PyObject_MALLOC(sizeof(int)); if (sym == NULL) Py_FatalError("no mem for new sym in calcfirstset"); nsyms = 1; @@ -73,7 +73,8 @@ calcfirstset(grammar *g, dfa *d) break; } if (j >= nsyms) { /* New label */ - PyMem_RESIZE(sym, int, nsyms + 1); + sym = (int *)PyObject_REALLOC(sym, + sizeof(int) * (nsyms + 1)); if (sym == NULL) Py_FatalError( "no mem to resize sym in calcfirstset"); @@ -108,5 +109,5 @@ calcfirstset(grammar *g, dfa *d) printf(" }\n"); } - PyMem_FREE(sym); + PyObject_FREE(sym); } diff --git a/Parser/grammar.c b/Parser/grammar.c index d8e3897..b0dafe7 100644 --- a/Parser/grammar.c +++ b/Parser/grammar.c @@ -20,7 +20,7 @@ newgrammar(int start) { grammar *g; - g = PyMem_NEW(grammar, 1); + g = (grammar *)PyObject_MALLOC(sizeof(grammar)); if (g == NULL) Py_FatalError("no mem for new grammar"); g->g_ndfas = 0; @@ -37,7 +37,8 @@ adddfa(grammar *g, int type, char *name) { dfa *d; - PyMem_RESIZE(g->g_dfa, dfa, g->g_ndfas + 1); + g->g_dfa = (dfa *)PyObject_REALLOC(g->g_dfa, + sizeof(dfa) * (g->g_ndfas + 1)); if (g->g_dfa == NULL) Py_FatalError("no mem to resize dfa in adddfa"); d = &g->g_dfa[g->g_ndfas++]; @@ -55,7 +56,8 @@ addstate(dfa *d) { state *s; - PyMem_RESIZE(d->d_state, state, d->d_nstates + 1); + d->d_state = (state *)PyObject_REALLOC(d->d_state, + sizeof(state) * (d->d_nstates + 1)); if (d->d_state == NULL) Py_FatalError("no mem to resize state in addstate"); s = &d->d_state[d->d_nstates++]; @@ -78,7 +80,7 @@ addarc(dfa *d, int from, int to, int lbl) assert(0 <= to && to < d->d_nstates); s = &d->d_state[from]; - PyMem_RESIZE(s->s_arc, arc, s->s_narcs + 1); + s->s_arc = (arc *)PyObject_REALLOC(s->s_arc, sizeof(arc) * (s->s_narcs + 1)); if (s->s_arc == NULL) Py_FatalError("no mem to resize arc list in addarc"); a = &s->s_arc[s->s_narcs++]; @@ -97,7 +99,8 @@ addlabel(labellist *ll, int type, char *str) strcmp(ll->ll_label[i].lb_str, str) == 0) return i; } - PyMem_RESIZE(ll->ll_label, label, ll->ll_nlabels + 1); + ll->ll_label = (label *)PyObject_REALLOC(ll->ll_label, + sizeof(label) * (ll->ll_nlabels + 1)); if (ll->ll_label == NULL) Py_FatalError("no mem to resize labellist in addlabel"); lb = &ll->ll_label[ll->ll_nlabels++]; @@ -195,7 +198,7 @@ translabel(grammar *g, label *lb) name_len = p - src; else name_len = strlen(src); - dest = malloc(name_len + 1); + dest = (char *)malloc(name_len + 1); strncpy(dest, src, name_len); dest[name_len] = '\0'; free(lb->lb_str); diff --git a/Parser/myreadline.c b/Parser/myreadline.c index a932a87..32a1088 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -111,7 +111,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) size_t n; char *p; n = 100; - if ((p = PyMem_MALLOC(n)) == NULL) + if ((p = (char *)PyMem_MALLOC(n)) == NULL) return NULL; fflush(sys_stdout); #ifndef RISCOS @@ -141,7 +141,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) n = strlen(p); while (n > 0 && p[n-1] != '\n') { size_t incr = n+2; - p = PyMem_REALLOC(p, n + incr); + p = (char *)PyMem_REALLOC(p, n + incr); if (p == NULL) return NULL; if (incr > INT_MAX) { @@ -151,7 +151,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) break; n += strlen(p+n); } - return PyMem_REALLOC(p, n+1); + return (char *)PyMem_REALLOC(p, n+1); } diff --git a/Parser/node.c b/Parser/node.c index 7ed6c0e..97f887a 100644 --- a/Parser/node.c +++ b/Parser/node.c @@ -62,7 +62,7 @@ fancy_roundup(int n) * Win98). * * In a run of compileall across the 2.3a0 Lib directory, Andrew MacIntyre - * reported that, with this scheme, 89% of PyMem_RESIZE calls in + * reported that, with this scheme, 89% of PyObject_REALLOC calls in * PyNode_AddChild passed 1 for the size, and 9% passed 4. So this usually * wastes very little memory, but is very effective at sidestepping * platform-realloc disasters on vulnernable platforms. diff --git a/Parser/parser.c b/Parser/parser.c index 213410c..45302ed 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -75,7 +75,7 @@ PyParser_New(grammar *g, int start) if (!g->g_accel) PyGrammar_AddAccelerators(g); - ps = PyMem_NEW(parser_state, 1); + ps = (parser_state *)PyMem_MALLOC(sizeof(parser_state)); if (ps == NULL) return NULL; ps->p_grammar = g; @@ -84,7 +84,7 @@ PyParser_New(grammar *g, int start) #endif ps->p_tree = PyNode_New(start); if (ps->p_tree == NULL) { - PyMem_DEL(ps); + PyMem_FREE(ps); return NULL; } s_reset(&ps->p_stack); @@ -98,7 +98,7 @@ PyParser_Delete(parser_state *ps) /* NB If you want to save the parse tree, you must set p_tree to NULL before calling delparser! */ PyNode_Free(ps->p_tree); - PyMem_DEL(ps); + PyMem_FREE(ps); } diff --git a/Parser/pgen.c b/Parser/pgen.c index e643d33..dfe7cac 100644 --- a/Parser/pgen.c +++ b/Parser/pgen.c @@ -49,7 +49,8 @@ addnfastate(nfa *nf) { nfastate *st; - PyMem_RESIZE(nf->nf_state, nfastate, nf->nf_nstates + 1); + nf->nf_state = (nfastate *)PyObject_REALLOC(nf->nf_state, + sizeof(nfastate) * (nf->nf_nstates + 1)); if (nf->nf_state == NULL) Py_FatalError("out of mem"); st = &nf->nf_state[nf->nf_nstates++]; @@ -65,7 +66,8 @@ addnfaarc(nfa *nf, int from, int to, int lbl) nfaarc *ar; st = &nf->nf_state[from]; - PyMem_RESIZE(st->st_arc, nfaarc, st->st_narcs + 1); + st->st_arc = (nfaarc *)PyObject_REALLOC(st->st_arc, + sizeof(nfaarc) * (st->st_narcs + 1)); if (st->st_arc == NULL) Py_FatalError("out of mem"); ar = &st->st_arc[st->st_narcs++]; @@ -79,7 +81,7 @@ newnfa(char *name) nfa *nf; static int type = NT_OFFSET; /* All types will be disjunct */ - nf = PyMem_NEW(nfa, 1); + nf = (nfa *)PyObject_MALLOC(sizeof(nfa)); if (nf == NULL) Py_FatalError("no mem for new nfa"); nf->nf_type = type++; @@ -104,7 +106,7 @@ newnfagrammar(void) { nfagrammar *gr; - gr = PyMem_NEW(nfagrammar, 1); + gr = (nfagrammar *)PyObject_MALLOC(sizeof(nfagrammar)); if (gr == NULL) Py_FatalError("no mem for new nfa grammar"); gr->gr_nnfas = 0; @@ -121,7 +123,8 @@ addnfa(nfagrammar *gr, char *name) nfa *nf; nf = newnfa(name); - PyMem_RESIZE(gr->gr_nfa, nfa *, gr->gr_nnfas + 1); + gr->gr_nfa = (nfa **)PyObject_REALLOC(gr->gr_nfa, + sizeof(nfa) * (gr->gr_nnfas + 1)); if (gr->gr_nfa == NULL) Py_FatalError("out of mem"); gr->gr_nfa[gr->gr_nnfas++] = nf; @@ -361,7 +364,7 @@ typedef struct _ss_arc { typedef struct _ss_state { bitset ss_ss; int ss_narcs; - ss_arc *ss_arc; + struct _ss_arc *ss_arc; int ss_deleted; int ss_finish; int ss_rename; @@ -392,7 +395,7 @@ makedfa(nfagrammar *gr, nfa *nf, dfa *d) ss = newbitset(nbits); addclosure(ss, nf, nf->nf_start); - xx_state = PyMem_NEW(ss_state, 1); + xx_state = (ss_state *)PyObject_MALLOC(sizeof(ss_state)); if (xx_state == NULL) Py_FatalError("no mem for xx_state in makedfa"); xx_nstates = 1; @@ -411,6 +414,7 @@ makedfa(nfagrammar *gr, nfa *nf, dfa *d) /* For each unmarked state... */ for (istate = 0; istate < xx_nstates; ++istate) { + size_t size; yy = &xx_state[istate]; ss = yy->ss_ss; /* For all its states... */ @@ -430,8 +434,9 @@ makedfa(nfagrammar *gr, nfa *nf, dfa *d) goto found; } /* Add new arc for this state */ - PyMem_RESIZE(yy->ss_arc, ss_arc, - yy->ss_narcs + 1); + size = sizeof(ss_arc) * (yy->ss_narcs + 1); + yy->ss_arc = (ss_arc *)PyObject_REALLOC( + yy->ss_arc, size); if (yy->ss_arc == NULL) Py_FatalError("out of mem"); zz = &yy->ss_arc[yy->ss_narcs++]; @@ -453,7 +458,9 @@ makedfa(nfagrammar *gr, nfa *nf, dfa *d) goto done; } } - PyMem_RESIZE(xx_state, ss_state, xx_nstates + 1); + size = sizeof(ss_state) * (xx_nstates + 1); + xx_state = (ss_state *)PyObject_REALLOC(xx_state, + size); if (xx_state == NULL) Py_FatalError("out of mem"); zz->sa_arrow = xx_nstates; diff --git a/Parser/pgenmain.c b/Parser/pgenmain.c index 695e2b7..fc27a2c 100644 --- a/Parser/pgenmain.c +++ b/Parser/pgenmain.c @@ -104,7 +104,7 @@ getgrammar(char *filename) putc(' ', stderr); } fprintf(stderr, "^\n"); - PyMem_DEL(err.text); + PyObject_FREE(err.text); } Py_Exit(1); } @@ -136,7 +136,7 @@ char * PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) { size_t n = 1000; - char *p = PyMem_MALLOC(n); + char *p = (char *)PyMem_MALLOC(n); char *q; if (p == NULL) return NULL; @@ -149,7 +149,7 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) n = strlen(p); if (n > 0 && p[n-1] != '\n') p[n-1] = '\n'; - return PyMem_REALLOC(p, n+1); + return (char *)PyMem_REALLOC(p, n+1); } /* No-nonsense fgets */ diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 0631ca3..d9dcc41 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -105,7 +105,8 @@ char *_PyParser_TokenNames[] = { static struct tok_state * tok_new(void) { - struct tok_state *tok = PyMem_NEW(struct tok_state, 1); + struct tok_state *tok = (struct tok_state *)PyMem_MALLOC( + sizeof(struct tok_state)); if (tok == NULL) return NULL; tok->buf = tok->cur = tok->end = tok->inp = tok->start = NULL; @@ -163,7 +164,7 @@ error_ret(struct tok_state *tok) /* XXX */ { tok->decoding_erred = 1; if (tok->fp != NULL && tok->buf != NULL) /* see PyTokenizer_Free */ - PyMem_DEL(tok->buf); + PyMem_FREE(tok->buf); tok->buf = NULL; return NULL; /* as if it were EOF */ } @@ -171,7 +172,7 @@ error_ret(struct tok_state *tok) /* XXX */ static char * new_string(const char *s, Py_ssize_t len) { - char* result = PyMem_NEW(char, len + 1); + char* result = (char *)PyMem_MALLOC(len + 1); if (result != NULL) { memcpy(result, s, len); result[len] = '\0'; @@ -236,7 +237,7 @@ get_coding_spec(const char *s, Py_ssize_t size) char* r = new_string(begin, t - begin); char* q = get_normal_name(r); if (r != q) { - PyMem_DEL(r); + PyMem_FREE(r); r = new_string(q, strlen(q)); } return r; @@ -277,18 +278,18 @@ check_coding_spec(const char* line, Py_ssize_t size, struct tok_state *tok, tok->decoding_state = -1; } else - PyMem_DEL(cs); + PyMem_FREE(cs); #else /* Without Unicode support, we cannot process the coding spec. Since there won't be any Unicode literals, that won't matter. */ - PyMem_DEL(cs); + PyMem_FREE(cs); #endif } } else { /* then, compare cs with BOM */ r = (strcmp(tok->encoding, cs) == 0); - PyMem_DEL(cs); + PyMem_FREE(cs); } } if (!r) { @@ -334,7 +335,7 @@ check_bom(int get_char(struct tok_state *), return 1; } if (tok->encoding != NULL) - PyMem_DEL(tok->encoding); + PyMem_FREE(tok->encoding); tok->encoding = new_string("utf-8", 5); /* resulting is in utf-8 */ return 1; NON_BOM: @@ -345,7 +346,7 @@ check_bom(int get_char(struct tok_state *), /* Read a line of text from TOK into S, using the stream in TOK. Return NULL on failure, else S. - + On entry, tok->decoding_buffer will be one of: 1) NULL: need to call tok->decoding_readline to get a new line 2) PyUnicodeObject *: decoding_feof has called tok->decoding_readline and @@ -354,7 +355,7 @@ check_bom(int get_char(struct tok_state *), (in the s buffer) to copy entire contents of the line read by tok->decoding_readline. tok->decoding_buffer has the overflow. In this case, fp_readl is called in a loop (with an expanded buffer) - until the buffer ends with a '\n' (or until the end of the file is + until the buffer ends with a '\n' (or until the end of the file is reached): see tok_nextc and its calls to decoding_fgets. */ @@ -470,7 +471,7 @@ decoding_fgets(char *s, int size, struct tok_state *tok) break; } else if (tok->decoding_state > 0) { /* We want a 'raw' read. */ - line = Py_UniversalNewlineFgets(s, size, + line = Py_UniversalNewlineFgets(s, size, tok->fp, NULL); break; } else { @@ -502,11 +503,11 @@ decoding_fgets(char *s, int size, struct tok_state *tok) char buf[500]; /* Need to add 1 to the line number, since this line has not been counted, yet. */ - sprintf(buf, + sprintf(buf, "Non-ASCII character '\\x%.2x' " "in file %.200s on line %i, " "but no encoding declared; " - "see http://www.python.org/peps/pep-0263.html for details", + "see http://www.python.org/peps/pep-0263.html for details", badchar, tok->filename, tok->lineno + 1); PyErr_SetString(PyExc_SyntaxError, buf); return error_ret(tok); @@ -537,13 +538,15 @@ decoding_feof(struct tok_state *tok) /* Fetch a byte from TOK, using the string buffer. */ -static int buf_getc(struct tok_state *tok) { +static int +buf_getc(struct tok_state *tok) { return Py_CHARMASK(*tok->str++); } /* Unfetch a byte from TOK, using the string buffer. */ -static void buf_ungetc(int c, struct tok_state *tok) { +static void +buf_ungetc(int c, struct tok_state *tok) { tok->str--; assert(Py_CHARMASK(*tok->str) == c); /* tok->cur may point to read-only segment */ } @@ -551,7 +554,8 @@ static void buf_ungetc(int c, struct tok_state *tok) { /* Set the readline function for TOK to ENC. For the string-based tokenizer, this means to just record the encoding. */ -static int buf_setreadl(struct tok_state *tok, const char* enc) { +static int +buf_setreadl(struct tok_state *tok, const char* enc) { tok->enc = enc; return 1; } @@ -653,7 +657,7 @@ PyTokenizer_FromFile(FILE *fp, char *ps1, char *ps2) struct tok_state *tok = tok_new(); if (tok == NULL) return NULL; - if ((tok->buf = PyMem_NEW(char, BUFSIZ)) == NULL) { + if ((tok->buf = (char *)PyMem_MALLOC(BUFSIZ)) == NULL) { PyTokenizer_Free(tok); return NULL; } @@ -672,14 +676,14 @@ void PyTokenizer_Free(struct tok_state *tok) { if (tok->encoding != NULL) - PyMem_DEL(tok->encoding); + PyMem_FREE(tok->encoding); #ifndef PGEN Py_XDECREF(tok->decoding_readline); Py_XDECREF(tok->decoding_buffer); #endif if (tok->fp != NULL && tok->buf != NULL) - PyMem_DEL(tok->buf); - PyMem_DEL(tok); + PyMem_FREE(tok->buf); + PyMem_FREE(tok); } #if !defined(PGEN) && defined(Py_USING_UNICODE) @@ -711,7 +715,9 @@ tok_stdin_decode(struct tok_state *tok, char **inp) if (utf8 == NULL) goto error_clear; - converted = new_string(PyString_AsString(utf8), PyString_Size(utf8)); + assert(PyString_Check(utf8)); + converted = new_string(PyString_AS_STRING(utf8), + PyString_GET_SIZE(utf8)); Py_DECREF(utf8); if (converted == NULL) goto error_nomem; @@ -719,7 +725,7 @@ tok_stdin_decode(struct tok_state *tok, char **inp) PyMem_FREE(*inp); *inp = converted; if (tok->encoding != NULL) - PyMem_DEL(tok->encoding); + PyMem_FREE(tok->encoding); tok->encoding = new_string(encoding, strlen(encoding)); if (tok->encoding == NULL) goto error_nomem; @@ -770,38 +776,38 @@ tok_nextc(register struct tok_state *tok) return Py_CHARMASK(*tok->cur++); } if (tok->prompt != NULL) { - char *new = PyOS_Readline(stdin, stdout, tok->prompt); + char *newtok = PyOS_Readline(stdin, stdout, tok->prompt); if (tok->nextprompt != NULL) tok->prompt = tok->nextprompt; - if (new == NULL) + if (newtok == NULL) tok->done = E_INTR; - else if (*new == '\0') { - PyMem_FREE(new); + else if (*newtok == '\0') { + PyMem_FREE(newtok); tok->done = E_EOF; } #if !defined(PGEN) && defined(Py_USING_UNICODE) - else if (tok_stdin_decode(tok, &new) != 0) - PyMem_FREE(new); + else if (tok_stdin_decode(tok, &newtok) != 0) + PyMem_FREE(newtok); #endif else if (tok->start != NULL) { size_t start = tok->start - tok->buf; size_t oldlen = tok->cur - tok->buf; - size_t newlen = oldlen + strlen(new); + size_t newlen = oldlen + strlen(newtok); char *buf = tok->buf; - PyMem_RESIZE(buf, char, newlen+1); + buf = (char *)PyMem_REALLOC(buf, newlen+1); tok->lineno++; if (buf == NULL) { - PyMem_DEL(tok->buf); + PyMem_FREE(tok->buf); tok->buf = NULL; - PyMem_FREE(new); + PyMem_FREE(newtok); tok->done = E_NOMEM; return EOF; } tok->buf = buf; tok->cur = tok->buf + oldlen; tok->line_start = tok->cur; - strcpy(tok->buf + oldlen, new); - PyMem_FREE(new); + strcpy(tok->buf + oldlen, newtok); + PyMem_FREE(newtok); tok->inp = tok->buf + newlen; tok->end = tok->inp + 1; tok->start = tok->buf + start; @@ -809,8 +815,8 @@ tok_nextc(register struct tok_state *tok) else { tok->lineno++; if (tok->buf != NULL) - PyMem_DEL(tok->buf); - tok->buf = new; + PyMem_FREE(tok->buf); + tok->buf = newtok; tok->line_start = tok->buf; tok->cur = tok->buf; tok->line_start = tok->buf; @@ -824,7 +830,8 @@ tok_nextc(register struct tok_state *tok) char *pt; if (tok->start == NULL) { if (tok->buf == NULL) { - tok->buf = PyMem_NEW(char, BUFSIZ); + tok->buf = (char *) + PyMem_MALLOC(BUFSIZ); if (tok->buf == NULL) { tok->done = E_NOMEM; return EOF; @@ -859,7 +866,8 @@ tok_nextc(register struct tok_state *tok) Py_ssize_t curvalid = tok->inp - tok->buf; Py_ssize_t newsize = curvalid + BUFSIZ; char *newbuf = tok->buf; - PyMem_RESIZE(newbuf, char, newsize); + newbuf = (char *)PyMem_REALLOC(newbuf, + newsize); if (newbuf == NULL) { tok->done = E_NOMEM; tok->cur = tok->inp; @@ -1182,9 +1190,9 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) } } } - + tok->start = tok->cur; - + /* Return pending indents/dedents */ if (tok->pendin != 0) { if (tok->pendin < 0) { @@ -1196,27 +1204,27 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) return INDENT; } } - + again: tok->start = NULL; /* Skip spaces */ do { c = tok_nextc(tok); } while (c == ' ' || c == '\t' || c == '\014'); - + /* Set start of current token */ tok->start = tok->cur - 1; - + /* Skip comment */ if (c == '#') while (c != EOF && c != '\n') c = tok_nextc(tok); - + /* Check for EOF and errors now */ if (c == EOF) { return tok->done == E_EOF ? ENDMARKER : ERRORTOKEN; } - + /* Identifier (most frequent token!) */ if (isalpha(c) || c == '_') { /* Process r"", u"" and ur"" */ @@ -1244,7 +1252,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) *p_end = tok->cur; return NAME; } - + /* Newline */ if (c == '\n') { tok->atbol = 1; @@ -1255,7 +1263,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) tok->cont_line = 0; return NEWLINE; } - + /* Period or number starting with period? */ if (c == '.') { c = tok_nextc(tok); @@ -1418,7 +1426,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) *p_end = tok->cur; return STRING; } - + /* Line continuation */ if (c == '\\') { c = tok_nextc(tok); @@ -1430,7 +1438,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) tok->cont_line = 1; goto again; /* Read next line */ } - + /* Check for two-character token */ { int c2 = tok_nextc(tok); @@ -1449,7 +1457,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) } tok_backup(tok, c2); } - + /* Keep track of parentheses nesting level */ switch (c) { case '(': @@ -1463,7 +1471,7 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) tok->level--; break; } - + /* Punctuation character */ *p_start = tok->start; *p_end = tok->cur; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 3f8345e..249f70e 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -331,6 +331,8 @@ static char *excepthandler_fields[]={ "type", "name", "body", + "lineno", + "col_offset", }; static PyTypeObject *arguments_type; static PyObject* ast2obj_arguments(void*); @@ -381,10 +383,10 @@ static PyTypeObject* make_type(char *type, PyTypeObject* base, char**fields, int static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) { - int i; + int i, result; PyObject *s, *l = PyList_New(num_fields); if (!l) return 0; - for(i=0; i < num_fields; i++) { + for(i = 0; i < num_fields; i++) { s = PyString_FromString(attrs[i]); if (!s) { Py_DECREF(l); @@ -392,7 +394,9 @@ static int add_attributes(PyTypeObject* type, char**attrs, int num_fields) } PyList_SET_ITEM(l, i, s); } - return PyObject_SetAttrString((PyObject*)type, "_attributes", l) >=0; + result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0; + Py_DECREF(l); + return result; } static PyObject* ast2obj_list(asdl_seq *seq, PyObject* (*func)(void*)) @@ -432,9 +436,9 @@ static PyObject* ast2obj_int(bool b) return PyInt_FromLong(b); } -static int initialized; static int init_types(void) { + static int initialized; if (initialized) return 1; AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0); mod_type = make_type("mod", AST_type, NULL, 0); @@ -710,7 +714,7 @@ static int init_types(void) comprehension_fields, 3); if (!comprehension_type) return 0; excepthandler_type = make_type("excepthandler", AST_type, - excepthandler_fields, 3); + excepthandler_fields, 5); if (!excepthandler_type) return 0; arguments_type = make_type("arguments", AST_type, arguments_fields, 4); if (!arguments_type) return 0; @@ -1499,8 +1503,8 @@ Yield(expr_ty value, int lineno, int col_offset, PyArena *arena) } expr_ty -Compare(expr_ty left, asdl_seq * ops, asdl_seq * comparators, int lineno, int - col_offset, PyArena *arena) +Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators, int lineno, + int col_offset, PyArena *arena) { expr_ty p; if (!left) { @@ -1841,7 +1845,8 @@ comprehension(expr_ty target, expr_ty iter, asdl_seq * ifs, PyArena *arena) } excepthandler_ty -excepthandler(expr_ty type, expr_ty name, asdl_seq * body, PyArena *arena) +excepthandler(expr_ty type, expr_ty name, asdl_seq * body, int lineno, int + col_offset, PyArena *arena) { excepthandler_ty p; p = (excepthandler_ty)PyArena_Malloc(arena, sizeof(*p)); @@ -1852,6 +1857,8 @@ excepthandler(expr_ty type, expr_ty name, asdl_seq * body, PyArena *arena) p->type = type; p->name = name; p->body = body; + p->lineno = lineno; + p->col_offset = col_offset; return p; } @@ -2915,6 +2922,16 @@ ast2obj_excepthandler(void* _o) if (PyObject_SetAttrString(result, "body", value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_int(o->lineno); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "lineno", value) == -1) + goto failed; + Py_DECREF(value); + value = ast2obj_int(o->col_offset); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "col_offset", value) == -1) + goto failed; + Py_DECREF(value); return result; failed: Py_XDECREF(value); @@ -3033,146 +3050,146 @@ init_ast(void) return; if (PyModule_AddStringConstant(m, "__version__", "42753") < 0) return; - if(PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; - if(PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) - return; - if(PyDict_SetItemString(d, "Interactive", (PyObject*)Interactive_type) - < 0) return; - if(PyDict_SetItemString(d, "Expression", (PyObject*)Expression_type) < - 0) return; - if(PyDict_SetItemString(d, "Suite", (PyObject*)Suite_type) < 0) return; - if(PyDict_SetItemString(d, "stmt", (PyObject*)stmt_type) < 0) return; - if(PyDict_SetItemString(d, "FunctionDef", (PyObject*)FunctionDef_type) - < 0) return; - if(PyDict_SetItemString(d, "ClassDef", (PyObject*)ClassDef_type) < 0) - return; - if(PyDict_SetItemString(d, "Return", (PyObject*)Return_type) < 0) - return; - if(PyDict_SetItemString(d, "Delete", (PyObject*)Delete_type) < 0) - return; - if(PyDict_SetItemString(d, "Assign", (PyObject*)Assign_type) < 0) - return; - if(PyDict_SetItemString(d, "AugAssign", (PyObject*)AugAssign_type) < 0) - return; - if(PyDict_SetItemString(d, "Print", (PyObject*)Print_type) < 0) return; - if(PyDict_SetItemString(d, "For", (PyObject*)For_type) < 0) return; - if(PyDict_SetItemString(d, "While", (PyObject*)While_type) < 0) return; - if(PyDict_SetItemString(d, "If", (PyObject*)If_type) < 0) return; - if(PyDict_SetItemString(d, "With", (PyObject*)With_type) < 0) return; - if(PyDict_SetItemString(d, "Raise", (PyObject*)Raise_type) < 0) return; - if(PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < 0) - return; - if(PyDict_SetItemString(d, "TryFinally", (PyObject*)TryFinally_type) < - 0) return; - if(PyDict_SetItemString(d, "Assert", (PyObject*)Assert_type) < 0) - return; - if(PyDict_SetItemString(d, "Import", (PyObject*)Import_type) < 0) - return; - if(PyDict_SetItemString(d, "ImportFrom", (PyObject*)ImportFrom_type) < - 0) return; - if(PyDict_SetItemString(d, "Exec", (PyObject*)Exec_type) < 0) return; - if(PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0) - return; - if(PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return; - if(PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return; - if(PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return; - if(PyDict_SetItemString(d, "Continue", (PyObject*)Continue_type) < 0) - return; - if(PyDict_SetItemString(d, "expr", (PyObject*)expr_type) < 0) return; - if(PyDict_SetItemString(d, "BoolOp", (PyObject*)BoolOp_type) < 0) - return; - if(PyDict_SetItemString(d, "BinOp", (PyObject*)BinOp_type) < 0) return; - if(PyDict_SetItemString(d, "UnaryOp", (PyObject*)UnaryOp_type) < 0) - return; - if(PyDict_SetItemString(d, "Lambda", (PyObject*)Lambda_type) < 0) - return; - if(PyDict_SetItemString(d, "IfExp", (PyObject*)IfExp_type) < 0) return; - if(PyDict_SetItemString(d, "Dict", (PyObject*)Dict_type) < 0) return; - if(PyDict_SetItemString(d, "ListComp", (PyObject*)ListComp_type) < 0) - return; - if(PyDict_SetItemString(d, "GeneratorExp", - (PyObject*)GeneratorExp_type) < 0) return; - if(PyDict_SetItemString(d, "Yield", (PyObject*)Yield_type) < 0) return; - if(PyDict_SetItemString(d, "Compare", (PyObject*)Compare_type) < 0) - return; - if(PyDict_SetItemString(d, "Call", (PyObject*)Call_type) < 0) return; - if(PyDict_SetItemString(d, "Repr", (PyObject*)Repr_type) < 0) return; - if(PyDict_SetItemString(d, "Num", (PyObject*)Num_type) < 0) return; - if(PyDict_SetItemString(d, "Str", (PyObject*)Str_type) < 0) return; - if(PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < 0) - return; - if(PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < 0) - return; - if(PyDict_SetItemString(d, "Name", (PyObject*)Name_type) < 0) return; - if(PyDict_SetItemString(d, "List", (PyObject*)List_type) < 0) return; - if(PyDict_SetItemString(d, "Tuple", (PyObject*)Tuple_type) < 0) return; - if(PyDict_SetItemString(d, "expr_context", - (PyObject*)expr_context_type) < 0) return; - if(PyDict_SetItemString(d, "Load", (PyObject*)Load_type) < 0) return; - if(PyDict_SetItemString(d, "Store", (PyObject*)Store_type) < 0) return; - if(PyDict_SetItemString(d, "Del", (PyObject*)Del_type) < 0) return; - if(PyDict_SetItemString(d, "AugLoad", (PyObject*)AugLoad_type) < 0) - return; - if(PyDict_SetItemString(d, "AugStore", (PyObject*)AugStore_type) < 0) - return; - if(PyDict_SetItemString(d, "Param", (PyObject*)Param_type) < 0) return; - if(PyDict_SetItemString(d, "slice", (PyObject*)slice_type) < 0) return; - if(PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0) - return; - if(PyDict_SetItemString(d, "Slice", (PyObject*)Slice_type) < 0) return; - if(PyDict_SetItemString(d, "ExtSlice", (PyObject*)ExtSlice_type) < 0) - return; - if(PyDict_SetItemString(d, "Index", (PyObject*)Index_type) < 0) return; - if(PyDict_SetItemString(d, "boolop", (PyObject*)boolop_type) < 0) - return; - if(PyDict_SetItemString(d, "And", (PyObject*)And_type) < 0) return; - if(PyDict_SetItemString(d, "Or", (PyObject*)Or_type) < 0) return; - if(PyDict_SetItemString(d, "operator", (PyObject*)operator_type) < 0) - return; - if(PyDict_SetItemString(d, "Add", (PyObject*)Add_type) < 0) return; - if(PyDict_SetItemString(d, "Sub", (PyObject*)Sub_type) < 0) return; - if(PyDict_SetItemString(d, "Mult", (PyObject*)Mult_type) < 0) return; - if(PyDict_SetItemString(d, "Div", (PyObject*)Div_type) < 0) return; - if(PyDict_SetItemString(d, "Mod", (PyObject*)Mod_type) < 0) return; - if(PyDict_SetItemString(d, "Pow", (PyObject*)Pow_type) < 0) return; - if(PyDict_SetItemString(d, "LShift", (PyObject*)LShift_type) < 0) - return; - if(PyDict_SetItemString(d, "RShift", (PyObject*)RShift_type) < 0) - return; - if(PyDict_SetItemString(d, "BitOr", (PyObject*)BitOr_type) < 0) return; - if(PyDict_SetItemString(d, "BitXor", (PyObject*)BitXor_type) < 0) - return; - if(PyDict_SetItemString(d, "BitAnd", (PyObject*)BitAnd_type) < 0) - return; - if(PyDict_SetItemString(d, "FloorDiv", (PyObject*)FloorDiv_type) < 0) - return; - if(PyDict_SetItemString(d, "unaryop", (PyObject*)unaryop_type) < 0) - return; - if(PyDict_SetItemString(d, "Invert", (PyObject*)Invert_type) < 0) - return; - if(PyDict_SetItemString(d, "Not", (PyObject*)Not_type) < 0) return; - if(PyDict_SetItemString(d, "UAdd", (PyObject*)UAdd_type) < 0) return; - if(PyDict_SetItemString(d, "USub", (PyObject*)USub_type) < 0) return; - if(PyDict_SetItemString(d, "cmpop", (PyObject*)cmpop_type) < 0) return; - if(PyDict_SetItemString(d, "Eq", (PyObject*)Eq_type) < 0) return; - if(PyDict_SetItemString(d, "NotEq", (PyObject*)NotEq_type) < 0) return; - if(PyDict_SetItemString(d, "Lt", (PyObject*)Lt_type) < 0) return; - if(PyDict_SetItemString(d, "LtE", (PyObject*)LtE_type) < 0) return; - if(PyDict_SetItemString(d, "Gt", (PyObject*)Gt_type) < 0) return; - if(PyDict_SetItemString(d, "GtE", (PyObject*)GtE_type) < 0) return; - if(PyDict_SetItemString(d, "Is", (PyObject*)Is_type) < 0) return; - if(PyDict_SetItemString(d, "IsNot", (PyObject*)IsNot_type) < 0) return; - if(PyDict_SetItemString(d, "In", (PyObject*)In_type) < 0) return; - if(PyDict_SetItemString(d, "NotIn", (PyObject*)NotIn_type) < 0) return; - if(PyDict_SetItemString(d, "comprehension", - (PyObject*)comprehension_type) < 0) return; - if(PyDict_SetItemString(d, "excepthandler", - (PyObject*)excepthandler_type) < 0) return; - if(PyDict_SetItemString(d, "arguments", (PyObject*)arguments_type) < 0) - return; - if(PyDict_SetItemString(d, "keyword", (PyObject*)keyword_type) < 0) - return; - if(PyDict_SetItemString(d, "alias", (PyObject*)alias_type) < 0) return; + if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; + if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) + return; + if (PyDict_SetItemString(d, "Interactive", (PyObject*)Interactive_type) + < 0) return; + if (PyDict_SetItemString(d, "Expression", (PyObject*)Expression_type) < + 0) return; + if (PyDict_SetItemString(d, "Suite", (PyObject*)Suite_type) < 0) return; + if (PyDict_SetItemString(d, "stmt", (PyObject*)stmt_type) < 0) return; + if (PyDict_SetItemString(d, "FunctionDef", (PyObject*)FunctionDef_type) + < 0) return; + if (PyDict_SetItemString(d, "ClassDef", (PyObject*)ClassDef_type) < 0) + return; + if (PyDict_SetItemString(d, "Return", (PyObject*)Return_type) < 0) + return; + if (PyDict_SetItemString(d, "Delete", (PyObject*)Delete_type) < 0) + return; + if (PyDict_SetItemString(d, "Assign", (PyObject*)Assign_type) < 0) + return; + if (PyDict_SetItemString(d, "AugAssign", (PyObject*)AugAssign_type) < + 0) return; + if (PyDict_SetItemString(d, "Print", (PyObject*)Print_type) < 0) return; + if (PyDict_SetItemString(d, "For", (PyObject*)For_type) < 0) return; + if (PyDict_SetItemString(d, "While", (PyObject*)While_type) < 0) return; + if (PyDict_SetItemString(d, "If", (PyObject*)If_type) < 0) return; + if (PyDict_SetItemString(d, "With", (PyObject*)With_type) < 0) return; + if (PyDict_SetItemString(d, "Raise", (PyObject*)Raise_type) < 0) return; + if (PyDict_SetItemString(d, "TryExcept", (PyObject*)TryExcept_type) < + 0) return; + if (PyDict_SetItemString(d, "TryFinally", (PyObject*)TryFinally_type) < + 0) return; + if (PyDict_SetItemString(d, "Assert", (PyObject*)Assert_type) < 0) + return; + if (PyDict_SetItemString(d, "Import", (PyObject*)Import_type) < 0) + return; + if (PyDict_SetItemString(d, "ImportFrom", (PyObject*)ImportFrom_type) < + 0) return; + if (PyDict_SetItemString(d, "Exec", (PyObject*)Exec_type) < 0) return; + if (PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0) + return; + if (PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return; + if (PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return; + if (PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return; + if (PyDict_SetItemString(d, "Continue", (PyObject*)Continue_type) < 0) + return; + if (PyDict_SetItemString(d, "expr", (PyObject*)expr_type) < 0) return; + if (PyDict_SetItemString(d, "BoolOp", (PyObject*)BoolOp_type) < 0) + return; + if (PyDict_SetItemString(d, "BinOp", (PyObject*)BinOp_type) < 0) return; + if (PyDict_SetItemString(d, "UnaryOp", (PyObject*)UnaryOp_type) < 0) + return; + if (PyDict_SetItemString(d, "Lambda", (PyObject*)Lambda_type) < 0) + return; + if (PyDict_SetItemString(d, "IfExp", (PyObject*)IfExp_type) < 0) return; + if (PyDict_SetItemString(d, "Dict", (PyObject*)Dict_type) < 0) return; + if (PyDict_SetItemString(d, "ListComp", (PyObject*)ListComp_type) < 0) + return; + if (PyDict_SetItemString(d, "GeneratorExp", + (PyObject*)GeneratorExp_type) < 0) return; + if (PyDict_SetItemString(d, "Yield", (PyObject*)Yield_type) < 0) return; + if (PyDict_SetItemString(d, "Compare", (PyObject*)Compare_type) < 0) + return; + if (PyDict_SetItemString(d, "Call", (PyObject*)Call_type) < 0) return; + if (PyDict_SetItemString(d, "Repr", (PyObject*)Repr_type) < 0) return; + if (PyDict_SetItemString(d, "Num", (PyObject*)Num_type) < 0) return; + if (PyDict_SetItemString(d, "Str", (PyObject*)Str_type) < 0) return; + if (PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < + 0) return; + if (PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < + 0) return; + if (PyDict_SetItemString(d, "Name", (PyObject*)Name_type) < 0) return; + if (PyDict_SetItemString(d, "List", (PyObject*)List_type) < 0) return; + if (PyDict_SetItemString(d, "Tuple", (PyObject*)Tuple_type) < 0) return; + if (PyDict_SetItemString(d, "expr_context", + (PyObject*)expr_context_type) < 0) return; + if (PyDict_SetItemString(d, "Load", (PyObject*)Load_type) < 0) return; + if (PyDict_SetItemString(d, "Store", (PyObject*)Store_type) < 0) return; + if (PyDict_SetItemString(d, "Del", (PyObject*)Del_type) < 0) return; + if (PyDict_SetItemString(d, "AugLoad", (PyObject*)AugLoad_type) < 0) + return; + if (PyDict_SetItemString(d, "AugStore", (PyObject*)AugStore_type) < 0) + return; + if (PyDict_SetItemString(d, "Param", (PyObject*)Param_type) < 0) return; + if (PyDict_SetItemString(d, "slice", (PyObject*)slice_type) < 0) return; + if (PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0) + return; + if (PyDict_SetItemString(d, "Slice", (PyObject*)Slice_type) < 0) return; + if (PyDict_SetItemString(d, "ExtSlice", (PyObject*)ExtSlice_type) < 0) + return; + if (PyDict_SetItemString(d, "Index", (PyObject*)Index_type) < 0) return; + if (PyDict_SetItemString(d, "boolop", (PyObject*)boolop_type) < 0) + return; + if (PyDict_SetItemString(d, "And", (PyObject*)And_type) < 0) return; + if (PyDict_SetItemString(d, "Or", (PyObject*)Or_type) < 0) return; + if (PyDict_SetItemString(d, "operator", (PyObject*)operator_type) < 0) + return; + if (PyDict_SetItemString(d, "Add", (PyObject*)Add_type) < 0) return; + if (PyDict_SetItemString(d, "Sub", (PyObject*)Sub_type) < 0) return; + if (PyDict_SetItemString(d, "Mult", (PyObject*)Mult_type) < 0) return; + if (PyDict_SetItemString(d, "Div", (PyObject*)Div_type) < 0) return; + if (PyDict_SetItemString(d, "Mod", (PyObject*)Mod_type) < 0) return; + if (PyDict_SetItemString(d, "Pow", (PyObject*)Pow_type) < 0) return; + if (PyDict_SetItemString(d, "LShift", (PyObject*)LShift_type) < 0) + return; + if (PyDict_SetItemString(d, "RShift", (PyObject*)RShift_type) < 0) + return; + if (PyDict_SetItemString(d, "BitOr", (PyObject*)BitOr_type) < 0) return; + if (PyDict_SetItemString(d, "BitXor", (PyObject*)BitXor_type) < 0) + return; + if (PyDict_SetItemString(d, "BitAnd", (PyObject*)BitAnd_type) < 0) + return; + if (PyDict_SetItemString(d, "FloorDiv", (PyObject*)FloorDiv_type) < 0) + return; + if (PyDict_SetItemString(d, "unaryop", (PyObject*)unaryop_type) < 0) + return; + if (PyDict_SetItemString(d, "Invert", (PyObject*)Invert_type) < 0) + return; + if (PyDict_SetItemString(d, "Not", (PyObject*)Not_type) < 0) return; + if (PyDict_SetItemString(d, "UAdd", (PyObject*)UAdd_type) < 0) return; + if (PyDict_SetItemString(d, "USub", (PyObject*)USub_type) < 0) return; + if (PyDict_SetItemString(d, "cmpop", (PyObject*)cmpop_type) < 0) return; + if (PyDict_SetItemString(d, "Eq", (PyObject*)Eq_type) < 0) return; + if (PyDict_SetItemString(d, "NotEq", (PyObject*)NotEq_type) < 0) return; + if (PyDict_SetItemString(d, "Lt", (PyObject*)Lt_type) < 0) return; + if (PyDict_SetItemString(d, "LtE", (PyObject*)LtE_type) < 0) return; + if (PyDict_SetItemString(d, "Gt", (PyObject*)Gt_type) < 0) return; + if (PyDict_SetItemString(d, "GtE", (PyObject*)GtE_type) < 0) return; + if (PyDict_SetItemString(d, "Is", (PyObject*)Is_type) < 0) return; + if (PyDict_SetItemString(d, "IsNot", (PyObject*)IsNot_type) < 0) return; + if (PyDict_SetItemString(d, "In", (PyObject*)In_type) < 0) return; + if (PyDict_SetItemString(d, "NotIn", (PyObject*)NotIn_type) < 0) return; + if (PyDict_SetItemString(d, "comprehension", + (PyObject*)comprehension_type) < 0) return; + if (PyDict_SetItemString(d, "excepthandler", + (PyObject*)excepthandler_type) < 0) return; + if (PyDict_SetItemString(d, "arguments", (PyObject*)arguments_type) < + 0) return; + if (PyDict_SetItemString(d, "keyword", (PyObject*)keyword_type) < 0) + return; + if (PyDict_SetItemString(d, "alias", (PyObject*)alias_type) < 0) return; } diff --git a/Python/asdl.c b/Python/asdl.c index 225df6e..72329b9 100644 --- a/Python/asdl.c +++ b/Python/asdl.c @@ -8,7 +8,24 @@ asdl_seq_new(int size, PyArena *arena) size_t n = sizeof(asdl_seq) + (size ? (sizeof(void *) * (size - 1)) : 0); - seq = (asdl_seq *)PyArena_Malloc(arena, n); + seq = (asdl_seq *)PyArena_Malloc(arena, n); + if (!seq) { + PyErr_NoMemory(); + return NULL; + } + memset(seq, 0, n); + seq->size = size; + return seq; +} + +asdl_int_seq * +asdl_int_seq_new(int size, PyArena *arena) +{ + asdl_int_seq *seq = NULL; + size_t n = sizeof(asdl_seq) + + (size ? (sizeof(int) * (size - 1)) : 0); + + seq = (asdl_int_seq *)PyArena_Malloc(arena, n); if (!seq) { PyErr_NoMemory(); return NULL; diff --git a/Python/ast.c b/Python/ast.c index bb1774b..0b3b485 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -31,7 +31,7 @@ static asdl_seq *seq_for_testlist(struct compiling *, const node *); static expr_ty ast_for_expr(struct compiling *, const node *); static stmt_ty ast_for_stmt(struct compiling *, const node *); static asdl_seq *ast_for_suite(struct compiling *, const node *); -static asdl_seq *ast_for_exprlist(struct compiling *, const node *, int); +static asdl_seq *ast_for_exprlist(struct compiling *, const node *, expr_context_ty); static expr_ty ast_for_testlist(struct compiling *, const node *); static expr_ty ast_for_testlist_gexp(struct compiling *, const node *); @@ -191,6 +191,10 @@ PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename, if (flags && flags->cf_flags & PyCF_SOURCE_IS_UTF8) { c.c_encoding = "utf-8"; + if (TYPE(n) == encoding_decl) { + ast_error(n, "encoding declaration in Unicode string"); + goto error; + } } else if (TYPE(n) == encoding_decl) { c.c_encoding = STR(n); n = CHILD(n, 0); @@ -243,7 +247,8 @@ PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename, stmts = asdl_seq_new(1, arena); if (!stmts) goto error; - asdl_seq_SET(stmts, 0, Pass(n->n_lineno, n->n_col_offset, arena)); + asdl_seq_SET(stmts, 0, Pass(n->n_lineno, n->n_col_offset, + arena)); return Interactive(stmts, arena); } else { @@ -311,7 +316,7 @@ get_operator(const node *n) case PERCENT: return Mod; default: - return 0; + return (operator_ty)0; } } @@ -419,7 +424,7 @@ set_context(expr_ty e, expr_context_ty ctx, const node *n) int i; for (i = 0; i < asdl_seq_LEN(s); i++) { - if (!set_context(asdl_seq_GET(s, i), ctx, n)) + if (!set_context((expr_ty)asdl_seq_GET(s, i), ctx, n)) return 0; } } @@ -460,7 +465,7 @@ ast_for_augassign(const node *n) return Mult; default: PyErr_Format(PyExc_SystemError, "invalid augassign: %s", STR(n)); - return 0; + return (operator_ty)0; } } @@ -494,7 +499,7 @@ ast_for_comp_op(const node *n) default: PyErr_Format(PyExc_SystemError, "invalid comp_op: %s", STR(n)); - return 0; + return (cmpop_ty)0; } } else if (NCH(n) == 2) { @@ -508,12 +513,12 @@ ast_for_comp_op(const node *n) default: PyErr_Format(PyExc_SystemError, "invalid comp_op: %s %s", STR(CHILD(n, 0)), STR(CHILD(n, 1))); - return 0; + return (cmpop_ty)0; } } PyErr_Format(PyExc_SystemError, "invalid comp_op: has %d children", NCH(n)); - return 0; + return (cmpop_ty)0; } static asdl_seq * @@ -564,8 +569,8 @@ compiler_complex_args(struct compiling *c, const node *n) ast_error(child, "assignment to None"); return NULL; } - arg = Name(NEW_IDENTIFIER(child), Store, LINENO(child), child->n_col_offset, - c->c_arena); + arg = Name(NEW_IDENTIFIER(child), Store, LINENO(child), + child->n_col_offset, c->c_arena); } else { arg = compiler_complex_args(c, CHILD(CHILD(n, 2*i), 1)); @@ -641,17 +646,25 @@ ast_for_arguments(struct compiling *c, const node *n) goto error; } if (NCH(ch) == 3) { - asdl_seq_SET(args, k++, - compiler_complex_args(c, CHILD(ch, 1))); - } - else if (TYPE(CHILD(ch, 0)) == NAME) { + ch = CHILD(ch, 1); + /* def foo((x)): is not complex, special case. */ + if (NCH(ch) != 1) { + /* We have complex arguments, setup for unpacking. */ + asdl_seq_SET(args, k++, compiler_complex_args(c, ch)); + } else { + /* def foo((x)): setup for checking NAME below. */ + ch = CHILD(ch, 0); + } + } + if (TYPE(CHILD(ch, 0)) == NAME) { expr_ty name; if (!strcmp(STR(CHILD(ch, 0)), "None")) { ast_error(CHILD(ch, 0), "assignment to None"); goto error; } name = Name(NEW_IDENTIFIER(CHILD(ch, 0)), - Param, LINENO(ch), ch->n_col_offset, c->c_arena); + Param, LINENO(ch), ch->n_col_offset, + c->c_arena); if (!name) goto error; asdl_seq_SET(args, k++, name); @@ -743,7 +756,8 @@ ast_for_decorator(struct compiling *c, const node *n) name_expr = NULL; } else if (NCH(n) == 5) { /* Call with no arguments */ - d = Call(name_expr, NULL, NULL, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); + d = Call(name_expr, NULL, NULL, NULL, NULL, LINENO(n), + n->n_col_offset, c->c_arena); if (!d) return NULL; name_expr = NULL; @@ -815,7 +829,8 @@ ast_for_funcdef(struct compiling *c, const node *n) if (!body) return NULL; - return FunctionDef(name, args, body, decorator_seq, LINENO(n), n->n_col_offset, c->c_arena); + return FunctionDef(name, args, body, decorator_seq, LINENO(n), + n->n_col_offset, c->c_arena); } static expr_ty @@ -861,7 +876,8 @@ ast_for_ifexpr(struct compiling *c, const node *n) orelse = ast_for_expr(c, CHILD(n, 4)); if (!orelse) return NULL; - return IfExp(expression, body, orelse, LINENO(n), n->n_col_offset, c->c_arena); + return IfExp(expression, body, orelse, LINENO(n), n->n_col_offset, + c->c_arena); } /* Count the number of 'for' loop in a list comprehension. @@ -969,10 +985,11 @@ ast_for_listcomp(struct compiling *c, const node *n) return NULL; if (asdl_seq_LEN(t) == 1) - lc = comprehension(asdl_seq_GET(t, 0), expression, NULL, + lc = comprehension((expr_ty)asdl_seq_GET(t, 0), expression, NULL, c->c_arena); else - lc = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, c->c_arena), + lc = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, + c->c_arena), expression, NULL, c->c_arena); if (!lc) return NULL; @@ -1114,10 +1131,11 @@ ast_for_genexp(struct compiling *c, const node *n) return NULL; if (asdl_seq_LEN(t) == 1) - ge = comprehension(asdl_seq_GET(t, 0), expression, + ge = comprehension((expr_ty)asdl_seq_GET(t, 0), expression, NULL, c->c_arena); else - ge = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, c->c_arena), + ge = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, + c->c_arena), expression, NULL, c->c_arena); if (!ge) @@ -1317,16 +1335,20 @@ ast_for_slice(struct compiling *c, const node *n) ch = CHILD(n, NCH(n) - 1); if (TYPE(ch) == sliceop) { - if (NCH(ch) == 1) - /* XXX: If only 1 child, then should just be a colon. Should we - just skip assigning and just get to the return? */ - ch = CHILD(ch, 0); - else - ch = CHILD(ch, 1); - if (TYPE(ch) == test) { - step = ast_for_expr(c, ch); + if (NCH(ch) == 1) { + /* No expression, so step is None */ + ch = CHILD(ch, 0); + step = Name(new_identifier("None", c->c_arena), Load, + LINENO(ch), ch->n_col_offset, c->c_arena); if (!step) return NULL; + } else { + ch = CHILD(ch, 1); + if (TYPE(ch) == test) { + step = ast_for_expr(c, ch); + if (!step) + return NULL; + } } } @@ -1343,7 +1365,7 @@ ast_for_binop(struct compiling *c, const node *n) int i, nops; expr_ty expr1, expr2, result; - operator_ty operator; + operator_ty newoperator; expr1 = ast_for_expr(c, CHILD(n, 0)); if (!expr1) @@ -1353,11 +1375,12 @@ ast_for_binop(struct compiling *c, const node *n) if (!expr2) return NULL; - operator = get_operator(CHILD(n, 1)); - if (!operator) + newoperator = get_operator(CHILD(n, 1)); + if (!newoperator) return NULL; - result = BinOp(expr1, operator, expr2, LINENO(n), n->n_col_offset, c->c_arena); + result = BinOp(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, + c->c_arena); if (!result) return NULL; @@ -1366,16 +1389,17 @@ ast_for_binop(struct compiling *c, const node *n) expr_ty tmp_result, tmp; const node* next_oper = CHILD(n, i * 2 + 1); - operator = get_operator(next_oper); - if (!operator) + newoperator = get_operator(next_oper); + if (!newoperator) return NULL; tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); if (!tmp) return NULL; - tmp_result = BinOp(result, operator, tmp, - LINENO(next_oper), next_oper->n_col_offset, c->c_arena); + tmp_result = BinOp(result, newoperator, tmp, + LINENO(next_oper), next_oper->n_col_offset, + c->c_arena); if (!tmp) return NULL; result = tmp_result; @@ -1393,7 +1417,8 @@ ast_for_trailer(struct compiling *c, const node *n, expr_ty left_expr) REQ(n, trailer); if (TYPE(CHILD(n, 0)) == LPAR) { if (NCH(n) == 2) - return Call(left_expr, NULL, NULL, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); + return Call(left_expr, NULL, NULL, NULL, NULL, LINENO(n), + n->n_col_offset, c->c_arena); else return ast_for_call(c, CHILD(n, 1), left_expr); } @@ -1409,7 +1434,8 @@ ast_for_trailer(struct compiling *c, const node *n, expr_ty left_expr) slice_ty slc = ast_for_slice(c, CHILD(n, 0)); if (!slc) return NULL; - return Subscript(left_expr, slc, Load, LINENO(n), n->n_col_offset, c->c_arena); + return Subscript(left_expr, slc, Load, LINENO(n), n->n_col_offset, + c->c_arena); } else { /* The grammar is ambiguous here. The ambiguity is resolved @@ -1550,7 +1576,8 @@ ast_for_expr(struct compiling *c, const node *n) asdl_seq_SET(seq, i / 2, e); } if (!strcmp(STR(CHILD(n, 1)), "and")) - return BoolOp(And, seq, LINENO(n), n->n_col_offset, c->c_arena); + return BoolOp(And, seq, LINENO(n), n->n_col_offset, + c->c_arena); assert(!strcmp(STR(CHILD(n, 1)), "or")); return BoolOp(Or, seq, LINENO(n), n->n_col_offset, c->c_arena); case not_test: @@ -1563,7 +1590,8 @@ ast_for_expr(struct compiling *c, const node *n) if (!expression) return NULL; - return UnaryOp(Not, expression, LINENO(n), n->n_col_offset, c->c_arena); + return UnaryOp(Not, expression, LINENO(n), n->n_col_offset, + c->c_arena); } case comparison: if (NCH(n) == 1) { @@ -1572,8 +1600,9 @@ ast_for_expr(struct compiling *c, const node *n) } else { expr_ty expression; - asdl_seq *ops, *cmps; - ops = asdl_seq_new(NCH(n) / 2, c->c_arena); + asdl_int_seq *ops; + asdl_seq *cmps; + ops = asdl_int_seq_new(NCH(n) / 2, c->c_arena); if (!ops) return NULL; cmps = asdl_seq_new(NCH(n) / 2, c->c_arena); @@ -1581,11 +1610,10 @@ ast_for_expr(struct compiling *c, const node *n) return NULL; } for (i = 1; i < NCH(n); i += 2) { - /* XXX cmpop_ty is just an enum */ - cmpop_ty operator; + cmpop_ty newoperator; - operator = ast_for_comp_op(CHILD(n, i)); - if (!operator) { + newoperator = ast_for_comp_op(CHILD(n, i)); + if (!newoperator) { return NULL; } @@ -1594,7 +1622,7 @@ ast_for_expr(struct compiling *c, const node *n) return NULL; } - asdl_seq_SET(ops, i / 2, (void *)(Py_uintptr_t)operator); + asdl_seq_SET(ops, i / 2, newoperator); asdl_seq_SET(cmps, i / 2, expression); } expression = ast_for_expr(c, CHILD(n, 0)); @@ -1602,7 +1630,8 @@ ast_for_expr(struct compiling *c, const node *n) return NULL; } - return Compare(expression, ops, cmps, LINENO(n), n->n_col_offset, c->c_arena); + return Compare(expression, ops, cmps, LINENO(n), + n->n_col_offset, c->c_arena); } break; @@ -1853,7 +1882,7 @@ ast_for_expr_stmt(struct compiling *c, const node *n) } else if (TYPE(CHILD(n, 1)) == augassign) { expr_ty expr1, expr2; - operator_ty operator; + operator_ty newoperator; node *ch = CHILD(n, 0); if (TYPE(ch) == testlist) @@ -1895,11 +1924,11 @@ ast_for_expr_stmt(struct compiling *c, const node *n) if (!expr2) return NULL; - operator = ast_for_augassign(CHILD(n, 1)); - if (!operator) + newoperator = ast_for_augassign(CHILD(n, 1)); + if (!newoperator) return NULL; - return AugAssign(expr1, operator, expr2, LINENO(n), n->n_col_offset, c->c_arena); + return AugAssign(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, c->c_arena); } else { int i; @@ -1973,7 +2002,7 @@ ast_for_print_stmt(struct compiling *c, const node *n) } static asdl_seq * -ast_for_exprlist(struct compiling *c, const node *n, int context) +ast_for_exprlist(struct compiling *c, const node *n, expr_context_ty context) { asdl_seq *seq; int i; @@ -2512,8 +2541,8 @@ ast_for_if_stmt(struct compiling *c, const node *n) int off = 5 + (n_elif - i - 1) * 4; expr_ty expression; asdl_seq *suite_seq; - asdl_seq *new = asdl_seq_new(1, c->c_arena); - if (!new) + asdl_seq *newobj = asdl_seq_new(1, c->c_arena); + if (!newobj) return NULL; expression = ast_for_expr(c, CHILD(n, off)); if (!expression) @@ -2522,10 +2551,10 @@ ast_for_if_stmt(struct compiling *c, const node *n) if (!suite_seq) return NULL; - asdl_seq_SET(new, 0, + asdl_seq_SET(newobj, 0, If(expression, suite_seq, orelse, LINENO(CHILD(n, off)), CHILD(n, off)->n_col_offset, c->c_arena)); - orelse = new; + orelse = newobj; } return If(ast_for_expr(c, CHILD(n, 1)), ast_for_suite(c, CHILD(n, 3)), @@ -2597,7 +2626,7 @@ ast_for_for_stmt(struct compiling *c, const node *n) if (!_target) return NULL; if (asdl_seq_LEN(_target) == 1) - target = asdl_seq_GET(_target, 0); + target = (expr_ty)asdl_seq_GET(_target, 0); else target = Tuple(_target, Store, LINENO(n), n->n_col_offset, c->c_arena); @@ -2608,7 +2637,8 @@ ast_for_for_stmt(struct compiling *c, const node *n) if (!suite_seq) return NULL; - return For(target, expression, suite_seq, seq, LINENO(n), n->n_col_offset, c->c_arena); + return For(target, expression, suite_seq, seq, LINENO(n), n->n_col_offset, + c->c_arena); } static excepthandler_ty @@ -2623,7 +2653,8 @@ ast_for_except_clause(struct compiling *c, const node *exc, node *body) if (!suite_seq) return NULL; - return excepthandler(NULL, NULL, suite_seq, c->c_arena); + return excepthandler(NULL, NULL, suite_seq, LINENO(exc), + exc->n_col_offset, c->c_arena); } else if (NCH(exc) == 2) { expr_ty expression; @@ -2636,7 +2667,8 @@ ast_for_except_clause(struct compiling *c, const node *exc, node *body) if (!suite_seq) return NULL; - return excepthandler(expression, NULL, suite_seq, c->c_arena); + return excepthandler(expression, NULL, suite_seq, LINENO(exc), + exc->n_col_offset, c->c_arena); } else if (NCH(exc) == 4) { asdl_seq *suite_seq; @@ -2653,7 +2685,8 @@ ast_for_except_clause(struct compiling *c, const node *exc, node *body) if (!suite_seq) return NULL; - return excepthandler(expression, e, suite_seq, c->c_arena); + return excepthandler(expression, e, suite_seq, LINENO(exc), + exc->n_col_offset, c->c_arena); } PyErr_Format(PyExc_SystemError, @@ -2722,7 +2755,8 @@ ast_for_try_stmt(struct compiling *c, const node *n) asdl_seq_SET(handlers, i, e); } - except_st = TryExcept(body, handlers, orelse, LINENO(n), n->n_col_offset, c->c_arena); + except_st = TryExcept(body, handlers, orelse, LINENO(n), + n->n_col_offset, c->c_arena); if (!finally) return except_st; @@ -2797,16 +2831,16 @@ ast_for_classdef(struct compiling *c, const node *n) s = ast_for_suite(c, CHILD(n, 3)); if (!s) return NULL; - return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n), n->n_col_offset, - c->c_arena); + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n), + n->n_col_offset, c->c_arena); } /* check for empty base list */ if (TYPE(CHILD(n,3)) == RPAR) { s = ast_for_suite(c, CHILD(n,5)); if (!s) return NULL; - return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n), n->n_col_offset, - c->c_arena); + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n), + n->n_col_offset, c->c_arena); } /* else handle the base class list */ @@ -2817,8 +2851,8 @@ ast_for_classdef(struct compiling *c, const node *n) s = ast_for_suite(c, CHILD(n, 6)); if (!s) return NULL; - return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), bases, s, LINENO(n), n->n_col_offset, - c->c_arena); + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), bases, s, LINENO(n), + n->n_col_offset, c->c_arena); } static stmt_ty @@ -3090,7 +3124,8 @@ parsestr(const char *s, const char *encoding) #ifndef Py_USING_UNICODE /* This should not happen - we never see any other encoding. */ - Py_FatalError("cannot deal with encodings in this build."); + Py_FatalError( + "cannot deal with encodings in this build."); #else PyObject *v, *u = PyUnicode_DecodeUTF8(s, len, NULL); if (u == NULL) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 342c2db..914e0d1 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -31,23 +31,25 @@ static PyObject *filterunicode(PyObject *, PyObject *); static PyObject *filtertuple (PyObject *, PyObject *); static PyObject * -builtin___import__(PyObject *self, PyObject *args) +builtin___import__(PyObject *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"name", "globals", "locals", "fromlist", + "level", 0}; char *name; PyObject *globals = NULL; PyObject *locals = NULL; PyObject *fromlist = NULL; int level = -1; - if (!PyArg_ParseTuple(args, "s|OOOi:__import__", - &name, &globals, &locals, &fromlist, &level)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOOi:__import__", + kwlist, &name, &globals, &locals, &fromlist, &level)) return NULL; return PyImport_ImportModuleLevel(name, globals, locals, fromlist, level); } PyDoc_STRVAR(import_doc, -"__import__(name, globals, locals, fromlist) -> module\n\ +"__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\ @@ -55,7 +57,10 @@ 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\ returns package A when fromlist is empty, but its submodule B when\n\ -fromlist is not empty."); +fromlist is not empty. Level is used to determine whether to perform \n\ +absolute or relative imports. -1 is the original strategy of attempting\n\ +both absolute and relative imports, 0 is absolute, a positive number\n\ +is the number of parent directories to search relative to the current module."); static PyObject * @@ -1704,32 +1709,34 @@ For most object types, eval(repr(object)) == object."); static PyObject * -builtin_round(PyObject *self, PyObject *args) +builtin_round(PyObject *self, PyObject *args, PyObject *kwds) { - double x; + double number; double f; int ndigits = 0; int i; + static char *kwlist[] = {"number", "ndigits", 0}; - if (!PyArg_ParseTuple(args, "d|i:round", &x, &ndigits)) - return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round", + kwlist, &number, &ndigits)) + return NULL; f = 1.0; i = abs(ndigits); while (--i >= 0) f = f*10.0; if (ndigits < 0) - x /= f; + number /= f; else - x *= f; - if (x >= 0.0) - x = floor(x + 0.5); + number *= f; + if (number >= 0.0) + number = floor(number + 0.5); else - x = ceil(x - 0.5); + number = ceil(number - 0.5); if (ndigits < 0) - x *= f; + number *= f; else - x /= f; - return PyFloat_FromDouble(x); + number /= f; + return PyFloat_FromDouble(number); } PyDoc_STRVAR(round_doc, @@ -2042,7 +2049,7 @@ in length to the length of the shortest argument sequence."); static PyMethodDef builtin_methods[] = { - {"__import__", builtin___import__, METH_VARARGS, import_doc}, + {"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc}, {"abs", builtin_abs, METH_O, abs_doc}, {"all", builtin_all, METH_O, all_doc}, {"any", builtin_any, METH_O, any_doc}, @@ -2079,7 +2086,7 @@ static PyMethodDef builtin_methods[] = { {"reduce", builtin_reduce, METH_VARARGS, reduce_doc}, {"reload", builtin_reload, METH_O, reload_doc}, {"repr", builtin_repr, METH_O, repr_doc}, - {"round", builtin_round, METH_VARARGS, round_doc}, + {"round", (PyCFunction)builtin_round, METH_VARARGS | METH_KEYWORDS, round_doc}, {"setattr", builtin_setattr, METH_VARARGS, setattr_doc}, {"sorted", (PyCFunction)builtin_sorted, METH_VARARGS | METH_KEYWORDS, sorted_doc}, {"sum", builtin_sum, METH_VARARGS, sum_doc}, diff --git a/Python/ceval.c b/Python/ceval.c index 6f76781..6c8afba 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -507,7 +507,7 @@ PyEval_EvalFrame(PyFrameObject *f) { } PyObject * -PyEval_EvalFrameEx(PyFrameObject *f, int throw) +PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { #ifdef DXPAIRS int lastopcode = 0; @@ -756,7 +756,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) x = Py_None; /* Not a reference, just anything non-NULL */ w = NULL; - if (throw) { /* support for generator.throw() */ + if (throwflag) { /* support for generator.throw() */ why = WHY_EXCEPTION; goto on_error; } @@ -2153,6 +2153,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) case SETUP_LOOP: case SETUP_EXCEPT: case SETUP_FINALLY: + /* NOTE: If you add any new block-setup opcodes that are not try/except/finally + handlers, you may need to update the PyGen_NeedsFinalizing() function. */ + PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, STACK_LEVEL()); continue; @@ -3180,132 +3183,29 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, int *instr_lb, int *instr_ub, int *instr_prev) { - /* The theory of SET_LINENO-less tracing. - - In a nutshell, we use the co_lnotab field of the code object - to tell when execution has moved onto a different line. - - As mentioned above, the basic idea is so set things up so - that - - *instr_lb <= frame->f_lasti < *instr_ub - - is true so long as execution does not change lines. - - This is all fairly simple. Digging the information out of - co_lnotab takes some work, but is conceptually clear. - - Somewhat harder to explain is why we don't *always* call the - line trace function when the above test fails. - - Consider this code: - - 1: def f(a): - 2: if a: - 3: print 1 - 4: else: - 5: print 2 - - which compiles to this: - - 2 0 LOAD_FAST 0 (a) - 3 JUMP_IF_FALSE 9 (to 15) - 6 POP_TOP - - 3 7 LOAD_CONST 1 (1) - 10 PRINT_ITEM - 11 PRINT_NEWLINE - 12 JUMP_FORWARD 6 (to 21) - >> 15 POP_TOP - - 5 16 LOAD_CONST 2 (2) - 19 PRINT_ITEM - 20 PRINT_NEWLINE - >> 21 LOAD_CONST 0 (None) - 24 RETURN_VALUE - - If 'a' is false, execution will jump to instruction at offset - 15 and the co_lnotab will claim that execution has moved to - line 3. This is at best misleading. In this case we could - associate the POP_TOP with line 4, but that doesn't make - sense in all cases (I think). - - What we do is only call the line trace function if the co_lnotab - indicates we have jumped to the *start* of a line, i.e. if the - current instruction offset matches the offset given for the - start of a line by the co_lnotab. - - This also takes care of the situation where 'a' is true. - Execution will jump from instruction offset 12 to offset 21. - Then the co_lnotab would imply that execution has moved to line - 5, which is again misleading. - - Why do we set f_lineno when tracing? Well, consider the code - above when 'a' is true. If stepping through this with 'n' in - pdb, you would stop at line 1 with a "call" type event, then - line events on lines 2 and 3, then a "return" type event -- but - you would be shown line 5 during this event. This is a change - from the behaviour in 2.2 and before, and I've found it - confusing in practice. By setting and using f_lineno when - tracing, one can report a line number different from that - suggested by f_lasti on this one occasion where it's desirable. - */ - int result = 0; + /* If the last instruction executed isn't in the current + instruction window, reset the window. If the last + instruction happens to fall at the start of a line or if it + represents a jump backwards, call the trace function. + */ if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) { - PyCodeObject* co = frame->f_code; - int size, addr, line; - unsigned char* p; - - size = PyString_GET_SIZE(co->co_lnotab) / 2; - p = (unsigned char*)PyString_AS_STRING(co->co_lnotab); - - addr = 0; - line = co->co_firstlineno; + int line; + PyAddrPair bounds; - /* possible optimization: if f->f_lasti == instr_ub - (likely to be a common case) then we already know - instr_lb -- if we stored the matching value of p - somwhere we could skip the first while loop. */ - - /* see comments in compile.c for the description of - co_lnotab. A point to remember: increments to p - should come in pairs -- although we don't care about - the line increments here, treating them as byte - increments gets confusing, to say the least. */ - - while (size > 0) { - if (addr + *p > frame->f_lasti) - break; - addr += *p++; - if (*p) *instr_lb = addr; - line += *p++; - --size; - } - - if (addr == frame->f_lasti) { + line = PyCode_CheckLineNumber(frame->f_code, frame->f_lasti, + &bounds); + if (line >= 0) { frame->f_lineno = line; result = call_trace(func, obj, frame, PyTrace_LINE, Py_None); - } - - if (size > 0) { - while (--size >= 0) { - addr += *p++; - if (*p++) - break; - } - *instr_ub = addr; - } - else { - *instr_ub = INT_MAX; - } + } + *instr_lb = bounds.ap_lower; + *instr_ub = bounds.ap_upper; } else if (frame->f_lasti <= *instr_prev) { - /* jumping back in the same line forces a trace event */ - result = call_trace(func, obj, frame, - PyTrace_LINE, Py_None); + result = call_trace(func, obj, frame, PyTrace_LINE, Py_None); } *instr_prev = frame->f_lasti; return result; @@ -3623,9 +3523,9 @@ call_function(PyObject ***pp_stack, int oparg Py_DECREF(func); } - /* Clear the stack of the function object and the arguments, - in case they weren't consumed already. - XXX(twouters) when are they not consumed already? + /* Clear the stack of the function object. Also removes + the arguments in case they weren't consumed already + (fast_function() and err_args() leave them on the stack). */ while ((*pp_stack) > pfunc) { w = EXT_POP(*pp_stack); @@ -3899,7 +3799,7 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) if (v != NULL) { Py_ssize_t x; if (PyInt_Check(v)) { - x = PyInt_AsLong(v); + x = PyInt_AsSsize_t(v); } else if (v->ob_type->tp_as_number && PyType_HasFeature(v->ob_type, Py_TPFLAGS_HAVE_INDEX) @@ -4302,8 +4202,8 @@ string_concatenate(PyObject *v, PyObject *w, /* Now we own the last reference to 'v', so we can resize it * in-place. */ - int v_len = PyString_GET_SIZE(v); - int w_len = PyString_GET_SIZE(w); + Py_ssize_t v_len = PyString_GET_SIZE(v); + Py_ssize_t w_len = PyString_GET_SIZE(w); if (_PyString_Resize(&v, v_len + w_len) != 0) { /* XXX if _PyString_Resize() fails, 'v' has been * deallocated so it cannot be put back into 'variable'. diff --git a/Python/codecs.c b/Python/codecs.c index 0e8c374..2124824 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -56,12 +56,12 @@ PyObject *normalizestring(const char *string) char *p; PyObject *v; - if (len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "string is too large"); - return NULL; - } + if (len > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "string is too large"); + return NULL; + } - v = PyString_FromStringAndSize(NULL, (int)len); + v = PyString_FromStringAndSize(NULL, len); if (v == NULL) return NULL; p = PyString_AS_STRING(v); @@ -200,148 +200,107 @@ PyObject *args_tuple(PyObject *object, return args; } -/* Build a codec by calling factory(stream[,errors]) or just - factory(errors) depending on whether the given parameters are - non-NULL. */ +/* Helper function to get a codec item */ static -PyObject *build_stream_codec(PyObject *factory, - PyObject *stream, - const char *errors) -{ - PyObject *args, *codec; - - args = args_tuple(stream, errors); - if (args == NULL) - return NULL; - - codec = PyEval_CallObject(factory, args); - Py_DECREF(args); - return codec; -} - -/* Convenience APIs to query the Codec registry. - - All APIs return a codec object with incremented refcount. - - */ - -PyObject *PyCodec_Encoder(const char *encoding) +PyObject *codec_getitem(const char *encoding, int index) { PyObject *codecs; PyObject *v; codecs = _PyCodec_Lookup(encoding); if (codecs == NULL) - goto onError; - v = PyTuple_GET_ITEM(codecs,0); + return NULL; + v = PyTuple_GET_ITEM(codecs, index); Py_DECREF(codecs); Py_INCREF(v); return v; - - onError: - return NULL; } -PyObject *PyCodec_Decoder(const char *encoding) +/* Helper function to create an incremental codec. */ + +static +PyObject *codec_getincrementalcodec(const char *encoding, + const char *errors, + const char *attrname) { - PyObject *codecs; - PyObject *v; + PyObject *codecs, *ret, *inccodec; codecs = _PyCodec_Lookup(encoding); if (codecs == NULL) - goto onError; - v = PyTuple_GET_ITEM(codecs,1); + return NULL; + inccodec = PyObject_GetAttrString(codecs, attrname); Py_DECREF(codecs); - Py_INCREF(v); - return v; - - onError: - return NULL; + if (inccodec == NULL) + return NULL; + if (errors) + ret = PyObject_CallFunction(inccodec, "s", errors); + else + ret = PyObject_CallFunction(inccodec, NULL); + Py_DECREF(inccodec); + return ret; } -PyObject *PyCodec_IncrementalEncoder(const char *encoding, - const char *errors) +/* Helper function to create a stream codec. */ + +static +PyObject *codec_getstreamcodec(const char *encoding, + PyObject *stream, + const char *errors, + const int index) { - PyObject *codecs, *ret, *encoder; + PyObject *codecs, *streamcodec; codecs = _PyCodec_Lookup(encoding); if (codecs == NULL) - goto onError; - encoder = PyObject_GetAttrString(codecs, "incrementalencoder"); - if (encoder == NULL) { - Py_DECREF(codecs); return NULL; - } - if (errors) - ret = PyObject_CallFunction(encoder, "O", errors); - else - ret = PyObject_CallFunction(encoder, NULL); - Py_DECREF(encoder); + + streamcodec = PyEval_CallFunction( + PyTuple_GET_ITEM(codecs, index), "Os", stream, errors); Py_DECREF(codecs); - return ret; + return streamcodec; +} - onError: - return NULL; +/* Convenience APIs to query the Codec registry. + + All APIs return a codec object with incremented refcount. + + */ + +PyObject *PyCodec_Encoder(const char *encoding) +{ + return codec_getitem(encoding, 0); } -PyObject *PyCodec_IncrementalDecoder(const char *encoding, - const char *errors) +PyObject *PyCodec_Decoder(const char *encoding) { - PyObject *codecs, *ret, *decoder; + return codec_getitem(encoding, 1); +} - codecs = _PyCodec_Lookup(encoding); - if (codecs == NULL) - goto onError; - decoder = PyObject_GetAttrString(codecs, "incrementaldecoder"); - if (decoder == NULL) { - Py_DECREF(codecs); - return NULL; - } - if (errors) - ret = PyObject_CallFunction(decoder, "O", errors); - else - ret = PyObject_CallFunction(decoder, NULL); - Py_DECREF(decoder); - Py_DECREF(codecs); - return ret; +PyObject *PyCodec_IncrementalEncoder(const char *encoding, + const char *errors) +{ + return codec_getincrementalcodec(encoding, errors, "incrementalencoder"); +} - onError: - return NULL; +PyObject *PyCodec_IncrementalDecoder(const char *encoding, + const char *errors) +{ + return codec_getincrementalcodec(encoding, errors, "incrementaldecoder"); } PyObject *PyCodec_StreamReader(const char *encoding, PyObject *stream, const char *errors) { - PyObject *codecs, *ret; - - codecs = _PyCodec_Lookup(encoding); - if (codecs == NULL) - goto onError; - ret = build_stream_codec(PyTuple_GET_ITEM(codecs,2),stream,errors); - Py_DECREF(codecs); - return ret; - - onError: - return NULL; + return codec_getstreamcodec(encoding, stream, errors, 2); } PyObject *PyCodec_StreamWriter(const char *encoding, PyObject *stream, const char *errors) { - PyObject *codecs, *ret; - - codecs = _PyCodec_Lookup(encoding); - if (codecs == NULL) - goto onError; - ret = build_stream_codec(PyTuple_GET_ITEM(codecs,3),stream,errors); - Py_DECREF(codecs); - return ret; - - onError: - return NULL; + return codec_getstreamcodec(encoding, stream, errors, 3); } /* Encode an object (e.g. an Unicode object) using the given encoding diff --git a/Python/compile.c b/Python/compile.c index b92fb62..12b190a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -58,8 +58,9 @@ struct instr { }; typedef struct basicblock_ { - /* next block in the list of blocks for a unit (don't confuse with - * b_next) */ + /* Each basicblock in a compilation unit is linked via b_list in the + reverse order that the block are allocated. b_list points to the next + block, not to be confused with b_next, which is next by control flow. */ struct basicblock_ *b_list; /* number of instructions used */ int b_iused; @@ -114,7 +115,9 @@ struct compiler_unit { PyObject *u_private; /* for private name mangling */ int u_argcount; /* number of arguments for block */ - basicblock *u_blocks; /* pointer to list of blocks */ + /* Pointer to the most recently allocated block. By following b_list + members, you can reach all early allocated blocks. */ + basicblock *u_blocks; basicblock *u_curblock; /* pointer to current block */ int u_tmpname; /* temporary variables for list comps */ @@ -194,19 +197,19 @@ static PyCodeObject *assemble(struct compiler *, int addNone); static PyObject *__doc__; PyObject * -_Py_Mangle(PyObject *private, PyObject *ident) +_Py_Mangle(PyObject *privateobj, PyObject *ident) { /* Name mangling: __private becomes _classname__private. This is independent from how the name is used. */ const char *p, *name = PyString_AsString(ident); char *buffer; size_t nlen, plen; - if (private == NULL || name == NULL || name[0] != '_' || + if (privateobj == NULL || name == NULL || name[0] != '_' || name[1] != '_') { Py_INCREF(ident); return ident; } - p = PyString_AsString(private); + p = PyString_AsString(privateobj); nlen = strlen(name); if (name[nlen-1] == '_' && name[nlen-2] == '_') { Py_INCREF(ident); @@ -311,7 +314,7 @@ compiler_free(struct compiler *c) if (c->c_st) PySymtable_Free(c->c_st); if (c->c_future) - PyMem_Free(c->c_future); + PyObject_Free(c->c_future); Py_DECREF(c->c_stack); } @@ -319,7 +322,9 @@ static PyObject * list2dict(PyObject *list) { Py_ssize_t i, n; - PyObject *v, *k, *dict = PyDict_New(); + PyObject *v, *k; + PyObject *dict = PyDict_New(); + if (!dict) return NULL; n = PyList_Size(list); for (i = 0; i < n; i++) { @@ -602,7 +607,7 @@ fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts) static unsigned int * markblocks(unsigned char *code, int len) { - unsigned int *blocks = PyMem_Malloc(len*sizeof(int)); + unsigned int *blocks = (unsigned int *)PyMem_Malloc(len*sizeof(int)); int i,j, opcode, blockcnt = 0; if (blocks == NULL) @@ -683,10 +688,11 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, goto exitUnchanged; /* Make a modifiable copy of the code string */ - codestr = PyMem_Malloc(codelen); + codestr = (unsigned char *)PyMem_Malloc(codelen); if (codestr == NULL) goto exitUnchanged; - codestr = memcpy(codestr, PyString_AS_STRING(code), codelen); + codestr = (unsigned char *)memcpy(codestr, + PyString_AS_STRING(code), codelen); /* Verify that RETURN_VALUE terminates the codestring. This allows the various transformation patterns to look ahead several @@ -697,7 +703,7 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, goto exitUnchanged; /* Mapping to new jump targets after NOPs are removed */ - addrmap = PyMem_Malloc(codelen * sizeof(int)); + addrmap = (int *)PyMem_Malloc(codelen * sizeof(int)); if (addrmap == NULL) goto exitUnchanged; @@ -1077,7 +1083,8 @@ compiler_enter_scope(struct compiler *c, identifier name, void *key, { struct compiler_unit *u; - u = PyObject_Malloc(sizeof(struct compiler_unit)); + u = (struct compiler_unit *)PyObject_Malloc(sizeof( + struct compiler_unit)); if (!u) { PyErr_NoMemory(); return 0; @@ -1187,7 +1194,7 @@ compiler_new_block(struct compiler *c) return NULL; } memset((void *)b, 0, sizeof(basicblock)); - assert (b->b_next == NULL); + /* Extend the singly linked list of blocks with new block. */ b->b_list = u->u_blocks; u->u_blocks = b; return b; @@ -1233,8 +1240,8 @@ compiler_next_instr(struct compiler *c, basicblock *b) { assert(b != NULL); if (b->b_instr == NULL) { - b->b_instr = PyObject_Malloc(sizeof(struct instr) * - DEFAULT_BLOCK_SIZE); + b->b_instr = (struct instr *)PyObject_Malloc( + sizeof(struct instr) * DEFAULT_BLOCK_SIZE); if (b->b_instr == NULL) { PyErr_NoMemory(); return -1; @@ -1252,7 +1259,8 @@ compiler_next_instr(struct compiler *c, basicblock *b) return -1; } b->b_ialloc <<= 1; - b->b_instr = PyObject_Realloc((void *)b->b_instr, newsize); + b->b_instr = (struct instr *)PyObject_Realloc( + (void *)b->b_instr, newsize); if (b->b_instr == NULL) return -1; memset((char *)b->b_instr + oldsize, 0, newsize - oldsize); @@ -1260,6 +1268,13 @@ compiler_next_instr(struct compiler *c, basicblock *b) return b->b_iused++; } +/* Set the i_lineno member of the instruction at offse off if the + line number for the current expression/statement (?) has not + already been set. If it has been set, the call has no effect. + + Every time a new node is b + */ + static void compiler_set_lineno(struct compiler *c, int off) { @@ -1600,7 +1615,6 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) off = compiler_next_instr(c, c->u->u_curblock); if (off < 0) return 0; - compiler_set_lineno(c, off); i = &c->u->u_curblock->b_instr[off]; i->i_opcode = opcode; i->i_target = b; @@ -1609,6 +1623,7 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) i->i_jabs = 1; else i->i_jrel = 1; + compiler_set_lineno(c, off); return 1; } @@ -1695,7 +1710,7 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) int _i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, _i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ if (!compiler_visit_ ## TYPE((C), elt)) \ return 0; \ } \ @@ -1705,7 +1720,7 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) int _i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, _i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ if (!compiler_visit_ ## TYPE((C), elt)) { \ compiler_exit_scope(c); \ return 0; \ @@ -1731,7 +1746,7 @@ compiler_body(struct compiler *c, asdl_seq *stmts) if (!asdl_seq_LEN(stmts)) return 1; - st = asdl_seq_GET(stmts, 0); + st = (stmt_ty)asdl_seq_GET(stmts, 0); if (compiler_isdocstring(st)) { i = 1; VISIT(c, expr, st->v.Expr.value); @@ -1739,7 +1754,7 @@ compiler_body(struct compiler *c, asdl_seq *stmts) return 0; } for (; i < asdl_seq_LEN(stmts); i++) - VISIT(c, stmt, asdl_seq_GET(stmts, i)); + VISIT(c, stmt, (stmt_ty)asdl_seq_GET(stmts, i)); return 1; } @@ -1765,7 +1780,8 @@ compiler_mod(struct compiler *c, mod_ty mod) break; case Interactive_kind: c->c_interactive = 1; - VISIT_SEQ_IN_SCOPE(c, stmt, mod->v.Interactive.body); + VISIT_SEQ_IN_SCOPE(c, stmt, + mod->v.Interactive.body); break; case Expression_kind: VISIT_IN_SCOPE(c, expr, mod->v.Expression.body); @@ -1882,7 +1898,7 @@ compiler_decorators(struct compiler *c, asdl_seq* decos) return 1; for (i = 0; i < asdl_seq_LEN(decos); i++) { - VISIT(c, expr, asdl_seq_GET(decos, i)); + VISIT(c, expr, (expr_ty)asdl_seq_GET(decos, i)); } return 1; } @@ -1894,7 +1910,7 @@ compiler_arguments(struct compiler *c, arguments_ty args) int n = asdl_seq_LEN(args->args); /* Correctly handle nested argument lists */ for (i = 0; i < n; i++) { - expr_ty arg = asdl_seq_GET(args->args, i); + expr_ty arg = (expr_ty)asdl_seq_GET(args->args, i); if (arg->kind == Tuple_kind) { PyObject *id = PyString_FromFormat(".%d", i); if (id == NULL) { @@ -1931,7 +1947,7 @@ compiler_function(struct compiler *c, stmt_ty s) s->lineno)) return 0; - st = asdl_seq_GET(s->v.FunctionDef.body, 0); + st = (stmt_ty)asdl_seq_GET(s->v.FunctionDef.body, 0); docstring = compiler_isdocstring(st); if (docstring) first_const = st->v.Expr.value->v.Str.s; @@ -1947,7 +1963,7 @@ compiler_function(struct compiler *c, stmt_ty s) n = asdl_seq_LEN(s->v.FunctionDef.body); /* if there was a docstring, we need to skip the first statement */ for (i = docstring; i < n; i++) { - stmt_ty s2 = asdl_seq_GET(s->v.FunctionDef.body, i); + stmt_ty s2 = (stmt_ty)asdl_seq_GET(s->v.FunctionDef.body, i); if (i == 0 && s2->kind == Expr_kind && s2->v.Expr.value->kind == Str_kind) continue; @@ -2221,7 +2237,7 @@ compiler_while(struct compiler *c, stmt_ty s) ADDOP(c, POP_BLOCK); } compiler_pop_fblock(c, LOOP, loop); - if (orelse != NULL) + if (orelse != NULL) /* what if orelse is just pass? */ VISIT_SEQ(c, stmt, s->v.While.orelse); compiler_use_next_block(c, end); @@ -2375,10 +2391,12 @@ compiler_try_except(struct compiler *c, stmt_ty s) n = asdl_seq_LEN(s->v.TryExcept.handlers); compiler_use_next_block(c, except); for (i = 0; i < n; i++) { - excepthandler_ty handler = asdl_seq_GET( + excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET( s->v.TryExcept.handlers, i); if (!handler->type && i < n-1) return compiler_error(c, "default 'except:' must be last"); + c->u->u_lineno_set = false; + c->u->u_lineno = handler->lineno; except = compiler_new_block(c); if (except == NULL) return 0; @@ -2453,7 +2471,7 @@ compiler_import(struct compiler *c, stmt_ty s) int i, n = asdl_seq_LEN(s->v.Import.names); for (i = 0; i < n; i++) { - alias_ty alias = asdl_seq_GET(s->v.Import.names, i); + alias_ty alias = (alias_ty)asdl_seq_GET(s->v.Import.names, i); int r; PyObject *level; @@ -2508,7 +2526,7 @@ compiler_from_import(struct compiler *c, stmt_ty s) /* build up the names */ for (i = 0; i < n; i++) { - alias_ty alias = asdl_seq_GET(s->v.ImportFrom.names, i); + alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); Py_INCREF(alias->name); PyTuple_SET_ITEM(names, i, alias->name); } @@ -2531,7 +2549,7 @@ compiler_from_import(struct compiler *c, stmt_ty s) Py_DECREF(names); ADDOP_NAME(c, IMPORT_NAME, s->v.ImportFrom.module, names); for (i = 0; i < n; i++) { - alias_ty alias = asdl_seq_GET(s->v.ImportFrom.names, i); + alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); identifier store_name; if (i == 0 && *PyString_AS_STRING(alias->name) == '*') { @@ -2592,8 +2610,10 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s) { int i, n; + /* Always assign a lineno to the next instruction for a stmt. */ c->u->u_lineno = s->lineno; c->u->u_lineno_set = false; + switch (s->kind) { case FunctionDef_kind: return compiler_function(c, s); @@ -2962,11 +2982,11 @@ compiler_boolop(struct compiler *c, expr_ty e) s = e->v.BoolOp.values; n = asdl_seq_LEN(s) - 1; for (i = 0; i < n; ++i) { - VISIT(c, expr, asdl_seq_GET(s, i)); + VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i)); ADDOP_JREL(c, jumpi, end); ADDOP(c, POP_TOP) } - VISIT(c, expr, asdl_seq_GET(s, n)); + VISIT(c, expr, (expr_ty)asdl_seq_GET(s, n)); compiler_use_next_block(c, end); return 1; } @@ -3013,24 +3033,25 @@ compiler_compare(struct compiler *c, expr_ty e) cleanup = compiler_new_block(c); if (cleanup == NULL) return 0; - VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, 0)); + VISIT(c, expr, + (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0)); } for (i = 1; i < n; i++) { ADDOP(c, DUP_TOP); ADDOP(c, ROT_THREE); - /* XXX We're casting a void* to cmpop_ty in the next stmt. */ ADDOP_I(c, COMPARE_OP, - cmpop((cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i - 1))); + cmpop((cmpop_ty)(asdl_seq_GET( + e->v.Compare.ops, i - 1)))); ADDOP_JREL(c, JUMP_IF_FALSE, cleanup); NEXT_BLOCK(c); ADDOP(c, POP_TOP); if (i < (n - 1)) - VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, i)); + VISIT(c, expr, + (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i)); } - VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, n - 1)); + VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n - 1)); ADDOP_I(c, COMPARE_OP, - /* XXX We're casting a void* to cmpop_ty in the next stmt. */ - cmpop((cmpop_ty)asdl_seq_GET(e->v.Compare.ops, n - 1))); + cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n - 1)))); if (n > 1) { basicblock *end = compiler_new_block(c); if (end == NULL) @@ -3043,6 +3064,7 @@ compiler_compare(struct compiler *c, expr_ty e) } return 1; } +#undef CMPCAST static int compiler_call(struct compiler *c, expr_ty e) @@ -3102,7 +3124,7 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname, anchor == NULL) return 0; - l = asdl_seq_GET(generators, gen_index); + l = (comprehension_ty)asdl_seq_GET(generators, gen_index); VISIT(c, expr, l->iter); ADDOP(c, GET_ITER); compiler_use_next_block(c, start); @@ -3113,7 +3135,7 @@ compiler_listcomp_generator(struct compiler *c, PyObject *tmpname, /* XXX this needs to be cleaned up...a lot! */ n = asdl_seq_LEN(l->ifs); for (i = 0; i < n; i++) { - expr_ty e = asdl_seq_GET(l->ifs, i); + expr_ty e = (expr_ty)asdl_seq_GET(l->ifs, i); VISIT(c, expr, e); ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); NEXT_BLOCK(c); @@ -3198,7 +3220,7 @@ compiler_genexp_generator(struct compiler *c, anchor == NULL || end == NULL) return 0; - ge = asdl_seq_GET(generators, gen_index); + ge = (comprehension_ty)asdl_seq_GET(generators, gen_index); ADDOP_JREL(c, SETUP_LOOP, end); if (!compiler_push_fblock(c, LOOP, start)) return 0; @@ -3221,7 +3243,7 @@ compiler_genexp_generator(struct compiler *c, /* XXX this needs to be cleaned up...a lot! */ n = asdl_seq_LEN(ge->ifs); for (i = 0; i < n; i++) { - expr_ty e = asdl_seq_GET(ge->ifs, i); + expr_ty e = (expr_ty)asdl_seq_GET(ge->ifs, i); VISIT(c, expr, e); ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); NEXT_BLOCK(c); @@ -3462,6 +3484,9 @@ compiler_visit_expr(struct compiler *c, expr_ty e) { int i, n; + /* If expr e has a different line number than the last expr/stmt, + set a new line number for the next instruction. + */ if (e->lineno > c->u->u_lineno) { c->u->u_lineno = e->lineno; c->u->u_lineno_set = false; @@ -3490,9 +3515,11 @@ compiler_visit_expr(struct compiler *c, expr_ty e) It wants the stack to look like (value) (dict) (key) */ for (i = 0; i < n; i++) { ADDOP(c, DUP_TOP); - VISIT(c, expr, asdl_seq_GET(e->v.Dict.values, i)); + VISIT(c, expr, + (expr_ty)asdl_seq_GET(e->v.Dict.values, i)); ADDOP(c, ROT_TWO); - VISIT(c, expr, asdl_seq_GET(e->v.Dict.keys, i)); + VISIT(c, expr, + (expr_ty)asdl_seq_GET(e->v.Dict.keys, i)); ADDOP(c, STORE_SUBSCR); } break; @@ -3859,7 +3886,8 @@ compiler_visit_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) if (ctx != AugStore) { int i, n = asdl_seq_LEN(s->v.ExtSlice.dims); for (i = 0; i < n; i++) { - slice_ty sub = asdl_seq_GET(s->v.ExtSlice.dims, i); + slice_ty sub = (slice_ty)asdl_seq_GET( + s->v.ExtSlice.dims, i); if (!compiler_visit_nested_slice(c, sub, ctx)) return 0; } @@ -4048,7 +4076,7 @@ assemble_lnotab(struct assembler *a, struct instr *i) { int d_bytecode, d_lineno; int len; - char *lnotab; + unsigned char *lnotab; d_bytecode = a->a_offset - a->a_lineno_off; d_lineno = i->i_lineno - a->a_lineno; @@ -4071,7 +4099,8 @@ assemble_lnotab(struct assembler *a, struct instr *i) if (_PyString_Resize(&a->a_lnotab, len) < 0) return 0; } - lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + lnotab = (unsigned char *) + PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; for (j = 0; j < ncodes; j++) { *lnotab++ = 255; *lnotab++ = 0; @@ -4092,7 +4121,8 @@ assemble_lnotab(struct assembler *a, struct instr *i) if (_PyString_Resize(&a->a_lnotab, len) < 0) return 0; } - lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + lnotab = (unsigned char *) + PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; *lnotab++ = 255; *lnotab++ = d_bytecode; d_bytecode = 0; @@ -4109,7 +4139,8 @@ assemble_lnotab(struct assembler *a, struct instr *i) if (_PyString_Resize(&a->a_lnotab, len * 2) < 0) return 0; } - lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + lnotab = (unsigned char *) + PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; a->a_lnotab_off += 2; if (d_bytecode) { diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 6e8f822..36746e2 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -11,10 +11,16 @@ const struct filedescr _PyImport_DynLoadFiletab[] = { #ifdef _DEBUG {"_d.pyd", "rb", C_EXTENSION}, + /* Temporarily disable .dll, to avoid conflicts between sqlite3.dll + and the sqlite3 package. If this needs to be reverted for 2.5, + some other solution for the naming conflict must be found. {"_d.dll", "rb", C_EXTENSION}, + */ #else {".pyd", "rb", C_EXTENSION}, + /* Likewise {".dll", "rb", C_EXTENSION}, + */ #endif {0, 0} }; diff --git a/Python/errors.c b/Python/errors.c index 67f86ed..2ae062f 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -16,6 +16,11 @@ extern char *strerror(int); #include +#ifdef __cplusplus +extern "C" { +#endif + + void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) { @@ -609,6 +614,7 @@ PyErr_WriteUnraisable(PyObject *obj) PyFile_WriteString(": ", f); PyFile_WriteObject(v, f, 0); } + Py_XDECREF(moduleName); } PyFile_WriteString(" in ", f); PyFile_WriteObject(obj, f, 0); @@ -796,3 +802,8 @@ PyErr_ProgramText(const char *filename, int lineno) } return NULL; } + +#ifdef __cplusplus +} +#endif + diff --git a/Python/exceptions.c b/Python/exceptions.c index b146c97..5c824e6 100644 --- a/Python/exceptions.c +++ b/Python/exceptions.c @@ -14,6 +14,7 @@ * Copyright (c) 1998-2000 by Secret Labs AB. All rights reserved. */ +#define PY_SSIZE_T_CLEAN #include "Python.h" #include "osdefs.h" @@ -893,7 +894,7 @@ SyntaxError__str__(PyObject *self, PyObject *args) if (have_filename) bufsize += PyString_GET_SIZE(filename); - buffer = PyMem_MALLOC(bufsize); + buffer = (char *)PyMem_MALLOC(bufsize); if (buffer != NULL) { if (have_filename && have_lineno) PyOS_snprintf(buffer, bufsize, "%s (%s, line %ld)", @@ -1450,8 +1451,8 @@ PyObject * PyUnicodeDecodeError_Create( assert(length < INT_MAX); assert(start < INT_MAX); assert(end < INT_MAX); - return PyObject_CallFunction(PyExc_UnicodeDecodeError, "ss#iis", - encoding, object, (int)length, (int)start, (int)end, reason); + return PyObject_CallFunction(PyExc_UnicodeDecodeError, "ss#nns", + encoding, object, length, start, end, reason); } @@ -1565,7 +1566,7 @@ PyObject * PyUnicodeTranslateError_Create( const Py_UNICODE *object, Py_ssize_t length, Py_ssize_t start, Py_ssize_t end, const char *reason) { - return PyObject_CallFunction(PyExc_UnicodeTranslateError, "u#iis", + return PyObject_CallFunction(PyExc_UnicodeTranslateError, "u#nns", object, length, start, end, reason); } #endif diff --git a/Python/future.c b/Python/future.c index 1902f1d..d23fad6 100644 --- a/Python/future.c +++ b/Python/future.c @@ -19,7 +19,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename) names = s->v.ImportFrom.names; for (i = 0; i < asdl_seq_LEN(names); i++) { - alias_ty name = asdl_seq_GET(names, i); + alias_ty name = (alias_ty)asdl_seq_GET(names, i); const char *feature = PyString_AsString(name->name); if (!feature) return 0; @@ -29,7 +29,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename) continue; } else if (strcmp(feature, FUTURE_DIVISION) == 0) { continue; - } else if (strcmp(feature, FUTURE_ABSIMPORT) == 0) { + } else if (strcmp(feature, FUTURE_ABSOLUTE_IMPORT) == 0) { continue; } else if (strcmp(feature, FUTURE_WITH_STATEMENT) == 0) { continue; @@ -73,7 +73,7 @@ future_parse(PyFutureFeatures *ff, mod_ty mod, const char *filename) for (i = 0; i < asdl_seq_LEN(mod->v.Module.body); i++) { - stmt_ty s = asdl_seq_GET(mod->v.Module.body, i); + stmt_ty s = (stmt_ty)asdl_seq_GET(mod->v.Module.body, i); if (done && s->lineno > prev_line) return 1; @@ -120,14 +120,14 @@ PyFuture_FromAST(mod_ty mod, const char *filename) { PyFutureFeatures *ff; - ff = (PyFutureFeatures *)PyMem_Malloc(sizeof(PyFutureFeatures)); + ff = (PyFutureFeatures *)PyObject_Malloc(sizeof(PyFutureFeatures)); if (ff == NULL) return NULL; ff->ff_features = 0; ff->ff_lineno = -1; if (!future_parse(ff, mod, filename)) { - PyMem_Free((void *)ff); + PyObject_Free(ff); return NULL; } return ff; diff --git a/Python/getargs.c b/Python/getargs.c index fac0b6f..b0715ba 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -6,6 +6,9 @@ #include +#ifdef __cplusplus +extern "C" { +#endif int PyArg_Parse(PyObject *, const char *, ...); int PyArg_ParseTuple(PyObject *, const char *, ...); int PyArg_VaParse(PyObject *, const char *, va_list); @@ -15,6 +18,18 @@ int PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, int PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *, const char *, char **, va_list); +#ifdef HAVE_DECLSPEC_DLL +/* Export functions */ +PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, char *, ...); +PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, char *, ...); +PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywords_SizeT(PyObject *, PyObject *, + const char *, char **, ...); +PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); +PyAPI_FUNC(int) _PyArg_VaParse_SizeT(PyObject *, char *, va_list); +PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *, PyObject *, + const char *, char **, va_list); +#endif + #define FLAG_COMPAT 1 #define FLAG_SIZE_T 2 @@ -631,8 +646,8 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, unsigned int ival; if (float_argument_error(arg)) return converterr("integer", arg, msgbuf, bufsize); - ival = PyInt_AsUnsignedLongMask(arg); - if (ival == -1 && PyErr_Occurred()) + ival = (unsigned int)PyInt_AsUnsignedLongMask(arg); + if (ival == (unsigned int)-1 && PyErr_Occurred()) return converterr("integer", arg, msgbuf, bufsize); else *p = ival; @@ -645,10 +660,10 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, Py_ssize_t *p = va_arg(*p_va, Py_ssize_t *); Py_ssize_t ival; if (float_argument_error(arg)) - return converterr("integer", arg, msgbuf, bufsize); + return converterr("integer", arg, msgbuf, bufsize); ival = PyInt_AsSsize_t(arg); if (ival == -1 && PyErr_Occurred()) - return converterr("integer", arg, msgbuf, bufsize); + return converterr("integer", arg, msgbuf, bufsize); *p = ival; break; } @@ -1040,11 +1055,8 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, STORE_SIZE(PyUnicode_GET_SIZE(arg)); } else { - char *buf; - Py_ssize_t count = convertbuffer(arg, p, &buf); - if (count < 0) - return converterr(buf, arg, msgbuf, bufsize); - STORE_SIZE(count/(sizeof(Py_UNICODE))); + return converterr("cannot convert raw buffers", + arg, msgbuf, bufsize); } format++; } else { @@ -1743,3 +1755,6 @@ _PyArg_NoKeywords(const char *funcname, PyObject *kw) funcname); return 0; } +#ifdef __cplusplus +}; +#endif diff --git a/Python/getmtime.c b/Python/getmtime.c index f0ac899..54edb53 100644 --- a/Python/getmtime.c +++ b/Python/getmtime.c @@ -6,6 +6,10 @@ #include "Python.h" #include "pyconfig.h" +#ifdef __cplusplus +extern "C" { +#endif + time_t PyOS_GetLastModificationTime(char *path, FILE *fp) { @@ -15,3 +19,8 @@ PyOS_GetLastModificationTime(char *path, FILE *fp) else return st.st_mtime; } + +#ifdef __cplusplus +} +#endif + diff --git a/Python/getopt.c b/Python/getopt.c index d80f607..5429fac5 100644 --- a/Python/getopt.c +++ b/Python/getopt.c @@ -27,6 +27,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + int _PyOS_opterr = 1; /* generate error messages */ int _PyOS_optind = 1; /* index into argv array */ char *_PyOS_optarg = NULL; /* optional argument */ @@ -81,3 +85,8 @@ int _PyOS_GetOpt(int argc, char **argv, char *optstring) return option; } + +#ifdef __cplusplus +} +#endif + diff --git a/Python/graminit.c b/Python/graminit.c index 40f1770..a6ac124 100644 --- a/Python/graminit.c +++ b/Python/graminit.c @@ -1675,7 +1675,7 @@ static arc arcs_77_0[1] = { {91, 1}, }; static arc arcs_77_1[1] = { - {26, 2}, + {105, 2}, }; static arc arcs_77_2[2] = { {162, 3}, @@ -1732,7 +1732,7 @@ static arc arcs_80_0[1] = { {91, 1}, }; static arc arcs_80_1[1] = { - {26, 2}, + {105, 2}, }; static arc arcs_80_2[2] = { {164, 3}, diff --git a/Python/import.c b/Python/import.c index b64594d..daae15f 100644 --- a/Python/import.c +++ b/Python/import.c @@ -17,6 +17,9 @@ #ifdef HAVE_FCNTL_H #include #endif +#ifdef __cplusplus +extern "C" { +#endif extern time_t PyOS_GetLastModificationTime(char *, FILE *); /* In getmtime.c */ @@ -40,6 +43,7 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); Python 1.5: 20121 Python 1.5.1: 20121 Python 1.5.2: 20121 + Python 1.6: 50428 Python 2.0: 50823 Python 2.0.1: 50823 Python 2.1: 60202 @@ -1217,12 +1221,12 @@ find_module(char *fullname, char *subname, PyObject *path, char *buf, #endif if (!PyString_Check(v)) continue; - len = PyString_Size(v); + len = PyString_GET_SIZE(v); if (len + 2 + namelen + MAXSUFFIXSIZE >= buflen) { Py_XDECREF(copy); continue; /* Too long */ } - strcpy(buf, PyString_AsString(v)); + strcpy(buf, PyString_AS_STRING(v)); if (strlen(buf) != len) { Py_XDECREF(copy); continue; /* v contains '\0' */ @@ -1934,6 +1938,16 @@ import_module_level(char *name, PyObject *globals, PyObject *locals, } tail = next; } + if (tail == Py_None) { + /* If tail is Py_None, both get_parent and load_next found + an empty module name: someone called __import__("") or + doctored faulty bytecode */ + Py_DECREF(tail); + Py_DECREF(head); + PyErr_SetString(PyExc_ValueError, + "Empty module name"); + return NULL; + } if (fromlist != NULL) { if (fromlist == Py_None || !PyObject_IsTrue(fromlist)) @@ -2094,7 +2108,8 @@ load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf, PyObject *result; if (strlen(name) == 0) { - /* empty module name only happens in 'from . import' */ + /* completely empty module name should only happen in + 'from . import' (or '__import__("")')*/ Py_INCREF(mod); *p_name = NULL; return mod; @@ -2936,3 +2951,7 @@ PyImport_AppendInittab(char *name, void (*initfunc)(void)) return PyImport_ExtendInittab(newtab); } + +#ifdef __cplusplus +} +#endif diff --git a/Python/mactoolboxglue.c b/Python/mactoolboxglue.c index 7f0627e..0aa2cfd 100644 --- a/Python/mactoolboxglue.c +++ b/Python/mactoolboxglue.c @@ -25,6 +25,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "Python.h" #include "pymactoolbox.h" +#include /* for ntohl, htonl */ /* Like strerror() but for Mac OS error numbers */ @@ -156,12 +157,14 @@ PyMac_GetFullPathname(FSSpec *fss, char *path, int len) int PyMac_GetOSType(PyObject *v, OSType *pr) { + uint32_t tmp; if (!PyString_Check(v) || PyString_Size(v) != 4) { PyErr_SetString(PyExc_TypeError, "OSType arg must be string of 4 chars"); return 0; } - memcpy((char *)pr, PyString_AsString(v), 4); + memcpy((char *)&tmp, PyString_AsString(v), 4); + *pr = (OSType)ntohl(tmp); return 1; } @@ -169,7 +172,8 @@ PyMac_GetOSType(PyObject *v, OSType *pr) PyObject * PyMac_BuildOSType(OSType t) { - return PyString_FromStringAndSize((char *)&t, 4); + uint32_t tmp = htonl((uint32_t)t); + return PyString_FromStringAndSize((char *)&tmp, 4); } /* Convert an NumVersion value to a 4-element tuple */ diff --git a/Python/modsupport.c b/Python/modsupport.c index cb6bdfd..e291014 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -3,8 +3,11 @@ #include "Python.h" +#define FLAG_SIZE_T 1 typedef double va_double; +static PyObject *va_build_value(const char *, va_list, int); + /* Package context -- the full module name for package imports */ char *_Py_PackageContext = NULL; @@ -146,14 +149,14 @@ countformat(const char *format, int endchar) /* Generic function to create a value -- the inverse of getargs() */ /* After an original idea and first implementation by Steven Miale */ -static PyObject *do_mktuple(const char**, va_list *, int, int); -static PyObject *do_mklist(const char**, va_list *, int, int); -static PyObject *do_mkdict(const char**, va_list *, int, int); -static PyObject *do_mkvalue(const char**, va_list *); +static PyObject *do_mktuple(const char**, va_list *, int, int, int); +static PyObject *do_mklist(const char**, va_list *, int, int, int); +static PyObject *do_mkdict(const char**, va_list *, int, int, int); +static PyObject *do_mkvalue(const char**, va_list *, int); static PyObject * -do_mkdict(const char **p_format, va_list *p_va, int endchar, int n) +do_mkdict(const char **p_format, va_list *p_va, int endchar, int n, int flags) { PyObject *d; int i; @@ -167,13 +170,13 @@ do_mkdict(const char **p_format, va_list *p_va, int endchar, int n) for (i = 0; i < n; i+= 2) { PyObject *k, *v; int err; - k = do_mkvalue(p_format, p_va); + k = do_mkvalue(p_format, p_va, flags); if (k == NULL) { itemfailed = 1; Py_INCREF(Py_None); k = Py_None; } - v = do_mkvalue(p_format, p_va); + v = do_mkvalue(p_format, p_va, flags); if (v == NULL) { itemfailed = 1; Py_INCREF(Py_None); @@ -199,7 +202,7 @@ do_mkdict(const char **p_format, va_list *p_va, int endchar, int n) } static PyObject * -do_mklist(const char **p_format, va_list *p_va, int endchar, int n) +do_mklist(const char **p_format, va_list *p_va, int endchar, int n, int flags) { PyObject *v; int i; @@ -212,13 +215,13 @@ do_mklist(const char **p_format, va_list *p_va, int endchar, int n) /* Note that we can't bail immediately on error as this will leak refcounts on any 'N' arguments. */ for (i = 0; i < n; i++) { - PyObject *w = do_mkvalue(p_format, p_va); + PyObject *w = do_mkvalue(p_format, p_va, flags); if (w == NULL) { itemfailed = 1; Py_INCREF(Py_None); w = Py_None; } - PyList_SetItem(v, i, w); + PyList_SET_ITEM(v, i, w); } if (itemfailed) { @@ -232,7 +235,6 @@ do_mklist(const char **p_format, va_list *p_va, int endchar, int n) "Unmatched paren in format"); return NULL; } - if (endchar) ++*p_format; return v; @@ -250,7 +252,7 @@ _ustrlen(Py_UNICODE *u) #endif static PyObject * -do_mktuple(const char **p_format, va_list *p_va, int endchar, int n) +do_mktuple(const char **p_format, va_list *p_va, int endchar, int n, int flags) { PyObject *v; int i; @@ -262,45 +264,46 @@ do_mktuple(const char **p_format, va_list *p_va, int endchar, int n) /* Note that we can't bail immediately on error as this will leak refcounts on any 'N' arguments. */ for (i = 0; i < n; i++) { - PyObject *w = do_mkvalue(p_format, p_va); + PyObject *w = do_mkvalue(p_format, p_va, flags); if (w == NULL) { itemfailed = 1; Py_INCREF(Py_None); w = Py_None; } - PyTuple_SetItem(v, i, w); + PyTuple_SET_ITEM(v, i, w); + } + if (itemfailed) { + /* do_mkvalue() should have already set an error */ + Py_DECREF(v); + return NULL; } - if (v != NULL && **p_format != endchar) { + if (**p_format != endchar) { Py_DECREF(v); - v = NULL; PyErr_SetString(PyExc_SystemError, "Unmatched paren in format"); + return NULL; } - else if (endchar) + if (endchar) ++*p_format; - if (itemfailed) { - Py_DECREF(v); - v = NULL; - } return v; } static PyObject * -do_mkvalue(const char **p_format, va_list *p_va) +do_mkvalue(const char **p_format, va_list *p_va, int flags) { for (;;) { switch (*(*p_format)++) { case '(': return do_mktuple(p_format, p_va, ')', - countformat(*p_format, ')')); + countformat(*p_format, ')'), flags); case '[': return do_mklist(p_format, p_va, ']', - countformat(*p_format, ']')); + countformat(*p_format, ']'), flags); case '{': return do_mkdict(p_format, p_va, '}', - countformat(*p_format, '}')); + countformat(*p_format, '}'), flags); case 'b': case 'B': @@ -351,10 +354,13 @@ do_mkvalue(const char **p_format, va_list *p_va) { PyObject *v; Py_UNICODE *u = va_arg(*p_va, Py_UNICODE *); - int n; + Py_ssize_t n; if (**p_format == '#') { ++*p_format; - n = va_arg(*p_va, int); + if (flags & FLAG_SIZE_T) + n = va_arg(*p_va, Py_ssize_t); + else + n = va_arg(*p_va, int); } else n = -1; @@ -393,10 +399,13 @@ do_mkvalue(const char **p_format, va_list *p_va) { PyObject *v; char *str = va_arg(*p_va, char *); - int n; + Py_ssize_t n; if (**p_format == '#') { ++*p_format; - n = va_arg(*p_va, int); + if (flags & FLAG_SIZE_T) + n = va_arg(*p_va, Py_ssize_t); + else + n = va_arg(*p_va, int); } else n = -1; @@ -407,7 +416,7 @@ do_mkvalue(const char **p_format, va_list *p_va) else { if (n < 0) { size_t m = strlen(str); - if (m > INT_MAX) { + if (m > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "string too long for Python string"); return NULL; @@ -472,7 +481,18 @@ Py_BuildValue(const char *format, ...) va_list va; PyObject* retval; va_start(va, format); - retval = Py_VaBuildValue(format, va); + retval = va_build_value(format, va, 0); + va_end(va); + return retval; +} + +PyObject * +_Py_BuildValue_SizeT(const char *format, ...) +{ + va_list va; + PyObject* retval; + va_start(va, format); + retval = va_build_value(format, va, FLAG_SIZE_T); va_end(va); return retval; } @@ -480,6 +500,18 @@ Py_BuildValue(const char *format, ...) PyObject * Py_VaBuildValue(const char *format, va_list va) { + return va_build_value(format, va, 0); +} + +PyObject * +_Py_VaBuildValue_SizeT(const char *format, va_list va) +{ + return va_build_value(format, va, FLAG_SIZE_T); +} + +static PyObject * +va_build_value(const char *format, va_list va, int flags) +{ const char *f = format; int n = countformat(f, '\0'); va_list lva; @@ -501,8 +533,8 @@ Py_VaBuildValue(const char *format, va_list va) return Py_None; } if (n == 1) - return do_mkvalue(&f, &lva); - return do_mktuple(&f, &lva, '\0', n); + return do_mkvalue(&f, &lva, flags); + return do_mktuple(&f, &lva, '\0', n, flags); } diff --git a/Python/pyarena.c b/Python/pyarena.c index 242ca1d..f11a905 100644 --- a/Python/pyarena.c +++ b/Python/pyarena.c @@ -6,9 +6,16 @@ Measurements with standard library modules suggest the average allocation is about 20 bytes and that most compiles use a single block. + + TODO(jhylton): Think about a realloc API, maybe just for the last + allocation? */ #define DEFAULT_BLOCK_SIZE 8192 +#define ALIGNMENT 8 +#define ALIGNMENT_MASK (ALIGNMENT - 1) +#define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK) + typedef struct _block { /* Total number of bytes owned by this block available to pass out. * Read-only after initialization. The first such byte starts at @@ -39,9 +46,25 @@ typedef struct _block { */ struct _arena { + /* Pointer to the first block allocated for the arena, never NULL. + It is used only to find the first block when the arena is + being freed. + */ block *a_head; + + /* Pointer to the block currently used for allocation. It's + ab_next field should be NULL. If it is not-null after a + call to block_alloc(), it means a new block has been allocated + and a_cur should be reset to point it. + */ block *a_cur; + + /* A Python list object containing references to all the PyObject + pointers associated with this area. They will be DECREFed + when the arena is freed. + */ PyObject *a_objects; + #if defined(Py_DEBUG) /* Debug output */ size_t total_allocs; @@ -63,7 +86,8 @@ block_new(size_t size) b->ab_size = size; b->ab_mem = (void *)(b + 1); b->ab_next = NULL; - b->ab_offset = 0; + b->ab_offset = ROUNDUP((Py_uintptr_t)(b->ab_mem)) - + (Py_uintptr_t)(b->ab_mem); return b; } @@ -81,19 +105,20 @@ block_alloc(block *b, size_t size) { void *p; assert(b); + size = ROUNDUP(size); if (b->ab_offset + size > b->ab_size) { /* If we need to allocate more memory than will fit in the default block, allocate a one-off block that is exactly the right size. */ /* TODO(jhylton): Think about space waste at end of block */ - block *new = block_new( + block *newbl = block_new( size < DEFAULT_BLOCK_SIZE ? DEFAULT_BLOCK_SIZE : size); - if (!new) + if (!newbl) return NULL; assert(!b->ab_next); - b->ab_next = new; - b = new; + b->ab_next = newbl; + b = newbl; } assert(b->ab_offset + size <= b->ab_size); @@ -134,6 +159,7 @@ PyArena_New() void PyArena_Free(PyArena *arena) { + int r; assert(arena); #if defined(Py_DEBUG) /* @@ -145,7 +171,17 @@ PyArena_Free(PyArena *arena) */ #endif block_free(arena->a_head); + /* This property normally holds, except when the code being compiled + is sys.getobjects(0), in which case there will be two references. assert(arena->a_objects->ob_refcnt == 1); + */ + + /* Clear all the elements from the list. This is necessary + to guarantee that they will be DECREFed. */ + r = PyList_SetSlice(arena->a_objects, + 0, PyList_GET_SIZE(arena->a_objects), NULL); + assert(r == 0); + assert(PyList_GET_SIZE(arena->a_objects) == 0); Py_DECREF(arena->a_objects); free(arena); } diff --git a/Python/pystate.c b/Python/pystate.c index 867334e..b8f460f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -23,13 +23,6 @@ the expense of doing their own locking). #endif -#define ZAP(x) { \ - PyObject *tmp = (PyObject *)(x); \ - (x) = NULL; \ - Py_XDECREF(tmp); \ -} - - #ifdef WITH_THREAD #include "pythread.h" static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */ @@ -37,6 +30,10 @@ static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */ #define HEAD_LOCK() PyThread_acquire_lock(head_mutex, WAIT_LOCK) #define HEAD_UNLOCK() PyThread_release_lock(head_mutex) +#ifdef __cplusplus +extern "C" { +#endif + /* The single PyInterpreterState used by this process' GILState implementation */ @@ -102,12 +99,12 @@ PyInterpreterState_Clear(PyInterpreterState *interp) for (p = interp->tstate_head; p != NULL; p = p->next) PyThreadState_Clear(p); HEAD_UNLOCK(); - ZAP(interp->codec_search_path); - ZAP(interp->codec_search_cache); - ZAP(interp->codec_error_registry); - ZAP(interp->modules); - ZAP(interp->sysdict); - ZAP(interp->builtins); + Py_CLEAR(interp->codec_search_path); + Py_CLEAR(interp->codec_search_cache); + Py_CLEAR(interp->codec_error_registry); + Py_CLEAR(interp->modules); + Py_CLEAR(interp->sysdict); + Py_CLEAR(interp->builtins); } @@ -211,23 +208,23 @@ PyThreadState_Clear(PyThreadState *tstate) fprintf(stderr, "PyThreadState_Clear: warning: thread still has a frame\n"); - ZAP(tstate->frame); + Py_CLEAR(tstate->frame); - ZAP(tstate->dict); - ZAP(tstate->async_exc); + Py_CLEAR(tstate->dict); + Py_CLEAR(tstate->async_exc); - ZAP(tstate->curexc_type); - ZAP(tstate->curexc_value); - ZAP(tstate->curexc_traceback); + Py_CLEAR(tstate->curexc_type); + Py_CLEAR(tstate->curexc_value); + Py_CLEAR(tstate->curexc_traceback); - ZAP(tstate->exc_type); - ZAP(tstate->exc_value); - ZAP(tstate->exc_traceback); + Py_CLEAR(tstate->exc_type); + Py_CLEAR(tstate->exc_value); + Py_CLEAR(tstate->exc_traceback); tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; - ZAP(tstate->c_profileobj); - ZAP(tstate->c_traceobj); + Py_CLEAR(tstate->c_profileobj); + Py_CLEAR(tstate->c_traceobj); } @@ -297,23 +294,23 @@ PyThreadState_Get(void) PyThreadState * -PyThreadState_Swap(PyThreadState *new) +PyThreadState_Swap(PyThreadState *newts) { - PyThreadState *old = _PyThreadState_Current; + PyThreadState *oldts = _PyThreadState_Current; - _PyThreadState_Current = new; + _PyThreadState_Current = newts; /* It should not be possible for more than one thread state to be used for a thread. Check this the best we can in debug builds. */ #if defined(Py_DEBUG) && defined(WITH_THREAD) - if (new) { + if (newts) { PyThreadState *check = PyGILState_GetThisThreadState(); - if (check && check->interp == new->interp && check != new) + if (check && check->interp == newts->interp && check != newts) Py_FatalError("Invalid thread state for this thread"); } #endif - return old; + return oldts; } /* An extension mechanism to store arbitrary additional per-thread state. @@ -356,7 +353,7 @@ PyThreadState_SetAsyncExc(long id, PyObject *exc) { for (p = interp->tstate_head; p != NULL; p = p->next) { if (p->thread_id != id) continue; - ZAP(p->async_exc); + Py_CLEAR(p->async_exc); Py_XINCREF(exc); p->async_exc = exc; count += 1; @@ -491,7 +488,7 @@ PyGILState_Ensure(void) called Py_Initialize() and usually PyEval_InitThreads(). */ assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */ - tcur = PyThread_get_key_value(autoTLSkey); + tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey); if (tcur == NULL) { /* Create a new thread state for this thread */ tcur = PyThreadState_New(autoInterpreterState); @@ -518,7 +515,8 @@ PyGILState_Ensure(void) void PyGILState_Release(PyGILState_STATE oldstate) { - PyThreadState *tcur = PyThread_get_key_value(autoTLSkey); + PyThreadState *tcur = (PyThreadState *)PyThread_get_key_value( + autoTLSkey); if (tcur == NULL) Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); @@ -551,4 +549,11 @@ PyGILState_Release(PyGILState_STATE oldstate) else if (oldstate == PyGILState_UNLOCKED) PyEval_SaveThread(); } + +#ifdef __cplusplus +} +#endif + #endif /* WITH_THREAD */ + + diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 83e792d..db4cad1 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -101,7 +101,7 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) char *copy, *c; /* We need to convert the '.' to the locale specific decimal point */ - copy = malloc(end - nptr + 1 + decimal_point_len); + copy = (char *)malloc(end - nptr + 1 + decimal_point_len); c = copy; memcpy(c, nptr, decimal_point_pos - nptr); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 2c5400d..99d6d9d 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -30,14 +30,15 @@ #endif #ifndef Py_REF_DEBUG -# define PRINT_TOTAL_REFS() +#define PRINT_TOTAL_REFS() #else /* Py_REF_DEBUG */ -# if defined(MS_WIN64) -# define PRINT_TOTAL_REFS() fprintf(stderr, "[%Id refs]\n", _Py_RefTotal); -# else /* ! MS_WIN64 */ -# define PRINT_TOTAL_REFS() fprintf(stderr, "[%ld refs]\n", \ - Py_SAFE_DOWNCAST(_Py_RefTotal, Py_ssize_t, long)); -# endif /* MS_WIN64 */ +#define PRINT_TOTAL_REFS() fprintf(stderr, \ + "[%" PY_FORMAT_SIZE_T "d refs]\n", \ + _Py_GetRefTotal()) +#endif + +#ifdef __cplusplus +extern "C" { #endif extern char *Py_GetPath(void); @@ -280,6 +281,16 @@ Py_InitializeEx(int install_sigs) } Py_XDECREF(sys_isatty); + sys_stream = PySys_GetObject("stderr"); + sys_isatty = PyObject_CallMethod(sys_stream, "isatty", ""); + if (!sys_isatty) + PyErr_Clear(); + if(sys_isatty && PyObject_IsTrue(sys_isatty)) { + if (!PyFile_SetEncoding(sys_stream, codeset)) + Py_FatalError("Cannot set codeset of stderr"); + } + Py_XDECREF(sys_isatty); + if (!Py_FileSystemDefaultEncoding) Py_FileSystemDefaultEncoding = codeset; else @@ -296,7 +307,7 @@ Py_Initialize(void) #ifdef COUNT_ALLOCS -extern void dump_counts(void); +extern void dump_counts(FILE*); #endif /* Undo the effect of Py_Initialize(). @@ -358,6 +369,13 @@ Py_Finalize(void) * XXX I haven't seen a real-life report of either of these. */ PyGC_Collect(); +#ifdef COUNT_ALLOCS + /* With COUNT_ALLOCS, it helps to run GC multiple times: + each collection might release some types from the type + list, so they become garbage. */ + while (PyGC_Collect() > 0) + /* nothing */; +#endif /* Destroy all modules */ PyImport_Cleanup(); @@ -386,10 +404,10 @@ Py_Finalize(void) /* Debugging stuff */ #ifdef COUNT_ALLOCS - dump_counts(); + dump_counts(stdout); #endif - PRINT_TOTAL_REFS() + PRINT_TOTAL_REFS(); #ifdef Py_TRACE_REFS /* Display all objects still alive -- this can invoke arbitrary @@ -679,7 +697,7 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flag } for (;;) { ret = PyRun_InteractiveOneFlags(fp, filename, flags); - PRINT_TOTAL_REFS() + PRINT_TOTAL_REFS(); if (ret == E_EOF) return 0; /* @@ -1451,7 +1469,7 @@ err_input(perrdetail *err) v = Py_BuildValue("(ziiz)", err->filename, err->lineno, err->offset, err->text); if (err->text != NULL) { - PyMem_DEL(err->text); + PyObject_FREE(err->text); err->text = NULL; } w = NULL; @@ -1666,16 +1684,113 @@ PyOS_setsig(int sig, PyOS_sighandler_t handler) /* Deprecated C API functions still provided for binary compatiblity */ #undef PyParser_SimpleParseFile -#undef PyParser_SimpleParseString - -node * +PyAPI_FUNC(node *) PyParser_SimpleParseFile(FILE *fp, const char *filename, int start) { return PyParser_SimpleParseFileFlags(fp, filename, start, 0); } -node * +#undef PyParser_SimpleParseString +PyAPI_FUNC(node *) PyParser_SimpleParseString(const char *str, int start) { return PyParser_SimpleParseStringFlags(str, start, 0); } + +#undef PyRun_AnyFile +PyAPI_FUNC(int) +PyRun_AnyFile(FILE *fp, const char *name) +{ + return PyRun_AnyFileExFlags(fp, name, 0, NULL); +} + +#undef PyRun_AnyFileEx +PyAPI_FUNC(int) +PyRun_AnyFileEx(FILE *fp, const char *name, int closeit) +{ + return PyRun_AnyFileExFlags(fp, name, closeit, NULL); +} + +#undef PyRun_AnyFileFlags +PyAPI_FUNC(int) +PyRun_AnyFileFlags(FILE *fp, const char *name, PyCompilerFlags *flags) +{ + return PyRun_AnyFileExFlags(fp, name, 0, flags); +} + +#undef PyRun_File +PyAPI_FUNC(PyObject *) +PyRun_File(FILE *fp, const char *p, int s, PyObject *g, PyObject *l) +{ + return PyRun_FileExFlags(fp, p, s, g, l, 0, NULL); +} + +#undef PyRun_FileEx +PyAPI_FUNC(PyObject *) +PyRun_FileEx(FILE *fp, const char *p, int s, PyObject *g, PyObject *l, int c) +{ + return PyRun_FileExFlags(fp, p, s, g, l, c, NULL); +} + +#undef PyRun_FileFlags +PyAPI_FUNC(PyObject *) +PyRun_FileFlags(FILE *fp, const char *p, int s, PyObject *g, PyObject *l, + PyCompilerFlags *flags) +{ + return PyRun_FileExFlags(fp, p, s, g, l, 0, flags); +} + +#undef PyRun_SimpleFile +PyAPI_FUNC(int) +PyRun_SimpleFile(FILE *f, const char *p) +{ + return PyRun_SimpleFileExFlags(f, p, 0, NULL); +} + +#undef PyRun_SimpleFileEx +PyAPI_FUNC(int) +PyRun_SimpleFileEx(FILE *f, const char *p, int c) +{ + return PyRun_SimpleFileExFlags(f, p, c, NULL); +} + + +#undef PyRun_String +PyAPI_FUNC(PyObject *) +PyRun_String(const char *str, int s, PyObject *g, PyObject *l) +{ + return PyRun_StringFlags(str, s, g, l, NULL); +} + +#undef PyRun_SimpleString +PyAPI_FUNC(int) +PyRun_SimpleString(const char *s) +{ + return PyRun_SimpleStringFlags(s, NULL); +} + +#undef Py_CompileString +PyAPI_FUNC(PyObject *) +Py_CompileString(const char *str, const char *p, int s) +{ + return Py_CompileStringFlags(str, p, s, NULL); +} + +#undef PyRun_InteractiveOne +PyAPI_FUNC(int) +PyRun_InteractiveOne(FILE *f, const char *p) +{ + return PyRun_InteractiveOneFlags(f, p, NULL); +} + +#undef PyRun_InteractiveLoop +PyAPI_FUNC(int) +PyRun_InteractiveLoop(FILE *f, const char *p) +{ + return PyRun_InteractiveLoopFlags(f, p, NULL); +} + +#ifdef __cplusplus +} +#endif + diff --git a/Python/symtable.c b/Python/symtable.c index c8eab58..184723d 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -227,7 +227,8 @@ PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) case Module_kind: seq = mod->v.Module.body; for (i = 0; i < asdl_seq_LEN(seq); i++) - if (!symtable_visit_stmt(st, asdl_seq_GET(seq, i))) + if (!symtable_visit_stmt(st, + (stmt_ty)asdl_seq_GET(seq, i))) goto error; break; case Expression_kind: @@ -237,7 +238,8 @@ PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) case Interactive_kind: seq = mod->v.Interactive.body; for (i = 0; i < asdl_seq_LEN(seq); i++) - if (!symtable_visit_stmt(st, asdl_seq_GET(seq, i))) + if (!symtable_visit_stmt(st, + (stmt_ty)asdl_seq_GET(seq, i))) goto error; break; case Suite_kind: @@ -506,7 +508,7 @@ check_unoptimized(const PySTEntryObject* ste) { */ static int update_symbols(PyObject *symbols, PyObject *scope, - PyObject *bound, PyObject *free, int class) + PyObject *bound, PyObject *free, int classflag) { PyObject *name, *v, *u, *w, *free_value = NULL; Py_ssize_t pos = 0; @@ -541,7 +543,7 @@ update_symbols(PyObject *symbols, PyObject *scope, the class that has the same name as a local or global in the class scope. */ - if (class && + if (classflag && PyInt_AS_LONG(o) & (DEF_BOUND | DEF_GLOBAL)) { long i = PyInt_AS_LONG(o) | DEF_FREE_CLASS; o = PyInt_FromLong(i); @@ -851,7 +853,7 @@ error: int i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (i = 0; i < asdl_seq_LEN(seq); i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ if (!symtable_visit_ ## TYPE((ST), elt)) \ return 0; \ } \ @@ -861,7 +863,7 @@ error: int i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (i = 0; i < asdl_seq_LEN(seq); i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ if (!symtable_visit_ ## TYPE((ST), elt)) { \ symtable_exit_block((ST), (S)); \ return 0; \ @@ -873,7 +875,7 @@ error: int i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (i = (START); i < asdl_seq_LEN(seq); i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ if (!symtable_visit_ ## TYPE((ST), elt)) \ return 0; \ } \ @@ -883,7 +885,7 @@ error: int i; \ asdl_seq *seq = (SEQ); /* avoid variable capture */ \ for (i = (START); i < asdl_seq_LEN(seq); i++) { \ - TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ if (!symtable_visit_ ## TYPE((ST), elt)) { \ symtable_exit_block((ST), (S)); \ return 0; \ @@ -1036,7 +1038,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) int i; asdl_seq *seq = s->v.Global.names; for (i = 0; i < asdl_seq_LEN(seq); i++) { - identifier name = asdl_seq_GET(seq, i); + identifier name = (identifier)asdl_seq_GET(seq, i); char *c_name = PyString_AS_STRING(name); long cur = symtable_lookup(st, name); if (cur < 0) @@ -1200,7 +1202,7 @@ symtable_visit_params(struct symtable *st, asdl_seq *args, int toplevel) /* go through all the toplevel arguments first */ for (i = 0; i < asdl_seq_LEN(args); i++) { - expr_ty arg = asdl_seq_GET(args, i); + expr_ty arg = (expr_ty)asdl_seq_GET(args, i); if (arg->kind == Name_kind) { assert(arg->v.Name.ctx == Param || (arg->v.Name.ctx == Store && !toplevel)); @@ -1236,7 +1238,7 @@ symtable_visit_params_nested(struct symtable *st, asdl_seq *args) { int i; for (i = 0; i < asdl_seq_LEN(args); i++) { - expr_ty arg = asdl_seq_GET(args, i); + expr_ty arg = (expr_ty)asdl_seq_GET(args, i); if (arg->kind == Tuple_kind && !symtable_visit_params(st, arg->v.Tuple.elts, 0)) return 0; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 875f73f..c6eb91f 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -600,10 +600,9 @@ sys_getrefcount(PyObject *self, PyObject *arg) static PyObject * sys_gettotalrefcount(PyObject *self) { - return PyInt_FromSsize_t(_Py_RefTotal); + return PyInt_FromSsize_t(_Py_GetRefTotal()); } - -#endif /* Py_TRACE_REFS */ +#endif /* Py_REF_DEBUG */ PyDoc_STRVAR(getrefcount_doc, "getrefcount(object) -> integer\n\ @@ -697,6 +696,10 @@ a 11-tuple where the entries in the tuple are counts of:\n\ 10. Number of stack pops performed by call_function()" ); +#ifdef __cplusplus +extern "C" { +#endif + #ifdef Py_TRACE_REFS /* Defined in objects.c because it uses static globals if that file */ extern PyObject *_Py_GetObjects(PyObject *, PyObject *); @@ -707,6 +710,10 @@ extern PyObject *_Py_GetObjects(PyObject *, PyObject *); extern PyObject *_Py_GetDXProfile(PyObject *, PyObject *); #endif +#ifdef __cplusplus +} +#endif + static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ {"callstats", (PyCFunction)PyEval_GetCallStats, METH_NOARGS, @@ -1065,6 +1072,11 @@ _PySys_Init(void) if (!PyFile_SetEncoding(sysout, buf)) return NULL; } + if(isatty(_fileno(stderr))) { + sprintf(buf, "cp%d", GetConsoleOutputCP()); + if (!PyFile_SetEncoding(syserr, buf)) + return NULL; + } #endif PyDict_SetItemString(sysdict, "stdin", sysin); @@ -1406,7 +1418,7 @@ mywrite(char *name, FILE *fp, const char *format, va_list va) PyErr_Clear(); fputs(buffer, fp); } - if (written < 0 || written >= sizeof(buffer)) { + if (written < 0 || (size_t)written >= sizeof(buffer)) { const char *truncated = "... truncated"; if (PyFile_WriteString(truncated, file) != 0) { PyErr_Clear(); diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 781467f..c29a61c 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -26,6 +26,16 @@ #endif #endif +/* Before FreeBSD 5.4, system scope threads was very limited resource + in default setting. So the process scope is preferred to get + enough number of threads to work. */ +#ifdef __FreeBSD__ +#include +#if __FreeBSD_version >= 500000 && __FreeBSD_version < 504101 +#undef PTHREAD_SYSTEM_SCHED_SUPPORTED +#endif +#endif + #if !defined(pthread_attr_default) # define pthread_attr_default ((pthread_attr_t *)NULL) #endif @@ -138,7 +148,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) #ifdef THREAD_STACK_SIZE pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE); #endif -#if defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) && !defined(__FreeBSD__) +#if defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM); #endif diff --git a/Python/traceback.c b/Python/traceback.c index 6c11cf5..cfbd833 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -39,24 +39,16 @@ tb_dealloc(PyTracebackObject *tb) static int tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg) { - int err = 0; - if (tb->tb_next) { - err = visit((PyObject *)tb->tb_next, arg); - if (err) - return err; - } - if (tb->tb_frame) - err = visit((PyObject *)tb->tb_frame, arg); - return err; + Py_VISIT(tb->tb_next); + Py_VISIT(tb->tb_frame); + return 0; } static void tb_clear(PyTracebackObject *tb) { - Py_XDECREF(tb->tb_next); - Py_XDECREF(tb->tb_frame); - tb->tb_next = NULL; - tb->tb_frame = NULL; + Py_CLEAR(tb->tb_next); + Py_CLEAR(tb->tb_frame); } PyTypeObject PyTraceBack_Type = { @@ -165,7 +157,7 @@ tb_displayline(PyObject *f, char *filename, int lineno, char *name) } if (PyString_Check(v)) { size_t len; - len = PyString_Size(v); + len = PyString_GET_SIZE(v); if (len + 1 + taillen >= MAXPATHLEN) continue; /* Too long */ strcpy(namebuf, PyString_AsString(v)); diff --git a/README b/README index 919af4c..1da5430 100644 --- a/README +++ b/README @@ -89,7 +89,7 @@ LaTeX formats; the LaTeX version is primarily for documentation authors, translators, and people with special formatting requirements. Unfortunately, new-style classes (new in Python 2.2) have not yet been -integrated into Python's standard documention. A collection of +integrated into Python's standard documentation. A collection of pointers to what has been written is at: http://www.python.org/doc/newstyle.html @@ -114,7 +114,7 @@ for Python-related announcements. These are also accessible as mailing lists: see http://www.python.org/community/lists.html for an overview of these and many other Python-related mailing lists. -Archives are accessible via the Google Groups usenet archive; see +Archives are accessible via the Google Groups Usenet archive; see http://groups.google.com/. The mailing lists are also archived, see http://www.python.org/community/lists.html for details. @@ -257,28 +257,28 @@ submit a documentation bug report to SourceForge (see Bug Reports above) so we can remove them!) Unix platforms: If your vendor still ships (and you still use) Berkeley DB - 1.85 you will need to edit Modules/Setup to build the bsddb185 - module and add a line to sitecustomize.py which makes it the - default. In Modules/Setup a line like + 1.85 you will need to edit Modules/Setup to build the bsddb185 + module and add a line to sitecustomize.py which makes it the + default. In Modules/Setup a line like - bsddb185 bsddbmodule.c + bsddb185 bsddbmodule.c - should work. (You may need to add -I, -L or -l flags to direct the - compiler and linker to your include files and libraries.) + should work. (You may need to add -I, -L or -l flags to direct the + compiler and linker to your include files and libraries.) XXX I think this next bit is out of date: 64-bit platforms: The modules audioop, imageop and rgbimg don't work. - The setup.py script disables them on 64-bit installations. - Don't try to enable them in the Modules/Setup file. They - contain code that is quite wordsize sensitive. (If you have a - fix, let us know!) + The setup.py script disables them on 64-bit installations. + Don't try to enable them in the Modules/Setup file. They + contain code that is quite wordsize sensitive. (If you have a + fix, let us know!) Solaris: When using Sun's C compiler with threads, at least on Solaris - 2.5.1, you need to add the "-mt" compiler option (the simplest - way is probably to specify the compiler with this option as - the "CC" environment variable when running the configure - script). + 2.5.1, you need to add the "-mt" compiler option (the simplest + way is probably to specify the compiler with this option as + the "CC" environment variable when running the configure + script). When using GCC on Solaris, beware of binutils 2.13 or GCC versions built using it. This mistakenly enables the @@ -290,136 +290,136 @@ Solaris: When using Sun's C compiler with threads, at least on Solaris and 2.8, but may also affect earlier and later versions of the OS. - When the dynamic loader complains about errors finding shared - libraries, such as + When the dynamic loader complains about errors finding shared + libraries, such as - ld.so.1: ./python: fatal: libstdc++.so.5: open failed: - No such file or directory + ld.so.1: ./python: fatal: libstdc++.so.5: open failed: + No such file or directory - you need to first make sure that the library is available on - your system. Then, you need to instruct the dynamic loader how - to find it. You can choose any of the following strategies: + you need to first make sure that the library is available on + your system. Then, you need to instruct the dynamic loader how + to find it. You can choose any of the following strategies: - 1. When compiling Python, set LD_RUN_PATH to the directories - containing missing libraries. - 2. When running Python, set LD_LIBRARY_PATH to these directories. - 3. Use crle(8) to extend the search path of the loader. - 4. Modify the installed GCC specs file, adding -R options into the - *link: section. + 1. When compiling Python, set LD_RUN_PATH to the directories + containing missing libraries. + 2. When running Python, set LD_LIBRARY_PATH to these directories. + 3. Use crle(8) to extend the search path of the loader. + 4. Modify the installed GCC specs file, adding -R options into the + *link: section. The complex object fails to compile on Solaris 10 with gcc 3.4 (at least up to 3.4.3). To work around it, define Py_HUGE_VAL as HUGE_VAL(), e.g.: make CPPFLAGS='-D"Py_HUGE_VAL=HUGE_VAL()" -I. -I$(srcdir)/Include' - ./python setup.py CPPFLAGS='-D"Py_HUGE_VAL=HUGE_VAL()"' + ./python setup.py CPPFLAGS='-D"Py_HUGE_VAL=HUGE_VAL()"' Linux: A problem with threads and fork() was tracked down to a bug in - the pthreads code in glibc version 2.0.5; glibc version 2.0.7 - solves the problem. This causes the popen2 test to fail; - problem and solution reported by Pablo Bleyer. + the pthreads code in glibc version 2.0.5; glibc version 2.0.7 + solves the problem. This causes the popen2 test to fail; + problem and solution reported by Pablo Bleyer. Red Hat Linux: Red Hat 9 built Python2.2 in UCS-4 mode and hacked - Tcl to support it. To compile Python2.3 with Tkinter, you will - need to pass --enable-unicode=ucs4 flag to ./configure. + Tcl to support it. To compile Python2.3 with Tkinter, you will + need to pass --enable-unicode=ucs4 flag to ./configure. - There's an executable /usr/bin/python which is Python - 1.5.2 on most older Red Hat installations; several key Red Hat tools - require this version. Python 2.1.x may be installed as - /usr/bin/python2. The Makefile installs Python as - /usr/local/bin/python, which may or may not take precedence - over /usr/bin/python, depending on how you have set up $PATH. + There's an executable /usr/bin/python which is Python + 1.5.2 on most older Red Hat installations; several key Red Hat tools + require this version. Python 2.1.x may be installed as + /usr/bin/python2. The Makefile installs Python as + /usr/local/bin/python, which may or may not take precedence + over /usr/bin/python, depending on how you have set up $PATH. FreeBSD 3.x and probably platforms with NCurses that use libmytinfo or - similar: When using cursesmodule, the linking is not done in - the correct order with the defaults. Remove "-ltermcap" from - the readline entry in Setup, and use as curses entry: "curses - cursesmodule.c -lmytinfo -lncurses -ltermcap" - "mytinfo" (so - called on FreeBSD) should be the name of the auxiliary library - required on your platform. Normally, it would be linked - automatically, but not necessarily in the correct order. - -BSDI: BSDI versions before 4.1 have known problems with threads, - which can cause strange errors in a number of modules (for - instance, the 'test_signal' test script will hang forever.) - Turning off threads (with --with-threads=no) or upgrading to - BSDI 4.1 solves this problem. + similar: When using cursesmodule, the linking is not done in + the correct order with the defaults. Remove "-ltermcap" from + the readline entry in Setup, and use as curses entry: "curses + cursesmodule.c -lmytinfo -lncurses -ltermcap" - "mytinfo" (so + called on FreeBSD) should be the name of the auxiliary library + required on your platform. Normally, it would be linked + automatically, but not necessarily in the correct order. + +BSDI: BSDI versions before 4.1 have known problems with threads, + which can cause strange errors in a number of modules (for + instance, the 'test_signal' test script will hang forever.) + Turning off threads (with --with-threads=no) or upgrading to + BSDI 4.1 solves this problem. DEC Unix: Run configure with --with-dec-threads, or with - --with-threads=no if no threads are desired (threads are on by - default). When using GCC, it is possible to get an internal - compiler error if optimization is used. This was reported for - GCC 2.7.2.3 on selectmodule.c. Manually compile the affected - file without optimization to solve the problem. + --with-threads=no if no threads are desired (threads are on by + default). When using GCC, it is possible to get an internal + compiler error if optimization is used. This was reported for + GCC 2.7.2.3 on selectmodule.c. Manually compile the affected + file without optimization to solve the problem. DEC Ultrix: compile with GCC to avoid bugs in the native compiler, - and pass SHELL=/bin/sh5 to Make when installing. + and pass SHELL=/bin/sh5 to Make when installing. -AIX: A complete overhaul of the shared library support is now in - place. See Misc/AIX-NOTES for some notes on how it's done. - (The optimizer bug reported at this place in previous releases - has been worked around by a minimal code change.) If you get - errors about pthread_* functions, during compile or during - testing, try setting CC to a thread-safe (reentrant) compiler, - like "cc_r". For full C++ module support, set CC="xlC_r" (or - CC="xlC" without thread support). +AIX: A complete overhaul of the shared library support is now in + place. See Misc/AIX-NOTES for some notes on how it's done. + (The optimizer bug reported at this place in previous releases + has been worked around by a minimal code change.) If you get + errors about pthread_* functions, during compile or during + testing, try setting CC to a thread-safe (reentrant) compiler, + like "cc_r". For full C++ module support, set CC="xlC_r" (or + CC="xlC" without thread support). AIX 5.3: To build a 64-bit version with IBM's compiler, I used the following: export PATH=/usr/bin:/usr/vacpp/bin - ./configure --with-gcc="xlc_r -q64" --with-cxx="xlC_r -q64" \ + ./configure --with-gcc="xlc_r -q64" --with-cxx="xlC_r -q64" \ --disable-ipv6 AR="ar -X64" - make + make HP-UX: When using threading, you may have to add -D_REENTRANT to the - OPT variable in the top-level Makefile; reported by Pat Knight, - this seems to make a difference (at least for HP-UX 10.20) - even though pyconfig.h defines it. This seems unnecessary when - using HP/UX 11 and later - threading seems to work "out of the - box". + OPT variable in the top-level Makefile; reported by Pat Knight, + this seems to make a difference (at least for HP-UX 10.20) + even though pyconfig.h defines it. This seems unnecessary when + using HP/UX 11 and later - threading seems to work "out of the + box". HP-UX ia64: When building on the ia64 (Itanium) platform using HP's - compiler, some experience has shown that the compiler's - optimiser produces a completely broken version of python - (see http://www.python.org/sf/814976). To work around this, - edit the Makefile and remove -O from the OPT line. + compiler, some experience has shown that the compiler's + optimiser produces a completely broken version of python + (see http://www.python.org/sf/814976). To work around this, + edit the Makefile and remove -O from the OPT line. - To build a 64-bit executable on an Itanium 2 system using HP's - compiler, use these environment variables: + To build a 64-bit executable on an Itanium 2 system using HP's + compiler, use these environment variables: - CC=cc - CXX=aCC - BASECFLAGS="+DD64" - LDFLAGS="+DD64 -lxnet" + CC=cc + CXX=aCC + BASECFLAGS="+DD64" + LDFLAGS="+DD64 -lxnet" - and call configure as: + and call configure as: - ./configure --without-gcc + ./configure --without-gcc - then *unset* the environment variables again before running - make. (At least one of these flags causes the build to fail - if it remains set.) You still have to edit the Makefile and - remove -O from the OPT line. + then *unset* the environment variables again before running + make. (At least one of these flags causes the build to fail + if it remains set.) You still have to edit the Makefile and + remove -O from the OPT line. HP PA-RISC 2.0: A recent bug report (http://www.python.org/sf/546117) - suggests that the C compiler in this 64-bit system has bugs - in the optimizer that break Python. Compiling without - optimization solves the problems. + suggests that the C compiler in this 64-bit system has bugs + in the optimizer that break Python. Compiling without + optimization solves the problems. -SCO: The following apply to SCO 3 only; Python builds out of the box - on SCO 5 (or so we've heard). +SCO: The following apply to SCO 3 only; Python builds out of the box + on SCO 5 (or so we've heard). - 1) Everything works much better if you add -U__STDC__ to the - defs. This is because all the SCO header files are broken. - Anything that isn't mentioned in the C standard is - conditionally excluded when __STDC__ is defined. + 1) Everything works much better if you add -U__STDC__ to the + defs. This is because all the SCO header files are broken. + Anything that isn't mentioned in the C standard is + conditionally excluded when __STDC__ is defined. - 2) Due to the U.S. export restrictions, SCO broke the crypt - stuff out into a separate library, libcrypt_i.a so the LIBS - needed be set to: + 2) Due to the U.S. export restrictions, SCO broke the crypt + stuff out into a separate library, libcrypt_i.a so the LIBS + needed be set to: - LIBS=' -lsocket -lcrypt_i' + LIBS=' -lsocket -lcrypt_i' UnixWare: There are known bugs in the math library of the system, as well as problems in the handling of threads (calling fork in one @@ -427,61 +427,61 @@ UnixWare: There are known bugs in the math library of the system, as well as tests involving threads will fail until those problems are fixed. SunOS 4.x: When using the SunPro C compiler, you may want to use the - '-Xa' option instead of '-Xc', to enable some needed non-ANSI - Sunisms. - THIS SYSTEM IS NO LONGER SUPPORTED. + '-Xa' option instead of '-Xc', to enable some needed non-ANSI + Sunisms. + THIS SYSTEM IS NO LONGER SUPPORTED. NeXT: Not supported anymore. Start with the MacOSX/Darwin code if you - want to revive it. + want to revive it. -QNX: Chris Herborth (chrish@qnx.com) writes: - configure works best if you use GNU bash; a port is available on - ftp.qnx.com in /usr/free. I used the following process to build, - test and install Python 1.5.x under QNX: +QNX: Chris Herborth (chrish@qnx.com) writes: + configure works best if you use GNU bash; a port is available on + ftp.qnx.com in /usr/free. I used the following process to build, + test and install Python 1.5.x under QNX: - 1) CONFIG_SHELL=/usr/local/bin/bash CC=cc RANLIB=: \ - ./configure --verbose --without-gcc --with-libm="" + 1) CONFIG_SHELL=/usr/local/bin/bash CC=cc RANLIB=: \ + ./configure --verbose --without-gcc --with-libm="" - 2) edit Modules/Setup to activate everything that makes sense for - your system... tested here at QNX with the following modules: + 2) edit Modules/Setup to activate everything that makes sense for + your system... tested here at QNX with the following modules: - array, audioop, binascii, cPickle, cStringIO, cmath, - crypt, curses, errno, fcntl, gdbm, grp, imageop, - _locale, math, md5, new, operator, parser, pcre, - posix, pwd, readline, regex, reop, rgbimg, rotor, - select, signal, socket, soundex, strop, struct, - syslog, termios, time, timing, zlib, audioop, imageop, rgbimg + array, audioop, binascii, cPickle, cStringIO, cmath, + crypt, curses, errno, fcntl, gdbm, grp, imageop, + _locale, math, md5, new, operator, parser, pcre, + posix, pwd, readline, regex, reop, rgbimg, rotor, + select, signal, socket, soundex, strop, struct, + syslog, termios, time, timing, zlib, audioop, imageop, rgbimg - 3) make SHELL=/usr/local/bin/bash + 3) make SHELL=/usr/local/bin/bash - or, if you feel the need for speed: + or, if you feel the need for speed: - make SHELL=/usr/local/bin/bash OPT="-5 -Oil+nrt" + make SHELL=/usr/local/bin/bash OPT="-5 -Oil+nrt" - 4) make SHELL=/usr/local/bin/bash test + 4) make SHELL=/usr/local/bin/bash test - Using GNU readline 2.2 seems to behave strangely, but I - think that's a problem with my readline 2.2 port. :-\ + Using GNU readline 2.2 seems to behave strangely, but I + think that's a problem with my readline 2.2 port. :-\ - 5) make SHELL=/usr/local/bin/bash install + 5) make SHELL=/usr/local/bin/bash install - If you get SIGSEGVs while running Python (I haven't yet, but - I've only run small programs and the test cases), you're - probably running out of stack; the default 32k could be a - little tight. To increase the stack size, edit the Makefile - to read: LDFLAGS = -N 48k + If you get SIGSEGVs while running Python (I haven't yet, but + I've only run small programs and the test cases), you're + probably running out of stack; the default 32k could be a + little tight. To increase the stack size, edit the Makefile + to read: LDFLAGS = -N 48k -BeOS: See Misc/BeOS-NOTES for notes about compiling/installing - Python on BeOS R3 or later. Note that only the PowerPC - platform is supported for R3; both PowerPC and x86 are - supported for R4. +BeOS: See Misc/BeOS-NOTES for notes about compiling/installing + Python on BeOS R3 or later. Note that only the PowerPC + platform is supported for R3; both PowerPC and x86 are + supported for R4. Cray T3E: Mark Hadfield (m.hadfield@niwa.co.nz) writes: - Python can be built satisfactorily on a Cray T3E but based on - my experience with the NIWA T3E (2002-05-22, version 2.2.1) - there are a few bugs and gotchas. For more information see a - thread on comp.lang.python in May 2002 entitled "Building - Python on Cray T3E". + Python can be built satisfactorily on a Cray T3E but based on + my experience with the NIWA T3E (2002-05-22, version 2.2.1) + there are a few bugs and gotchas. For more information see a + thread on comp.lang.python in May 2002 entitled "Building + Python on Cray T3E". 1) Use Cray's cc and not gcc. The latter was reported not to work by Konrad Hinsen. It may work now, but it may not. @@ -491,16 +491,16 @@ Cray T3E: Mark Hadfield (m.hadfield@niwa.co.nz) writes: MACHDEP=unicosmk - 2) Run configure with option "--enable-unicode=ucs4". + 2) Run configure with option "--enable-unicode=ucs4". - 3) The Cray T3E does not support dynamic linking, so extension - modules have to be built by adding (or uncommenting) lines - in Modules/Setup. The minimum set of modules is + 3) The Cray T3E does not support dynamic linking, so extension + modules have to be built by adding (or uncommenting) lines + in Modules/Setup. The minimum set of modules is - posix, new, _sre, unicodedata + posix, new, _sre, unicodedata - On NIWA's vanilla T3E system the following have also been - included successfully: + On NIWA's vanilla T3E system the following have also been + included successfully: _codecs, _locale, _socket, _symtable, _testcapi, _weakref array, binascii, cmath, cPickle, crypt, cStringIO, dbm @@ -508,28 +508,28 @@ Cray T3E: Mark Hadfield (m.hadfield@niwa.co.nz) writes: rotor, select, struct, strop, syslog, termios, time, timing - 4) Once the python executable and library have been built, make - will execute setup.py, which will attempt to build remaining - extensions and link them dynamically. Each of these attempts - will fail but should not halt the make process. This is - normal. - - 5) Running "make test" uses a lot of resources and causes - problems on our system. You might want to try running tests - singly or in small groups. - -SGI: SGI's standard "make" utility (/bin/make or /usr/bin/make) - does not check whether a command actually changed the file it - is supposed to build. This means that whenever you say "make" - it will redo the link step. The remedy is to use SGI's much - smarter "smake" utility (/usr/sbin/smake), or GNU make. If - you set the first line of the Makefile to #!/usr/sbin/smake - smake will be invoked by make (likewise for GNU make). - - WARNING: There are bugs in the optimizer of some versions of - SGI's compilers that can cause bus errors or other strange - behavior, especially on numerical operations. To avoid this, - try building with "make OPT=". + 4) Once the python executable and library have been built, make + will execute setup.py, which will attempt to build remaining + extensions and link them dynamically. Each of these attempts + will fail but should not halt the make process. This is + normal. + + 5) Running "make test" uses a lot of resources and causes + problems on our system. You might want to try running tests + singly or in small groups. + +SGI: SGI's standard "make" utility (/bin/make or /usr/bin/make) + does not check whether a command actually changed the file it + is supposed to build. This means that whenever you say "make" + it will redo the link step. The remedy is to use SGI's much + smarter "smake" utility (/usr/sbin/smake), or GNU make. If + you set the first line of the Makefile to #!/usr/sbin/smake + smake will be invoked by make (likewise for GNU make). + + WARNING: There are bugs in the optimizer of some versions of + SGI's compilers that can cause bus errors or other strange + behavior, especially on numerical operations. To avoid this, + try building with "make OPT=". OS/2: If you are running Warp3 or Warp4 and have IBM's VisualAge C/C++ compiler installed, just change into the pc\os2vacpp directory @@ -569,8 +569,8 @@ MacOSX: The tests will crash on both 10.1 and 10.2 with SEGV in additions. Some people have reported problems building Python after using "fink" - to install additional unix software. Disabling fink (remove all references - to /sw from your .profile or .login) should solve this. + to install additional unix software. Disabling fink (remove all + references to /sw from your .profile or .login) should solve this. You may want to try the configure option "--enable-framework" which installs Python as a framework. The location can be set @@ -602,8 +602,8 @@ Cygwin: With recent (relative to the time of writing, 2001-12-19) #SSL=/usr/local/ssl #_socket socketmodule.c \ - # -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ - # -L$(SSL)/lib -lssl -lcrypto + # -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ + # -L$(SSL)/lib -lssl -lcrypto and remove "local/" from the SSL variable. Finally, just run "make"! @@ -648,69 +648,69 @@ Cygwin: With recent (relative to the time of writing, 2001-12-19) AtheOS: From Octavian Cerna : - Before building: + Before building: - Make sure you have shared versions of the libraries you - want to use with Python. You will have to compile them - yourself, or download precompiled packages. + Make sure you have shared versions of the libraries you + want to use with Python. You will have to compile them + yourself, or download precompiled packages. - Recommended libraries: + Recommended libraries: - ncurses-4.2 - readline-4.2a - zlib-1.1.4 + ncurses-4.2 + readline-4.2a + zlib-1.1.4 - Build: + Build: - $ ./configure --prefix=/usr/python - $ make + $ ./configure --prefix=/usr/python + $ make - Python is always built as a shared library, otherwise - dynamic loading would not work. + Python is always built as a shared library, otherwise + dynamic loading would not work. - Testing: + Testing: - $ make test + $ make test - Install: + Install: - # make install - # pkgmanager -a /usr/python + # make install + # pkgmanager -a /usr/python - AtheOS issues: + AtheOS issues: - - large file support: due to a stdio bug in glibc/libio, - access to large files may not work correctly. fseeko() - tries to seek to a negative offset. ftello() returns a - negative offset, it looks like a 32->64bit - sign-extension issue. The lowlevel functions (open, - lseek, etc) are OK. - - sockets: AF_UNIX is defined in the C library and in - Python, but not implemented in the system. - - select: poll is available in the C library, but does not - work (It does not return POLLNVAL for bad fds and - hangs). - - posix: statvfs and fstatvfs always return ENOSYS. - - disabled modules: - - mmap: not yet implemented in AtheOS - - nis: broken (on an unconfigured system - yp_get_default_domain() returns junk instead of - error) - - dl: dynamic loading doesn't work via dlopen() - - resource: getrimit and setrlimit are not yet - implemented + - large file support: due to a stdio bug in glibc/libio, + access to large files may not work correctly. fseeko() + tries to seek to a negative offset. ftello() returns a + negative offset, it looks like a 32->64bit + sign-extension issue. The lowlevel functions (open, + lseek, etc) are OK. + - sockets: AF_UNIX is defined in the C library and in + Python, but not implemented in the system. + - select: poll is available in the C library, but does not + work (It does not return POLLNVAL for bad fds and + hangs). + - posix: statvfs and fstatvfs always return ENOSYS. + - disabled modules: + - mmap: not yet implemented in AtheOS + - nis: broken (on an unconfigured system + yp_get_default_domain() returns junk instead of + error) + - dl: dynamic loading doesn't work via dlopen() + - resource: getrimit and setrlimit are not yet + implemented - - if you are getting segmentation faults, you probably are - low on memory. AtheOS doesn't handle very well an - out-of-memory condition and simply SEGVs the process. + - if you are getting segmentation faults, you probably are + low on memory. AtheOS doesn't handle very well an + out-of-memory condition and simply SEGVs the process. - Tested on: + Tested on: - AtheOS-0.3.7 - gcc-2.95 - binutils-2.10 - make-3.78 + AtheOS-0.3.7 + gcc-2.95 + binutils-2.10 + make-3.78 Configuring the bsddb and dbm modules @@ -719,15 +719,25 @@ Configuring the bsddb and dbm modules Beginning with Python version 2.3, the PyBsddb package was adopted into Python as the bsddb package, exposing a set of package-level functions which provide -backwards-compatible behavior. Only versions 3.1 through 4.1 of +backwards-compatible behavior. Only versions 3.3 through 4.4 of Sleepycat's libraries provide the necessary API, so older versions aren't supported through this interface. The old bsddb module has been retained as bsddb185, though it is not built by default. Users wishing to use it will have to tweak Modules/Setup to build it. The dbm module will still be built against the Sleepycat libraries if -other preferred alternatives (ndbm, gdbm) are not found, though -versions of the Sleepycat library prior to 3.1 are not considered. +other preferred alternatives (ndbm, gdbm) are not found. +Building the sqlite3 module +--------------------------- + +To build the sqlite3 module, you'll need the sqlite3 or libsqlite3 +packages installed, including the header files. Many modern operating +systems distribute the headers in a separate package to the library - +often it will be the same name as the main package, but with a -dev or +-devel suffix. + +The version of pysqlite2 that's including in Python needs sqlite3 3.0.8 +or later. setup.py attempts to check that it can find a correct version. Configuring threads ------------------- @@ -757,17 +767,17 @@ incorrectly, please report that as a bug. SunOS 5.{1-5}/{gcc,SunPro cc}/solaris -mt SunOS 5.5/{gcc,SunPro cc}/POSIX (nothing) DEC OSF/1 3.x/cc/DCE -threads - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) Digital UNIX 4.x/cc/DCE -threads - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) Digital UNIX 4.x/cc/POSIX -pthread - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) AIX 4.1.4/cc_r/d7 (nothing) - (buhrt@iquest.net) + (buhrt@iquest.net) AIX 4.1.4/cc_r4/DCE (nothing) - (buhrt@iquest.net) + (buhrt@iquest.net) IRIX 6.2/cc/POSIX (nothing) - (robertl@cwi.nl) + (robertl@cwi.nl) Linker (ld) libraries and flags for threads @@ -778,15 +788,15 @@ Linker (ld) libraries and flags for threads SunOS 5.{1-5}/solaris -lthread SunOS 5.5/POSIX -lpthread DEC OSF/1 3.x/DCE -lpthreads -lmach -lc_r -lc - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) Digital UNIX 4.x/DCE -lpthreads -lpthread -lmach -lexc -lc - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) Digital UNIX 4.x/POSIX -lpthread -lmach -lexc -lc - (butenhof@zko.dec.com) + (butenhof@zko.dec.com) AIX 4.1.4/{draft7,DCE} (nothing) - (buhrt@iquest.net) + (buhrt@iquest.net) IRIX 6.2/POSIX -lpthread - (jph@emilia.engr.sgi.com) + (jph@emilia.engr.sgi.com) Building a shared libpython @@ -896,7 +906,7 @@ IMPORTANT: If the tests fail and you decide to mail a bug report, *don't* include the output of "make test". It is useless. Run the failing test manually, as follows: - ./python ./Lib/test/test_whatever.py + ./python ./Lib/test/test_whatever.py (substituting the top of the source tree for '.' if you built in a different directory). This runs the test in verbose mode. @@ -909,7 +919,7 @@ To install the Python binary, library modules, shared library modules (see below), include files, configuration files, and the manual page, just type - make install + make install This will install all platform-independent files in subdirectories of the directory given with the --prefix option to configure or to the @@ -934,7 +944,7 @@ by default. If you have a previous installation of Python that you don't want to replace yet, use - make altinstall + make altinstall This installs the same set of files as "make install" except it doesn't create the hard link to "python" named "python" and @@ -963,104 +973,124 @@ after changing --prefix or --exec-prefix, all you need to do is remove Modules/getpath.o. --with(out)-gcc: The configure script uses gcc (the GNU C compiler) if - it finds it. If you don't want this, or if this compiler is - installed but broken on your platform, pass the option - --without-gcc. You can also pass "CC=cc" (or whatever the - name of the proper C compiler is) in the environment, but the - advantage of using --without-gcc is that this option is - remembered by the config.status script for its --recheck - option. + it finds it. If you don't want this, or if this compiler is + installed but broken on your platform, pass the option + --without-gcc. You can also pass "CC=cc" (or whatever the + name of the proper C compiler is) in the environment, but the + advantage of using --without-gcc is that this option is + remembered by the config.status script for its --recheck + option. --prefix, --exec-prefix: If you want to install the binaries and the - Python library somewhere else than in /usr/local/{bin,lib}, - you can pass the option --prefix=DIRECTORY; the interpreter - binary will be installed as DIRECTORY/bin/python and the - library files as DIRECTORY/lib/python/*. If you pass - --exec-prefix=DIRECTORY (as well) this overrides the - installation prefix for architecture-dependent files (like the - interpreter binary). Note that --prefix=DIRECTORY also - affects the default module search path (sys.path), when - Modules/config.c is compiled. Passing make the option - prefix=DIRECTORY (and/or exec_prefix=DIRECTORY) overrides the - prefix set at configuration time; this may be more convenient - than re-running the configure script if you change your mind - about the install prefix. + Python library somewhere else than in /usr/local/{bin,lib}, + you can pass the option --prefix=DIRECTORY; the interpreter + binary will be installed as DIRECTORY/bin/python and the + library files as DIRECTORY/lib/python/*. If you pass + --exec-prefix=DIRECTORY (as well) this overrides the + installation prefix for architecture-dependent files (like the + interpreter binary). Note that --prefix=DIRECTORY also + affects the default module search path (sys.path), when + Modules/config.c is compiled. Passing make the option + prefix=DIRECTORY (and/or exec_prefix=DIRECTORY) overrides the + prefix set at configuration time; this may be more convenient + than re-running the configure script if you change your mind + about the install prefix. --with-readline: This option is no longer supported. GNU - readline is automatically enabled by setup.py when present. + readline is automatically enabled by setup.py when present. --with-threads: On most Unix systems, you can now use multiple - threads, and support for this is enabled by default. To - disable this, pass --with-threads=no. If the library required - for threads lives in a peculiar place, you can use - --with-thread=DIRECTORY. IMPORTANT: run "make clean" after - changing (either enabling or disabling) this option, or you - will get link errors! Note: for DEC Unix use - --with-dec-threads instead. + threads, and support for this is enabled by default. To + disable this, pass --with-threads=no. If the library required + for threads lives in a peculiar place, you can use + --with-thread=DIRECTORY. IMPORTANT: run "make clean" after + changing (either enabling or disabling) this option, or you + will get link errors! Note: for DEC Unix use + --with-dec-threads instead. --with-sgi-dl: On SGI IRIX 4, dynamic loading of extension modules is - supported by the "dl" library by Jack Jansen, which is - ftp'able from ftp://ftp.cwi.nl/pub/dynload/dl-1.6.tar.Z. - This is enabled (after you've ftp'ed and compiled the dl - library) by passing --with-sgi-dl=DIRECTORY where DIRECTORY - is the absolute pathname of the dl library. (Don't bother on - IRIX 5, it already has dynamic linking using SunOS style - shared libraries.) THIS OPTION IS UNSUPPORTED. + supported by the "dl" library by Jack Jansen, which is + ftp'able from ftp://ftp.cwi.nl/pub/dynload/dl-1.6.tar.Z. + This is enabled (after you've ftp'ed and compiled the dl + library) by passing --with-sgi-dl=DIRECTORY where DIRECTORY + is the absolute pathname of the dl library. (Don't bother on + IRIX 5, it already has dynamic linking using SunOS style + shared libraries.) THIS OPTION IS UNSUPPORTED. --with-dl-dld: Dynamic loading of modules is rumored to be supported - on some other systems: VAX (Ultrix), Sun3 (SunOS 3.4), Sequent - Symmetry (Dynix), and Atari ST. This is done using a - combination of the GNU dynamic loading package - (ftp://ftp.cwi.nl/pub/dynload/dl-dld-1.1.tar.Z) and an - emulation of the SGI dl library mentioned above (the emulation - can be found at - ftp://ftp.cwi.nl/pub/dynload/dld-3.2.3.tar.Z). To - enable this, ftp and compile both libraries, then call - configure, passing it the option - --with-dl-dld=DL_DIRECTORY,DLD_DIRECTORY where DL_DIRECTORY is - the absolute pathname of the dl emulation library and - DLD_DIRECTORY is the absolute pathname of the GNU dld library. - (Don't bother on SunOS 4 or 5, they already have dynamic - linking using shared libraries.) THIS OPTION IS UNSUPPORTED. + on some other systems: VAX (Ultrix), Sun3 (SunOS 3.4), Sequent + Symmetry (Dynix), and Atari ST. This is done using a + combination of the GNU dynamic loading package + (ftp://ftp.cwi.nl/pub/dynload/dl-dld-1.1.tar.Z) and an + emulation of the SGI dl library mentioned above (the emulation + can be found at + ftp://ftp.cwi.nl/pub/dynload/dld-3.2.3.tar.Z). To + enable this, ftp and compile both libraries, then call + configure, passing it the option + --with-dl-dld=DL_DIRECTORY,DLD_DIRECTORY where DL_DIRECTORY is + the absolute pathname of the dl emulation library and + DLD_DIRECTORY is the absolute pathname of the GNU dld library. + (Don't bother on SunOS 4 or 5, they already have dynamic + linking using shared libraries.) THIS OPTION IS UNSUPPORTED. --with-libm, --with-libc: It is possible to specify alternative - versions for the Math library (default -lm) and the C library - (default the empty string) using the options - --with-libm=STRING and --with-libc=STRING, respectively. For - example, if your system requires that you pass -lc_s to the C - compiler to use the shared C library, you can pass - --with-libc=-lc_s. These libraries are passed after all other - libraries, the C library last. + versions for the Math library (default -lm) and the C library + (default the empty string) using the options + --with-libm=STRING and --with-libc=STRING, respectively. For + example, if your system requires that you pass -lc_s to the C + compiler to use the shared C library, you can pass + --with-libc=-lc_s. These libraries are passed after all other + libraries, the C library last. --with-libs='libs': Add 'libs' to the LIBS that the python interpreter - is linked against. - ---with-cxx=: Some C++ compilers require that main() is - compiled with the C++ if there is any C++ code in the application. - Specifically, g++ on a.out systems may require that to support - construction of global objects. With this option, the main() function - of Python will be compiled with ; use that only if you - plan to use C++ extension modules, and if your compiler requires - compilation of main() as a C++ program. + is linked against. + +--with-cxx-main=: If you plan to use C++ extension modules, + then -- on some platforms -- you need to compile python's main() + function with the C++ compiler. With this option, make will use + to compile main() *and* to link the python executable. + It is likely that the resulting executable depends on the C++ + runtime library of . (The default is --without-cxx-main.) + + There are platforms that do not require you to build Python + with a C++ compiler in order to use C++ extension modules. + E.g., x86 Linux with ELF shared binaries and GCC 3.x, 4.x is such + a platform. We recommend that you configure Python + --without-cxx-main on those platforms because a mismatch + between the C++ compiler version used to build Python and to + build a C++ extension module is likely to cause a crash at + runtime. + + The Python installation also stores the variable CXX that + determines, e.g., the C++ compiler distutils calls by default + to build C++ extensions. If you set CXX on the configure command + line to any string of non-zero length, then configure won't + change CXX. If you do not preset CXX but pass + --with-cxx-main=, then configure sets CXX=. + In all other cases, configure looks for a C++ compiler by + some common names (c++, g++, gcc, CC, cxx, cc++, cl) and sets + CXX to the first compiler it finds. If it does not find any + C++ compiler, then it sets CXX="". + + Similarly, if you want to change the command used to link the + python executable, then set LINKCC on the configure command line. --with-pydebug: Enable additional debugging code to help track down - memory management problems. This allows printing a list of all - live objects when the interpreter terminates. + memory management problems. This allows printing a list of all + live objects when the interpreter terminates. --with(out)-universal-newlines: enable reading of text files with - foreign newline convention (default: enabled). In other words, - any of \r, \n or \r\n is acceptable as end-of-line character. - If enabled import and execfile will automatically accept any newline - in files. Python code can open a file with open(file, 'U') to - read it in universal newline mode. THIS OPTION IS UNSUPPORTED. + foreign newline convention (default: enabled). In other words, + any of \r, \n or \r\n is acceptable as end-of-line character. + If enabled import and execfile will automatically accept any newline + in files. Python code can open a file with open(file, 'U') to + read it in universal newline mode. THIS OPTION IS UNSUPPORTED. --with-tsc: Profile using the Pentium timestamping counter (TSC). ---with-fpectl: Enable building the ``fpectl'' module which can be used - to control the generation of SIGFPE and its conversion into a - Python exception. Note: this module is dangerous or useless - except in the hands of experts. +--with-system-ffi: Build the _ctypes extension module using an ffi + library installed on the system. Building for multiple architectures (using the VPATH feature) @@ -1081,13 +1111,13 @@ For example, the following is all you need to build a minimal Python in /usr/tmp/python (assuming ~guido/src/python is the toplevel directory and you want to build in /usr/tmp/python): - $ mkdir /usr/tmp/python - $ cd /usr/tmp/python - $ ~guido/src/python/configure - [...] - $ make - [...] - $ + $ mkdir /usr/tmp/python + $ cd /usr/tmp/python + $ ~guido/src/python/configure + [...] + $ make + [...] + $ Note that configure copies the original Setup file to the build directory if it finds no Setup file there. This means that you can @@ -1184,13 +1214,12 @@ Distribution structure Most subdirectories have their own README files. Most files have comments. -.cvsignore Additional filename matching patterns for CVS to ignore -BeOS/ Files specific to the BeOS port +BeOS/ Files specific to the BeOS port Demo/ Demonstration scripts, modules and programs -Doc/ Documentation sources (LaTeX) +Doc/ Documentation sources (LaTeX) Grammar/ Input for the parser generator Include/ Public header files -LICENSE Licensing information +LICENSE Licensing information Lib/ Python library modules Mac/ Macintosh specific resources Makefile.pre.in Source from which config.status creates the Makefile.pre @@ -1198,7 +1227,7 @@ Misc/ Miscellaneous useful files Modules/ Implementation of most built-in modules Objects/ Implementation of most built-in object types PC/ Files specific to PC ports (DOS, Windows, OS/2) -PCbuild/ Build directory for Microsoft Visual C++ +PCbuild/ Build directory for Microsoft Visual C++ Parser/ The parser and tokenizer and their input handling Python/ The byte-compiler and interpreter README The file you're reading now @@ -1207,6 +1236,7 @@ pyconfig.h.in Source from which pyconfig.h is created (GNU autoheader output) configure Configuration shell script (GNU autoconf output) configure.in Configuration specification (input for GNU autoconf) install-sh Shell script used to install files +setup.py Python script used to build extension modules The following files will (may) be created in the toplevel directory by the configuration and build processes: @@ -1218,8 +1248,8 @@ config.cache Cache of configuration variables pyconfig.h Configuration header config.log Log from last configure run config.status Status from last run of the configure script -getbuildinfo.o Object file from Modules/getbuildinfo.c -libpython.a The library archive +getbuildinfo.o Object file from Modules/getbuildinfo.c +libpython.a The library archive python The executable interpreter tags, TAGS Tags files for vi and Emacs diff --git a/RISCOS/Makefile b/RISCOS/Makefile index 92f5272..1788b5c 100644 --- a/RISCOS/Makefile +++ b/RISCOS/Makefile @@ -74,7 +74,6 @@ MODULES_DYNAMIC =\ @.^.Lib.md5/pyd\ @.^.Lib.operator/pyd\ @.^.Lib.parser/pyd\ - @.^.Lib.regex/pyd\ @.^.Lib.rgbimg/pyd\ @.^.Lib.sha/pyd\ @.^.Lib.signal/pyd\ @@ -284,10 +283,6 @@ $(LIB_PYTHON): $(OBJECTS) @.^.Lib.parser/pyd: @.^.Modules.o.parsermodule s.linktab $(MAKEDLK) -d @.^.Lib.parser/pyd -s s.linktab -o @.^.Modules.o.parsermodule -e initparser -@.^.Lib.regex/pyd: @.^.Modules.o.regexmodule @.^.Modules.o.regexpr s.linktab - $(LINK) -aof -o @.^.Modules.o.regexlink @.^.Modules.o.regexmodule @.^.Modules.o.regexpr - $(MAKEDLK) -d @.^.Lib.regex/pyd -s s.linktab -o @.^.Modules.o.regexlink -e initregex - @.^.Lib.rgbimg/pyd: @.^.Modules.o.rgbimgmodule s.linktab $(MAKEDLK) -d @.^.Lib.rgbimg/pyd -s s.linktab -o @.^.Modules.o.rgbimgmodule -e initrgbimg diff --git a/Tools/bgen/bgen/bgenObjectDefinition.py b/Tools/bgen/bgen/bgenObjectDefinition.py index a802f93..6f9bd53 100644 --- a/Tools/bgen/bgen/bgenObjectDefinition.py +++ b/Tools/bgen/bgen/bgenObjectDefinition.py @@ -383,6 +383,8 @@ class PEP253Mixin(PEP252Mixin): Output("%s_tp_free, /* tp_free */", self.prefix) def output_tp_initBody_basecall(self): + """If a type shares its init call with its base type set output_tp_initBody + to output_tp_initBody_basecall""" if self.basetype: Output("if (%s.tp_init)", self.basetype) OutLbrace() @@ -395,7 +397,6 @@ class PEP253Mixin(PEP252Mixin): if self.output_tp_initBody: Output("static int %s_tp_init(PyObject *_self, PyObject *_args, PyObject *_kwds)", self.prefix) OutLbrace() - self.output_tp_initBody_basecall() self.output_tp_initBody() OutRbrace() else: @@ -425,7 +426,7 @@ class PEP253Mixin(PEP252Mixin): if self.basetype: Output("if (%s.tp_new)", self.basetype) OutLbrace() - Output("if ( (*%s.tp_init)(_self, _args, _kwds) == NULL) return NULL;", self.basetype) + Output("if ( (*%s.tp_new)(type, _args, _kwds) == NULL) return NULL;", self.basetype) Dedent() Output("} else {") Indent() diff --git a/Tools/buildbot/build.bat b/Tools/buildbot/build.bat index e3b77be..e96323c 100644 --- a/Tools/buildbot/build.bat +++ b/Tools/buildbot/build.bat @@ -1,4 +1,5 @@ @rem Used by the buildbot "compile" step. cmd /c Tools\buildbot\external.bat call "%VS71COMNTOOLS%vsvars32.bat" +cmd /q/c Tools\buildbot\kill_python.bat devenv.com /useenv /build Debug PCbuild\pcbuild.sln diff --git a/Tools/buildbot/external.bat b/Tools/buildbot/external.bat index 1b032ae..c6d252d 100644 --- a/Tools/buildbot/external.bat +++ b/Tools/buildbot/external.bat @@ -12,3 +12,24 @@ if not exist db-4.4.20 svn export http://svn.python.org/projects/external/db-4.4 if not exist db-4.4.20\build_win32\debug\libdb44sd.lib ( devenv db-4.4.20\build_win32\Berkeley_DB.sln /build Debug /project db_static ) + +@rem OpenSSL +if not exist openssl-0.9.8a svn export http://svn.python.org/projects/external/openssl-0.9.8a + +@rem tcltk +if not exist tcl8.4.12 ( + if exist tcltk rd /s/q tcltk + svn export http://svn.python.org/projects/external/tcl8.4.12 + svn export http://svn.python.org/projects/external/tk8.4.12 + cd tcl8.4.12\win + nmake -f makefile.vc + nmake -f makefile.vc INSTALLDIR=..\..\tcltk install + cd ..\.. + cd tk8.4.12\win + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.12 + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.12 INSTALLDIR=..\..\tcltk install +) + +@rem sqlite +if not exist sqlite-source-3.3.4 svn export http://svn.python.org/projects/external/sqlite-source-3.3.4 +if not exist build\PCbuild\sqlite3.dll copy sqlite-source-3.3.4\sqlite3.dll build\PCbuild diff --git a/Tools/buildbot/kill_python.bat b/Tools/buildbot/kill_python.bat new file mode 100644 index 0000000..d78b6d4 --- /dev/null +++ b/Tools/buildbot/kill_python.bat @@ -0,0 +1,3 @@ +cd Tools\buildbot +nmake /C /S /f kill_python.mak +kill_python.exe diff --git a/Tools/buildbot/kill_python.c b/Tools/buildbot/kill_python.c new file mode 100644 index 0000000..ebc9aa4 --- /dev/null +++ b/Tools/buildbot/kill_python.c @@ -0,0 +1,56 @@ +/* This program looks for processes which have build\PCbuild\python.exe + in their path and terminates them. */ +#include +#include +#include + +int main() +{ + DWORD pids[1024], cbNeeded; + int i, num_processes; + if (!EnumProcesses(pids, sizeof(pids), &cbNeeded)) { + printf("EnumProcesses failed\n"); + return 1; + } + num_processes = cbNeeded/sizeof(pids[0]); + for (i = 0; i < num_processes; i++) { + HANDLE hProcess; + char path[MAX_PATH]; + HMODULE mods[1024]; + int k, num_mods; + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + | PROCESS_VM_READ + | PROCESS_TERMINATE , + FALSE, pids[i]); + if (!hProcess) + /* process not accessible */ + continue; + if (!EnumProcessModules(hProcess, mods, sizeof(mods), &cbNeeded)) { + /* For unknown reasons, this sometimes returns ERROR_PARTIAL_COPY; + this apparently means we are not supposed to read the process. */ + if (GetLastError() == ERROR_PARTIAL_COPY) { + CloseHandle(hProcess); + continue; + } + printf("EnumProcessModules failed: %d\n", GetLastError()); + return 1; + } + if (!GetModuleFileNameEx(hProcess, NULL, path, sizeof(path))) { + printf("GetProcessImageFileName failed\n"); + return 1; + } + + _strlwr(path); + /* printf("%s\n", path); */ + if (strstr(path, "build\\pcbuild\\python_d.exe") != NULL) { + printf("Terminating %s (pid %d)\n", path, pids[i]); + if (!TerminateProcess(hProcess, 1)) { + printf("Termination failed: %d\n", GetLastError()); + return 1; + } + return 0; + } + + CloseHandle(hProcess); + } +} diff --git a/Tools/buildbot/kill_python.mak b/Tools/buildbot/kill_python.mak new file mode 100644 index 0000000..6027d3f --- /dev/null +++ b/Tools/buildbot/kill_python.mak @@ -0,0 +1,2 @@ +kill_python.exe: kill_python.c + cl -nologo -o kill_python.exe kill_python.c psapi.lib diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index b4ae3e9..6433131 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -127,7 +127,7 @@ def make(filename, outfile): section = None fuzzy = 0 # Record a fuzzy mark - if l[:2] == '#,' and l.find('fuzzy'): + if l[:2] == '#,' and 'fuzzy' in l: fuzzy = 1 # Skip comments if l[0] == '#': diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py index 6665d36..efa1696 100644 --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -6,6 +6,7 @@ from msilib import Feature, CAB, Directory, Dialog, Binary, add_data import uisample from win32com.client import constants from distutils.spawn import find_executable +from uuids import product_codes # Settings can be overridden in config.py below # 0 for official python.org releases @@ -23,6 +24,8 @@ srcdir = os.path.abspath("../..") full_current_version = None # Is Tcl available at all? have_tcl = True +# Where is sqlite3.dll located, relative to srcdir? +sqlite_dir = "../sqlite-source-3.3.4" try: from config import * @@ -62,30 +65,6 @@ current_version = "%s.%d" % (short_version, FIELD3) upgrade_code_snapshot='{92A24481-3ECB-40FC-8836-04B7966EC0D5}' upgrade_code='{65E6DE48-A358-434D-AA4F-4AF72DB4718F}' -# This should be extended for each Python release. -# The product code must change whenever the name of the MSI file -# changes, and when new component codes are issued for existing -# components. See "Changing the Product Code". As we change the -# component codes with every build, we need a new product code -# each time. For intermediate (snapshot) releases, they are automatically -# generated. For official releases, we record the product codes, -# so people can refer to them. -product_codes = { - '2.4.101': '{0e9b4d8e-6cda-446e-a208-7b92f3ddffa0}', # 2.4a1, released as a snapshot - '2.4.102': '{1b998745-4901-4edb-bc52-213689e1b922}', # 2.4a2 - '2.4.103': '{33fc8bd2-1e8f-4add-a40a-ade2728d5942}', # 2.4a3 - '2.4.111': '{51a7e2a8-2025-4ef0-86ff-e6aab742d1fa}', # 2.4b1 - '2.4.112': '{4a5e7c1d-c659-4fe3-b8c9-7c65bd9c95a5}', # 2.4b2 - '2.4.121': '{75508821-a8e9-40a8-95bd-dbe6033ddbea}', # 2.4c1 - '2.4.122': '{83a9118b-4bdd-473b-afc3-bcb142feca9e}', # 2.4c2 - '2.4.150': '{82d9302e-f209-4805-b548-52087047483a}', # 2.4.0 - '2.4.1121':'{be027411-8e6b-4440-a29b-b07df0690230}', # 2.4.1c1 - '2.4.1122':'{02818752-48bf-4074-a281-7a4114c4f1b1}', # 2.4.1c2 - '2.4.1150':'{4d4f5346-7e4a-40b5-9387-fdb6181357fc}', # 2.4.1 - '2.4.2121':'{5ef9d6b6-df78-45d2-ab09-14786a3c5a99}', # 2.4.2c1 - '2.4.2150':'{b191e49c-ea23-43b2-b28a-14e0784069b8}', # 2.4.2 -} - if snapshot: current_version = "%s.%s.%s" % (major, minor, int(time.time()/3600/24)) product_code = msilib.gen_uuid() @@ -109,14 +88,10 @@ extensions = [ '_tkinter.pyd', '_msi.pyd', '_ctypes.pyd', - '_ctypes_test.pyd' + '_ctypes_test.pyd', + '_sqlite3.pyd' ] -if major+minor <= "24": - extensions.extend([ - 'zlib.pyd', - ]) - # Well-known component UUIDs # These are needed for SharedDLLs reference counter; if # a different UUID was used for each incarnation of, say, @@ -392,7 +367,7 @@ def add_ui(db): ("VerdanaRed9", "Verdana", 9, 255, 0), ]) - compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x badsyntax [TARGETDIR]Lib" + compileargs = r"-Wi [TARGETDIR]Lib\compileall.py -f -x bad_coding|badsyntax|site-packages [TARGETDIR]Lib" # See "CustomAction Table" add_data(db, "CustomAction", [ # msidbCustomActionTypeFirstSequence + msidbCustomActionTypeTextData + msidbCustomActionTypeProperty @@ -491,7 +466,7 @@ def add_ui(db): c = exit_dialog.text("warning", 135, 200, 220, 40, 0x30003, "{\\VerdanaRed9}Warning: Python 2.5.x is the last " "Python release for Windows 9x.") - c.condition("Hide", "NOT Version9x") + c.condition("Hide", "NOT Version9X") exit_dialog.text("Description", 135, 235, 220, 20, 0x30003, "Click the Finish button to exit the Installer.") @@ -914,7 +889,7 @@ def add_files(db): continue tcltk.set_current() elif dir in ['test', 'tests', 'data', 'output']: - # test: Lib, Lib/email, Lib/bsddb + # test: Lib, Lib/email, Lib/bsddb, Lib/ctypes, Lib/sqlite3 # tests: Lib/distutils # data: Lib/email/test # output: Lib/test @@ -941,6 +916,8 @@ def add_files(db): lib.add_file("test.xml.out") lib.add_file("testtar.tar") lib.add_file("test_difflib_expect.html") + lib.add_file("check_soundcard.vbs") + lib.add_file("empty.vbs") lib.glob("*.uue") lib.add_file("readme.txt", src="README") if dir=='decimaltestdata': @@ -990,6 +967,14 @@ def add_files(db): tcldir = os.path.normpath(srcdir+"/../tcltk/bin") for f in glob.glob1(tcldir, "*.dll"): lib.add_file(f, src=os.path.join(tcldir, f)) + # Add sqlite + if msilib.msi_type=="Intel64;1033": + sqlite_arch = "/ia64" + elif msilib.msi_type=="x64;1033": + sqlite_arch = "/amd64" + else: + sqlite_arch = "" + lib.add_file(srcdir+"/"+sqlite_dir+sqlite_arch+"/sqlite3.dll") # check whether there are any unknown extensions for f in glob.glob1(srcdir+"/PCBuild", "*.pyd"): if f.endswith("_d.pyd"): continue # debug version diff --git a/Tools/msi/uuids.py b/Tools/msi/uuids.py new file mode 100644 index 0000000..ce7e604 --- /dev/null +++ b/Tools/msi/uuids.py @@ -0,0 +1,33 @@ +# This should be extended for each Python release. +# The product code must change whenever the name of the MSI file +# changes, and when new component codes are issued for existing +# components. See "Changing the Product Code". As we change the +# component codes with every build, we need a new product code +# each time. For intermediate (snapshot) releases, they are automatically +# generated. For official releases, we record the product codes, +# so people can refer to them. +product_codes = { + '2.4.101': '{0e9b4d8e-6cda-446e-a208-7b92f3ddffa0}', # 2.4a1, released as a snapshot + '2.4.102': '{1b998745-4901-4edb-bc52-213689e1b922}', # 2.4a2 + '2.4.103': '{33fc8bd2-1e8f-4add-a40a-ade2728d5942}', # 2.4a3 + '2.4.111': '{51a7e2a8-2025-4ef0-86ff-e6aab742d1fa}', # 2.4b1 + '2.4.112': '{4a5e7c1d-c659-4fe3-b8c9-7c65bd9c95a5}', # 2.4b2 + '2.4.121': '{75508821-a8e9-40a8-95bd-dbe6033ddbea}', # 2.4c1 + '2.4.122': '{83a9118b-4bdd-473b-afc3-bcb142feca9e}', # 2.4c2 + '2.4.150': '{82d9302e-f209-4805-b548-52087047483a}', # 2.4.0 + '2.4.1121':'{be027411-8e6b-4440-a29b-b07df0690230}', # 2.4.1c1 + '2.4.1122':'{02818752-48bf-4074-a281-7a4114c4f1b1}', # 2.4.1c2 + '2.4.1150':'{4d4f5346-7e4a-40b5-9387-fdb6181357fc}', # 2.4.1 + '2.4.2121':'{5ef9d6b6-df78-45d2-ab09-14786a3c5a99}', # 2.4.2c1 + '2.4.2150':'{b191e49c-ea23-43b2-b28a-14e0784069b8}', # 2.4.2 + '2.4.3121':'{f669ed4d-1dce-41c4-9617-d985397187a1}', # 2.4.3c1 + '2.4.3150':'{75e71add-042c-4f30-bfac-a9ec42351313}', # 2.4.3 + '2.5.101': '{bc14ce3e-5e72-4a64-ac1f-bf59a571898c}', # 2.5a1 + '2.5.102': '{5eed51c1-8e9d-4071-94c5-b40de5d49ba5}', # 2.5a2 + '2.5.103': '{73dcd966-ffec-415f-bb39-8342c1f47017}', # 2.5a3 + '2.5.111': '{c797ecf8-a8e6-4fec-bb99-526b65f28626}', # 2.5b1 + '2.5.112': '{32beb774-f625-439d-b587-7187487baf15}', # 2.5b2 + '2.5.121': '{8e9321bc-6b24-48a3-8fd4-c95f8e531e5f}', # 2.5c1 + '2.5.122': '{a6cd508d-9599-45da-a441-cbffa9f7e070}', # 2.5c2 + '2.5.150': '{0a2c5854-557e-48c8-835a-3b9f074bdcaa}', # 2.5.0 +} diff --git a/Tools/pybench/Arithmetic.py b/Tools/pybench/Arithmetic.py new file mode 100644 index 0000000..e95c30a --- /dev/null +++ b/Tools/pybench/Arithmetic.py @@ -0,0 +1,778 @@ +from pybench import Test + +class SimpleIntegerArithmetic(Test): + + version = 0.3 + operations = 5 * (3 + 5 + 5 + 3 + 3 + 3) + rounds = 120000 + + def test(self): + + for i in xrange(self.rounds): + + a = 2 + b = 3 + c = 3 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + def calibrate(self): + + for i in xrange(self.rounds): + pass + +class SimpleFloatArithmetic(Test): + + version = 0.3 + operations = 5 * (3 + 5 + 5 + 3 + 3 + 3) + rounds = 100000 + + def test(self): + + for i in xrange(self.rounds): + + a = 2.1 + b = 3.3332 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2.1 + b = 3.3332 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2.1 + b = 3.3332 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2.1 + b = 3.3332 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2.1 + b = 3.3332 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + def calibrate(self): + + for i in xrange(self.rounds): + pass + +class SimpleIntFloatArithmetic(Test): + + version = 0.3 + operations = 5 * (3 + 5 + 5 + 3 + 3 + 3) + rounds = 120000 + + def test(self): + + for i in xrange(self.rounds): + + a = 2 + b = 3 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + b = 3 + c = 3.14159 + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class SimpleLongArithmetic(Test): + + version = 0.3 + operations = 5 * (3 + 5 + 5 + 3 + 3 + 3) + rounds = 30000 + + def test(self): + + for i in xrange(self.rounds): + + a = 2220001L + b = 100001L + c = 30005L + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2220001L + b = 100001L + c = 30005L + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2220001L + b = 100001L + c = 30005L + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2220001L + b = 100001L + c = 30005L + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2220001L + b = 100001L + c = 30005L + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + def calibrate(self): + + for i in xrange(self.rounds): + pass + +class SimpleComplexArithmetic(Test): + + version = 0.3 + operations = 5 * (3 + 5 + 5 + 3 + 3 + 3) + rounds = 40000 + + def test(self): + + for i in xrange(self.rounds): + + a = 2 + 3j + b = 2.5 + 4.5j + c = 1.2 + 6.2j + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + 3j + b = 2.5 + 4.5j + c = 1.2 + 6.2j + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + 3j + b = 2.5 + 4.5j + c = 1.2 + 6.2j + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + 3j + b = 2.5 + 4.5j + c = 1.2 + 6.2j + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + a = 2 + 3j + b = 2.5 + 4.5j + c = 1.2 + 6.2j + + c = a + b + c = b + c + c = c + a + c = a + b + c = b + c + + c = c - a + c = a - b + c = b - c + c = c - a + c = b - c + + c = a / b + c = b / a + c = c / b + + c = a * b + c = b * a + c = c * b + + c = a / b + c = b / a + c = c / b + + def calibrate(self): + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Calls.py b/Tools/pybench/Calls.py new file mode 100644 index 0000000..82e7a91 --- /dev/null +++ b/Tools/pybench/Calls.py @@ -0,0 +1,410 @@ +from pybench import Test + +class PythonFunctionCalls(Test): + + version = 0.3 + operations = 5*(1+4+4+2) + rounds = 60000 + + def test(self): + + global f,f1,g,h + + # define functions + def f(): + pass + + def f1(x): + pass + + def g(a,b,c): + return a,b,c + + def h(a,b,c,d=1,e=2,f=3): + return d,e,f + + # do calls + for i in xrange(self.rounds): + + f() + f1(i) + f1(i) + f1(i) + f1(i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + h(i,i,3,i,i) + h(i,i,i,2,i,3) + + f() + f1(i) + f1(i) + f1(i) + f1(i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + h(i,i,3,i,i) + h(i,i,i,2,i,3) + + f() + f1(i) + f1(i) + f1(i) + f1(i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + h(i,i,3,i,i) + h(i,i,i,2,i,3) + + f() + f1(i) + f1(i) + f1(i) + f1(i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + h(i,i,3,i,i) + h(i,i,i,2,i,3) + + f() + f1(i) + f1(i) + f1(i) + f1(i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + g(i,i,i) + h(i,i,3,i,i) + h(i,i,i,2,i,3) + + def calibrate(self): + + global f,f1,g,h + + # define functions + def f(): + pass + + def f1(x): + pass + + def g(a,b,c): + return a,b,c + + def h(a,b,c,d=1,e=2,f=3): + return d,e,f + + # do calls + for i in xrange(self.rounds): + pass + +### + +class BuiltinFunctionCalls(Test): + + version = 0.4 + operations = 5*(2+5+5+5) + rounds = 30000 + + def test(self): + + # localize functions + f0 = globals + f1 = hash + f2 = cmp + f3 = range + + # do calls + for i in xrange(self.rounds): + + f0() + f0() + f1(i) + f1(i) + f1(i) + f1(i) + f1(i) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + + f0() + f0() + f1(i) + f1(i) + f1(i) + f1(i) + f1(i) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + + f0() + f0() + f1(i) + f1(i) + f1(i) + f1(i) + f1(i) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + + f0() + f0() + f1(i) + f1(i) + f1(i) + f1(i) + f1(i) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + + f0() + f0() + f1(i) + f1(i) + f1(i) + f1(i) + f1(i) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f2(1,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + f3(1,3,2) + + def calibrate(self): + + # localize functions + f0 = dir + f1 = hash + f2 = range + f3 = range + + # do calls + for i in xrange(self.rounds): + pass + +### + +class PythonMethodCalls(Test): + + version = 0.3 + operations = 5*(6 + 5 + 4) + rounds = 20000 + + def test(self): + + class c: + + x = 2 + s = 'string' + + def f(self): + + return self.x + + def j(self,a,b): + + self.y = a + self.t = b + return self.y + + def k(self,a,b,c=3): + + self.y = a + self.s = b + self.t = c + + o = c() + + for i in xrange(self.rounds): + + o.f() + o.f() + o.f() + o.f() + o.f() + o.f() + o.j(i,i) + o.j(i,i) + o.j(i,2) + o.j(i,2) + o.j(2,2) + o.k(i,i) + o.k(i,2) + o.k(i,2,3) + o.k(i,i,c=4) + + o.f() + o.f() + o.f() + o.f() + o.f() + o.f() + o.j(i,i) + o.j(i,i) + o.j(i,2) + o.j(i,2) + o.j(2,2) + o.k(i,i) + o.k(i,2) + o.k(i,2,3) + o.k(i,i,c=4) + + o.f() + o.f() + o.f() + o.f() + o.f() + o.f() + o.j(i,i) + o.j(i,i) + o.j(i,2) + o.j(i,2) + o.j(2,2) + o.k(i,i) + o.k(i,2) + o.k(i,2,3) + o.k(i,i,c=4) + + o.f() + o.f() + o.f() + o.f() + o.f() + o.f() + o.j(i,i) + o.j(i,i) + o.j(i,2) + o.j(i,2) + o.j(2,2) + o.k(i,i) + o.k(i,2) + o.k(i,2,3) + o.k(i,i,c=4) + + o.f() + o.f() + o.f() + o.f() + o.f() + o.f() + o.j(i,i) + o.j(i,i) + o.j(i,2) + o.j(i,2) + o.j(2,2) + o.k(i,i) + o.k(i,2) + o.k(i,2,3) + o.k(i,i,c=4) + + def calibrate(self): + + class c: + + x = 2 + s = 'string' + + def f(self): + + return self.x + + def j(self,a,b): + + self.y = a + self.t = b + + def k(self,a,b,c=3): + + self.y = a + self.s = b + self.t = c + + o = c + + for i in xrange(self.rounds): + pass + +### + +class Recursion(Test): + + version = 0.3 + operations = 5 + rounds = 50000 + + def test(self): + + global f + + def f(x): + + if x > 1: + return f(x-1) + return 1 + + for i in xrange(self.rounds): + f(10) + f(10) + f(10) + f(10) + f(10) + + def calibrate(self): + + global f + + def f(x): + + if x > 0: + return f(x-1) + return 1 + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/CommandLine.py b/Tools/pybench/CommandLine.py new file mode 100644 index 0000000..fb7e07b --- /dev/null +++ b/Tools/pybench/CommandLine.py @@ -0,0 +1,634 @@ +""" CommandLine - Get and parse command line options + + NOTE: This still is very much work in progress !!! + + Different version are likely to be incompatible. + + TODO: + + * Incorporate the changes made by (see Inbox) + * Add number range option using srange() + +""" + +__copyright__ = """\ +Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com) +Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com) +See the documentation for further information on copyrights, +or contact the author. All Rights Reserved. +""" + +__version__ = '1.2' + +import sys, getopt, string, glob, os, re, exceptions, traceback + +### Helpers + +def _getopt_flags(options): + + """ Convert the option list to a getopt flag string and long opt + list + + """ + s = [] + l = [] + for o in options: + if o.prefix == '-': + # short option + s.append(o.name) + if o.takes_argument: + s.append(':') + else: + # long option + if o.takes_argument: + l.append(o.name+'=') + else: + l.append(o.name) + return string.join(s,''),l + +def invisible_input(prompt='>>> '): + + """ Get raw input from a terminal without echoing the characters to + the terminal, e.g. for password queries. + + """ + import getpass + entry = getpass.getpass(prompt) + if entry is None: + raise KeyboardInterrupt + return entry + +def fileopen(name, mode='wb', encoding=None): + + """ Open a file using mode. + + Default mode is 'wb' meaning to open the file for writing in + binary mode. If encoding is given, I/O to and from the file is + transparently encoded using the given encoding. + + Files opened for writing are chmod()ed to 0600. + + """ + if name == 'stdout': + return sys.stdout + elif name == 'stderr': + return sys.stderr + elif name == 'stdin': + return sys.stdin + else: + if encoding is not None: + import codecs + f = codecs.open(name, mode, encoding) + else: + f = open(name, mode) + if 'w' in mode: + os.chmod(name, 0600) + return f + +def option_dict(options): + + """ Return a dictionary mapping option names to Option instances. + """ + d = {} + for option in options: + d[option.name] = option + return d + +# Alias +getpasswd = invisible_input + +_integerRE = re.compile('\s*(-?\d+)\s*$') +_integerRangeRE = re.compile('\s*(-?\d+)\s*-\s*(-?\d+)\s*$') + +def srange(s, + + split=string.split,integer=_integerRE, + integerRange=_integerRangeRE): + + """ Converts a textual representation of integer numbers and ranges + to a Python list. + + Supported formats: 2,3,4,2-10,-1 - -3, 5 - -2 + + Values are appended to the created list in the order specified + in the string. + + """ + l = [] + append = l.append + for entry in split(s,','): + m = integer.match(entry) + if m: + append(int(m.groups()[0])) + continue + m = integerRange.match(entry) + if m: + start,end = map(int,m.groups()) + l[len(l):] = range(start,end+1) + return l + +def abspath(path, + + expandvars=os.path.expandvars,expanduser=os.path.expanduser, + join=os.path.join,getcwd=os.getcwd): + + """ Return the corresponding absolute path for path. + + path is expanded in the usual shell ways before + joining it with the current working directory. + + """ + try: + path = expandvars(path) + except AttributeError: + pass + try: + path = expanduser(path) + except AttributeError: + pass + return join(getcwd(), path) + +### Option classes + +class Option: + + """ Option base class. Takes no argument. + + """ + default = None + helptext = '' + prefix = '-' + takes_argument = 0 + has_default = 0 + tab = 15 + + def __init__(self,name,help=None): + + if not name[:1] == '-': + raise TypeError,'option names must start with "-"' + if name[1:2] == '-': + self.prefix = '--' + self.name = name[2:] + else: + self.name = name[1:] + if help: + self.help = help + + def __str__(self): + + o = self + name = o.prefix + o.name + if o.takes_argument: + name = name + ' arg' + if len(name) > self.tab: + name = name + '\n' + ' ' * (self.tab + 1 + len(o.prefix)) + else: + name = '%-*s ' % (self.tab, name) + description = o.help + if o.has_default: + description = description + ' (%s)' % o.default + return '%s %s' % (name, description) + +class ArgumentOption(Option): + + """ Option that takes an argument. + + An optional default argument can be given. + + """ + def __init__(self,name,help=None,default=None): + + # Basemethod + Option.__init__(self,name,help) + + if default is not None: + self.default = default + self.has_default = 1 + self.takes_argument = 1 + +class SwitchOption(Option): + + """ Options that can be on or off. Has an optional default value. + + """ + def __init__(self,name,help=None,default=None): + + # Basemethod + Option.__init__(self,name,help) + + if default is not None: + self.default = default + self.has_default = 1 + +### Application baseclass + +class Application: + + """ Command line application interface with builtin argument + parsing. + + """ + # Options the program accepts (Option instances) + options = [] + + # Standard settings; these are appended to options in __init__ + preset_options = [SwitchOption('-v', + 'generate verbose output'), + SwitchOption('-h', + 'show this help text'), + SwitchOption('--help', + 'show this help text'), + SwitchOption('--debug', + 'enable debugging'), + SwitchOption('--copyright', + 'show copyright'), + SwitchOption('--examples', + 'show examples of usage')] + + # The help layout looks like this: + # [header] - defaults to '' + # + # [synopsis] - formatted as ' %s' % self.synopsis + # + # options: + # [options] - formatted from self.options + # + # [version] - formatted as 'Version:\n %s' % self.version, if given + # + # [about] - defaults to '' + # + # Note: all fields that do not behave as template are formatted + # using the instances dictionary as substitution namespace, + # e.g. %(name)s will be replaced by the applications name. + # + + # Header (default to program name) + header = '' + + # Name (defaults to program name) + name = '' + + # Synopsis (%(name)s is replaced by the program name) + synopsis = '%(name)s [option] files...' + + # Version (optional) + version = '' + + # General information printed after the possible options (optional) + about = '' + + # Examples of usage to show when the --examples option is given (optional) + examples = '' + + # Copyright to show + copyright = __copyright__ + + # Apply file globbing ? + globbing = 1 + + # Generate debug output ? + debug = 0 + + # Generate verbose output ? + verbose = 0 + + # Internal errors to catch + InternalError = exceptions.Exception + + # Instance variables: + values = None # Dictionary of passed options (or default values) + # indexed by the options name, e.g. '-h' + files = None # List of passed filenames + optionlist = None # List of passed options + + def __init__(self,argv=None): + + # Setup application specs + if argv is None: + argv = sys.argv + self.filename = os.path.split(argv[0])[1] + if not self.name: + self.name = os.path.split(self.filename)[1] + else: + self.name = self.name + if not self.header: + self.header = self.name + else: + self.header = self.header + + # Init .arguments list + self.arguments = argv[1:] + + # Setup Option mapping + self.option_map = option_dict(self.options) + + # Append preset options + for option in self.preset_options: + if not self.option_map.has_key(option.name): + self.add_option(option) + + # Init .files list + self.files = [] + + # Start Application + try: + # Process startup + rc = self.startup() + if rc is not None: + raise SystemExit,rc + + # Parse command line + rc = self.parse() + if rc is not None: + raise SystemExit,rc + + # Start application + rc = self.main() + if rc is None: + rc = 0 + + except SystemExit,rc: + pass + + except KeyboardInterrupt: + print + print '* User Break' + print + rc = 1 + + except self.InternalError: + print + print '* Internal Error' + if self.debug: + print + traceback.print_exc(20, sys.stdout) + elif self.verbose: + print ' %s: %s' % sys.exc_info()[:2] + print + rc = 1 + + raise SystemExit,rc + + def add_option(self, option): + + """ Add a new Option instance to the Application dynamically. + + Note that this has to be done *before* .parse() is being + executed. + + """ + self.options.append(option) + self.option_map[option.name] = option + + def startup(self): + + """ Set user defined instance variables. + + If this method returns anything other than None, the + process is terminated with the return value as exit code. + + """ + return None + + def exit(self, rc=0): + + """ Exit the program. + + rc is used as exit code and passed back to the calling + program. It defaults to 0 which usually means: OK. + + """ + raise SystemExit, rc + + def parse(self): + + """ Parse the command line and fill in self.values and self.files. + + After having parsed the options, the remaining command line + arguments are interpreted as files and passed to .handle_files() + for processing. + + As final step the option handlers are called in the order + of the options given on the command line. + + """ + # Parse arguments + self.values = values = {} + for o in self.options: + if o.has_default: + values[o.prefix+o.name] = o.default + else: + values[o.prefix+o.name] = 0 + flags,lflags = _getopt_flags(self.options) + try: + optlist,files = getopt.getopt(self.arguments,flags,lflags) + if self.globbing: + l = [] + for f in files: + gf = glob.glob(f) + if not gf: + l.append(f) + else: + l[len(l):] = gf + files = l + self.optionlist = optlist + self.files = files + self.files + except getopt.error,why: + self.help(why) + sys.exit(1) + + # Call file handler + rc = self.handle_files(self.files) + if rc is not None: + sys.exit(rc) + + # Call option handlers + for optionname, value in optlist: + + # Try to convert value to integer + try: + value = string.atoi(value) + except ValueError: + pass + + # Find handler and call it (or count the number of option + # instances on the command line) + handlername = 'handle' + string.replace(optionname, '-', '_') + try: + handler = getattr(self, handlername) + except AttributeError: + if value == '': + # count the number of occurances + if values.has_key(optionname): + values[optionname] = values[optionname] + 1 + else: + values[optionname] = 1 + else: + values[optionname] = value + else: + rc = handler(value) + if rc is not None: + raise SystemExit, rc + + # Apply final file check (for backward compatibility) + rc = self.check_files(self.files) + if rc is not None: + sys.exit(rc) + + def check_files(self,filelist): + + """ Apply some user defined checks on the files given in filelist. + + This may modify filelist in place. A typical application + is checking that at least n files are given. + + If this method returns anything other than None, the + process is terminated with the return value as exit code. + + """ + return None + + def help(self,note=''): + + self.print_header() + if self.synopsis: + print 'Synopsis:' + # To remain backward compatible: + try: + synopsis = self.synopsis % self.name + except (NameError, KeyError, TypeError): + synopsis = self.synopsis % self.__dict__ + print ' ' + synopsis + print + self.print_options() + if self.version: + print 'Version:' + print ' %s' % self.version + print + if self.about: + print string.strip(self.about % self.__dict__) + print + if note: + print '-'*72 + print 'Note:',note + print + + def notice(self,note): + + print '-'*72 + print 'Note:',note + print '-'*72 + print + + def print_header(self): + + print '-'*72 + print self.header % self.__dict__ + print '-'*72 + print + + def print_options(self): + + options = self.options + print 'Options and default settings:' + if not options: + print ' None' + return + long = filter(lambda x: x.prefix == '--', options) + short = filter(lambda x: x.prefix == '-', options) + items = short + long + for o in options: + print ' ',o + print + + # + # Example handlers: + # + # If a handler returns anything other than None, processing stops + # and the return value is passed to sys.exit() as argument. + # + + # File handler + def handle_files(self,files): + + """ This may process the files list in place. + """ + return None + + # Short option handler + def handle_h(self,arg): + + self.help() + return 0 + + def handle_v(self, value): + + """ Turn on verbose output. + """ + self.verbose = 1 + + # Handlers for long options have two underscores in their name + def handle__help(self,arg): + + self.help() + return 0 + + def handle__debug(self,arg): + + self.debug = 1 + # We don't want to catch internal errors: + self.InternalError = None + + def handle__copyright(self,arg): + + self.print_header() + print string.strip(self.copyright % self.__dict__) + print + return 0 + + def handle__examples(self,arg): + + self.print_header() + if self.examples: + print 'Examples:' + print + print string.strip(self.examples % self.__dict__) + print + else: + print 'No examples available.' + print + return 0 + + def main(self): + + """ Override this method as program entry point. + + The return value is passed to sys.exit() as argument. If + it is None, 0 is assumed (meaning OK). Unhandled + exceptions are reported with exit status code 1 (see + __init__ for further details). + + """ + return None + +# Alias +CommandLine = Application + +def _test(): + + class MyApplication(Application): + header = 'Test Application' + version = __version__ + options = [Option('-v','verbose')] + + def handle_v(self,arg): + print 'VERBOSE, Yeah !' + + cmd = MyApplication() + if not cmd.values['-h']: + cmd.help() + print 'files:',cmd.files + print 'Bye...' + +if __name__ == '__main__': + _test() diff --git a/Tools/pybench/Constructs.py b/Tools/pybench/Constructs.py new file mode 100644 index 0000000..aba888f --- /dev/null +++ b/Tools/pybench/Constructs.py @@ -0,0 +1,565 @@ +from pybench import Test + +class IfThenElse(Test): + + version = 0.31 + operations = 30*3 # hard to say... + rounds = 150000 + + def test(self): + + a,b,c = 1,2,3 + for i in xrange(self.rounds): + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + if a == 1: + if b == 2: + if c != 3: + c = 3 + b = 3 + else: + c = 2 + elif b == 3: + b = 2 + a = 2 + elif a == 2: + a = 3 + else: + a = 1 + + def calibrate(self): + + a,b,c = 1,2,3 + for i in xrange(self.rounds): + pass + +class NestedForLoops(Test): + + version = 0.3 + operations = 1000*10*5 + rounds = 150 + + def test(self): + + l1 = range(1000) + l2 = range(10) + l3 = range(5) + for i in xrange(self.rounds): + for i in l1: + for j in l2: + for k in l3: + pass + + def calibrate(self): + + l1 = range(1000) + l2 = range(10) + l3 = range(5) + for i in xrange(self.rounds): + pass + +class ForLoops(Test): + + version = 0.1 + operations = 5 * 5 + rounds = 8000 + + def test(self): + + l1 = range(100) + for i in xrange(self.rounds): + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + for i in l1: + pass + + def calibrate(self): + + l1 = range(1000) + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Dict.py b/Tools/pybench/Dict.py new file mode 100644 index 0000000..207d88f --- /dev/null +++ b/Tools/pybench/Dict.py @@ -0,0 +1,503 @@ +from pybench import Test + +class DictCreation(Test): + + version = 0.3 + operations = 5*(5 + 5) + rounds = 60000 + + def test(self): + + for i in xrange(self.rounds): + + d1 = {} + d2 = {} + d3 = {} + d4 = {} + d5 = {} + + d1 = {1:2,3:4,5:6} + d2 = {2:3,4:5,6:7} + d3 = {3:4,5:6,7:8} + d4 = {4:5,6:7,8:9} + d5 = {6:7,8:9,10:11} + + d1 = {} + d2 = {} + d3 = {} + d4 = {} + d5 = {} + + d1 = {1:2,3:4,5:6} + d2 = {2:3,4:5,6:7} + d3 = {3:4,5:6,7:8} + d4 = {4:5,6:7,8:9} + d5 = {6:7,8:9,10:11} + + d1 = {} + d2 = {} + d3 = {} + d4 = {} + d5 = {} + + d1 = {1:2,3:4,5:6} + d2 = {2:3,4:5,6:7} + d3 = {3:4,5:6,7:8} + d4 = {4:5,6:7,8:9} + d5 = {6:7,8:9,10:11} + + d1 = {} + d2 = {} + d3 = {} + d4 = {} + d5 = {} + + d1 = {1:2,3:4,5:6} + d2 = {2:3,4:5,6:7} + d3 = {3:4,5:6,7:8} + d4 = {4:5,6:7,8:9} + d5 = {6:7,8:9,10:11} + + d1 = {} + d2 = {} + d3 = {} + d4 = {} + d5 = {} + + d1 = {1:2,3:4,5:6} + d2 = {2:3,4:5,6:7} + d3 = {3:4,5:6,7:8} + d4 = {4:5,6:7,8:9} + d5 = {6:7,8:9,10:11} + + def calibrate(self): + + for i in xrange(self.rounds): + pass + +class DictWithStringKeys(Test): + + version = 0.1 + operations = 5*(6 + 6) + rounds = 200000 + + def test(self): + + d = {} + + for i in xrange(self.rounds): + + d['abc'] = 1 + d['def'] = 2 + d['ghi'] = 3 + d['jkl'] = 4 + d['mno'] = 5 + d['pqr'] = 6 + + d['abc'] + d['def'] + d['ghi'] + d['jkl'] + d['mno'] + d['pqr'] + + d['abc'] = 1 + d['def'] = 2 + d['ghi'] = 3 + d['jkl'] = 4 + d['mno'] = 5 + d['pqr'] = 6 + + d['abc'] + d['def'] + d['ghi'] + d['jkl'] + d['mno'] + d['pqr'] + + d['abc'] = 1 + d['def'] = 2 + d['ghi'] = 3 + d['jkl'] = 4 + d['mno'] = 5 + d['pqr'] = 6 + + d['abc'] + d['def'] + d['ghi'] + d['jkl'] + d['mno'] + d['pqr'] + + d['abc'] = 1 + d['def'] = 2 + d['ghi'] = 3 + d['jkl'] = 4 + d['mno'] = 5 + d['pqr'] = 6 + + d['abc'] + d['def'] + d['ghi'] + d['jkl'] + d['mno'] + d['pqr'] + + d['abc'] = 1 + d['def'] = 2 + d['ghi'] = 3 + d['jkl'] = 4 + d['mno'] = 5 + d['pqr'] = 6 + + d['abc'] + d['def'] + d['ghi'] + d['jkl'] + d['mno'] + d['pqr'] + + def calibrate(self): + + d = {} + + for i in xrange(self.rounds): + pass + +class DictWithFloatKeys(Test): + + version = 0.1 + operations = 5*(6 + 6) + rounds = 200000 + + def test(self): + + d = {} + + for i in xrange(self.rounds): + + d[1.234] = 1 + d[2.345] = 2 + d[3.456] = 3 + d[4.567] = 4 + d[5.678] = 5 + d[6.789] = 6 + + d[1.234] + d[2.345] + d[3.456] + d[4.567] + d[5.678] + d[6.789] + + d[1.234] = 1 + d[2.345] = 2 + d[3.456] = 3 + d[4.567] = 4 + d[5.678] = 5 + d[6.789] = 6 + + d[1.234] + d[2.345] + d[3.456] + d[4.567] + d[5.678] + d[6.789] + + d[1.234] = 1 + d[2.345] = 2 + d[3.456] = 3 + d[4.567] = 4 + d[5.678] = 5 + d[6.789] = 6 + + d[1.234] + d[2.345] + d[3.456] + d[4.567] + d[5.678] + d[6.789] + + d[1.234] = 1 + d[2.345] = 2 + d[3.456] = 3 + d[4.567] = 4 + d[5.678] = 5 + d[6.789] = 6 + + d[1.234] + d[2.345] + d[3.456] + d[4.567] + d[5.678] + d[6.789] + + d[1.234] = 1 + d[2.345] = 2 + d[3.456] = 3 + d[4.567] = 4 + d[5.678] = 5 + d[6.789] = 6 + + d[1.234] + d[2.345] + d[3.456] + d[4.567] + d[5.678] + d[6.789] + + def calibrate(self): + + d = {} + + for i in xrange(self.rounds): + pass + +class DictWithIntegerKeys(Test): + + version = 0.1 + operations = 5*(6 + 6) + rounds = 200000 + + def test(self): + + d = {} + + for i in xrange(self.rounds): + + d[1] = 1 + d[2] = 2 + d[3] = 3 + d[4] = 4 + d[5] = 5 + d[6] = 6 + + d[1] + d[2] + d[3] + d[4] + d[5] + d[6] + + d[1] = 1 + d[2] = 2 + d[3] = 3 + d[4] = 4 + d[5] = 5 + d[6] = 6 + + d[1] + d[2] + d[3] + d[4] + d[5] + d[6] + + d[1] = 1 + d[2] = 2 + d[3] = 3 + d[4] = 4 + d[5] = 5 + d[6] = 6 + + d[1] + d[2] + d[3] + d[4] + d[5] + d[6] + + d[1] = 1 + d[2] = 2 + d[3] = 3 + d[4] = 4 + d[5] = 5 + d[6] = 6 + + d[1] + d[2] + d[3] + d[4] + d[5] + d[6] + + d[1] = 1 + d[2] = 2 + d[3] = 3 + d[4] = 4 + d[5] = 5 + d[6] = 6 + + d[1] + d[2] + d[3] + d[4] + d[5] + d[6] + + def calibrate(self): + + d = {} + + for i in xrange(self.rounds): + pass + +class SimpleDictManipulation(Test): + + version = 0.3 + operations = 5*(6 + 6 + 6 + 6) + rounds = 50000 + + def test(self): + + d = {} + + for i in xrange(self.rounds): + + d[0] = 3 + d[1] = 4 + d[2] = 5 + d[3] = 3 + d[4] = 4 + d[5] = 5 + + x = d[0] + x = d[1] + x = d[2] + x = d[3] + x = d[4] + x = d[5] + + d.has_key(0) + d.has_key(2) + d.has_key(4) + d.has_key(6) + d.has_key(8) + d.has_key(10) + + del d[0] + del d[1] + del d[2] + del d[3] + del d[4] + del d[5] + + d[0] = 3 + d[1] = 4 + d[2] = 5 + d[3] = 3 + d[4] = 4 + d[5] = 5 + + x = d[0] + x = d[1] + x = d[2] + x = d[3] + x = d[4] + x = d[5] + + d.has_key(0) + d.has_key(2) + d.has_key(4) + d.has_key(6) + d.has_key(8) + d.has_key(10) + + del d[0] + del d[1] + del d[2] + del d[3] + del d[4] + del d[5] + + d[0] = 3 + d[1] = 4 + d[2] = 5 + d[3] = 3 + d[4] = 4 + d[5] = 5 + + x = d[0] + x = d[1] + x = d[2] + x = d[3] + x = d[4] + x = d[5] + + d.has_key(0) + d.has_key(2) + d.has_key(4) + d.has_key(6) + d.has_key(8) + d.has_key(10) + + del d[0] + del d[1] + del d[2] + del d[3] + del d[4] + del d[5] + + d[0] = 3 + d[1] = 4 + d[2] = 5 + d[3] = 3 + d[4] = 4 + d[5] = 5 + + x = d[0] + x = d[1] + x = d[2] + x = d[3] + x = d[4] + x = d[5] + + d.has_key(0) + d.has_key(2) + d.has_key(4) + d.has_key(6) + d.has_key(8) + d.has_key(10) + + del d[0] + del d[1] + del d[2] + del d[3] + del d[4] + del d[5] + + d[0] = 3 + d[1] = 4 + d[2] = 5 + d[3] = 3 + d[4] = 4 + d[5] = 5 + + x = d[0] + x = d[1] + x = d[2] + x = d[3] + x = d[4] + x = d[5] + + d.has_key(0) + d.has_key(2) + d.has_key(4) + d.has_key(6) + d.has_key(8) + d.has_key(10) + + del d[0] + del d[1] + del d[2] + del d[3] + del d[4] + del d[5] + + def calibrate(self): + + d = {} + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Exceptions.py b/Tools/pybench/Exceptions.py new file mode 100644 index 0000000..295c83a --- /dev/null +++ b/Tools/pybench/Exceptions.py @@ -0,0 +1,681 @@ +from pybench import Test + +class TryRaiseExcept(Test): + + version = 0.1 + operations = 2 + 3 + rounds = 60000 + + def test(self): + + error = ValueError + + for i in xrange(self.rounds): + try: + raise error + except: + pass + try: + raise error + except: + pass + try: + raise error,"something" + except: + pass + try: + raise error,"something" + except: + pass + try: + raise error,"something" + except: + pass + + def calibrate(self): + + error = ValueError + + for i in xrange(self.rounds): + pass + + +class TryExcept(Test): + + version = 0.1 + operations = 15 * 10 + rounds = 200000 + + def test(self): + + for i in xrange(self.rounds): + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + try: + pass + except: + pass + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + diff --git a/Tools/pybench/Imports.py b/Tools/pybench/Imports.py new file mode 100644 index 0000000..eb458b4 --- /dev/null +++ b/Tools/pybench/Imports.py @@ -0,0 +1,139 @@ +from pybench import Test + +# First imports: +import os +import package.submodule + +class SecondImport(Test): + + version = 0.1 + operations = 5 * 5 + rounds = 20000 + + def test(self): + + for i in xrange(self.rounds): + import os + import os + import os + import os + import os + + import os + import os + import os + import os + import os + + import os + import os + import os + import os + import os + + import os + import os + import os + import os + import os + + import os + import os + import os + import os + import os + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class SecondPackageImport(Test): + + version = 0.1 + operations = 5 * 5 + rounds = 20000 + + def test(self): + + for i in xrange(self.rounds): + import package + import package + import package + import package + import package + + import package + import package + import package + import package + import package + + import package + import package + import package + import package + import package + + import package + import package + import package + import package + import package + + import package + import package + import package + import package + import package + + def calibrate(self): + + for i in xrange(self.rounds): + pass + +class SecondSubmoduleImport(Test): + + version = 0.1 + operations = 5 * 5 + rounds = 20000 + + def test(self): + + for i in xrange(self.rounds): + import package.submodule + import package.submodule + import package.submodule + import package.submodule + import package.submodule + + import package.submodule + import package.submodule + import package.submodule + import package.submodule + import package.submodule + + import package.submodule + import package.submodule + import package.submodule + import package.submodule + import package.submodule + + import package.submodule + import package.submodule + import package.submodule + import package.submodule + import package.submodule + + import package.submodule + import package.submodule + import package.submodule + import package.submodule + import package.submodule + + def calibrate(self): + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Instances.py b/Tools/pybench/Instances.py new file mode 100644 index 0000000..7663e23 --- /dev/null +++ b/Tools/pybench/Instances.py @@ -0,0 +1,68 @@ +from pybench import Test + +class CreateInstances(Test): + + version = 0.2 + operations = 3 + 7 + 4 + rounds = 60000 + + def test(self): + + class c: + pass + + class d: + def __init__(self,a,b,c): + self.a = a + self.b = b + self.c = c + + class e: + def __init__(self,a,b,c=4): + self.a = a + self.b = b + self.c = c + self.d = a + self.e = b + self.f = c + + for i in xrange(self.rounds): + o = c() + o1 = c() + o2 = c() + p = d(i,i,3) + p1 = d(i,i,3) + p2 = d(i,3,3) + p3 = d(3,i,3) + p4 = d(i,i,i) + p5 = d(3,i,3) + p6 = d(i,i,i) + q = e(i,i,3) + q1 = e(i,i,3) + q2 = e(i,i,3) + q3 = e(i,i) + + def calibrate(self): + + class c: + pass + + class d: + def __init__(self,a,b,c): + self.a = a + self.b = b + self.c = c + + class e: + def __init__(self,a,b,c=4): + self.a = a + self.b = b + self.c = c + self.d = a + self.e = b + self.f = c + + for i in xrange(self.rounds): + pass + + diff --git a/Tools/pybench/LICENSE b/Tools/pybench/LICENSE new file mode 100644 index 0000000..17c6a6b --- /dev/null +++ b/Tools/pybench/LICENSE @@ -0,0 +1,25 @@ +pybench License +--------------- + +This copyright notice and license applies to all files in the pybench +directory of the pybench distribution. + +Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com) +Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com) + + All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee or royalty 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 or portions thereof, including +modifications, that you make. + +THE AUTHOR MARC-ANDRE LEMBURG DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THE AUTHOR 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 ! diff --git a/Tools/pybench/Lists.py b/Tools/pybench/Lists.py new file mode 100644 index 0000000..a06b44c --- /dev/null +++ b/Tools/pybench/Lists.py @@ -0,0 +1,292 @@ +from pybench import Test + +class SimpleListManipulation(Test): + + version = 0.3 + operations = 5* (6 + 6 + 6) + rounds = 60000 + + def test(self): + + l = [] + + for i in xrange(self.rounds): + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + x = l[0] + x = l[1] + x = l[2] + x = l[3] + x = l[4] + x = l[5] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + x = l[0] + x = l[1] + x = l[2] + x = l[3] + x = l[4] + x = l[5] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + x = l[0] + x = l[1] + x = l[2] + x = l[3] + x = l[4] + x = l[5] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + x = l[0] + x = l[1] + x = l[2] + x = l[3] + x = l[4] + x = l[5] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + x = l[0] + x = l[1] + x = l[2] + x = l[3] + x = l[4] + x = l[5] + + if len(l) > 10000: + # cut down the size + del l[:] + + def calibrate(self): + + l = [] + + for i in xrange(self.rounds): + pass + +class ListSlicing(Test): + + version = 0.4 + operations = 25*(3+1+2+1) + rounds = 400 + + def test(self): + + n = range(100) + r = range(25) + + for i in xrange(self.rounds): + + l = range(100) + + for j in r: + + m = l[50:] + m = l[:25] + m = l[50:55] + l[:3] = n + m = l[:-1] + m = l[1:] + l[-1:] = n + + def calibrate(self): + + n = range(100) + r = range(25) + + for i in xrange(self.rounds): + + l = range(100) + + for j in r: + pass + +class SmallLists(Test): + + version = 0.3 + operations = 5*(1+ 6 + 6 + 3 + 1) + rounds = 60000 + + def test(self): + + for i in xrange(self.rounds): + + l = [] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + l[:3] = [1,2,3] + m = l[:-1] + m = l[1:] + + l[-1:] = [4,5,6] + + l = [] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + l[:3] = [1,2,3] + m = l[:-1] + m = l[1:] + + l[-1:] = [4,5,6] + + l = [] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + l[:3] = [1,2,3] + m = l[:-1] + m = l[1:] + + l[-1:] = [4,5,6] + + l = [] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + l[:3] = [1,2,3] + m = l[:-1] + m = l[1:] + + l[-1:] = [4,5,6] + + l = [] + + l.append(2) + l.append(3) + l.append(4) + l.append(2) + l.append(3) + l.append(4) + + l[0] = 3 + l[1] = 4 + l[2] = 5 + l[3] = 3 + l[4] = 4 + l[5] = 5 + + l[:3] = [1,2,3] + m = l[:-1] + m = l[1:] + + l[-1:] = [4,5,6] + + def calibrate(self): + + for i in xrange(self.rounds): + l = [] + diff --git a/Tools/pybench/Lookups.py b/Tools/pybench/Lookups.py new file mode 100644 index 0000000..fbbc0ed --- /dev/null +++ b/Tools/pybench/Lookups.py @@ -0,0 +1,946 @@ +from pybench import Test + +class SpecialClassAttribute(Test): + + version = 0.3 + operations = 5*(12 + 12) + rounds = 100000 + + def test(self): + + class c: + pass + + for i in xrange(self.rounds): + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + c.__a = 2 + c.__b = 3 + c.__c = 4 + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + x = c.__a + x = c.__b + x = c.__c + + def calibrate(self): + + class c: + pass + + for i in xrange(self.rounds): + pass + +class NormalClassAttribute(Test): + + version = 0.3 + operations = 5*(12 + 12) + rounds = 100000 + + def test(self): + + class c: + pass + + for i in xrange(self.rounds): + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + c.a = 2 + c.b = 3 + c.c = 4 + + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + x = c.a + x = c.b + x = c.c + + def calibrate(self): + + class c: + pass + + for i in xrange(self.rounds): + pass + +class SpecialInstanceAttribute(Test): + + version = 0.3 + operations = 5*(12 + 12) + rounds = 100000 + + def test(self): + + class c: + pass + o = c() + + for i in xrange(self.rounds): + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + o.__a__ = 2 + o.__b__ = 3 + o.__c__ = 4 + + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + x = o.__a__ + x = o.__b__ + x = o.__c__ + + def calibrate(self): + + class c: + pass + o = c() + + for i in xrange(self.rounds): + pass + +class NormalInstanceAttribute(Test): + + version = 0.3 + operations = 5*(12 + 12) + rounds = 100000 + + def test(self): + + class c: + pass + o = c() + + for i in xrange(self.rounds): + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + o.a = 2 + o.b = 3 + o.c = 4 + + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + x = o.a + x = o.b + x = o.c + + def calibrate(self): + + class c: + pass + o = c() + + for i in xrange(self.rounds): + pass + +class BuiltinMethodLookup(Test): + + version = 0.3 + operations = 5*(3*5 + 3*5) + rounds = 70000 + + def test(self): + + l = [] + d = {} + + for i in xrange(self.rounds): + + l.append + l.append + l.append + l.append + l.append + + l.insert + l.insert + l.insert + l.insert + l.insert + + l.sort + l.sort + l.sort + l.sort + l.sort + + d.has_key + d.has_key + d.has_key + d.has_key + d.has_key + + d.items + d.items + d.items + d.items + d.items + + d.get + d.get + d.get + d.get + d.get + + l.append + l.append + l.append + l.append + l.append + + l.insert + l.insert + l.insert + l.insert + l.insert + + l.sort + l.sort + l.sort + l.sort + l.sort + + d.has_key + d.has_key + d.has_key + d.has_key + d.has_key + + d.items + d.items + d.items + d.items + d.items + + d.get + d.get + d.get + d.get + d.get + + l.append + l.append + l.append + l.append + l.append + + l.insert + l.insert + l.insert + l.insert + l.insert + + l.sort + l.sort + l.sort + l.sort + l.sort + + d.has_key + d.has_key + d.has_key + d.has_key + d.has_key + + d.items + d.items + d.items + d.items + d.items + + d.get + d.get + d.get + d.get + d.get + + l.append + l.append + l.append + l.append + l.append + + l.insert + l.insert + l.insert + l.insert + l.insert + + l.sort + l.sort + l.sort + l.sort + l.sort + + d.has_key + d.has_key + d.has_key + d.has_key + d.has_key + + d.items + d.items + d.items + d.items + d.items + + d.get + d.get + d.get + d.get + d.get + + l.append + l.append + l.append + l.append + l.append + + l.insert + l.insert + l.insert + l.insert + l.insert + + l.sort + l.sort + l.sort + l.sort + l.sort + + d.has_key + d.has_key + d.has_key + d.has_key + d.has_key + + d.items + d.items + d.items + d.items + d.items + + d.get + d.get + d.get + d.get + d.get + + def calibrate(self): + + l = [] + d = {} + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Numbers.py b/Tools/pybench/Numbers.py new file mode 100644 index 0000000..75cf2ed --- /dev/null +++ b/Tools/pybench/Numbers.py @@ -0,0 +1,784 @@ +from pybench import Test + +class CompareIntegers(Test): + + version = 0.1 + operations = 30 * 5 + rounds = 120000 + + def test(self): + + for i in xrange(self.rounds): + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + 2 < 3 + 2 > 3 + 2 == 3 + 2 > 3 + 2 < 3 + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class CompareFloats(Test): + + version = 0.1 + operations = 30 * 5 + rounds = 60000 + + def test(self): + + for i in xrange(self.rounds): + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + 2.1 < 3.31 + 2.1 > 3.31 + 2.1 == 3.31 + 2.1 > 3.31 + 2.1 < 3.31 + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class CompareFloatsIntegers(Test): + + version = 0.1 + operations = 30 * 5 + rounds = 60000 + + def test(self): + + for i in xrange(self.rounds): + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + 2.1 < 4 + 2.1 > 4 + 2.1 == 4 + 2.1 > 4 + 2.1 < 4 + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class CompareLongs(Test): + + version = 0.1 + operations = 30 * 5 + rounds = 60000 + + def test(self): + + for i in xrange(self.rounds): + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + 1234567890L < 3456789012345L + 1234567890L > 3456789012345L + 1234567890L == 3456789012345L + 1234567890L > 3456789012345L + 1234567890L < 3456789012345L + + def calibrate(self): + + for i in xrange(self.rounds): + pass diff --git a/Tools/pybench/README b/Tools/pybench/README new file mode 100644 index 0000000..634e41b --- /dev/null +++ b/Tools/pybench/README @@ -0,0 +1,372 @@ +________________________________________________________________________ + +PYBENCH - A Python Benchmark Suite +________________________________________________________________________ + + Extendable suite of of low-level benchmarks for measuring + the performance of the Python implementation + (interpreter, compiler or VM). + +pybench is a collection of tests that provides a standardized way to +measure the performance of Python implementations. It takes a very +close look at different aspects of Python programs and let's you +decide which factors are more important to you than others, rather +than wrapping everything up in one number, like the other performance +tests do (e.g. pystone which is included in the Python Standard +Library). + +pybench has been used in the past by several Python developers to +track down performance bottlenecks or to demonstrate the impact of +optimizations and new features in Python. + +The command line interface for pybench is the file pybench.py. Run +this script with option '--help' to get a listing of the possible +options. Without options, pybench will simply execute the benchmark +and then print out a report to stdout. + + +Micro-Manual +------------ + +Run 'pybench.py -h' to see the help screen. +Run 'pybench.py' to just let the benchmark suite do it's thing and +'pybench.py -f ' to have it store the results in a file too. + +This is the current output of pybench.py --help: + +Synopsis: + pybench.py [option] files... + +Options and default settings: + -n arg number of rounds (10) + -f arg save benchmark to file arg () + -c arg compare benchmark with the one in file arg () + -s arg show benchmark in file arg, then exit () + -S show statistics of benchmarks (0) + -w arg set warp factor to arg (20) + -d hide noise in compares (0) + --no-gc disable garbage collection (0) + -v generate verbose output + -h show this help text + --help show this help text + --debug enable debugging + --copyright show copyright + --examples show examples of usage + +Version: + 1.3 + +The normal operation is to run the suite and display the +results. Use -f to save them for later reuse or comparisms. + +Examples: + +python1.5 pybench.py -w 100 -f p15 +python1.4 pybench.py -w 100 -f p14 +python pybench.py -s p15 -c p14 + + +License +------- + +See LICENSE file. + + +Sample output +------------- + +PYBENCH 1.3 + +Machine Details: + Platform ID: Linux-2.6.8-24.19-default-x86_64-with-SuSE-9.2-x86-64 + Executable: /home/lemburg/projects/Python/Installation/bin/python + Python: 2.5a1.0 + Compiler: GCC 3.3.4 (pre 3.3.5 20040809) + Build: Apr 9 2006 01:50:57 (#trunk) + +Searching for tests... + BuiltinFunctionCalls + BuiltinMethodLookup + CompareFloats + CompareFloatsIntegers + CompareIntegers + CompareInternedStrings + CompareLongs + CompareStrings + CompareUnicode + ConcatStrings + ConcatUnicode + CreateInstances + CreateStringsWithConcat + CreateUnicodeWithConcat + DictCreation + DictWithFloatKeys + DictWithIntegerKeys + DictWithStringKeys + ForLoops + IfThenElse + ListSlicing + NestedForLoops + NormalClassAttribute + NormalInstanceAttribute + PythonFunctionCalls + PythonMethodCalls + Recursion + SecondImport + SecondPackageImport + SecondSubmoduleImport + SimpleComplexArithmetic + SimpleDictManipulation + SimpleFloatArithmetic + SimpleIntFloatArithmetic + SimpleIntegerArithmetic + SimpleListManipulation + SimpleLongArithmetic + SmallLists + SmallTuples + SpecialClassAttribute + SpecialInstanceAttribute + StringMappings + StringPredicates + StringSlicing + TryExcept + TryRaiseExcept + TupleSlicing + UnicodeMappings + UnicodePredicates + UnicodeProperties + UnicodeSlicing + +Running 10 round(s) of the suite: + +... + + Round 10 real abs overhead + BuiltinFunctionCalls: 0.030r 0.030a 0.000o + BuiltinMethodLookup: 0.059r 0.060a 0.001o + CompareFloats: 0.050r 0.050a 0.000o + CompareFloatsIntegers: 0.050r 0.050a 0.000o + CompareIntegers: 0.070r 0.070a 0.000o + CompareInternedStrings: 0.039r 0.040a 0.001o + CompareLongs: 0.050r 0.050a 0.000o + CompareStrings: 0.060r 0.060a 0.000o + CompareUnicode: 0.060r 0.060a 0.000o + ConcatStrings: 0.040r 0.040a 0.000o + ConcatUnicode: 0.050r 0.050a 0.000o + CreateInstances: 0.050r 0.050a 0.000o + CreateStringsWithConcat: 0.029r 0.030a 0.001o + CreateUnicodeWithConcat: 0.060r 0.060a 0.000o + DictCreation: 0.040r 0.040a 0.000o + DictWithFloatKeys: 0.089r 0.090a 0.000o + DictWithIntegerKeys: 0.059r 0.060a 0.001o + DictWithStringKeys: 0.070r 0.070a 0.001o + ForLoops: 0.050r 0.050a 0.000o + IfThenElse: 0.070r 0.070a 0.000o + ListSlicing: 0.030r 0.030a 0.000o + NestedForLoops: 0.030r 0.030a 0.000o + NormalClassAttribute: 0.060r 0.060a 0.000o + NormalInstanceAttribute: 0.060r 0.060a 0.000o + PythonFunctionCalls: 0.060r 0.060a 0.000o + PythonMethodCalls: 0.050r 0.050a 0.000o + Recursion: 0.050r 0.050a 0.000o + SecondImport: 0.030r 0.030a 0.000o + SecondPackageImport: 0.030r 0.030a 0.000o + SecondSubmoduleImport: 0.040r 0.040a 0.000o + SimpleComplexArithmetic: 0.030r 0.030a 0.000o + SimpleDictManipulation: 0.040r 0.040a 0.000o + SimpleFloatArithmetic: 0.050r 0.050a 0.001o + SimpleIntFloatArithmetic: 0.060r 0.060a 0.000o + SimpleIntegerArithmetic: 0.060r 0.060a 0.000o + SimpleListManipulation: 0.030r 0.030a 0.000o + SimpleLongArithmetic: 0.030r 0.030a 0.000o + SmallLists: 0.050r 0.050a 0.000o + SmallTuples: 0.050r 0.050a 0.000o + SpecialClassAttribute: 0.060r 0.060a 0.000o + SpecialInstanceAttribute: 0.079r 0.080a 0.001o + StringMappings: 0.060r 0.060a 0.000o + StringPredicates: 0.049r 0.050a 0.001o + StringSlicing: 0.039r 0.040a 0.000o + TryExcept: 0.079r 0.080a 0.001o + TryRaiseExcept: 0.059r 0.060a 0.001o + TupleSlicing: 0.050r 0.050a 0.000o + UnicodeMappings: 0.070r 0.070a 0.001o + UnicodePredicates: 0.059r 0.060a 0.001o + UnicodeProperties: 0.059r 0.060a 0.001o + UnicodeSlicing: 0.050r 0.050a 0.000o + ---------------------- + Average round time: 2.937 seconds + + +Tests: per run per oper. overhead +------------------------------------------------------------------------ + BuiltinFunctionCalls: 29.85 ms 0.23 us 0.00 ms + BuiltinMethodLookup: 66.85 ms 0.13 us 0.50 ms + CompareFloats: 43.00 ms 0.10 us 0.00 ms + CompareFloatsIntegers: 51.80 ms 0.12 us 0.00 ms + CompareIntegers: 70.70 ms 0.08 us 0.50 ms + CompareInternedStrings: 41.40 ms 0.08 us 0.50 ms + CompareLongs: 47.90 ms 0.11 us 0.00 ms + CompareStrings: 58.50 ms 0.12 us 0.50 ms + CompareUnicode: 56.55 ms 0.15 us 0.50 ms + ConcatStrings: 44.75 ms 0.30 us 0.00 ms + ConcatUnicode: 54.55 ms 0.36 us 0.50 ms + CreateInstances: 50.95 ms 1.21 us 0.00 ms + CreateStringsWithConcat: 28.85 ms 0.14 us 0.50 ms + CreateUnicodeWithConcat: 53.75 ms 0.27 us 0.00 ms + DictCreation: 41.90 ms 0.28 us 0.00 ms + DictWithFloatKeys: 88.50 ms 0.15 us 0.50 ms + DictWithIntegerKeys: 62.55 ms 0.10 us 0.50 ms + DictWithStringKeys: 60.50 ms 0.10 us 0.50 ms + ForLoops: 46.90 ms 4.69 us 0.00 ms + IfThenElse: 60.55 ms 0.09 us 0.00 ms + ListSlicing: 29.90 ms 8.54 us 0.00 ms + NestedForLoops: 33.95 ms 0.10 us 0.00 ms + NormalClassAttribute: 62.75 ms 0.10 us 0.50 ms + NormalInstanceAttribute: 61.80 ms 0.10 us 0.50 ms + PythonFunctionCalls: 60.00 ms 0.36 us 0.00 ms + PythonMethodCalls: 50.00 ms 0.67 us 0.00 ms + Recursion: 46.85 ms 3.75 us 0.00 ms + SecondImport: 35.00 ms 1.40 us 0.00 ms + SecondPackageImport: 32.00 ms 1.28 us 0.00 ms + SecondSubmoduleImport: 38.00 ms 1.52 us 0.00 ms + SimpleComplexArithmetic: 26.85 ms 0.12 us 0.00 ms + SimpleDictManipulation: 40.85 ms 0.14 us 0.00 ms + SimpleFloatArithmetic: 48.70 ms 0.09 us 0.50 ms + SimpleIntFloatArithmetic: 57.70 ms 0.09 us 0.00 ms + SimpleIntegerArithmetic: 58.75 ms 0.09 us 0.50 ms + SimpleListManipulation: 34.80 ms 0.13 us 0.00 ms + SimpleLongArithmetic: 30.95 ms 0.19 us 0.50 ms + SmallLists: 47.60 ms 0.19 us 0.00 ms + SmallTuples: 48.80 ms 0.20 us 0.50 ms + SpecialClassAttribute: 61.70 ms 0.10 us 0.00 ms + SpecialInstanceAttribute: 76.70 ms 0.13 us 0.50 ms + StringMappings: 58.70 ms 0.47 us 0.00 ms + StringPredicates: 50.00 ms 0.18 us 1.00 ms + StringSlicing: 39.65 ms 0.23 us 0.50 ms + TryExcept: 84.45 ms 0.06 us 0.50 ms + TryRaiseExcept: 61.75 ms 4.12 us 0.50 ms + TupleSlicing: 48.95 ms 0.47 us 0.00 ms + UnicodeMappings: 71.50 ms 3.97 us 0.50 ms + UnicodePredicates: 52.75 ms 0.23 us 1.00 ms + UnicodeProperties: 61.90 ms 0.31 us 1.00 ms + UnicodeSlicing: 53.75 ms 0.31 us 0.50 ms +------------------------------------------------------------------------ + Average round time: 2937.00 ms + +________________________________________________________________________ + +Writing New Tests +________________________________________________________________________ + +pybench tests are simple modules defining one or more pybench.Test +subclasses. + +Writing a test essentially boils down to providing two methods: +.test() which runs .rounds number of .operations test operations each +and .calibrate() which does the same except that it doesn't actually +execute the operations. + + +Here's an example: +------------------ + +from pybench import Test + +class IntegerCounting(Test): + + # Version number of the test as float (x.yy); this is important + # for comparisons of benchmark runs - tests with unequal version + # number will not get compared. + version = 1.0 + + # The number of abstract operations done in each round of the + # test. An operation is the basic unit of what you want to + # measure. The benchmark will output the amount of run-time per + # operation. Note that in order to raise the measured timings + # significantly above noise level, it is often required to repeat + # sets of operations more than once per test round. The measured + # overhead per test round should be less than 1 second. + operations = 20 + + # Number of rounds to execute per test run. This should be + # adjusted to a figure that results in a test run-time of between + # 20-50 seconds. + rounds = 100000 + + def test(self): + + """ Run the test. + + The test needs to run self.rounds executing + self.operations number of operations each. + + """ + # Init the test + a = 1 + + # Run test rounds + # + # NOTE: Use xrange() for all test loops unless you want to face + # a 20MB process ! + # + for i in xrange(self.rounds): + + # Repeat the operations per round to raise the run-time + # per operation significantly above the noise level of the + # for-loop overhead. + + # Execute 20 operations (a += 1): + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + a += 1 + + def calibrate(self): + + """ Calibrate the test. + + This method should execute everything that is needed to + setup and run the test - except for the actual operations + that you intend to measure. pybench uses this method to + measure the test implementation overhead. + + """ + # Init the test + a = 1 + + # Run test rounds (without actually doing any operation) + for i in xrange(self.rounds): + + # Skip the actual execution of the operations, since we + # only want to measure the test's administration overhead. + pass + +Registering a new test module +----------------------------- + +To register a test module with pybench, the classes need to be +imported into the pybench.Setup module. pybench will then scan all the +symbols defined in that module for subclasses of pybench.Test and +automatically add them to the benchmark suite. + + +Have fun, +-- +Marc-Andre Lemburg +mal@lemburg.com diff --git a/Tools/pybench/Setup.py b/Tools/pybench/Setup.py new file mode 100644 index 0000000..906a2a9 --- /dev/null +++ b/Tools/pybench/Setup.py @@ -0,0 +1,35 @@ +#!python + +# Setup file for pybench +# +# This file has to import all tests to be run; it is executed as +# Python source file, so you can do all kinds of manipulations here +# rather than having to edit the tests themselves. +# +# Note: Please keep this module compatible to Python 1.5.2. +# +# Tests may include features in later Python versions, but these +# should then be embedded in try-except clauses in this configuration +# module. + +# Defaults +Number_of_rounds = 10 +Warp_factor = 20 + +# Import tests +from Arithmetic import * +from Calls import * +from Constructs import * +from Lookups import * +from Instances import * +from Lists import * +from Tuples import * +from Dict import * +from Exceptions import * +from Imports import * +from Strings import * +from Numbers import * +try: + from Unicode import * +except (ImportError, SyntaxError): + pass diff --git a/Tools/pybench/Strings.py b/Tools/pybench/Strings.py new file mode 100644 index 0000000..5ab458e --- /dev/null +++ b/Tools/pybench/Strings.py @@ -0,0 +1,564 @@ +from pybench import Test +from string import join + +class ConcatStrings(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 60000 + + def test(self): + + # Make sure the strings are *not* interned + s = join(map(str,range(100))) + t = join(map(str,range(1,101))) + + for i in xrange(self.rounds): + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + def calibrate(self): + + s = join(map(str,range(100))) + t = join(map(str,range(1,101))) + + for i in xrange(self.rounds): + pass + + +class CompareStrings(Test): + + version = 0.2 + operations = 10 * 5 + rounds = 200000 + + def test(self): + + # Make sure the strings are *not* interned + s = join(map(str,range(10))) + t = join(map(str,range(10))) + "abc" + + for i in xrange(self.rounds): + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + def calibrate(self): + + s = join(map(str,range(10))) + t = join(map(str,range(10))) + "abc" + + for i in xrange(self.rounds): + pass + + +class CompareInternedStrings(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 200000 + + def test(self): + + # Make sure the strings *are* interned + s = intern(join(map(str,range(10)))) + t = s + + for i in xrange(self.rounds): + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + t == s + t == s + t >= s + t > s + t < s + + def calibrate(self): + + s = intern(join(map(str,range(10)))) + t = s + + for i in xrange(self.rounds): + pass + + +class CreateStringsWithConcat(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 80000 + + def test(self): + + for i in xrange(self.rounds): + s = 'om' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + s = s + 'xax' + s = s + 'xbx' + s = s + 'xcx' + s = s + 'xdx' + s = s + 'xex' + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class StringSlicing(Test): + + version = 0.1 + operations = 5 * 7 + rounds = 100000 + + def test(self): + + s = join(map(str,range(100))) + + for i in xrange(self.rounds): + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + def calibrate(self): + + s = join(map(str,range(100))) + + for i in xrange(self.rounds): + pass + +### String methods + +if hasattr('', 'lower'): + + class StringMappings(Test): + + version = 0.1 + operations = 3 * (5 + 4 + 2 + 1) + rounds = 70000 + + def test(self): + + s = join(map(chr,range(20)),'') + t = join(map(chr,range(50)),'') + u = join(map(chr,range(100)),'') + v = join(map(chr,range(256)),'') + + for i in xrange(self.rounds): + + s.lower() + s.lower() + s.lower() + s.lower() + s.lower() + + s.upper() + s.upper() + s.upper() + s.upper() + s.upper() + + s.title() + s.title() + s.title() + s.title() + s.title() + + t.lower() + t.lower() + t.lower() + t.lower() + + t.upper() + t.upper() + t.upper() + t.upper() + + t.title() + t.title() + t.title() + t.title() + + u.lower() + u.lower() + + u.upper() + u.upper() + + u.title() + u.title() + + v.lower() + + v.upper() + + v.title() + + def calibrate(self): + + s = join(map(chr,range(20)),'') + t = join(map(chr,range(50)),'') + u = join(map(chr,range(100)),'') + v = join(map(chr,range(256)),'') + + for i in xrange(self.rounds): + pass + + class StringPredicates(Test): + + version = 0.1 + operations = 10 * 7 + rounds = 80000 + + def test(self): + + data = ('abc', '123', ' ', '\xe4\xf6\xfc', '\xdf'*10) + len_data = len(data) + + for i in xrange(self.rounds): + s = data[i % len_data] + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdigit() + s.islower() + s.isspace() + s.istitle() + s.isupper() + + def calibrate(self): + + data = ('abc', '123', ' ', '\u1234\u2345\u3456', '\uFFFF'*10) + data = ('abc', '123', ' ', '\xe4\xf6\xfc', '\xdf'*10) + len_data = len(data) + + for i in xrange(self.rounds): + s = data[i % len_data] + + diff --git a/Tools/pybench/Tuples.py b/Tools/pybench/Tuples.py new file mode 100644 index 0000000..7854def --- /dev/null +++ b/Tools/pybench/Tuples.py @@ -0,0 +1,365 @@ +from pybench import Test + +class TupleSlicing(Test): + + version = 0.31 + operations = 3 * 25 * 10 * 7 + rounds = 400 + + def test(self): + + r = range(25) + + for i in xrange(self.rounds): + + t = tuple(range(100)) + + for j in r: + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + m = t[50:] + m = t[:25] + m = t[50:55] + m = t[:-1] + m = t[1:] + m = t[-10:] + m = t[:10] + + def calibrate(self): + + r = range(25) + + for i in xrange(self.rounds): + + t = tuple(range(100)) + + for j in r: + + pass + +class SmallTuples(Test): + + version = 0.3 + operations = 5*(1 + 3 + 6 + 2) + rounds = 80000 + + def test(self): + + for i in xrange(self.rounds): + + t = (1,2,3,4,5,6) + + a,b,c,d,e,f = t + a,b,c,d,e,f = t + a,b,c,d,e,f = t + + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + + l = list(t) + t = tuple(l) + + t = (1,2,3,4,5,6) + + a,b,c,d,e,f = t + a,b,c,d,e,f = t + a,b,c,d,e,f = t + + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + + l = list(t) + t = tuple(l) + + t = (1,2,3,4,5,6) + + a,b,c,d,e,f = t + a,b,c,d,e,f = t + a,b,c,d,e,f = t + + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + + l = list(t) + t = tuple(l) + + t = (1,2,3,4,5,6) + + a,b,c,d,e,f = t + a,b,c,d,e,f = t + a,b,c,d,e,f = t + + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + + l = list(t) + t = tuple(l) + + t = (1,2,3,4,5,6) + + a,b,c,d,e,f = t + a,b,c,d,e,f = t + a,b,c,d,e,f = t + + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + a,b,c = t[:3] + + l = list(t) + t = tuple(l) + + def calibrate(self): + + for i in xrange(self.rounds): + pass + diff --git a/Tools/pybench/Unicode.py b/Tools/pybench/Unicode.py new file mode 100644 index 0000000..855fcf2 --- /dev/null +++ b/Tools/pybench/Unicode.py @@ -0,0 +1,542 @@ +try: + unicode +except NameError: + raise ImportError + +from pybench import Test +from string import join + +class ConcatUnicode(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 60000 + + def test(self): + + # Make sure the strings are *not* interned + s = unicode(join(map(str,range(100)))) + t = unicode(join(map(str,range(1,101)))) + + for i in xrange(self.rounds): + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + t + s + t + s + t + s + t + s + t + s + + def calibrate(self): + + s = unicode(join(map(str,range(100)))) + t = unicode(join(map(str,range(1,101)))) + + for i in xrange(self.rounds): + pass + + +class CompareUnicode(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 150000 + + def test(self): + + # Make sure the strings are *not* interned + s = unicode(join(map(str,range(10)))) + t = unicode(join(map(str,range(10))) + "abc") + + for i in xrange(self.rounds): + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + t < s + t > s + t == s + t > s + t < s + + def calibrate(self): + + s = unicode(join(map(str,range(10)))) + t = unicode(join(map(str,range(10))) + "abc") + + for i in xrange(self.rounds): + pass + + +class CreateUnicodeWithConcat(Test): + + version = 0.1 + operations = 10 * 5 + rounds = 80000 + + def test(self): + + for i in xrange(self.rounds): + s = u'om' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + s = s + u'xax' + s = s + u'xbx' + s = s + u'xcx' + s = s + u'xdx' + s = s + u'xex' + + def calibrate(self): + + for i in xrange(self.rounds): + pass + + +class UnicodeSlicing(Test): + + version = 0.1 + operations = 5 * 7 + rounds = 100000 + + def test(self): + + s = unicode(join(map(str,range(100)))) + + for i in xrange(self.rounds): + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + s[50:] + s[:25] + s[50:55] + s[-1:] + s[:1] + s[2:] + s[11:-11] + + def calibrate(self): + + s = unicode(join(map(str,range(100)))) + + for i in xrange(self.rounds): + pass + +### String methods + +class UnicodeMappings(Test): + + version = 0.1 + operations = 3 * (5 + 4 + 2 + 1) + rounds = 10000 + + def test(self): + + s = join(map(unichr,range(20)),'') + t = join(map(unichr,range(100)),'') + u = join(map(unichr,range(500)),'') + v = join(map(unichr,range(1000)),'') + + for i in xrange(self.rounds): + + s.lower() + s.lower() + s.lower() + s.lower() + s.lower() + + s.upper() + s.upper() + s.upper() + s.upper() + s.upper() + + s.title() + s.title() + s.title() + s.title() + s.title() + + t.lower() + t.lower() + t.lower() + t.lower() + + t.upper() + t.upper() + t.upper() + t.upper() + + t.title() + t.title() + t.title() + t.title() + + u.lower() + u.lower() + + u.upper() + u.upper() + + u.title() + u.title() + + v.lower() + + v.upper() + + v.title() + + def calibrate(self): + + s = join(map(unichr,range(20)),'') + t = join(map(unichr,range(100)),'') + u = join(map(unichr,range(500)),'') + v = join(map(unichr,range(1000)),'') + + for i in xrange(self.rounds): + pass + +class UnicodePredicates(Test): + + version = 0.1 + operations = 5 * 9 + rounds = 100000 + + def test(self): + + data = (u'abc', u'123', u' ', u'\u1234\u2345\u3456', u'\uFFFF'*10) + len_data = len(data) + + for i in xrange(self.rounds): + s = data[i % len_data] + + s.isalnum() + s.isalpha() + s.isdecimal() + s.isdigit() + s.islower() + s.isnumeric() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdecimal() + s.isdigit() + s.islower() + s.isnumeric() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdecimal() + s.isdigit() + s.islower() + s.isnumeric() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdecimal() + s.isdigit() + s.islower() + s.isnumeric() + s.isspace() + s.istitle() + s.isupper() + + s.isalnum() + s.isalpha() + s.isdecimal() + s.isdigit() + s.islower() + s.isnumeric() + s.isspace() + s.istitle() + s.isupper() + + def calibrate(self): + + data = (u'abc', u'123', u' ', u'\u1234\u2345\u3456', u'\uFFFF'*10) + len_data = len(data) + + for i in xrange(self.rounds): + s = data[i % len_data] + +try: + import unicodedata +except ImportError: + pass +else: + class UnicodeProperties(Test): + + version = 0.1 + operations = 5 * 8 + rounds = 100000 + + def test(self): + + data = (u'a', u'1', u' ', u'\u1234', u'\uFFFF') + len_data = len(data) + digit = unicodedata.digit + numeric = unicodedata.numeric + decimal = unicodedata.decimal + category = unicodedata.category + bidirectional = unicodedata.bidirectional + decomposition = unicodedata.decomposition + mirrored = unicodedata.mirrored + combining = unicodedata.combining + + for i in xrange(self.rounds): + + c = data[i % len_data] + + digit(c, None) + numeric(c, None) + decimal(c, None) + category(c) + bidirectional(c) + decomposition(c) + mirrored(c) + combining(c) + + digit(c, None) + numeric(c, None) + decimal(c, None) + category(c) + bidirectional(c) + decomposition(c) + mirrored(c) + combining(c) + + digit(c, None) + numeric(c, None) + decimal(c, None) + category(c) + bidirectional(c) + decomposition(c) + mirrored(c) + combining(c) + + digit(c, None) + numeric(c, None) + decimal(c, None) + category(c) + bidirectional(c) + decomposition(c) + mirrored(c) + combining(c) + + digit(c, None) + numeric(c, None) + decimal(c, None) + category(c) + bidirectional(c) + decomposition(c) + mirrored(c) + combining(c) + + def calibrate(self): + + data = (u'a', u'1', u' ', u'\u1234', u'\uFFFF') + len_data = len(data) + digit = unicodedata.digit + numeric = unicodedata.numeric + decimal = unicodedata.decimal + category = unicodedata.category + bidirectional = unicodedata.bidirectional + decomposition = unicodedata.decomposition + mirrored = unicodedata.mirrored + combining = unicodedata.combining + + for i in xrange(self.rounds): + + c = data[i % len_data] diff --git a/Tools/pybench/package/__init__.py b/Tools/pybench/package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Tools/pybench/package/submodule.py b/Tools/pybench/package/submodule.py new file mode 100644 index 0000000..e69de29 diff --git a/Tools/pybench/pybench.py b/Tools/pybench/pybench.py new file mode 100755 index 0000000..6f10bd1 --- /dev/null +++ b/Tools/pybench/pybench.py @@ -0,0 +1,461 @@ +#!/usr/local/bin/python -O + +""" A Python Benchmark Suite + +""" +# +# Note: Please keep this module compatible to Python 1.5.2. +# +# Tests may include features in later Python versions, but these +# should then be embedded in try-except clauses in the configuration +# module Setup.py. +# + +# pybench Copyright +__copyright__ = """\ +Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com) +Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com) + + All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee or royalty 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 or portions thereof, including +modifications, that you make. + +THE AUTHOR MARC-ANDRE LEMBURG DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THE AUTHOR 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 ! +""" + +# Version number +__version__ = '1.3' + +# +# NOTE: Use xrange for all test loops unless you want to face +# a 20MB process ! +# +# All tests should have rounds set to values so that a run() +# takes between 20-50 seconds. This is to get fairly good +# clock() values. You can use option -w to speedup the tests +# by a fixed integer factor (the "warp factor"). +# + +import sys,time,operator +from CommandLine import * + +try: + import cPickle + pickle = cPickle +except ImportError: + import pickle + +### Test baseclass + +class Test: + + """ All test must have this class as baseclass. It provides + the necessary interface to the benchmark machinery. + + The tests must set .rounds to a value high enough to let the + test run between 20-50 seconds. This is needed because + clock()-timing only gives rather inaccurate values (on Linux, + for example, it is accurate to a few hundreths of a + second). If you don't want to wait that long, use a warp + factor larger than 1. + + It is also important to set the .operations variable to a + value representing the number of "virtual operations" done per + call of .run(). + + If you change a test in some way, don't forget to increase + it's version number. + + """ + + ### Instance variables that each test should override + + # Version number of the test as float (x.yy); this is important + # for comparisons of benchmark runs - tests with unequal version + # number will not get compared. + version = 1.0 + + # The number of abstract operations done in each round of the + # test. An operation is the basic unit of what you want to + # measure. The benchmark will output the amount of run-time per + # operation. Note that in order to raise the measured timings + # significantly above noise level, it is often required to repeat + # sets of operations more than once per test round. The measured + # overhead per test round should be less than 1 second. + operations = 1 + + # Number of rounds to execute per test run. This should be + # adjusted to a figure that results in a test run-time of between + # 20-50 seconds. + rounds = 100000 + + ### Internal variables + + # Mark this class as implementing a test + is_a_test = 1 + + # Misc. internal variables + last_timing = (0,0,0) # last timing (real,run,calibration) + warp = 1 # warp factor this test uses + cruns = 20 # number of calibration runs + overhead = None # list of calibration timings + + def __init__(self,warp=1): + + if warp > 1: + self.rounds = self.rounds / warp + self.warp = warp + self.times = [] + self.overhead = [] + # We want these to be in the instance dict, so that pickle + # saves them + self.version = self.version + self.operations = self.operations + self.rounds = self.rounds + + def run(self): + + """ Run the test in two phases: first calibrate, then + do the actual test. Be careful to keep the calibration + timing low w/r to the test timing. + + """ + test = self.test + calibrate = self.calibrate + clock = time.clock + cruns = self.cruns + # first calibrate + offset = 0.0 + for i in range(cruns): + t = clock() + calibrate() + t = clock() - t + offset = offset + t + offset = offset / cruns + # now the real thing + t = clock() + test() + t = clock() - t + self.last_timing = (t-offset,t,offset) + self.times.append(t-offset) + + def calibrate(self): + + """ Calibrate the test. + + This method should execute everything that is needed to + setup and run the test - except for the actual operations + that you intend to measure. pybench uses this method to + measure the test implementation overhead. + + """ + return + + def test(self): + + """ Run the test. + + The test needs to run self.rounds executing + self.operations number of operations each. + + """ + # do some tests + return + + def stat(self): + + """ Returns two value: average time per run and average per + operation. + + """ + runs = len(self.times) + if runs == 0: + return 0,0 + totaltime = reduce(operator.add,self.times,0.0) + avg = totaltime / float(runs) + op_avg = totaltime / float(runs * self.rounds * self.operations) + if self.overhead: + totaloverhead = reduce(operator.add,self.overhead,0.0) + ov_avg = totaloverhead / float(runs) + else: + # use self.last_timing - not too accurate + ov_avg = self.last_timing[2] + return avg,op_avg,ov_avg + +### Load Setup + +# This has to be done after the definition of the Test class, since +# the Setup module will import subclasses using this class. + +import Setup + +### Benchmark base class + +class Benchmark: + + name = '?' # Name of the benchmark + rounds = 1 # Number of rounds to run + warp = 1 # Warp factor + roundtime = 0 # Average round time + version = None # Benchmark version number (see __init__) + # as float x.yy + starttime = None # Benchmark start time + + def __init__(self): + + self.tests = {} + self.version = 0.31 + + def load_tests(self,setupmod,warp=1): + + self.warp = warp + tests = self.tests + print 'Searching for tests...' + setupmod.__dict__.values() + for c in setupmod.__dict__.values(): + if hasattr(c,'is_a_test') and c.__name__ != 'Test': + tests[c.__name__] = c(warp) + l = tests.keys() + l.sort() + for t in l: + print ' ',t + print + + def run(self): + + tests = self.tests.items() + tests.sort() + clock = time.clock + print 'Running %i round(s) of the suite: ' % self.rounds + print + self.starttime = time.time() + roundtime = clock() + for i in range(self.rounds): + print ' Round %-25i real abs overhead' % (i+1) + for j in range(len(tests)): + name,t = tests[j] + print '%30s:' % name, + t.run() + print ' %.3fr %.3fa %.3fo' % t.last_timing + print ' ----------------------' + print ' Average round time: %.3f seconds' % \ + ((clock() - roundtime)/(i+1)) + print + self.roundtime = (clock() - roundtime) / self.rounds + print + + def print_stat(self, compare_to=None, hidenoise=0): + + if not compare_to: + print '%-30s per run per oper. overhead' % 'Tests:' + print '-'*72 + tests = self.tests.items() + tests.sort() + for name,t in tests: + avg,op_avg,ov_avg = t.stat() + print '%30s: %10.2f ms %7.2f us %7.2f ms' % \ + (name,avg*1000.0,op_avg*1000000.0,ov_avg*1000.0) + print '-'*72 + print '%30s: %10.2f ms' % \ + ('Average round time',self.roundtime * 1000.0) + + else: + print '%-30s per run per oper. diff *)' % \ + 'Tests:' + print '-'*72 + tests = self.tests.items() + tests.sort() + compatible = 1 + for name,t in tests: + avg,op_avg,ov_avg = t.stat() + try: + other = compare_to.tests[name] + except KeyError: + other = None + if other and other.version == t.version and \ + other.operations == t.operations: + avg1,op_avg1,ov_avg1 = other.stat() + qop_avg = (op_avg/op_avg1-1.0)*100.0 + if hidenoise and abs(qop_avg) < 10: + qop_avg = '' + else: + qop_avg = '%+7.2f%%' % qop_avg + else: + qavg,qop_avg = 'n/a', 'n/a' + compatible = 0 + print '%30s: %10.2f ms %7.2f us %8s' % \ + (name,avg*1000.0,op_avg*1000000.0,qop_avg) + print '-'*72 + if compatible and compare_to.roundtime > 0 and \ + compare_to.version == self.version: + print '%30s: %10.2f ms %+7.2f%%' % \ + ('Average round time',self.roundtime * 1000.0, + ((self.roundtime*self.warp)/ + (compare_to.roundtime*compare_to.warp)-1.0)*100.0) + else: + print '%30s: %10.2f ms n/a' % \ + ('Average round time',self.roundtime * 1000.0) + print + print '*) measured against: %s (rounds=%i, warp=%i)' % \ + (compare_to.name,compare_to.rounds,compare_to.warp) + print + +def print_machine(): + + import platform + print 'Machine Details:' + print ' Platform ID: %s' % platform.platform() + print ' Executable: %s' % sys.executable + # There's a bug in Python 2.2b1+... + if sys.version[:6] == '2.2b1+': + return + print ' Python: %s' % platform.python_version() + print ' Compiler: %s' % platform.python_compiler() + buildno, builddate = platform.python_build() + print ' Build: %s (#%s)' % (builddate, buildno) + +class PyBenchCmdline(Application): + + header = ("PYBENCH - a benchmark test suite for Python " + "interpreters/compilers.") + + version = __version__ + + options = [ArgumentOption('-n','number of rounds',Setup.Number_of_rounds), + ArgumentOption('-f','save benchmark to file arg',''), + ArgumentOption('-c','compare benchmark with the one in file arg',''), + ArgumentOption('-s','show benchmark in file arg, then exit',''), + SwitchOption('-S','show statistics of benchmarks',0), + ArgumentOption('-w','set warp factor to arg',Setup.Warp_factor), + SwitchOption('-d','hide noise in compares', 0), + SwitchOption('--no-gc','disable garbage collection', 0), + ] + + about = """\ +The normal operation is to run the suite and display the +results. Use -f to save them for later reuse or comparisms. + +Examples: + +python1.5 pybench.py -w 100 -f p15 +python1.4 pybench.py -w 100 -f p14 +python pybench.py -s p15 -c p14 +""" + copyright = __copyright__ + + def handle_S(self, value): + + """ Display one line stats for each benchmark file given on the + command line. + + """ + for benchmark in self.files: + try: + f = open(benchmark, 'rb') + bench = pickle.load(f) + f.close() + except IOError: + print '* Error opening/reading file %s' % repr(benchmark) + else: + print '%s,%-.2f,ms' % (benchmark, bench.roundtime*1000.0) + return 0 + + def main(self): + + rounds = self.values['-n'] + reportfile = self.values['-f'] + show_bench = self.values['-s'] + compare_to = self.values['-c'] + hidenoise = self.values['-d'] + warp = self.values['-w'] + nogc = self.values['--no-gc'] + + # Switch off GC + if nogc: + try: + import gc + except ImportError: + nogc = 0 + else: + if self.values['--no-gc']: + gc.disable() + + print 'PYBENCH',__version__ + print + + if not compare_to: + print_machine() + print + + if compare_to: + try: + f = open(compare_to,'rb') + bench = pickle.load(f) + bench.name = compare_to + f.close() + compare_to = bench + except IOError: + print '* Error opening/reading file',compare_to + compare_to = None + + if show_bench: + try: + f = open(show_bench,'rb') + bench = pickle.load(f) + bench.name = show_bench + f.close() + print 'Benchmark: %s (rounds=%i, warp=%i)' % \ + (bench.name,bench.rounds,bench.warp) + print + bench.print_stat(compare_to, hidenoise) + except IOError: + print '* Error opening/reading file',show_bench + print + return + + if reportfile: + if nogc: + print 'Benchmark: %s (rounds=%i, warp=%i, no GC)' % \ + (reportfile,rounds,warp) + else: + print 'Benchmark: %s (rounds=%i, warp=%i)' % \ + (reportfile,rounds,warp) + print + + # Create benchmark object + bench = Benchmark() + bench.rounds = rounds + bench.load_tests(Setup,warp) + try: + bench.run() + except KeyboardInterrupt: + print + print '*** KeyboardInterrupt -- Aborting' + print + return + bench.print_stat(compare_to) + # ring bell + sys.stderr.write('\007') + + if reportfile: + try: + f = open(reportfile,'wb') + bench.name = reportfile + pickle.dump(bench,f) + f.close() + except IOError: + print '* Error opening/writing reportfile' + +if __name__ == '__main__': + PyBenchCmdline() diff --git a/Tools/scripts/byext.py b/Tools/scripts/byext.py index 93759bc..09610b0 100644 --- a/Tools/scripts/byext.py +++ b/Tools/scripts/byext.py @@ -17,7 +17,7 @@ class Stats: elif os.path.isfile(arg): self.statfile(arg) else: - sys.stderr.write("Can't find %s\n" % file) + sys.stderr.write("Can't find %s\n" % arg) self.addstats("", "unknown", 1) def statdir(self, dir): @@ -25,8 +25,8 @@ class Stats: try: names = os.listdir(dir) except os.error, err: - sys.stderr.write("Can't list %s: %s\n" % (file, err)) - self.addstats(ext, "unlistable", 1) + sys.stderr.write("Can't list %s: %s\n" % (dir, err)) + self.addstats("", "unlistable", 1) return names.sort() for name in names: @@ -42,9 +42,9 @@ class Stats: else: self.statfile(full) - def statfile(self, file): - head, ext = os.path.splitext(file) - head, base = os.path.split(file) + def statfile(self, filename): + head, ext = os.path.splitext(filename) + head, base = os.path.split(filename) if ext == base: ext = "" # E.g. .cvsignore is deemed not to have an extension ext = os.path.normcase(ext) @@ -52,9 +52,9 @@ class Stats: ext = "" self.addstats(ext, "files", 1) try: - f = open(file, "rb") + f = open(filename, "rb") except IOError, err: - sys.stderr.write("Can't open %s: %s\n" % (file, err)) + sys.stderr.write("Can't open %s: %s\n" % (filename, err)) self.addstats(ext, "unopenable", 1) return data = f.read() diff --git a/Tools/scripts/classfix.py b/Tools/scripts/classfix.py index cdf006a..d30700f 100755 --- a/Tools/scripts/classfix.py +++ b/Tools/scripts/classfix.py @@ -30,7 +30,7 @@ # into a program for a different change to Python programs... import sys -import regex +import re import os from stat import * @@ -53,7 +53,7 @@ def main(): if fix(arg): bad = 1 sys.exit(bad) -ispythonprog = regex.compile('^[a-zA-Z0-9_]+\.py$') +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') def ispython(name): return ispythonprog.match(name) >= 0 @@ -148,12 +148,12 @@ def fix(filename): # This expression doesn't catch *all* class definition headers, # but it's pretty darn close. -classexpr = '^\([ \t]*class +[a-zA-Z0-9_]+\) *( *) *\(\(=.*\)?\):' -classprog = regex.compile(classexpr) +classexpr = '^([ \t]*class +[a-zA-Z0-9_]+) *( *) *((=.*)?):' +classprog = re.compile(classexpr) # Expressions for finding base class expressions. -baseexpr = '^ *\(.*\) *( *) *$' -baseprog = regex.compile(baseexpr) +baseexpr = '^ *(.*) *( *) *$' +baseprog = re.compile(baseexpr) def fixline(line): if classprog.match(line) < 0: # No 'class' keyword -- no change diff --git a/Tools/scripts/fixcid.py b/Tools/scripts/fixcid.py index 42aa835..433a425 100755 --- a/Tools/scripts/fixcid.py +++ b/Tools/scripts/fixcid.py @@ -35,7 +35,7 @@ # files. import sys -import regex +import re import os from stat import * import getopt @@ -90,7 +90,7 @@ def main(): # Change this regular expression to select a different set of files Wanted = '^[a-zA-Z0-9_]+\.[ch]$' def wanted(name): - return regex.match(Wanted, name) >= 0 + return re.match(Wanted, name) >= 0 def recursedown(dirname): dbg('recursedown(%r)\n' % (dirname,)) @@ -212,12 +212,12 @@ Number = Floatnumber + '\|' + Intnumber # Anything else is an operator -- don't list this explicitly because of '/*' OutsideComment = (Identifier, Number, String, Char, CommentStart) -OutsideCommentPattern = '\(' + '\|'.join(OutsideComment) + '\)' -OutsideCommentProgram = regex.compile(OutsideCommentPattern) +OutsideCommentPattern = '(' + '|'.join(OutsideComment) + ')' +OutsideCommentProgram = re.compile(OutsideCommentPattern) InsideComment = (Identifier, Number, CommentEnd) -InsideCommentPattern = '\(' + '\|'.join(InsideComment) + '\)' -InsideCommentProgram = regex.compile(InsideCommentPattern) +InsideCommentPattern = '(' + '|'.join(InsideComment) + ')' +InsideCommentProgram = re.compile(InsideCommentPattern) def initfixline(): global Program diff --git a/Tools/scripts/ifdef.py b/Tools/scripts/ifdef.py index 7e7b5cc..2ed7a66 100755 --- a/Tools/scripts/ifdef.py +++ b/Tools/scripts/ifdef.py @@ -27,7 +27,6 @@ # preprocessor commands. import sys -import regex import getopt defs = [] diff --git a/Tools/scripts/methfix.py b/Tools/scripts/methfix.py index a872ab7..b81871f 100755 --- a/Tools/scripts/methfix.py +++ b/Tools/scripts/methfix.py @@ -27,7 +27,7 @@ # into a program for a different change to Python programs... import sys -import regex +import re import os from stat import * @@ -50,7 +50,7 @@ def main(): if fix(arg): bad = 1 sys.exit(bad) -ispythonprog = regex.compile('^[a-zA-Z0-9_]+\.py$') +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') def ispython(name): return ispythonprog.match(name) >= 0 @@ -101,7 +101,7 @@ def fix(filename): if lineno == 1 and g is None and line[:2] == '#!': # Check for non-Python scripts words = line[2:].split() - if words and regex.search('[pP]ython', words[0]) < 0: + if words and re.search('[pP]ython', words[0]) < 0: msg = filename + ': ' + words[0] msg = msg + ' script; not fixed\n' err(msg) @@ -158,8 +158,8 @@ def fix(filename): return 0 -fixpat = '^[ \t]+def +[a-zA-Z0-9_]+ *( *self *, *\(( *\(.*\) *)\) *) *:' -fixprog = regex.compile(fixpat) +fixpat = '^[ \t]+def +[a-zA-Z0-9_]+ *( *self *, *(( *(.*) *)) *) *:' +fixprog = re.compile(fixpat) def fixline(line): if fixprog.match(line) >= 0: diff --git a/Tools/scripts/objgraph.py b/Tools/scripts/objgraph.py index 01060f9..f74c2b6 100755 --- a/Tools/scripts/objgraph.py +++ b/Tools/scripts/objgraph.py @@ -22,7 +22,7 @@ import sys import os import getopt -import regex +import re # Types of symbols. # @@ -32,7 +32,7 @@ ignore = 'Nntrgdsbavuc' # Regular expression to parse "nm -o" output. # -matcher = regex.compile('\(.*\):\t?........ \(.\) \(.*\)$') +matcher = re.compile('(.*):\t?........ (.) (.*)$') # Store "item" in "dict" under "key". # The dictionary maps keys to lists of items. diff --git a/Tools/scripts/pathfix.py b/Tools/scripts/pathfix.py index 5cb5add..7f6f191 100755 --- a/Tools/scripts/pathfix.py +++ b/Tools/scripts/pathfix.py @@ -20,7 +20,7 @@ # into a program for a different change to Python programs... import sys -import regex +import re import os from stat import * import getopt @@ -59,7 +59,7 @@ def main(): if fix(arg): bad = 1 sys.exit(bad) -ispythonprog = regex.compile('^[a-zA-Z0-9_]+\.py$') +ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') def ispython(name): return ispythonprog.match(name) >= 0 diff --git a/Tools/scripts/pdeps.py b/Tools/scripts/pdeps.py index e835f84..da63e35 100755 --- a/Tools/scripts/pdeps.py +++ b/Tools/scripts/pdeps.py @@ -21,7 +21,7 @@ import sys -import regex +import re import os @@ -57,8 +57,8 @@ def main(): # Compiled regular expressions to search for import statements # -m_import = regex.compile('^[ \t]*from[ \t]+\([^ \t]+\)[ \t]+') -m_from = regex.compile('^[ \t]*import[ \t]+\([^#]+\)') +m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+') +m_from = re.compile('^[ \t]*import[ \t]+([^#]+)') # Collect data from one file diff --git a/Tools/unicode/Makefile b/Tools/unicode/Makefile index f266d4d..fbd3557 100644 --- a/Tools/unicode/Makefile +++ b/Tools/unicode/Makefile @@ -15,7 +15,7 @@ RM = /bin/rm all: distclean mappings codecs -codecs: misc windows iso apple ebcdic custom-mappings +codecs: misc windows iso apple ebcdic custom-mappings cjk ### Mappings @@ -72,6 +72,9 @@ ebcdic: build/ $(PYTHON) gencodec.py MAPPINGS/VENDORS/MICSFT/EBCDIC/ build/ $(RM) -f build/readme.* +cjk: build/ + $(PYTHON) gencjkcodecs.py build/ + ### Cleanup clean: diff --git a/Tools/unicode/gencjkcodecs.py b/Tools/unicode/gencjkcodecs.py new file mode 100644 index 0000000..975c19c --- /dev/null +++ b/Tools/unicode/gencjkcodecs.py @@ -0,0 +1,68 @@ +import os, string + +codecs = { + 'cn': ('gb2312', 'gbk', 'gb18030', 'hz'), + 'tw': ('big5', 'cp950'), + 'hk': ('big5hkscs',), + 'jp': ('cp932', 'shift_jis', 'euc_jp', 'euc_jisx0213', 'shift_jisx0213', + 'euc_jis_2004', 'shift_jis_2004'), + 'kr': ('cp949', 'euc_kr', 'johab'), + 'iso2022': ('iso2022_jp', 'iso2022_jp_1', 'iso2022_jp_2', + 'iso2022_jp_2004', 'iso2022_jp_3', 'iso2022_jp_ext', + 'iso2022_kr'), +} + +TEMPLATE = string.Template("""\ +# +# $encoding.py: Python Unicode Codec for $ENCODING +# +# Written by Hye-Shik Chang +# + +import _codecs_$owner, codecs +import _multibytecodec as mbc + +codec = _codecs_$owner.getcodec('$encoding') + +class Codec(codecs.Codec): + encode = codec.encode + decode = codec.decode + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, + codecs.IncrementalEncoder): + codec = codec + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, + codecs.IncrementalDecoder): + codec = codec + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): + codec = codec + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec = codec + +def getregentry(): + return codecs.CodecInfo( + name='$encoding', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) +""") + +def gencodecs(prefix): + for loc, encodings in codecs.iteritems(): + for enc in encodings: + code = TEMPLATE.substitute(ENCODING=enc.upper(), + encoding=enc.lower(), + owner=loc) + codecpath = os.path.join(prefix, enc + '.py') + open(codecpath, 'w').write(code) + +if __name__ == '__main__': + import sys + gencodecs(sys.argv[1]) diff --git a/Tools/unicode/gencodec.py b/Tools/unicode/gencodec.py index bb1c9da..3cfef20 100644 --- a/Tools/unicode/gencodec.py +++ b/Tools/unicode/gencodec.py @@ -348,7 +348,7 @@ def getregentry(): l.extend(encoding_map_code) # Final new-line - l.append('\n') + l.append('') return '\n'.join(l).expandtabs() diff --git a/configure b/configure index 3e322e6..0250d9e 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 42563 . +# From configure.in Revision: 43459 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for python 3.0. # @@ -312,7 +312,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION SOVERSION CONFIG_ARGS PYTHONFRAMEWORK PYTHONFRAMEWORKDIR PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLDIR MACHDEP SGI_ABI EXTRAPLATDIR EXTRAMACHDEPPATH CONFIGURE_MACOSX_DEPLOYMENT_TARGET CXX MAINOBJ EXEEXT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC OBJEXT CPP EGREP BUILDEXEEXT LIBRARY LDLIBRARY DLLLIBRARY BLDLIBRARY LDLIBRARYDIR INSTSONAME RUNSHARED LINKCC RANLIB ac_ct_RANLIB AR SVNVERSION INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN OPT BASECFLAGS OTHER_LIBTOOL_OPT LIBTOOL_CRUFT SO LDSHARED BLDSHARED CCSHARED LINKFORSHARED CFLAGSFORSHARED SHLIBS USE_SIGNAL_MODULE SIGNAL_OBJS USE_THREAD_MODULE LDLAST THREADOBJ DLINCLDIR DYNLOADFILE MACHDEP_OBJS TRUE LIBOBJS HAVE_GETHOSTBYNAME_R_6_ARG HAVE_GETHOSTBYNAME_R_5_ARG HAVE_GETHOSTBYNAME_R_3_ARG HAVE_GETHOSTBYNAME_R HAVE_GETHOSTBYNAME LIBM LIBC UNICODE_OBJS THREADHEADERS SRCDIRS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION SOVERSION CONFIG_ARGS PYTHONFRAMEWORK PYTHONFRAMEWORKDIR PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLDIR MACHDEP SGI_ABI EXTRAPLATDIR EXTRAMACHDEPPATH CONFIGURE_MACOSX_DEPLOYMENT_TARGET CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX MAINCC CPP EGREP BUILDEXEEXT LIBRARY LDLIBRARY DLLLIBRARY BLDLIBRARY LDLIBRARYDIR INSTSONAME RUNSHARED LINKCC RANLIB ac_ct_RANLIB AR SVNVERSION INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN OPT BASECFLAGS OTHER_LIBTOOL_OPT LIBTOOL_CRUFT SO LDSHARED BLDSHARED CCSHARED LINKFORSHARED CFLAGSFORSHARED SHLIBS USE_SIGNAL_MODULE SIGNAL_OBJS USE_THREAD_MODULE LDLAST THREADOBJ DLINCLDIR DYNLOADFILE MACHDEP_OBJS TRUE LIBOBJS HAVE_GETHOSTBYNAME_R_6_ARG HAVE_GETHOSTBYNAME_R_5_ARG HAVE_GETHOSTBYNAME_R_3_ARG HAVE_GETHOSTBYNAME_R HAVE_GETHOSTBYNAME LIBM LIBC UNICODE_OBJS THREADHEADERS SRCDIRS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -859,10 +859,13 @@ Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-gcc never use gcc - --with-cxx= enable C++ support + --with-cxx-main= + compile main() and link python executable with C++ + compiler --with-suffix=.exe set executable suffix --with-pydebug build with Py_DEBUG defined --with-libs='lib1 ...' link against additional libs + --with-system-ffi build _ctypes module using an installed ffi library --with-signal-module disable/enable signal module --with-dec-threads use DEC Alpha/OSF1 thread-safe libraries --with(out)-threads[=DIRECTORY] @@ -979,7 +982,7 @@ esac else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi - cd $ac_popdir + cd "$ac_popdir" done fi @@ -1513,7 +1516,9 @@ case $ac_sys_system/$ac_sys_release in ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/[78].*) define_xopen_source=no ;; @@ -1676,258 +1681,6 @@ fi; echo "$as_me:$LINENO: result: $without_gcc" >&5 echo "${ECHO_T}$without_gcc" >&6 - - -MAINOBJ=python.o -echo "$as_me:$LINENO: checking for --with-cxx=" >&5 -echo $ECHO_N "checking for --with-cxx=... $ECHO_C" >&6 - -# Check whether --with-cxx or --without-cxx was given. -if test "${with_cxx+set}" = set; then - withval="$with_cxx" - - check_cxx=no - case $withval in - no) CXX= - with_cxx=no;; - *) CXX=$withval - MAINOBJ=ccpython.o - with_cxx=$withval;; - esac -else - - with_cxx=no - check_cxx=yes - -fi; -echo "$as_me:$LINENO: result: $with_cxx" >&5 -echo "${ECHO_T}$with_cxx" >&6 - -if test "$with_cxx" = "yes" -then - { { echo "$as_me:$LINENO: error: must supply a compiler when using --with-cxx" >&5 -echo "$as_me: error: must supply a compiler when using --with-cxx" >&2;} - { (exit 1); exit 1; }; } -fi - - - - -if test "$check_cxx" = "yes" -then - for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - echo "$as_me:$LINENO: result: $CXX" >&5 -echo "${ECHO_T}$CXX" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$CXX" && break -done -test -n "$CXX" || CXX="notfound" - - if test "$CXX" = "notfound" - then - CXX= - else - ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 -echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6 -ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` -if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 - (eval $ac_link_default) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Find the output, starting from the most likely. This scheme is -# not robust to junk in `.', hence go to wildcards (a.*) only as a last -# resort. - -# Be careful to initialize this variable, since it used to be cached. -# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. -ac_cv_exeext= -# b.out is created by i960 compilers. -for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) - ;; - conftest.$ac_ext ) - # This is the source file. - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - # FIXME: I believe we export ac_cv_exeext for Libtool, - # but it would be cool to find out if it's true. Does anybody - # maintain Libtool? --akim. - export ac_cv_exeext - break;; - * ) - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables -See \`config.log' for more details." >&5 -echo "$as_me: error: C++ compiler cannot create executables -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } -fi - -ac_exeext=$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_file" >&5 -echo "${ECHO_T}$ac_file" >&6 - -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 -echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 -# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run C++ compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - fi - fi -fi -echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 - -rm -f a.out a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 -echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 -echo "$as_me:$LINENO: result: $cross_compiling" >&5 -echo "${ECHO_T}$cross_compiling" >&6 - -echo "$as_me:$LINENO: checking for suffix of executables" >&5 -echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - export ac_cv_exeext - break;; - * ) break;; - esac -done -else - { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 -echo "${ECHO_T}$ac_cv_exeext" >&6 - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT - - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - fi -fi - # 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 @@ -2506,8 +2259,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2565,8 +2317,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2682,8 +2433,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2737,8 +2487,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2783,8 +2532,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2828,8 +2576,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -2869,6 +2616,190 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for --with-cxx-main=" >&5 +echo $ECHO_N "checking for --with-cxx-main=... $ECHO_C" >&6 + +# Check whether --with-cxx_main or --without-cxx_main was given. +if test "${with_cxx_main+set}" = set; then + withval="$with_cxx_main" + + + case $withval in + no) with_cxx_main=no + MAINCC='$(CC)';; + yes) with_cxx_main=yes + MAINCC='$(CXX)';; + *) with_cxx_main=yes + MAINCC=$withval + if test -z "$CXX" + then + CXX=$withval + fi;; + esac +else + + with_cxx_main=no + MAINCC='$(CC)' + +fi; +echo "$as_me:$LINENO: result: $with_cxx_main" >&5 +echo "${ECHO_T}$with_cxx_main" >&6 + +preset_cxx="$CXX" +if test -z "$CXX" +then + case "$CC" in + gcc) # Extract the first word of "g++", so it can be a program name with args. +set dummy g++; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $CXX in + [\\/]* | ?:[\\/]*) + ac_cv_path_CXX="$CXX" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in notfound +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CXX="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_CXX" && ac_cv_path_CXX="g++" + ;; +esac +fi +CXX=$ac_cv_path_CXX + +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + cc) # Extract the first word of "c++", so it can be a program name with args. +set dummy c++; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $CXX in + [\\/]* | ?:[\\/]*) + ac_cv_path_CXX="$CXX" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in notfound +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CXX="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_CXX" && ac_cv_path_CXX="c++" + ;; +esac +fi +CXX=$ac_cv_path_CXX + +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + esac + if test "$CXX" = "notfound" + then + CXX="" + fi +fi +if test -z "$CXX" +then + for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break +done +test -n "$CXX" || CXX="notfound" + + if test "$CXX" = "notfound" + then + CXX="" + fi +fi +if test "$preset_cxx" != "$CXX" +then + { echo "$as_me:$LINENO: WARNING: + + By default, distutils will build C++ extension modules with \"$CXX\". + If this is not intended, then set CXX on the configure command line. + " >&5 +echo "$as_me: WARNING: + + By default, distutils will build C++ extension modules with \"$CXX\". + If this is not intended, then set CXX on the configure command line. + " >&2;} +fi + + # checks for UNIX variants that set C preprocessor variables ac_ext=c @@ -3271,22 +3202,7 @@ echo "$as_me:$LINENO: checking LINKCC" >&5 echo $ECHO_N "checking LINKCC... $ECHO_C" >&6 if test -z "$LINKCC" then - if test -z "$CXX"; then - LINKCC="\$(PURIFY) \$(CC)" - else - echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc - $CXX -c conftest_a.cc # 2>&5 - echo 'void foo(){}' > conftest_b.$ac_ext - $CC -c conftest_b.$ac_ext # 2>&5 - if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ - && test -s conftest$ac_exeext && ./conftest$ac_exeext - then - LINKCC="\$(PURIFY) \$(CC)" - else - LINKCC="\$(PURIFY) \$(CXX)" - fi - rm -fr conftest* - fi + LINKCC='$(PURIFY) $(MAINCC)' case $ac_sys_system in AIX*) exp_extra="\"\"" @@ -3600,6 +3516,7 @@ do done done + test -z "$ac_cv_prog_SVNVERSION" && ac_cv_prog_SVNVERSION="not-found" fi fi SVNVERSION=$ac_cv_prog_SVNVERSION @@ -3788,18 +3705,21 @@ if test -z "$OPT" then case $GCC in yes) + if test "$CC" != 'g++' ; then + STRICT_PROTO="-Wstrict-prototypes" + fi case $ac_cv_prog_cc_g in yes) if test "$Py_DEBUG" = 'true' ; then # Optimization messes up debuggers, so turn it off for # debug builds. - OPT="-g -Wall -Wstrict-prototypes" + OPT="-g -Wall $STRICT_PROTO" else - OPT="-g -O3 -Wall -Wstrict-prototypes" + OPT="-g -O3 -Wall $STRICT_PROTO" fi ;; *) - OPT="-O3 -Wall -Wstrict-prototypes" + OPT="-O3 -Wall $STRICT_PROTO" ;; esac case $ac_sys_system in @@ -3882,6 +3802,9 @@ echo "${ECHO_T}$ac_cv_no_strict_aliasing_ok" >&6 Darwin*) BASECFLAGS="$BASECFLAGS -Wno-long-double -no-cpp-precomp -mno-fused-madd" ;; + OSF*) + BASECFLAGS="$BASECFLAGS -mieee" + ;; esac ;; @@ -4366,8 +4289,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4537,8 +4459,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4652,8 +4573,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4808,8 +4728,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4881,8 +4800,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -4936,8 +4854,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5008,8 +4925,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5063,8 +4979,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5127,8 +5042,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5185,8 +5099,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5333,8 +5246,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5486,8 +5398,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5556,8 +5467,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5647,8 +5557,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5698,8 +5607,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5776,8 +5684,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5858,8 +5765,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5924,8 +5830,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -5990,8 +5895,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6063,8 +5967,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6125,8 +6028,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6229,8 +6131,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6296,8 +6197,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6359,8 +6259,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6400,8 +6299,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6457,8 +6355,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6498,8 +6395,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6563,8 +6459,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6595,10 +6490,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -6710,8 +6603,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6773,8 +6665,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6814,8 +6705,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6871,8 +6761,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6912,8 +6801,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -6977,8 +6865,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7009,10 +6896,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -7124,8 +7009,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7187,8 +7071,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7228,8 +7111,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7285,8 +7167,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7326,8 +7207,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7391,8 +7271,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7423,10 +7302,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -7538,8 +7415,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7601,8 +7477,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7642,8 +7517,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7699,8 +7573,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7740,8 +7613,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7805,8 +7677,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -7837,10 +7708,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -7952,8 +7821,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8015,8 +7883,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8056,8 +7923,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8113,8 +7979,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8154,8 +8019,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8219,8 +8083,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8251,10 +8114,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -8366,8 +8227,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8429,8 +8289,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8470,8 +8329,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8527,8 +8385,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8568,8 +8425,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8633,8 +8489,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8665,10 +8520,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -8780,8 +8633,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8843,8 +8695,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8884,8 +8735,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8941,8 +8791,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -8982,8 +8831,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9047,8 +8895,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9079,10 +8926,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -9194,8 +9039,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9257,8 +9101,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9298,8 +9141,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9355,8 +9197,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9396,8 +9237,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9461,8 +9301,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9493,10 +9332,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -9604,8 +9441,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9666,8 +9502,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9729,8 +9564,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9770,8 +9604,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9827,8 +9660,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9868,8 +9700,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9933,8 +9764,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -9965,10 +9795,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -10077,8 +9905,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10139,8 +9966,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10202,8 +10028,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10243,8 +10068,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10300,8 +10124,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10341,8 +10164,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10406,8 +10228,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10438,10 +10259,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -10693,8 +10512,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -10813,7 +10631,7 @@ echo "${ECHO_T}$enable_toolbox_glue" >&6 case $ac_sys_system/$ac_sys_release in - Darwin/[01234567].*) + Darwin/[01567]\..*) OTHER_LIBTOOL_OPT="-prebind -seg1addr 0x10000000" ;; Darwin/*) @@ -10823,7 +10641,7 @@ esac case $ac_sys_system/$ac_sys_release in - Darwin/[01234567].*) + Darwin/[01567]\..*) LIBTOOL_CRUFT="-framework System -lcc_dynamic -arch_only `arch`" LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; @@ -11170,8 +10988,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11244,8 +11061,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11320,8 +11136,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11375,8 +11190,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11451,8 +11265,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11514,8 +11327,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11586,8 +11398,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11654,8 +11465,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11725,8 +11535,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -11774,6 +11583,22 @@ else echo "${ECHO_T}no" >&6 fi; +# Check for use of the system libffi library +echo "$as_me:$LINENO: checking for --with-system-ffi" >&5 +echo $ECHO_N "checking for --with-system-ffi... $ECHO_C" >&6 + +# Check whether --with-system_ffi or --without-system_ffi was given. +if test "${with_system_ffi+set}" = set; then + withval="$with_system_ffi" + +fi; + +if test -z "$with_system_ffi" +then with_system_ffi="no" +fi +echo "$as_me:$LINENO: result: $with_system_ffi" >&5 +echo "${ECHO_T}$with_system_ffi" >&6 + # Determine if signalmodule should be used. @@ -11979,8 +11804,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12134,8 +11958,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12315,8 +12138,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12408,8 +12230,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12472,8 +12293,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12622,8 +12442,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12780,8 +12599,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12854,8 +12672,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -12928,8 +12745,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13002,8 +12818,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13105,8 +12920,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13181,8 +12995,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13384,8 +13197,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13533,8 +13345,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13924,8 +13735,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -13974,7 +13784,8 @@ then ;; BeOS*) DYNLOADFILE="dynload_beos.o";; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; - Darwin/*) DYNLOADFILE="dynload_next.o";; + # Use dynload_next.c only on 10.2 and below, which don't have native dlopen() + Darwin/[0156]\..*) DYNLOADFILE="dynload_next.o";; atheos*) DYNLOADFILE="dynload_atheos.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub @@ -14086,6 +13897,8 @@ echo "${ECHO_T}MACHDEP_OBJS" >&6 + + for ac_func in alarm bind_textdomain_codeset chown clock confstr ctermid \ execv fork fpathconf ftime ftruncate \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ @@ -14097,7 +13910,7 @@ for ac_func in alarm bind_textdomain_codeset chown clock confstr ctermid \ setlocale setregid setreuid setsid setpgid setpgrp setuid setvbuf snprintf \ sigaction siginterrupt sigrelse strftime \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unsetenv utimes waitpid wcscoll _getpty + truncate uname unsetenv utimes waitpid wait3 wait4 wcscoll _getpty do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -14165,8 +13978,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14227,8 +14039,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14282,8 +14093,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14337,8 +14147,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14392,8 +14201,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14447,8 +14255,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14502,8 +14309,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14567,8 +14373,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14626,8 +14431,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14685,8 +14489,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14795,8 +14598,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14863,8 +14665,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14931,8 +14732,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -14994,8 +14794,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15057,8 +14856,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15120,8 +14918,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15221,8 +15018,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15290,8 +15086,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15360,8 +15155,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15469,8 +15263,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15538,8 +15331,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15608,8 +15400,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15724,8 +15515,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15831,8 +15621,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15943,8 +15732,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -15996,8 +15784,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16094,8 +15881,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16147,8 +15933,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16245,8 +16030,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16298,8 +16082,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16365,8 +16148,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16434,8 +16216,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16672,8 +16453,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16740,8 +16520,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16803,8 +16582,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16869,8 +16647,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16916,8 +16693,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -16991,8 +16767,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17056,8 +16831,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17100,8 +16874,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17166,8 +16939,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17210,8 +16982,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17276,8 +17047,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17320,8 +17090,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17386,8 +17155,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17430,8 +17198,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17496,8 +17263,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17540,8 +17306,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17606,8 +17371,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17650,8 +17414,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17729,8 +17492,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17793,8 +17555,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17853,8 +17614,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17917,8 +17677,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -17984,8 +17743,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18090,8 +17848,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18150,8 +17907,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18206,8 +17962,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18262,8 +18017,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18329,8 +18083,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18389,8 +18142,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18448,8 +18200,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18510,8 +18261,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18610,8 +18360,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18679,8 +18428,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18748,8 +18496,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18815,8 +18562,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -18930,8 +18676,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19041,8 +18786,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19108,8 +18852,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19301,8 +19044,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19377,8 +19119,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19536,8 +19277,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19600,8 +19340,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19642,8 +19381,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19700,8 +19438,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19742,8 +19479,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19808,8 +19544,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -19840,10 +19575,8 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run test program while cross compiling -See \`config.log' for more details." >&2;} + { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 +echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -19957,8 +19690,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20010,7 +19742,8 @@ cat >>conftest.$ac_ext <<_ACEOF #include int main() { - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 1 : 0); + /* Success: exit code 0 */ + exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } _ACEOF @@ -20168,8 +19901,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20211,8 +19943,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20269,8 +20000,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20459,8 +20189,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20536,8 +20265,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20612,8 +20340,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20688,8 +20415,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20820,8 +20546,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -20894,8 +20619,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21157,8 +20881,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21204,8 +20927,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21279,8 +21001,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21463,8 +21184,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21530,8 +21250,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21597,8 +21316,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21664,8 +21382,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -21774,8 +21491,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? @@ -22455,15 +22171,15 @@ s,@SGI_ABI@,$SGI_ABI,;t t s,@EXTRAPLATDIR@,$EXTRAPLATDIR,;t t s,@EXTRAMACHDEPPATH@,$EXTRAMACHDEPPATH,;t t s,@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@,$CONFIGURE_MACOSX_DEPLOYMENT_TARGET,;t t -s,@CXX@,$CXX,;t t -s,@MAINOBJ@,$MAINOBJ,;t t -s,@EXEEXT@,$EXEEXT,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t +s,@CXX@,$CXX,;t t +s,@MAINCC@,$MAINCC,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t s,@BUILDEXEEXT@,$BUILDEXEEXT,;t t @@ -22681,11 +22397,6 @@ esac *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac - if test x"$ac_file" != x-; then - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - rm -f "$ac_file" - fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ @@ -22724,6 +22435,12 @@ echo "$as_me: error: cannot find input file: $f" >&2;} fi;; esac done` || { (exit 1); exit 1; } + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub @@ -23007,13 +22724,13 @@ if test "$no_create" != yes; then fi -echo "creating Setup" +echo "creating Modules/Setup" if test ! -f Modules/Setup then cp $srcdir/Modules/Setup.dist Modules/Setup fi -echo "creating Setup.local" +echo "creating Modules/Setup.local" if test ! -f Modules/Setup.local then echo "# Edit this file for local setup changes" >Modules/Setup.local diff --git a/configure.in b/configure.in index f3525b1..7a3a37f 100644 --- a/configure.in +++ b/configure.in @@ -173,7 +173,9 @@ case $ac_sys_system/$ac_sys_release in ;; # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE # disables platform specific features beyond repair. - Darwin/8.*) + # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE + # has no effect, don't bother defineing them + Darwin/@<:@78@:>@.*) define_xopen_source=no ;; @@ -311,64 +313,69 @@ AC_ARG_WITH(gcc, esac]) AC_MSG_RESULT($without_gcc) +# 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 + AC_MSG_ERROR([cached CC is different -- throw away $cache_file +(it is also a good idea to do 'make clean' before compiling)]) +fi + +AC_PROG_CC + AC_SUBST(CXX) -AC_SUBST(MAINOBJ) -MAINOBJ=python.o -AC_MSG_CHECKING(for --with-cxx=) -AC_ARG_WITH(cxx, - AC_HELP_STRING(--with-cxx=, enable C++ support), +AC_SUBST(MAINCC) +AC_MSG_CHECKING(for --with-cxx-main=) +AC_ARG_WITH(cxx_main, + AC_HELP_STRING([--with-cxx-main=], + [compile main() and link python executable with C++ compiler]), [ - check_cxx=no + case $withval in - no) CXX= - with_cxx=no;; - *) CXX=$withval - MAINOBJ=ccpython.o - with_cxx=$withval;; + no) with_cxx_main=no + MAINCC='$(CC)';; + yes) with_cxx_main=yes + MAINCC='$(CXX)';; + *) with_cxx_main=yes + MAINCC=$withval + if test -z "$CXX" + then + CXX=$withval + fi;; esac], [ - with_cxx=no - check_cxx=yes + with_cxx_main=no + MAINCC='$(CC)' ]) -AC_MSG_RESULT($with_cxx) +AC_MSG_RESULT($with_cxx_main) -if test "$with_cxx" = "yes" +preset_cxx="$CXX" +if test -z "$CXX" then - AC_MSG_ERROR([must supply a compiler when using --with-cxx]) + case "$CC" in + gcc) AC_PATH_PROG(CXX, [g++], [g++], [notfound]) ;; + cc) AC_PATH_PROG(CXX, [c++], [c++], [notfound]) ;; + esac + if test "$CXX" = "notfound" + then + CXX="" + fi fi - -dnl The following fragment works similar to AC_PROG_CXX. -dnl It does not fail if CXX is not found, and it is not executed if -dnl --without-cxx was given. -dnl Finally, it does not test whether CXX is g++. - -dnl Autoconf 2.5x does not have AC_PROG_CXX_WORKS anymore -ifdef([AC_PROG_CXX_WORKS],[], - [AC_DEFUN([AC_PROG_CXX_WORKS], - [AC_LANG_PUSH(C++)dnl - _AC_COMPILER_EXEEXT - AC_LANG_POP() - ] -)]) - -if test "$check_cxx" = "yes" +if test -z "$CXX" then AC_CHECK_PROGS(CXX, $CCC c++ g++ gcc CC cxx cc++ cl, notfound) if test "$CXX" = "notfound" then - CXX= - else - AC_PROG_CXX_WORKS + CXX="" fi fi - -# 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" +if test "$preset_cxx" != "$CXX" then - AC_MSG_ERROR([cached CC is different -- throw away $cache_file -(it is also a good idea to do 'make clean' before compiling)]) + AC_MSG_WARN([ + + By default, distutils will build C++ extension modules with "$CXX". + If this is not intended, then set CXX on the configure command line. + ]) fi -AC_PROG_CC # checks for UNIX variants that set C preprocessor variables AC_AIX @@ -478,22 +485,7 @@ AC_SUBST(LINKCC) AC_MSG_CHECKING(LINKCC) if test -z "$LINKCC" then - if test -z "$CXX"; then - LINKCC="\$(PURIFY) \$(CC)" - else - echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc - $CXX -c conftest_a.cc # 2>&5 - echo 'void foo(){}' > conftest_b.$ac_ext - $CC -c conftest_b.$ac_ext # 2>&5 - if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ - && test -s conftest$ac_exeext && ./conftest$ac_exeext - then - LINKCC="\$(PURIFY) \$(CC)" - else - LINKCC="\$(PURIFY) \$(CXX)" - fi - rm -fr conftest* - fi + LINKCC='$(PURIFY) $(MAINCC)' case $ac_sys_system in AIX*) exp_extra="\"\"" @@ -619,7 +611,7 @@ AC_SUBST(AR) AC_CHECK_PROGS(AR, ar aal, ar) AC_SUBST(SVNVERSION) -AC_CHECK_PROG(SVNVERSION, svnversion, found) +AC_CHECK_PROG(SVNVERSION, svnversion, found, not-found) if test $SVNVERSION = found then SVNVERSION="svnversion \$(srcdir)" @@ -683,18 +675,21 @@ if test -z "$OPT" then case $GCC in yes) + if test "$CC" != 'g++' ; then + STRICT_PROTO="-Wstrict-prototypes" + fi case $ac_cv_prog_cc_g in yes) if test "$Py_DEBUG" = 'true' ; then # Optimization messes up debuggers, so turn it off for # debug builds. - OPT="-g -Wall -Wstrict-prototypes" + OPT="-g -Wall $STRICT_PROTO" else - OPT="-g -O3 -Wall -Wstrict-prototypes" + OPT="-g -O3 -Wall $STRICT_PROTO" fi ;; *) - OPT="-O3 -Wall -Wstrict-prototypes" + OPT="-O3 -Wall $STRICT_PROTO" ;; esac case $ac_sys_system in @@ -746,6 +741,9 @@ yes) Darwin*) BASECFLAGS="$BASECFLAGS -Wno-long-double -no-cpp-precomp -mno-fused-madd" ;; + OSF*) + BASECFLAGS="$BASECFLAGS -mieee" + ;; esac ;; @@ -1254,7 +1252,7 @@ AC_MSG_RESULT($enable_toolbox_glue) AC_SUBST(OTHER_LIBTOOL_OPT) case $ac_sys_system/$ac_sys_release in - Darwin/@<:@01234567@:>@.*) + Darwin/@<:@01567@:>@\..*) OTHER_LIBTOOL_OPT="-prebind -seg1addr 0x10000000" ;; Darwin/*) @@ -1264,7 +1262,7 @@ esac AC_SUBST(LIBTOOL_CRUFT) case $ac_sys_system/$ac_sys_release in - Darwin/@<:@01234567@:>@.*) + Darwin/@<:@01567@:>@\..*) LIBTOOL_CRUFT="-framework System -lcc_dynamic -arch_only `arch`" LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; @@ -1599,6 +1597,16 @@ LIBS="$withval $LIBS" ], [AC_MSG_RESULT(no)]) +# Check for use of the system libffi library +AC_MSG_CHECKING(for --with-system-ffi) +AC_ARG_WITH(system_ffi, + AC_HELP_STRING(--with-system-ffi, build _ctypes module using an installed ffi library)) + +if test -z "$with_system_ffi" +then with_system_ffi="no" +fi +AC_MSG_RESULT($with_system_ffi) + # Determine if signalmodule should be used. AC_SUBST(USE_SIGNAL_MODULE) AC_SUBST(SIGNAL_OBJS) @@ -2105,7 +2113,8 @@ then ;; BeOS*) DYNLOADFILE="dynload_beos.o";; hp*|HP*) DYNLOADFILE="dynload_hpux.o";; - Darwin/*) DYNLOADFILE="dynload_next.o";; + # Use dynload_next.c only on 10.2 and below, which don't have native dlopen() + Darwin/@<:@0156@:>@\..*) DYNLOADFILE="dynload_next.o";; atheos*) DYNLOADFILE="dynload_atheos.o";; *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub @@ -2148,7 +2157,7 @@ AC_CHECK_FUNCS(alarm bind_textdomain_codeset chown clock confstr ctermid \ setlocale setregid setreuid setsid setpgid setpgrp setuid setvbuf snprintf \ sigaction siginterrupt sigrelse strftime \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unsetenv utimes waitpid wcscoll _getpty) + truncate uname unsetenv utimes waitpid wait3 wait4 wcscoll _getpty) # For some functions, having a definition is not sufficient, since # we want to take their address. @@ -2786,7 +2795,8 @@ then #include int main() { - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 1 : 0); + /* Success: exit code 0 */ + exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } ], ac_cv_wchar_t_signed=yes, @@ -3211,13 +3221,13 @@ AC_MSG_RESULT(done) AC_CONFIG_FILES(Makefile.pre Modules/Setup.config) AC_OUTPUT -echo "creating Setup" +echo "creating Modules/Setup" if test ! -f Modules/Setup then cp $srcdir/Modules/Setup.dist Modules/Setup fi -echo "creating Setup.local" +echo "creating Modules/Setup.local" if test ! -f Modules/Setup.local then echo "# Edit this file for local setup changes" >Modules/Setup.local diff --git a/pyconfig.h.in b/pyconfig.h.in index 9c3ca53..8df7f9b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -670,6 +670,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H +/* Define to 1 if you have the `wait3' function. */ +#undef HAVE_WAIT3 + +/* Define to 1 if you have the `wait4' function. */ +#undef HAVE_WAIT4 + /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID diff --git a/setup.py b/setup.py index 09b79fd..61c1fec 100644 --- a/setup.py +++ b/setup.py @@ -180,6 +180,10 @@ class PyBuildExt(build_ext): def build_extension(self, ext): + if ext.name == '_ctypes': + if not self.configure_ctypes(ext): + return + try: build_ext.build_extension(self, ext) except (CCompilerError, DistutilsError), why: @@ -326,8 +330,6 @@ class PyBuildExt(build_ext): # # Some modules that are normally always on: - exts.append( Extension('regex', ['regexmodule.c', 'regexpr.c']) ) - exts.append( Extension('_weakref', ['_weakref.c']) ) # array objects @@ -551,8 +553,8 @@ class PyBuildExt(build_ext): exts.append( Extension('_sha', ['shamodule.c']) ) # The _md5 module implements the RSA Data Security, Inc. MD5 # Message-Digest Algorithm, described in RFC 1321. The - # necessary files md5c.c and md5.h are included here. - exts.append( Extension('_md5', ['md5module.c', 'md5c.c']) ) + # necessary files md5.c and md5.h are included here. + exts.append( Extension('_md5', ['md5module.c', 'md5.c']) ) if (openssl_ver < 0x00908000): # OpenSSL doesn't do these until 0.9.8 so we'll bring our own hash @@ -569,14 +571,13 @@ class PyBuildExt(build_ext): # Sleepycat Berkeley DB interface. http://www.sleepycat.com # - # This requires the Sleepycat DB code. The earliest supported version - # of that library is 3.2, the latest supported version is 4.4. A list - # of available releases can be found at - # - # http://www.sleepycat.com/update/index.html + # This requires the Sleepycat DB code. The supported versions + # are set below. Visit http://www.sleepycat.com/ to download + # a release. Most open source OSes come with one or more + # versions of BerkeleyDB already installed. max_db_ver = (4, 4) - min_db_ver = (3, 2) + min_db_ver = (3, 3) db_setup_debug = False # verbose debug prints from this script? # construct a list of paths to look for the header file in on @@ -691,6 +692,88 @@ class PyBuildExt(build_ext): dblibs = [] dblib_dir = None + # The sqlite interface + sqlite_setup_debug = True # verbose debug prints from this script? + + # We hunt for #define SQLITE_VERSION "n.n.n" + # We need to find >= sqlite version 3.0.8 + sqlite_incdir = sqlite_libdir = None + sqlite_inc_paths = [ '/usr/include', + '/usr/include/sqlite', + '/usr/include/sqlite3', + '/usr/local/include', + '/usr/local/include/sqlite', + '/usr/local/include/sqlite3', + ] + MIN_SQLITE_VERSION_NUMBER = (3, 0, 8) + MIN_SQLITE_VERSION = ".".join([str(x) + for x in MIN_SQLITE_VERSION_NUMBER]) + for d in sqlite_inc_paths + inc_dirs: + f = os.path.join(d, "sqlite3.h") + if os.path.exists(f): + if sqlite_setup_debug: print "sqlite: found %s"%f + incf = open(f).read() + m = re.search( + r'\s*.*#\s*.*define\s.*SQLITE_VERSION\W*"(.*)"', incf) + if m: + sqlite_version = m.group(1) + sqlite_version_tuple = tuple([int(x) + for x in sqlite_version.split(".")]) + if sqlite_version_tuple >= MIN_SQLITE_VERSION_NUMBER: + # we win! + print "%s/sqlite3.h: version %s"%(d, sqlite_version) + sqlite_incdir = d + break + else: + if sqlite_setup_debug: + print "%s: version %d is too old, need >= %s"%(d, + sqlite_version, MIN_SQLITE_VERSION) + elif sqlite_setup_debug: + print "sqlite: %s had no SQLITE_VERSION"%(f,) + + if sqlite_incdir: + sqlite_dirs_to_check = [ + os.path.join(sqlite_incdir, '..', 'lib64'), + os.path.join(sqlite_incdir, '..', 'lib'), + os.path.join(sqlite_incdir, '..', '..', 'lib64'), + os.path.join(sqlite_incdir, '..', '..', 'lib'), + ] + sqlite_libfile = self.compiler.find_library_file( + sqlite_dirs_to_check + lib_dirs, 'sqlite3') + sqlite_libdir = [os.path.abspath(os.path.dirname(sqlite_libfile))] + + if sqlite_incdir and sqlite_libdir: + sqlite_srcs = ['_sqlite/adapters.c', + '_sqlite/cache.c', + '_sqlite/connection.c', + '_sqlite/converters.c', + '_sqlite/cursor.c', + '_sqlite/microprotocols.c', + '_sqlite/module.c', + '_sqlite/prepare_protocol.c', + '_sqlite/row.c', + '_sqlite/statement.c', + '_sqlite/util.c', ] + + PYSQLITE_VERSION = "2.2.0" + sqlite_defines = [] + if sys.platform != "win32": + sqlite_defines.append(('MODULE_NAME', '"sqlite3"')) + else: + sqlite_defines.append(('MODULE_NAME', '\\"sqlite3\\"')) + + sqlite_defines.append(('PY_MAJOR_VERSION', + str(sys.version_info[0]))) + sqlite_defines.append(('PY_MINOR_VERSION', + str(sys.version_info[1]))) + + exts.append(Extension('_sqlite3', sqlite_srcs, + define_macros=sqlite_defines, + include_dirs=["Modules/_sqlite", + sqlite_incdir], + library_dirs=sqlite_libdir, + runtime_library_dirs=sqlite_libdir, + libraries=["sqlite3",])) # Look for Berkeley db 1.85. Note that it is built as a different # module name so it can be included even when later versions are @@ -865,7 +948,7 @@ class PyBuildExt(build_ext): # Fredrik Lundh's cElementTree module. Note that this also # uses expat (via the CAPI hook in pyexpat). - if os.path.isfile('Modules/_elementtree.c'): + if os.path.isfile(os.path.join(srcdir, 'Modules', '_elementtree.c')): define_macros.append(('USE_PYEXPAT_CAPI', None)) exts.append(Extension('_elementtree', define_macros = define_macros, @@ -885,11 +968,11 @@ class PyBuildExt(build_ext): if sys.maxint == 0x7fffffff: # This requires sizeof(int) == sizeof(long) == sizeof(char*) dl_inc = find_file('dlfcn.h', [], inc_dirs) - if (dl_inc is not None) and (platform not in ['atheos', 'darwin']): + if (dl_inc is not None) and (platform not in ['atheos']): exts.append( Extension('dl', ['dlmodule.c']) ) # Thomas Heller's _ctypes module - self.detect_ctypes() + self.detect_ctypes(inc_dirs, lib_dirs) # Platform-specific libraries if platform == 'linux2': @@ -905,82 +988,86 @@ class PyBuildExt(build_ext): exts.append( Extension('sunaudiodev', ['sunaudiodev.c']) ) if platform == 'darwin' and ("--disable-toolbox-glue" not in - sysconfig.get_config_var("CONFIG_ARGS")): + sysconfig.get_config_var("CONFIG_ARGS")): + + if os.uname()[2] > '8.': + # We're on Mac OS X 10.4 or later, the compiler should + # support '-Wno-deprecated-declarations'. This will + # surpress deprecation warnings for the Carbon extensions, + # these extensions wrap the Carbon APIs and even those + # parts that are deprecated. + carbon_extra_compile_args = ['-Wno-deprecated-declarations'] + else: + carbon_extra_compile_args = [] + # Mac OS X specific modules. - exts.append( Extension('_CF', ['cf/_CFmodule.c', 'cf/pycfbridge.c'], - extra_link_args=['-framework', 'CoreFoundation']) ) - - exts.append( Extension('ColorPicker', ['ColorPickermodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('autoGIL', ['autoGIL.c'], - extra_link_args=['-framework', 'CoreFoundation']) ) - exts.append( Extension('gestalt', ['gestaltmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('MacOS', ['macosmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('OSATerminology', ['OSATerminology.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('icglue', ['icgluemodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Res', ['res/_Resmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Snd', ['snd/_Sndmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('Nav', ['Nav.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_AE', ['ae/_AEmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_AH', ['ah/_AHmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_App', ['app/_Appmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_CarbonEvt', ['carbonevt/_CarbonEvtmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_CG', ['cg/_CGmodule.c'], - extra_link_args=['-framework', 'ApplicationServices']) ) - exts.append( Extension('_Cm', ['cm/_Cmmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Ctl', ['ctl/_Ctlmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Dlg', ['dlg/_Dlgmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Drag', ['drag/_Dragmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Evt', ['evt/_Evtmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_File', ['file/_Filemodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Folder', ['folder/_Foldermodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Fm', ['fm/_Fmmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Help', ['help/_Helpmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Icn', ['icn/_Icnmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_IBCarbon', ['ibcarbon/_IBCarbon.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Launch', ['launch/_Launchmodule.c'], - extra_link_args=['-framework', 'ApplicationServices']) ) - exts.append( Extension('_List', ['list/_Listmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Menu', ['menu/_Menumodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Mlte', ['mlte/_Mltemodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_OSA', ['osa/_OSAmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Qd', ['qd/_Qdmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_Qdoffs', ['qdoffs/_Qdoffsmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) + def macSrcExists(name1, name2=''): + if not name1: + return None + names = (name1,) + if name2: + names = (name1, name2) + path = os.path.join(srcdir, 'Mac', 'Modules', *names) + return os.path.exists(path) + + def addMacExtension(name, kwds, extra_srcs=[]): + dirname = '' + if name[0] == '_': + dirname = name[1:].lower() + cname = name + '.c' + cmodulename = name + 'module.c' + # Check for NNN.c, NNNmodule.c, _nnn/NNN.c, _nnn/NNNmodule.c + if macSrcExists(cname): + srcs = [cname] + elif macSrcExists(cmodulename): + srcs = [cmodulename] + elif macSrcExists(dirname, cname): + # XXX(nnorwitz): If all the names ended with module, we + # wouldn't need this condition. ibcarbon is the only one. + srcs = [os.path.join(dirname, cname)] + elif macSrcExists(dirname, cmodulename): + srcs = [os.path.join(dirname, cmodulename)] + else: + raise RuntimeError("%s not found" % name) + + # Here's the whole point: add the extension with sources + exts.append(Extension(name, srcs + extra_srcs, **kwds)) + + # Core Foundation + core_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework', 'CoreFoundation'], + } + addMacExtension('_CF', core_kwds, ['cf/pycfbridge.c']) + addMacExtension('autoGIL', core_kwds) + + # Carbon + carbon_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework', 'Carbon'], + } + CARBON_EXTS = ['ColorPicker', 'gestalt', 'MacOS', 'Nav', + 'OSATerminology', 'icglue', + # All these are in subdirs + '_AE', '_AH', '_App', '_CarbonEvt', '_Cm', '_Ctl', + '_Dlg', '_Drag', '_Evt', '_File', '_Folder', '_Fm', + '_Help', '_Icn', '_IBCarbon', '_List', + '_Menu', '_Mlte', '_OSA', '_Res', '_Qd', '_Qdoffs', + '_Scrap', '_Snd', '_TE', '_Win', + ] + for name in CARBON_EXTS: + addMacExtension(name, carbon_kwds) + + # Application Services & QuickTime + app_kwds = {'extra_compile_args': carbon_extra_compile_args, + 'extra_link_args': ['-framework','ApplicationServices'], + } + addMacExtension('_Launch', app_kwds) + addMacExtension('_CG', app_kwds) + exts.append( Extension('_Qt', ['qt/_Qtmodule.c'], - extra_link_args=['-framework', 'QuickTime', + extra_compile_args=carbon_extra_compile_args, + extra_link_args=['-framework', 'QuickTime', '-framework', 'Carbon']) ) - exts.append( Extension('_Scrap', ['scrap/_Scrapmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) - exts.append( Extension('_TE', ['te/_TEmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) + # As there is no standardized place (yet) to put # user-installed Mac libraries on OSX, we search for "waste" # in parent directories of the Python source tree. You @@ -992,7 +1079,6 @@ class PyBuildExt(build_ext): waste_libs = find_library_file(self.compiler, "WASTE", [], ["../"*n + "waste/Static Libraries" for n in (0,1,2,3,4)]) if waste_incs != None and waste_libs != None: - (srcdir,) = sysconfig.get_config_vars('srcdir') exts.append( Extension('waste', ['waste/wastemodule.c'] + [ os.path.join(srcdir, d) for d in @@ -1005,8 +1091,6 @@ class PyBuildExt(build_ext): libraries = ['WASTE'], extra_link_args = ['-framework', 'Carbon'], ) ) - exts.append( Extension('_Win', ['win/_Winmodule.c'], - extra_link_args=['-framework', 'Carbon']) ) self.extensions.extend(exts) @@ -1183,44 +1267,55 @@ class PyBuildExt(build_ext): # *** Uncomment these for TOGL extension only: # -lGL -lGLU -lXext -lXmu \ - def detect_ctypes(self): - (srcdir,) = sysconfig.get_config_vars('srcdir') - ffi_builddir = os.path.join(self.build_temp, 'libffi') - ffi_srcdir = os.path.abspath(os.path.join(srcdir, 'Modules', - '_ctypes', 'libffi')) - ffi_configfile = os.path.join(ffi_builddir, 'fficonfig.py') - - if self.force or not os.path.exists(ffi_configfile): - from distutils.dir_util import mkpath - mkpath(ffi_builddir) - config_args = [] - - # Pass empty CFLAGS because we'll just append the resulting CFLAGS - # to Python's; -g or -O2 is to be avoided. - cmd = "cd %s && env CFLAGS='' '%s/configure' %s" \ - % (ffi_builddir, ffi_srcdir, " ".join(config_args)) - - res = os.system(cmd) - if res or not os.path.exists(ffi_configfile): - print "Failed to configure _ctypes module" - return - - fficonfig = {} - execfile(ffi_configfile, globals(), fficonfig) - ffi_srcdir = os.path.join(fficonfig['ffi_srcdir'], 'src') - - # Add .S (preprocessed assembly) to C compiler source extensions. - self.compiler.src_extensions.append('.S') - - include_dirs = [os.path.join(ffi_builddir, 'include'), - ffi_builddir, ffi_srcdir] - extra_compile_args = fficonfig['ffi_cflags'].split() + def configure_ctypes(self, ext): + if not self.use_system_libffi: + (srcdir,) = sysconfig.get_config_vars('srcdir') + ffi_builddir = os.path.join(self.build_temp, 'libffi') + ffi_srcdir = os.path.abspath(os.path.join(srcdir, 'Modules', + '_ctypes', 'libffi')) + ffi_configfile = os.path.join(ffi_builddir, 'fficonfig.py') + + if self.force or not os.path.exists(ffi_configfile): + from distutils.dir_util import mkpath + mkpath(ffi_builddir) + config_args = [] + + # Pass empty CFLAGS because we'll just append the resulting + # CFLAGS to Python's; -g or -O2 is to be avoided. + cmd = "cd %s && env CFLAGS='' '%s/configure' %s" \ + % (ffi_builddir, ffi_srcdir, " ".join(config_args)) + + res = os.system(cmd) + if res or not os.path.exists(ffi_configfile): + print "Failed to configure _ctypes module" + return False + + fficonfig = {} + execfile(ffi_configfile, globals(), fficonfig) + ffi_srcdir = os.path.join(fficonfig['ffi_srcdir'], 'src') + + # Add .S (preprocessed assembly) to C compiler source extensions. + self.compiler.src_extensions.append('.S') + + include_dirs = [os.path.join(ffi_builddir, 'include'), + ffi_builddir, ffi_srcdir] + extra_compile_args = fficonfig['ffi_cflags'].split() + + ext.sources.extend(fficonfig['ffi_sources']) + ext.include_dirs.extend(include_dirs) + ext.extra_compile_args.extend(extra_compile_args) + return True + + def detect_ctypes(self, inc_dirs, lib_dirs): + self.use_system_libffi = False + include_dirs = [] + extra_compile_args = [] sources = ['_ctypes/_ctypes.c', '_ctypes/callbacks.c', '_ctypes/callproc.c', '_ctypes/stgdict.c', '_ctypes/cfield.c', - '_ctypes/malloc_closure.c'] + fficonfig['ffi_sources'] + '_ctypes/malloc_closure.c'] depends = ['_ctypes/ctypes.h'] if sys.platform == 'darwin': @@ -1232,12 +1327,40 @@ class PyBuildExt(build_ext): ext = Extension('_ctypes', include_dirs=include_dirs, extra_compile_args=extra_compile_args, + libraries=[], sources=sources, depends=depends) ext_test = Extension('_ctypes_test', sources=['_ctypes/_ctypes_test.c']) self.extensions.extend([ext, ext_test]) + if not '--with-system-ffi' in sysconfig.get_config_var("CONFIG_ARGS"): + return + + ffi_inc = find_file('ffi.h', [], inc_dirs) + if ffi_inc is not None: + ffi_h = ffi_inc[0] + '/ffi.h' + fp = open(ffi_h) + while 1: + line = fp.readline() + if not line: + ffi_inc = None + break + if line.startswith('#define LIBFFI_H'): + break + ffi_lib = None + if ffi_inc is not None: + for lib_name in ('ffi_convenience', 'ffi_pic', 'ffi'): + if (self.compiler.find_library_file(lib_dirs, lib_name)): + ffi_lib = lib_name + break + + if ffi_inc and ffi_lib: + ext.include_dirs.extend(ffi_inc) + ext.libraries.append(ffi_lib) + self.use_system_libffi = True + + class PyBuildInstall(install): # Suppress the warning about installation into the lib_dynload # directory, which is not in sys.path when running Python during -- cgit v0.12 From 3fc2ca3884bec42d9b68336183bb07a83311ea56 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 11:28:17 +0000 Subject: Merge trunk up to 45598. --- Objects/typeobject.c | 8 ++++---- Tools/msi/msi.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4caf538..38820d4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4183,10 +4183,10 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value) if (value == NULL) res = call_method(self, "__delitem__", &delitem_str, - "(i)", index); + "(n)", index); else res = call_method(self, "__setitem__", &setitem_str, - "(iO)", index, value); + "(nO)", index, value); if (res == NULL) return -1; Py_DECREF(res); @@ -4201,10 +4201,10 @@ slot_sq_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) if (value == NULL) res = call_method(self, "__delslice__", &delslice_str, - "(ii)", i, j); + "(nn)", i, j); else res = call_method(self, "__setslice__", &setslice_str, - "(iiO)", i, j, value); + "(nnO)", i, j, value); if (res == NULL) return -1; Py_DECREF(res); diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py index efa1696..f7d384a 100644 --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -908,6 +908,12 @@ def add_files(db): if files: # Add an entry to the RemoveFile table to remove bytecode files. lib.remove_pyc() + if dir.endswith('.egg-info'): + lib.add_file('entry_points.txt') + lib.add_file('PKG-INFO') + lib.add_file('top_level.txt') + lib.add_file('zip-safe') + continue if dir=='test' and parent.physical=='Lib': lib.add_file("185test.db") lib.add_file("audiotest.au") @@ -930,9 +936,12 @@ def add_files(db): if dir=="Icons": lib.glob("*.gif") lib.add_file("idle.icns") - if dir=="command": + if dir=="command" and parent.physical=="distutils": lib.add_file("wininst-6.exe") lib.add_file("wininst-7.1.exe") + if dir=="setuptools": + lib.add_file("cli.exe") + lib.add_file("gui.exe") if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email": # This should contain all non-.svn files listed in subversion for f in os.listdir(lib.absolute): -- cgit v0.12 From dcc6d32ee4b9e7517da2af4975dbd273b8cf556b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 11:30:52 +0000 Subject: Fix merge glitch. --- Lib/test/test_decimal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 341ad6d..40c4466 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -511,7 +511,7 @@ class DecimalImplicitConstructionTest(unittest.TestCase): ('+', '__add__', '__radd__'), ('-', '__sub__', '__rsub__'), ('*', '__mul__', '__rmul__'), - ('/', '__truediv__', '__rtruediv__') + ('/', '__truediv__', '__rtruediv__'), ('%', '__mod__', '__rmod__'), ('//', '__floordiv__', '__rfloordiv__'), ('**', '__pow__', '__rpow__') -- cgit v0.12 From d8073285ffd92eefee1546d2bbe7156eb957e5d7 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 11:36:13 +0000 Subject: Fix newly merged test for p3ykness. --- Lib/test/test_getargs2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 748ad44..8864e8e 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -186,7 +186,7 @@ class Signed_TestCase(unittest.TestCase): from _testcapi import getargs_n # n returns 'Py_ssize_t', and does range checking # (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX) - self.failUnlessEqual(3, getargs_n(3.14)) + self.assertRaises(TypeError, getargs_n, 3.14) self.failUnlessEqual(99, getargs_n(Long())) self.failUnlessEqual(99, getargs_n(Int())) -- cgit v0.12 From e4dea98e5aed37b71625cd76988433f5f8bee379 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 21 Apr 2006 13:45:00 +0000 Subject: Fix the unit tests by adding dispatch tables for DateTime and Binary. I'm not convinced this is the right thing to do... InstanceType makes no sense any longer. --- Lib/xmlrpclib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py index 6fb6c68..bac0a9f 100644 --- a/Lib/xmlrpclib.py +++ b/Lib/xmlrpclib.py @@ -749,6 +749,8 @@ class Marshaller: # store instance attributes as a struct (really?) self.dump_struct(value.__dict__, write) dispatch[InstanceType] = dump_instance + dispatch[DateTime] = dump_instance + dispatch[Binary] = dump_instance ## # XML-RPC unmarshaller. -- cgit v0.12 From 7ec155f5be6cdcbf55a607c2846cbd4ea80c3681 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 21 Apr 2006 13:52:56 +0000 Subject: Fix relative import. This fixes test_sax.py. --- Lib/xmlcore/sax/xmlreader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/xmlcore/sax/xmlreader.py b/Lib/xmlcore/sax/xmlreader.py index 6b37d37..1cade65 100644 --- a/Lib/xmlcore/sax/xmlreader.py +++ b/Lib/xmlcore/sax/xmlreader.py @@ -113,7 +113,7 @@ class IncrementalParser(XMLReader): XMLReader.__init__(self) def parse(self, source): - import saxutils + from . import saxutils source = saxutils.prepare_input_source(source) self.prepareParser(source) -- cgit v0.12 From c350d1a1106658d968badf2c35f5b10ddaa59a3e Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 14:02:59 +0000 Subject: Commit regenerated Python-ast.c. --- Python/Python-ast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 249f70e..8e16cfb 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -3048,7 +3048,7 @@ init_ast(void) if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return; if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0) return; - if (PyModule_AddStringConstant(m, "__version__", "42753") < 0) + if (PyModule_AddStringConstant(m, "__version__", "45597") < 0) return; if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) -- cgit v0.12 From 6da48561cb7844526164d3a656c6d16afe904e3d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 21 Apr 2006 15:44:20 +0000 Subject: Fix test_scriptpackage with pure will power. I really don't know why I bother; these are all generated files. But I don't recall how to regenerate them nor how to fix the generator. The hardest part was fixing two mutual recursive imports; somehow changing "import foo" into "from . import foo" where foo and bar import each other AND both are imported from __init__.py caused things to break. Bah. --- .../CodeWarrior/CodeWarrior_suite.py | 2 +- .../lib-scriptpackages/CodeWarrior/__init__.py | 16 +++++----- .../lib-scriptpackages/Explorer/__init__.py | 24 +++++++-------- .../Finder/Containers_and_folders.py | 4 +-- Lib/plat-mac/lib-scriptpackages/Finder/Files.py | 2 +- .../lib-scriptpackages/Finder/Finder_Basics.py | 8 ++--- Lib/plat-mac/lib-scriptpackages/Finder/__init__.py | 36 +++++++++++----------- .../lib-scriptpackages/Netscape/__init__.py | 28 ++++++++--------- .../lib-scriptpackages/StdSuites/__init__.py | 36 +++++++++++----------- .../SystemEvents/Disk_Folder_File_Suite.py | 13 +++++--- .../SystemEvents/Folder_Actions_Suite.py | 14 ++++++--- .../SystemEvents/Login_Items_Suite.py | 2 +- .../lib-scriptpackages/SystemEvents/Power_Suite.py | 10 +++--- .../SystemEvents/Processes_Suite.py | 8 ++--- .../SystemEvents/System_Events_Suite.py | 10 +++--- .../lib-scriptpackages/SystemEvents/Text_Suite.py | 2 +- .../lib-scriptpackages/SystemEvents/__init__.py | 32 +++++++++---------- .../lib-scriptpackages/Terminal/Terminal_Suite.py | 2 +- .../lib-scriptpackages/Terminal/Text_Suite.py | 2 +- .../lib-scriptpackages/Terminal/__init__.py | 12 ++++---- .../lib-scriptpackages/_builtinSuites/__init__.py | 4 +-- 21 files changed, 139 insertions(+), 128 deletions(-) diff --git a/Lib/plat-mac/lib-scriptpackages/CodeWarrior/CodeWarrior_suite.py b/Lib/plat-mac/lib-scriptpackages/CodeWarrior/CodeWarrior_suite.py index 0fd562b..17284d7 100644 --- a/Lib/plat-mac/lib-scriptpackages/CodeWarrior/CodeWarrior_suite.py +++ b/Lib/plat-mac/lib-scriptpackages/CodeWarrior/CodeWarrior_suite.py @@ -454,7 +454,7 @@ single_class_browser._privpropdict = { } single_class_browser._privelemdict = { } -import Standard_Suite +from . import Standard_Suite single_class_hierarchy._superclassnames = ['document'] single_class_hierarchy._privpropdict = { 'inherits' : _Prop_inherits, diff --git a/Lib/plat-mac/lib-scriptpackages/CodeWarrior/__init__.py b/Lib/plat-mac/lib-scriptpackages/CodeWarrior/__init__.py index 5ef7ca9..c57fd1f 100644 --- a/Lib/plat-mac/lib-scriptpackages/CodeWarrior/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/CodeWarrior/__init__.py @@ -3,10 +3,10 @@ Package generated from /Volumes/Sap/Applications (Mac OS 9)/Metrowerks CodeWarri """ import aetools Error = aetools.Error -import CodeWarrior_suite -import Standard_Suite -import Metrowerks_Shell_Suite -import Required +from . import CodeWarrior_suite +from . import Standard_Suite +from . import Metrowerks_Shell_Suite +from . import Required _code_to_module = { @@ -25,10 +25,10 @@ _code_to_fullname = { 'reqd' : ('CodeWarrior.Required', 'Required'), } -from CodeWarrior_suite import * -from Standard_Suite import * -from Metrowerks_Shell_Suite import * -from Required import * +from CodeWarrior.CodeWarrior_suite import * +from CodeWarrior.Standard_Suite import * +from CodeWarrior.Metrowerks_Shell_Suite import * +from CodeWarrior.Required import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/Explorer/__init__.py b/Lib/plat-mac/lib-scriptpackages/Explorer/__init__.py index a9b09b0..734c90f 100644 --- a/Lib/plat-mac/lib-scriptpackages/Explorer/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/Explorer/__init__.py @@ -3,12 +3,12 @@ Package generated from /Applications/Internet Explorer.app """ import aetools Error = aetools.Error -import Standard_Suite -import URL_Suite -import Netscape_Suite -import Microsoft_Internet_Explorer -import Web_Browser_Suite -import Required_Suite +from . import Standard_Suite +from . import URL_Suite +from . import Netscape_Suite +from . import Microsoft_Internet_Explorer +from . import Web_Browser_Suite +from . import Required_Suite _code_to_module = { @@ -31,12 +31,12 @@ _code_to_fullname = { 'reqd' : ('Explorer.Required_Suite', 'Required_Suite'), } -from Standard_Suite import * -from URL_Suite import * -from Netscape_Suite import * -from Microsoft_Internet_Explorer import * -from Web_Browser_Suite import * -from Required_Suite import * +from Explorer.Standard_Suite import * +from Explorer.URL_Suite import * +from Explorer.Netscape_Suite import * +from Explorer.Microsoft_Internet_Explorer import * +from Explorer.Web_Browser_Suite import * +from Explorer.Required_Suite import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/Finder/Containers_and_folders.py b/Lib/plat-mac/lib-scriptpackages/Finder/Containers_and_folders.py index e495ef4..474ac6f 100644 --- a/Lib/plat-mac/lib-scriptpackages/Finder/Containers_and_folders.py +++ b/Lib/plat-mac/lib-scriptpackages/Finder/Containers_and_folders.py @@ -148,8 +148,8 @@ class _Prop_warns_before_emptying(aetools.NProperty): # element 'inlf' as ['indx', 'name'] # element 'pack' as ['indx', 'name'] disk._superclassnames = ['container'] -import Files -import Finder_items +from . import Files +from . import Finder_items disk._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'capacity' : _Prop_capacity, diff --git a/Lib/plat-mac/lib-scriptpackages/Finder/Files.py b/Lib/plat-mac/lib-scriptpackages/Finder/Files.py index ddbe1ba..1b96540 100644 --- a/Lib/plat-mac/lib-scriptpackages/Finder/Files.py +++ b/Lib/plat-mac/lib-scriptpackages/Finder/Files.py @@ -148,7 +148,7 @@ document_file._privpropdict = { } document_file._privelemdict = { } -import Finder_items +from . import Finder_items file._superclassnames = ['item'] file._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, diff --git a/Lib/plat-mac/lib-scriptpackages/Finder/Finder_Basics.py b/Lib/plat-mac/lib-scriptpackages/Finder/Finder_Basics.py index c0a7b20..831e101 100644 --- a/Lib/plat-mac/lib-scriptpackages/Finder/Finder_Basics.py +++ b/Lib/plat-mac/lib-scriptpackages/Finder/Finder_Basics.py @@ -141,10 +141,10 @@ visible = _Prop_visible() # element 'lwnd' as ['indx', 'name'] # element 'pack' as ['indx', 'name'] application._superclassnames = [] -import Files -import Window_classes -import Containers_and_folders -import Finder_items +from . import Files +from . import Window_classes +from . import Containers_and_folders +from . import Finder_items application._privpropdict = { 'Finder_preferences' : _Prop_Finder_preferences, 'clipboard' : _Prop_clipboard, diff --git a/Lib/plat-mac/lib-scriptpackages/Finder/__init__.py b/Lib/plat-mac/lib-scriptpackages/Finder/__init__.py index 8782a4f..364d467 100644 --- a/Lib/plat-mac/lib-scriptpackages/Finder/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/Finder/__init__.py @@ -3,15 +3,15 @@ Package generated from /System/Library/CoreServices/Finder.app """ import aetools Error = aetools.Error -import Standard_Suite -import Legacy_suite -import Containers_and_folders -import Files -import Finder_Basics -import Finder_items -import Window_classes -import Type_Definitions -import Enumerations +from . import Standard_Suite +from . import Legacy_suite +from . import Containers_and_folders +from . import Files +from . import Finder_Basics +from . import Finder_items +from . import Window_classes +from . import Type_Definitions +from . import Enumerations _code_to_module = { @@ -40,15 +40,15 @@ _code_to_fullname = { 'tpnm' : ('Finder.Enumerations', 'Enumerations'), } -from Standard_Suite import * -from Legacy_suite import * -from Containers_and_folders import * -from Files import * -from Finder_Basics import * -from Finder_items import * -from Window_classes import * -from Type_Definitions import * -from Enumerations import * +from Finder.Standard_Suite import * +from Finder.Legacy_suite import * +from Finder.Containers_and_folders import * +from Finder.Files import * +from Finder.Finder_Basics import * +from Finder.Finder_items import * +from Finder.Window_classes import * +from Finder.Type_Definitions import * +from Finder.Enumerations import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/Netscape/__init__.py b/Lib/plat-mac/lib-scriptpackages/Netscape/__init__.py index 0dc5398..6fc495f 100644 --- a/Lib/plat-mac/lib-scriptpackages/Netscape/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/Netscape/__init__.py @@ -3,13 +3,13 @@ Package generated from /Volumes/Sap/Applications (Mac OS 9)/Netscape Communicato """ import aetools Error = aetools.Error -import Standard_Suite -import Standard_URL_suite -import Mozilla_suite -import Text -import WorldWideWeb_suite -import PowerPlant -import Required_suite +from . import Standard_Suite +from . import Standard_URL_suite +from . import Mozilla_suite +from . import Text +from . import WorldWideWeb_suite +from . import PowerPlant +from . import Required_suite _code_to_module = { @@ -34,13 +34,13 @@ _code_to_fullname = { 'reqd' : ('Netscape.Required_suite', 'Required_suite'), } -from Standard_Suite import * -from Standard_URL_suite import * -from Mozilla_suite import * -from Text import * -from WorldWideWeb_suite import * -from PowerPlant import * -from Required_suite import * +from Netscape.Standard_Suite import * +from Netscape.Standard_URL_suite import * +from Netscape.Mozilla_suite import * +from Netscape.Text import * +from Netscape.WorldWideWeb_suite import * +from Netscape.PowerPlant import * +from Netscape.Required_suite import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/StdSuites/__init__.py b/Lib/plat-mac/lib-scriptpackages/StdSuites/__init__.py index b80e3ba..b07f160 100644 --- a/Lib/plat-mac/lib-scriptpackages/StdSuites/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/StdSuites/__init__.py @@ -4,15 +4,15 @@ Resource aeut resid 0 Standard Event Suites for English """ import aetools Error = aetools.Error -import Text_Suite -import AppleScript_Suite -import Standard_Suite -import Macintosh_Connectivity_Clas -import QuickDraw_Graphics_Suite -import QuickDraw_Graphics_Suppleme -import Required_Suite -import Table_Suite -import Type_Names_Suite +from . import Text_Suite +from . import AppleScript_Suite +from . import Standard_Suite +from . import Macintosh_Connectivity_Clas +from . import QuickDraw_Graphics_Suite +from . import QuickDraw_Graphics_Suppleme +from . import Required_Suite +from . import Table_Suite +from . import Type_Names_Suite _code_to_module = { @@ -41,15 +41,15 @@ _code_to_fullname = { 'tpnm' : ('StdSuites.Type_Names_Suite', 'Type_Names_Suite'), } -from Text_Suite import * -from AppleScript_Suite import * -from Standard_Suite import * -from Macintosh_Connectivity_Clas import * -from QuickDraw_Graphics_Suite import * -from QuickDraw_Graphics_Suppleme import * -from Required_Suite import * -from Table_Suite import * -from Type_Names_Suite import * +from StdSuites.Text_Suite import * +from StdSuites.AppleScript_Suite import * +from StdSuites.Standard_Suite import * +from StdSuites.Macintosh_Connectivity_Clas import * +from StdSuites.QuickDraw_Graphics_Suite import * +from StdSuites.QuickDraw_Graphics_Suppleme import * +from StdSuites.Required_Suite import * +from StdSuites.Table_Suite import * +from StdSuites.Type_Names_Suite import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Disk_Folder_File_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Disk_Folder_File_Suite.py index a9ac3dd..a0b199f 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Disk_Folder_File_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Disk_Folder_File_Suite.py @@ -5,6 +5,11 @@ Generated from /System/Library/CoreServices/System Events.app AETE/AEUT resource version 1/0, language 0, script 0 """ +# Crutch for recursive import +import sys +import SystemEvents +SystemEvents.Disk_Folder_File_Suite = sys.modules[__name__] + import aetools import MacOS @@ -210,10 +215,10 @@ class _Prop_version(aetools.NProperty): files = file application._superclassnames = [] -import Standard_Suite -import Folder_Actions_Suite -import Login_Items_Suite -import Processes_Suite +from . import Standard_Suite +from . import Folder_Actions_Suite +from . import Login_Items_Suite +from . import Processes_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'folder_actions_enabled' : _Prop_folder_actions_enabled, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Folder_Actions_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Folder_Actions_Suite.py index c2c7e5e..cb07a14 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Folder_Actions_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Folder_Actions_Suite.py @@ -5,6 +5,11 @@ Generated from /System/Library/CoreServices/System Events.app AETE/AEUT resource version 1/0, language 0, script 0 """ +# Crutch for recursive import +import sys +import SystemEvents +SystemEvents.Folder_Actions_Suite = sys.modules[__name__] + import aetools import MacOS @@ -208,10 +213,11 @@ class _Prop_POSIX_path(aetools.NProperty): scripts = script application._superclassnames = [] -import Disk_Folder_File_Suite -import Standard_Suite -import Login_Items_Suite -import Processes_Suite +import sys +from . import Disk_Folder_File_Suite +from . import Standard_Suite +from . import Login_Items_Suite +from . import Processes_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'folder_actions_enabled' : _Prop_folder_actions_enabled, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Login_Items_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Login_Items_Suite.py index ed09245..0409558 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Login_Items_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Login_Items_Suite.py @@ -40,7 +40,7 @@ class _Prop_path(aetools.NProperty): want = 'utxt' login_items = login_item -import Standard_Suite +from . import Standard_Suite login_item._superclassnames = ['item'] login_item._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Power_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Power_Suite.py index ef539b1..bfdf536 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Power_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Power_Suite.py @@ -105,11 +105,11 @@ properties = _Prop_properties() applications = application application._superclassnames = [] -import Disk_Folder_File_Suite -import Standard_Suite -import Folder_Actions_Suite -import Login_Items_Suite -import Processes_Suite +from . import Disk_Folder_File_Suite +from . import Standard_Suite +from . import Folder_Actions_Suite +from . import Login_Items_Suite +from . import Processes_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'folder_actions_enabled' : _Prop_folder_actions_enabled, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Processes_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Processes_Suite.py index 9dcb85f..a55c0a1 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Processes_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Processes_Suite.py @@ -121,10 +121,10 @@ class _Prop_visible(aetools.NProperty): processes = process application._superclassnames = [] -import Disk_Folder_File_Suite -import Standard_Suite -import Folder_Actions_Suite -import Login_Items_Suite +from . import Disk_Folder_File_Suite +from . import Standard_Suite +from . import Folder_Actions_Suite +from . import Login_Items_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'folder_actions_enabled' : _Prop_folder_actions_enabled, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/System_Events_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/System_Events_Suite.py index d5b4bfc..cf3e213 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/System_Events_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/System_Events_Suite.py @@ -65,11 +65,11 @@ properties = _Prop_properties() applications = application application._superclassnames = [] -import Disk_Folder_File_Suite -import Standard_Suite -import Folder_Actions_Suite -import Login_Items_Suite -import Processes_Suite +from . import Disk_Folder_File_Suite +from . import Standard_Suite +from . import Folder_Actions_Suite +from . import Login_Items_Suite +from . import Processes_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'folder_actions_enabled' : _Prop_folder_actions_enabled, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Text_Suite.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Text_Suite.py index 9f109e1..ec66c2c 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/Text_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/Text_Suite.py @@ -101,7 +101,7 @@ attachment._privelemdict = { 'paragraph' : paragraph, 'word' : word, } -import Standard_Suite +from . import Standard_Suite attribute_run._superclassnames = ['item'] attribute_run._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, diff --git a/Lib/plat-mac/lib-scriptpackages/SystemEvents/__init__.py b/Lib/plat-mac/lib-scriptpackages/SystemEvents/__init__.py index 95f985c..ce3b0e2 100644 --- a/Lib/plat-mac/lib-scriptpackages/SystemEvents/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/SystemEvents/__init__.py @@ -3,14 +3,14 @@ Package generated from /System/Library/CoreServices/System Events.app """ import aetools Error = aetools.Error -import Standard_Suite -import Text_Suite -import Disk_Folder_File_Suite -import Folder_Actions_Suite -import Login_Items_Suite -import Power_Suite -import Processes_Suite -import System_Events_Suite +from . import Standard_Suite +from . import Text_Suite +from . import Disk_Folder_File_Suite +from . import Folder_Actions_Suite +from . import Login_Items_Suite +from . import Power_Suite +from . import Processes_Suite +from . import System_Events_Suite _code_to_module = { @@ -37,14 +37,14 @@ _code_to_fullname = { 'sevs' : ('SystemEvents.System_Events_Suite', 'System_Events_Suite'), } -from Standard_Suite import * -from Text_Suite import * -from Disk_Folder_File_Suite import * -from Folder_Actions_Suite import * -from Login_Items_Suite import * -from Power_Suite import * -from Processes_Suite import * -from System_Events_Suite import * +from SystemEvents.Standard_Suite import * +from SystemEvents.Text_Suite import * +from SystemEvents.Disk_Folder_File_Suite import * +from SystemEvents.Folder_Actions_Suite import * +from SystemEvents.Login_Items_Suite import * +from SystemEvents.Power_Suite import * +from SystemEvents.Processes_Suite import * +from SystemEvents.System_Events_Suite import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/Terminal/Terminal_Suite.py b/Lib/plat-mac/lib-scriptpackages/Terminal/Terminal_Suite.py index afa02b3..caa53d4 100644 --- a/Lib/plat-mac/lib-scriptpackages/Terminal/Terminal_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/Terminal/Terminal_Suite.py @@ -173,7 +173,7 @@ class _Prop_title_displays_window_size(aetools.NProperty): windows = window application._superclassnames = [] -import Standard_Suite +from . import Standard_Suite application._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, 'properties' : _Prop_properties, diff --git a/Lib/plat-mac/lib-scriptpackages/Terminal/Text_Suite.py b/Lib/plat-mac/lib-scriptpackages/Terminal/Text_Suite.py index 62f4744..1303208 100644 --- a/Lib/plat-mac/lib-scriptpackages/Terminal/Text_Suite.py +++ b/Lib/plat-mac/lib-scriptpackages/Terminal/Text_Suite.py @@ -101,7 +101,7 @@ attachment._privelemdict = { 'paragraph' : paragraph, 'word' : word, } -import Standard_Suite +from . import Standard_Suite attribute_run._superclassnames = ['item'] attribute_run._privpropdict = { '_3c_Inheritance_3e_' : _Prop__3c_Inheritance_3e_, diff --git a/Lib/plat-mac/lib-scriptpackages/Terminal/__init__.py b/Lib/plat-mac/lib-scriptpackages/Terminal/__init__.py index 47b75f2..d400409 100644 --- a/Lib/plat-mac/lib-scriptpackages/Terminal/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/Terminal/__init__.py @@ -3,9 +3,9 @@ Package generated from /Applications/Utilities/Terminal.app """ import aetools Error = aetools.Error -import Standard_Suite -import Text_Suite -import Terminal_Suite +from . import Standard_Suite +from . import Text_Suite +from . import Terminal_Suite _code_to_module = { @@ -22,9 +22,9 @@ _code_to_fullname = { 'trmx' : ('Terminal.Terminal_Suite', 'Terminal_Suite'), } -from Standard_Suite import * -from Text_Suite import * -from Terminal_Suite import * +from Terminal.Standard_Suite import * +from Terminal.Text_Suite import * +from Terminal.Terminal_Suite import * def getbaseclasses(v): if not getattr(v, '_propdict', None): diff --git a/Lib/plat-mac/lib-scriptpackages/_builtinSuites/__init__.py b/Lib/plat-mac/lib-scriptpackages/_builtinSuites/__init__.py index 2c722d9..b327d58 100644 --- a/Lib/plat-mac/lib-scriptpackages/_builtinSuites/__init__.py +++ b/Lib/plat-mac/lib-scriptpackages/_builtinSuites/__init__.py @@ -5,7 +5,7 @@ in the Required suite according to the Apple docs, but they often seem to be in the Standard suite. """ import aetools -import builtin_Suite +from . import builtin_Suite _code_to_module = { @@ -20,7 +20,7 @@ _code_to_fullname = { 'core' : ('_builtinSuites.builtin_Suite', 'builtin_Suite'), } -from builtin_Suite import * +from _builtinSuites.builtin_Suite import * class _builtinSuites(builtin_Suite_Events, aetools.TalkTo): -- cgit v0.12 From 13247bfc8bd35cedcb44a3a8ec9d89e7c1a9f7ef Mon Sep 17 00:00:00 2001 From: Hye-Shik Chang Date: Fri, 21 Apr 2006 16:21:44 +0000 Subject: Add empty __init__ methods for stateful multibytecodec instances. This resolves a problem found by Thomas Wouters: http://mail.python.org/pipermail/python-dev/2006-April/064051.html --- Modules/cjkcodecs/multibytecodec.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 340de18..aa0096a 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -927,6 +927,12 @@ errorexit: } static int +mbiencoder_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static int mbiencoder_traverse(MultibyteIncrementalEncoderObject *self, visitproc visit, void *arg) { @@ -982,7 +988,7 @@ static PyTypeObject MultibyteIncrementalEncoder_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + mbiencoder_init, /* tp_init */ 0, /* tp_alloc */ mbiencoder_new, /* tp_new */ }; @@ -1122,6 +1128,12 @@ errorexit: } static int +mbidecoder_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static int mbidecoder_traverse(MultibyteIncrementalDecoderObject *self, visitproc visit, void *arg) { @@ -1177,7 +1189,7 @@ static PyTypeObject MultibyteIncrementalDecoder_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + mbidecoder_init, /* tp_init */ 0, /* tp_alloc */ mbidecoder_new, /* tp_new */ }; @@ -1427,6 +1439,12 @@ errorexit: } static int +mbstreamreader_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static int mbstreamreader_traverse(MultibyteStreamReaderObject *self, visitproc visit, void *arg) { @@ -1484,7 +1502,7 @@ static PyTypeObject MultibyteStreamReader_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + mbstreamreader_init, /* tp_init */ 0, /* tp_alloc */ mbstreamreader_new, /* tp_new */ }; @@ -1632,6 +1650,12 @@ errorexit: } static int +mbstreamwriter_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static int mbstreamwriter_traverse(MultibyteStreamWriterObject *self, visitproc visit, void *arg) { @@ -1706,7 +1730,7 @@ static PyTypeObject MultibyteStreamWriter_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + mbstreamwriter_init, /* tp_init */ 0, /* tp_alloc */ mbstreamwriter_new, /* tp_new */ }; -- cgit v0.12 From d4ec0c3e2cbf76fe59c2f2a172fdcac09b3018ff Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 16:44:05 +0000 Subject: Merge with trunk up to revision 45620. --- Doc/howto/unicode.rst | 1 + Doc/lib/libcodecs.tex | 64 ++++++++++++------------- Doc/whatsnew/whatsnew25.tex | 114 +++++++++++++++++++++++++++----------------- Lib/SimpleXMLRPCServer.py | 1 + Objects/stringobject.c | 2 +- PCbuild/_bsddb.vcproj | 4 +- PCbuild/_ctypes.vcproj | 4 +- PCbuild/_ctypes_test.vcproj | 4 +- PCbuild/_elementtree.vcproj | 4 +- PCbuild/_msi.vcproj | 4 +- PCbuild/_socket.vcproj | 4 +- PCbuild/_sqlite3.vcproj | 4 +- PCbuild/_testcapi.vcproj | 4 +- PCbuild/_tkinter.vcproj | 4 +- PCbuild/bz2.vcproj | 4 +- PCbuild/pyexpat.vcproj | 4 +- PCbuild/python.vcproj | 4 +- PCbuild/pythoncore.vcproj | 4 +- PCbuild/pythonw.vcproj | 4 +- PCbuild/readme.txt | 2 +- PCbuild/select.vcproj | 8 ++-- PCbuild/unicodedata.vcproj | 4 +- PCbuild/winsound.vcproj | 4 +- 23 files changed, 141 insertions(+), 115 deletions(-) diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index dac5c74..0946bdc 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -733,6 +733,7 @@ Version 1.02: posted August 16 2005. Corrects factual errors. .. comment Additional topic: building Python w/ UCS2 or UCS4 support .. comment Describe obscure -U switch somewhere? +.. comment Describe use of codecs.StreamRecoder and StreamReaderWriter .. comment Original outline: diff --git a/Doc/lib/libcodecs.tex b/Doc/lib/libcodecs.tex index 8a2417e..6e0bc8d 100644 --- a/Doc/lib/libcodecs.tex +++ b/Doc/lib/libcodecs.tex @@ -93,21 +93,21 @@ additional functions which use \function{lookup()} for the codec lookup: \begin{funcdesc}{getencoder}{encoding} -Lookup up the codec for the given encoding and return its encoder +Look up the codec for the given encoding and return its encoder function. Raises a \exception{LookupError} in case the encoding cannot be found. \end{funcdesc} \begin{funcdesc}{getdecoder}{encoding} -Lookup up the codec for the given encoding and return its decoder +Look up the codec for the given encoding and return its decoder function. Raises a \exception{LookupError} in case the encoding cannot be found. \end{funcdesc} \begin{funcdesc}{getincrementalencoder}{encoding} -Lookup up the codec for the given encoding and return its incremental encoder +Look up the codec for the given encoding and return its incremental encoder class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found or the @@ -116,7 +116,7 @@ codec doesn't support an incremental encoder. \end{funcdesc} \begin{funcdesc}{getincrementaldecoder}{encoding} -Lookup up the codec for the given encoding and return its incremental decoder +Look up the codec for the given encoding and return its incremental decoder class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found or the @@ -125,14 +125,14 @@ codec doesn't support an incremental decoder. \end{funcdesc} \begin{funcdesc}{getreader}{encoding} -Lookup up the codec for the given encoding and return its StreamReader +Look up the codec for the given encoding and return its StreamReader class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found. \end{funcdesc} \begin{funcdesc}{getwriter}{encoding} -Lookup up the codec for the given encoding and return its StreamWriter +Look up the codec for the given encoding and return its StreamWriter class or factory function. Raises a \exception{LookupError} in case the encoding cannot be found. @@ -353,7 +353,7 @@ incremental encoder/decoder. The incremental encoder/decoder keeps track of the encoding/decoding process during method calls. The joined output of calls to the \method{encode}/\method{decode} method is the -same as if the all single inputs where joined into one, and this input was +same as if all the single inputs were joined into one, and this input was encoded/decoded with the stateless encoder/decoder. @@ -363,7 +363,7 @@ encoded/decoded with the stateless encoder/decoder. The \class{IncrementalEncoder} class is used for encoding an input in multiple steps. It defines the following methods which every incremental encoder must -define in order to be compatible to the Python codec registry. +define in order to be compatible with the Python codec registry. \begin{classdesc}{IncrementalEncoder}{\optional{errors}} Constructor for a \class{IncrementalEncoder} instance. @@ -410,7 +410,7 @@ define in order to be compatible to the Python codec registry. The \class{IncrementalDecoder} class is used for decoding an input in multiple steps. It defines the following methods which every incremental decoder must -define in order to be compatible to the Python codec registry. +define in order to be compatible with the Python codec registry. \begin{classdesc}{IncrementalDecoder}{\optional{errors}} Constructor for a \class{IncrementalDecoder} instance. @@ -456,15 +456,15 @@ define in order to be compatible to the Python codec registry. The \class{StreamWriter} and \class{StreamReader} classes provide generic working interfaces which can be used to implement new -encodings submodules very easily. See \module{encodings.utf_8} for an -example on how this is done. +encoding submodules very easily. See \module{encodings.utf_8} for an +example of how this is done. \subsubsection{StreamWriter Objects \label{stream-writer-objects}} The \class{StreamWriter} class is a subclass of \class{Codec} and defines the following methods which every stream writer must define in -order to be compatible to the Python codec registry. +order to be compatible with the Python codec registry. \begin{classdesc}{StreamWriter}{stream\optional{, errors}} Constructor for a \class{StreamWriter} instance. @@ -473,7 +473,7 @@ order to be compatible to the Python codec registry. free to add additional keyword arguments, but only the ones defined here are used by the Python codec registry. - \var{stream} must be a file-like object open for writing (binary) + \var{stream} must be a file-like object open for writing binary data. The \class{StreamWriter} may implement different error handling @@ -512,19 +512,19 @@ order to be compatible to the Python codec registry. Flushes and resets the codec buffers used for keeping state. Calling this method should ensure that the data on the output is put - into a clean state, that allows appending of new fresh data without + into a clean state that allows appending of new fresh data without having to rescan the whole stream to recover state. \end{methoddesc} In addition to the above methods, the \class{StreamWriter} must also -inherit all other methods and attribute from the underlying stream. +inherit all other methods and attributes from the underlying stream. \subsubsection{StreamReader Objects \label{stream-reader-objects}} The \class{StreamReader} class is a subclass of \class{Codec} and defines the following methods which every stream reader must define in -order to be compatible to the Python codec registry. +order to be compatible with the Python codec registry. \begin{classdesc}{StreamReader}{stream\optional{, errors}} Constructor for a \class{StreamReader} instance. @@ -589,20 +589,20 @@ order to be compatible to the Python codec registry. \var{size}, if given, is passed as size argument to the stream's \method{readline()} method. - If \var{keepends} is false lineends will be stripped from the + If \var{keepends} is false line-endings will be stripped from the lines returned. \versionchanged[\var{keepends} argument added]{2.4} \end{methoddesc} \begin{methoddesc}{readlines}{\optional{sizehint\optional{, keepends}}} - Read all lines available on the input stream and return them as list + Read all lines available on the input stream and return them as a list of lines. - Line breaks are implemented using the codec's decoder method and are + Line-endings are implemented using the codec's decoder method and are included in the list entries if \var{keepends} is true. - \var{sizehint}, if given, is passed as \var{size} argument to the + \var{sizehint}, if given, is passed as the \var{size} argument to the stream's \method{read()} method. \end{methoddesc} @@ -614,7 +614,7 @@ order to be compatible to the Python codec registry. \end{methoddesc} In addition to the above methods, the \class{StreamReader} must also -inherit all other methods and attribute from the underlying stream. +inherit all other methods and attributes from the underlying stream. The next two base classes are included for convenience. They are not needed by the codec registry, but may provide useful in practice. @@ -640,7 +640,7 @@ the \function{lookup()} function to construct the instance. \class{StreamReaderWriter} instances define the combined interfaces of \class{StreamReader} and \class{StreamWriter} classes. They inherit -all other methods and attribute from the underlying stream. +all other methods and attributes from the underlying stream. \subsubsection{StreamRecoder Objects \label{stream-recoder-objects}} @@ -666,14 +666,14 @@ the \function{lookup()} function to construct the instance. \var{stream} must be a file-like object. \var{encode}, \var{decode} must adhere to the \class{Codec} - interface, \var{Reader}, \var{Writer} must be factory functions or + interface. \var{Reader}, \var{Writer} must be factory functions or classes providing objects of the \class{StreamReader} and \class{StreamWriter} interface respectively. \var{encode} and \var{decode} are needed for the frontend translation, \var{Reader} and \var{Writer} for the backend translation. The intermediate format used is determined by the two - sets of codecs, e.g. the Unicode codecs will use Unicode as + sets of codecs, e.g. the Unicode codecs will use Unicode as the intermediate encoding. Error handling is done in the same way as defined for the @@ -682,7 +682,7 @@ the \function{lookup()} function to construct the instance. \class{StreamRecoder} instances define the combined interfaces of \class{StreamReader} and \class{StreamWriter} classes. They inherit -all other methods and attribute from the underlying stream. +all other methods and attributes from the underlying stream. \subsection{Encodings and Unicode\label{encodings-overview}} @@ -695,7 +695,7 @@ compiled (either via \longprogramopt{enable-unicode=ucs2} or memory, CPU endianness and how these arrays are stored as bytes become an issue. Transforming a unicode object into a sequence of bytes is called encoding and recreating the unicode object from the sequence of -bytes is known as decoding. There are many different methods how this +bytes is known as decoding. There are many different methods for how this transformation can be done (these methods are also called encodings). The simplest method is to map the codepoints 0-255 to the bytes \code{0x0}-\code{0xff}. This means that a unicode object that contains @@ -742,7 +742,7 @@ been decoded into a Unicode string; as a \samp{ZERO WIDTH NO-BREAK SPACE} it's a normal character that will be decoded like any other. There's another encoding that is able to encoding the full range of -Unicode characters: UTF-8. UTF-8 is an 8bit encoding, which means +Unicode characters: UTF-8. UTF-8 is an 8-bit encoding, which means there are no issues with byte order in UTF-8. Each byte in a UTF-8 byte sequence consists of two parts: Marker bits (the most significant bits) and payload bits. The marker bits are a sequence of zero to six @@ -762,7 +762,7 @@ character): The least significant bit of the Unicode character is the rightmost x bit. -As UTF-8 is an 8bit encoding no BOM is required and any \code{U+FEFF} +As UTF-8 is an 8-bit encoding no BOM is required and any \code{U+FEFF} character in the decoded Unicode string (even if it's the first character) is treated as a \samp{ZERO WIDTH NO-BREAK SPACE}. @@ -775,7 +775,7 @@ with which a UTF-8 encoding can be detected, Microsoft invented a variant of UTF-8 (that Python 2.5 calls \code{"utf-8-sig"}) for its Notepad program: Before any of the Unicode characters is written to the file, a UTF-8 encoded BOM (which looks like this as a byte sequence: \code{0xef}, -\code{0xbb}, \code{0xbf}) is written. As it's rather improbably that any +\code{0xbb}, \code{0xbf}) is written. As it's rather improbable that any charmap encoded file starts with these byte values (which would e.g. map to LATIN SMALL LETTER I WITH DIAERESIS \\ @@ -794,8 +794,8 @@ first three bytes in the file. \subsection{Standard Encodings\label{standard-encodings}} -Python comes with a number of codecs builtin, either implemented as C -functions, or with dictionaries as mapping tables. The following table +Python comes with a number of codecs built-in, either implemented as C +functions or with dictionaries as mapping tables. The following table lists the codecs by name, together with a few common aliases, and the languages for which the encoding is likely used. Neither the list of aliases nor the list of languages is meant to be exhaustive. Notice @@ -1337,7 +1337,7 @@ Convert a label to Unicode, as specified in \rfc{3490}. UTF-8 codec with BOM signature} \declaremodule{standard}{encodings.utf-8-sig} % XXX utf_8_sig gives TeX errors \modulesynopsis{UTF-8 codec with BOM signature} -\moduleauthor{Walter D\"orwald} +\moduleauthor{Walter D\"orwald}{} \versionadded{2.5} diff --git a/Doc/whatsnew/whatsnew25.tex b/Doc/whatsnew/whatsnew25.tex index db6c25a..750162f 100644 --- a/Doc/whatsnew/whatsnew25.tex +++ b/Doc/whatsnew/whatsnew25.tex @@ -4,7 +4,6 @@ % The easy_install stuff % Describe the pkgutil module -% Stateful codec changes % Fix XXX comments % Count up the patches and bugs @@ -35,7 +34,7 @@ rationale, refer to the PEP for a particular new feature. %====================================================================== -\section{PEP 243: Uploading Modules to PyPI} +\section{PEP 243: Uploading Modules to PyPI\label{pep-243}} PEP 243 describes an HTTP-based protocol for submitting software packages to a central archive. The Python package index at @@ -61,7 +60,7 @@ describe what's implemented in PyPI.} %====================================================================== -\section{PEP 308: Conditional Expressions} +\section{PEP 308: Conditional Expressions\label{pep-308}} For a long time, people have been requesting a way to write conditional expressions, expressions that return value A or value B @@ -152,7 +151,7 @@ Wouters.} %====================================================================== -\section{PEP 309: Partial Function Application} +\section{PEP 309: Partial Function Application\label{pep-309}} The \module{functional} module is intended to contain tools for functional-style programming. Currently it only contains a @@ -214,7 +213,7 @@ Raymond Hettinger.} %====================================================================== -\section{PEP 314: Metadata for Python Software Packages v1.1} +\section{PEP 314: Metadata for Python Software Packages v1.1\label{pep-314}} Some simple dependency support was added to Distutils. The \function{setup()} function now has \code{requires}, \code{provides}, @@ -248,7 +247,7 @@ implemented by Richard Jones and Fred Drake.} %====================================================================== -\section{PEP 328: Absolute and Relative Imports} +\section{PEP 328: Absolute and Relative Imports\label{pep-328}} The simpler part of PEP 328 was implemented in Python 2.4: parentheses could now be used to enclose the names imported from a module using @@ -342,7 +341,7 @@ form of the import statement, only the \code{from ... import} form. %====================================================================== -\section{PEP 338: Executing Modules as Scripts} +\section{PEP 338: Executing Modules as Scripts\label{pep-338}} The \programopt{-m} switch added in Python 2.4 to execute a module as a script gained a few more abilities. Instead of being implemented in @@ -366,7 +365,7 @@ implemented by Nick Coghlan.} %====================================================================== -\section{PEP 341: Unified try/except/finally} +\section{PEP 341: Unified try/except/finally\label{pep-341}} Until Python 2.5, the \keyword{try} statement came in two flavours. You could use a \keyword{finally} block to ensure that code @@ -412,7 +411,7 @@ implementation by Thomas Lee.} %====================================================================== -\section{PEP 342: New Generator Features\label{section-generators}} +\section{PEP 342: New Generator Features\label{pep-342}} Python 2.5 adds a simple way to pass values \emph{into} a generator. As introduced in Python 2.3, generators only produce output; once a @@ -578,9 +577,9 @@ Sugalski.} %====================================================================== -\section{PEP 343: The 'with' statement} +\section{PEP 343: The 'with' statement\label{pep-343}} -The \keyword{with} statement allows a clearer version of code that +The '\keyword{with}' statement allows a clearer version of code that uses \code{try...finally} blocks to ensure that clean-up code is executed. @@ -589,7 +588,7 @@ used. In the next section, I'll examine the implementation details and show how to write objects called ``context managers'' and ``contexts'' for use with this statement. -The \keyword{with} statement is a new control-flow structure whose +The '\keyword{with}' statement is a new control-flow structure whose basic structure is: \begin{verbatim} @@ -625,11 +624,11 @@ with open('/etc/passwd', 'r') as f: \end{verbatim} After this statement has executed, the file object in \var{f} will -have been automatically closed at this point, even if the 'for' loop +have been automatically closed, even if the 'for' loop raised an exception part-way through the block. The \module{threading} module's locks and condition variables -also support the \keyword{with} statement: +also support the '\keyword{with}' statement: \begin{verbatim} lock = threading.Lock() @@ -658,10 +657,10 @@ with decimal.Context(prec=16): print v1.sqrt() \end{verbatim} -\subsection{Writing Context Managers} +\subsection{Writing Context Managers\label{context-managers}} -Under the hood, the \keyword{with} statement is fairly complicated. -Most people will only use \keyword{with} in company with +Under the hood, the '\keyword{with}' statement is fairly complicated. +Most people will only use '\keyword{with}' in company with existing objects that are documented to work as context managers, and don't need to know these details, so you can skip the following section if you like. Authors of new context managers will need to understand the @@ -678,7 +677,7 @@ that's a context manager, meaning that it has a return a context object. \item The context's \method{__enter__()} method is called. -The value returned is assigned to \var{VAR}. If no \code{as \var{VAR}} +The value returned is assigned to \var{VAR}. If no \code{'as \var{VAR}'} clause is present, the value is simply discarded. \item The code in \var{BLOCK} is executed. @@ -690,7 +689,7 @@ with the exception's information, the same values returned by controls whether the exception is re-raised: any false value re-raises the exception, and \code{True} will result in suppressing it. You'll only rarely want to suppress the exception; the -author of the code containing the \keyword{with} statement will +author of the code containing the '\keyword{with}' statement will never realize anything went wrong. \item If \var{BLOCK} didn't raise an exception, @@ -761,7 +760,7 @@ The \method {__enter__()} method is pretty easy, having only to start a new transaction. In this example, the resulting cursor object would be a useful result, so the method will return it. The user can -then add \code{as cursor} to their \keyword{with} statement +then add \code{as cursor} to their '\keyword{with}' statement to bind the cursor to a variable name. \begin{verbatim} @@ -806,7 +805,7 @@ a simple context manager as a generator. The generator should yield exactly one value. The code up to the \keyword{yield} will be executed as the \method{__enter__()} method, and the value yielded will be the method's return value that will get bound to the variable -in the \keyword{with} statement's \keyword{as} clause, if any. The +in the '\keyword{with}' statement's \keyword{as} clause, if any. The code after the \keyword{yield} will be executed in the \method{__exit__()} method. Any exception raised in the block will be raised by the \keyword{yield} statement. @@ -854,7 +853,7 @@ class DatabaseConnection: There's a \function{nested(\var{mgr1}, \var{mgr2}, ...)} manager that combines a number of context managers so you don't need to write -nested \keyword{with} statements. This example statement does two +nested '\keyword{with}' statements. This example statement does two things, starting a database transaction and acquiring a thread lock: \begin{verbatim} @@ -880,7 +879,7 @@ with closing(urllib.urlopen('http://www.yahoo.com')) as f: \seepep{343}{The ``with'' statement}{PEP written by Guido van~Rossum and Nick Coghlan; implemented by Mike Bland, Guido van~Rossum, and -Neal Norwitz. The PEP shows the code generated for a \keyword{with} +Neal Norwitz. The PEP shows the code generated for a '\keyword{with}' statement, which can be helpful in learning how context managers work.} @@ -891,7 +890,7 @@ for the \module{contextlib} module.} %====================================================================== -\section{PEP 352: Exceptions as New-Style Classes} +\section{PEP 352: Exceptions as New-Style Classes\label{pep-352}} Exception classes can now be new-style classes, not just classic classes, and the built-in \exception{Exception} class and all the @@ -957,7 +956,7 @@ Brett Cannon and Guido van~Rossum; implemented by Brett Cannon.} %====================================================================== -\section{PEP 353: Using ssize_t as the index type\label{section-353}} +\section{PEP 353: Using ssize_t as the index type\label{pep-353}} A wide-ranging change to Python's C API, using a new \ctype{Py_ssize_t} type definition instead of \ctype{int}, @@ -1019,7 +1018,7 @@ platforms. %====================================================================== -\section{PEP 357: The '__index__' method} +\section{PEP 357: The '__index__' method\label{pep-357}} The NumPy developers had a problem that could only be solved by adding a new special method, \method{__index__}. When using slice notation, @@ -1092,8 +1091,8 @@ print d[3], d[4] # Prints 0, 0 \end{verbatim} \item The \function{min()} and \function{max()} built-in functions -gained a \code{key} keyword argument analogous to the \code{key} -argument for \method{sort()}. This argument supplies a function that +gained a \code{key} keyword parameter analogous to the \code{key} +argument for \method{sort()}. This parameter supplies a function that takes a single argument and is called for every value in the list; \function{min()}/\function{max()} will return the element with the smallest/largest return value from this function. @@ -1186,7 +1185,7 @@ pystone benchmark around XXX\% faster than Python 2.4. %====================================================================== -\section{New, Improved, and Deprecated Modules} +\section{New, Improved, and Removed Modules} The standard library received many enhancements and bug fixes in Python 2.5. Here's a partial list of the most notable changes, sorted @@ -1196,13 +1195,23 @@ the SVN logs for all the details. \begin{itemize} -% the cPickle module no longer accepts the deprecated None option in the -% args tuple returned by __reduce__(). - \item The \module{audioop} module now supports the a-LAW encoding, and the code for u-LAW encoding has been improved. (Contributed by Lars Immisch.) +\item The \module{codecs} module gained support for incremental +codecs. The \function{codec.lookup()} function now +returns a \class{CodecInfo} instance instead of a tuple. +\class{CodecInfo} instances behave like a 4-tuple to preserve backward +compatibility but also have the attributes \member{encode}, +\member{decode}, \member{incrementalencoder}, \member{incrementaldecoder}, +\member{streamwriter}, and \member{streamreader}. Incremental codecs +can receive input and produce output in multiple chunks; the output is +the same as if the entire input was fed to the non-incremental codec. +See the \module{codecs} module documentation for details. +(Designed and implemented by Walter D\"orwald.) +% Patch 1436130 + \item The \module{collections} module gained a new type, \class{defaultdict}, that subclasses the standard \class{dict} type. The new type mostly behaves like a dictionary but constructs a @@ -1244,7 +1253,7 @@ method that removes the first occurrence of \var{value} in the queue, raising \exception{ValueError} if the value isn't found. \item New module: The \module{contextlib} module contains helper functions for use -with the new \keyword{with} statement. See +with the new '\keyword{with}' statement. See section~\ref{module-contextlib} for more about this module. (Contributed by Phillip J. Eby.) @@ -1257,6 +1266,11 @@ which is also written in C but doesn't match the \module{profile} module's interface, will continue to be maintained in future versions of Python. (Contributed by Armin Rigo.) +Also, the \module{pstats} module used to analyze the data measured by +the profiler now supports directing the output to any file stream +by supplying a \var{stream} argument to the \class{Stats} constructor. +(Contributed by Skip Montanaro.) + \item The \module{csv} module, which parses files in comma-separated value format, received several enhancements and a number of bugfixes. You can now set the maximum size in bytes of a @@ -1302,7 +1316,7 @@ to specify which generation to collect. \item The \function{nsmallest()} and \function{nlargest()} functions in the \module{heapq} module -now support a \code{key} keyword argument similar to the one +now support a \code{key} keyword parameter similar to the one provided by the \function{min()}/\function{max()} functions and the \method{sort()} methods. For example: Example: @@ -1375,14 +1389,20 @@ The \member{st_flags} member is also available, if the platform supports it. (Contributed by Antti Louko and Diego Petten\`o.) % (Patch 1180695, 1212117) +\item The \module{pickle} and \module{cPickle} modules no +longer accept a return value of \code{None} from the +\method{__reduce__()} method; the method must return a tuple of +arguments instead. The ability to return \code{None} was deprecated +in Python 2.4, so this completes the removal of the feature. + \item The old \module{regex} and \module{regsub} modules, which have been deprecated ever since Python 2.0, have finally been deleted. Other deleted modules: \module{statcache}, \module{tzparse}, \module{whrandom}. -\item The \file{lib-old} directory, +\item Also deleted: the \file{lib-old} directory, which includes ancient modules such as \module{dircmp} and -\module{ni}, was also deleted. \file{lib-old} wasn't on the default +\module{ni}, was removed. \file{lib-old} wasn't on the default \code{sys.path}, so unless your programs explicitly added the directory to \code{sys.path}, this removal shouldn't affect your code. @@ -1824,7 +1844,7 @@ Changes to Python's build process and to the C API include: \item The largest change to the C API came from \pep{353}, which modifies the interpreter to use a \ctype{Py_ssize_t} type definition instead of \ctype{int}. See the earlier -section~\ref{section-353} for a discussion of this change. +section~\ref{pep-353} for a discussion of this change. \item The design of the bytecode compiler has changed a great deal, to no longer generate bytecode by traversing the parse tree. Instead @@ -1969,19 +1989,23 @@ a syntax error if a module contains string literals with 8-bit characters but doesn't have an encoding declaration. In Python 2.4 this triggered a warning, not a syntax error. -\item The \module{pickle} module no longer uses the deprecated \var{bin} parameter. - \item Previously, the \member{gi_frame} attribute of a generator was always a frame object. Because of the \pep{342} changes -described in section~\ref{section-generators}, it's now possible +described in section~\ref{pep-342}, it's now possible for \member{gi_frame} to be \code{None}. + +\item Library: The \module{pickle} and \module{cPickle} modules no +longer accept a return value of \code{None} from the +\method{__reduce__()} method; the method must return a tuple of +arguments instead. The modules also no longer accept the deprecated +\var{bin} keyword parameter. + \item C API: Many functions now use \ctype{Py_ssize_t} -instead of \ctype{int} to allow processing more data -on 64-bit machines. Extension code may need to make -the same change to avoid warnings and to support 64-bit machines. -See the earlier -section~\ref{section-353} for a discussion of this change. +instead of \ctype{int} to allow processing more data on 64-bit +machines. Extension code may need to make the same change to avoid +warnings and to support 64-bit machines. See the earlier +section~\ref{pep-353} for a discussion of this change. \item C API: The obmalloc changes mean that diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/SimpleXMLRPCServer.py index a0b44e1..1d4f00f 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/SimpleXMLRPCServer.py @@ -560,6 +560,7 @@ class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): self.handle_xmlrpc(request_text) if __name__ == '__main__': + print 'Running XML-RPC server on port 8000' server = SimpleXMLRPCServer(("localhost", 8000)) server.register_function(pow) server.register_function(lambda x,y: x+y, 'add') diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 32e825e..b34dcb2 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -865,7 +865,7 @@ PyString_Repr(PyObject *obj, int smartquotes) *p++ = quote; *p = '\0'; _PyString_Resize( - &v, (int) (p - PyString_AS_STRING(v))); + &v, (p - PyString_AS_STRING(v))); return v; } } diff --git a/PCbuild/_bsddb.vcproj b/PCbuild/_bsddb.vcproj index daaa789..581c3fc 100644 --- a/PCbuild/_bsddb.vcproj +++ b/PCbuild/_bsddb.vcproj @@ -133,7 +133,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include;..\PC;"..\..\db-4.4.20\build_win32"" + AdditionalIncludeDirectories="..\Include;..\PC;"..\..\db-4.4.20\build_win32"" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -195,7 +195,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include;..\PC;"..\..\db-4.4.20\build_win32"" + AdditionalIncludeDirectories="..\Include;..\PC;"..\..\db-4.4.20\build_win32"" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_ctypes.vcproj b/PCbuild/_ctypes.vcproj index 4990c9e..a77fdd4 100644 --- a/PCbuild/_ctypes.vcproj +++ b/PCbuild/_ctypes.vcproj @@ -130,7 +130,7 @@ Name="VCCLCompilerTool" AdditionalOptions=" /USECL:MS_OPTERON" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC,..\Modules\_ctypes\libffi_msvc" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\_ctypes\libffi_msvc" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -189,7 +189,7 @@ Name="VCCLCompilerTool" AdditionalOptions=" /USECL:MS_ITANIUM" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC,..\Modules\_ctypes\libffi_msvc" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\_ctypes\libffi_msvc" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_ctypes_test.vcproj b/PCbuild/_ctypes_test.vcproj index 9467b14..a4bb7f1 100644 --- a/PCbuild/_ctypes_test.vcproj +++ b/PCbuild/_ctypes_test.vcproj @@ -126,7 +126,7 @@ Name="VCCLCompilerTool" AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="0" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS" MinimalRebuild="FALSE" BasicRuntimeChecks="0" @@ -181,7 +181,7 @@ Name="VCCLCompilerTool" AdditionalOptions=" /USECL:MS_OPTERON" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_elementtree.vcproj b/PCbuild/_elementtree.vcproj index e7f9117..3278874 100644 --- a/PCbuild/_elementtree.vcproj +++ b/PCbuild/_elementtree.vcproj @@ -132,7 +132,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC,..\Modules\expat" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\expat" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;XML_NS;XML_DTD;BYTEORDER=1234;XML_CONTEXT_BYTES=1024;USE_PYEXPAT_CAPI;XML_STATIC;HAVE_MEMMOVE" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -193,7 +193,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC,..\Modules\expat" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\expat" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;XML_NS;XML_DTD;BYTEORDER=1234;XML_CONTEXT_BYTES=1024;USE_PYEXPAT_CAPI;XML_STATIC;HAVE_MEMMOVE" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_msi.vcproj b/PCbuild/_msi.vcproj index 7a48469..503c174 100644 --- a/PCbuild/_msi.vcproj +++ b/PCbuild/_msi.vcproj @@ -132,7 +132,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -192,7 +192,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_socket.vcproj b/PCbuild/_socket.vcproj index 3078f6d..bdfc9d9 100644 --- a/PCbuild/_socket.vcproj +++ b/PCbuild/_socket.vcproj @@ -131,7 +131,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -192,7 +192,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_sqlite3.vcproj b/PCbuild/_sqlite3.vcproj index bdb1a9b..e81d3a0 100644 --- a/PCbuild/_sqlite3.vcproj +++ b/PCbuild/_sqlite3.vcproj @@ -134,7 +134,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include;..\PC;..\..\sqlite-source-3.3.4" + AdditionalIncludeDirectories="..\Include;..\PC;..\..\sqlite-source-3.3.4" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;MODULE_NAME=\"sqlite3\"" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -196,7 +196,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include;..\PC;..\..\sqlite-source-3.3.4" + AdditionalIncludeDirectories="..\Include;..\PC;..\..\sqlite-source-3.3.4" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;MODULE_NAME=\"sqlite3\"" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_testcapi.vcproj b/PCbuild/_testcapi.vcproj index 3f8ef30..f286a30 100644 --- a/PCbuild/_testcapi.vcproj +++ b/PCbuild/_testcapi.vcproj @@ -129,7 +129,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MMAP_EXPORTS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -188,7 +188,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MMAP_EXPORTS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/_tkinter.vcproj b/PCbuild/_tkinter.vcproj index f33f7f8..57b7606 100644 --- a/PCbuild/_tkinter.vcproj +++ b/PCbuild/_tkinter.vcproj @@ -133,7 +133,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\..\tcltk\include,..\Include,..\PC" + AdditionalIncludeDirectories="..\..\tcltk\include,..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;WITH_APPINIT" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -195,7 +195,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\..\tcltk\include,..\Include,..\PC" + AdditionalIncludeDirectories="..\..\tcltk\include,..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;WITH_APPINIT" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/bz2.vcproj b/PCbuild/bz2.vcproj index 96b2e53..841e94d 100644 --- a/PCbuild/bz2.vcproj +++ b/PCbuild/bz2.vcproj @@ -140,7 +140,7 @@ nmake /nologo /f makefile.msc lib AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC,..\..\bzip2-1.0.3" + AdditionalIncludeDirectories="..\Include,..\PC,..\..\bzip2-1.0.3" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -205,7 +205,7 @@ nmake /nologo /f makefile.msc lib AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC,..\..\bzip2-1.0.3" + AdditionalIncludeDirectories="..\Include,..\PC,..\..\bzip2-1.0.3" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/pyexpat.vcproj b/PCbuild/pyexpat.vcproj index 6f00403..c2b8824 100644 --- a/PCbuild/pyexpat.vcproj +++ b/PCbuild/pyexpat.vcproj @@ -131,7 +131,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC,..\Modules\expat" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\expat" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;XML_NS;XML_DTD;BYTEORDER=1234;XML_CONTEXT_BYTES=1024;XML_STATIC;HAVE_MEMMOVE" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -192,7 +192,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC,..\Modules\expat" + AdditionalIncludeDirectories="..\Include,..\PC,..\Modules\expat" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;XML_NS;XML_DTD;BYTEORDER=1234;XML_CONTEXT_BYTES=1024;XML_STATIC;HAVE_MEMMOVE" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/python.vcproj b/PCbuild/python.vcproj index fc9ecb8..aa432d4 100644 --- a/PCbuild/python.vcproj +++ b/PCbuild/python.vcproj @@ -140,7 +140,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM /VSEXTCOMP_VERBOSE" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -204,7 +204,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 3bd740f..910dbb1 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -147,7 +147,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;USE_DL_EXPORT" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -214,7 +214,7 @@ AdditionalOptions="/Zm200 /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;USE_DL_EXPORT" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/pythonw.vcproj b/PCbuild/pythonw.vcproj index 9bc3b8a..2cc7d61 100644 --- a/PCbuild/pythonw.vcproj +++ b/PCbuild/pythonw.vcproj @@ -133,7 +133,7 @@ AdditionalOptions=" /USECL:MS_ITANIUM" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl;{MSSDKPATH}\include\Win64\crt;{MSSDKPATH}\include\Win64\crt\sys;{MSSDKPATH}\include\Win64\mfc;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" @@ -195,7 +195,7 @@ AdditionalOptions=" /USECL:MS_OPTERON" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="{MSSDKPATH}\include\Win64\atl\amd64;{MSSDKPATH}\include\Win64\crt\amd64;{MSSDKPATH}\include\Win64\crt\amd64\sys;{MSSDKPATH}\include\Win64\mfc\amd64;..\Include,..\PC" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" StringPooling="TRUE" BasicRuntimeChecks="0" diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index e303313..184d11f 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -264,7 +264,7 @@ SDK, in particular the 64-bit support. This includes an Itanium compiler In addition, you need the Visual Studio plugin for external C compilers, from http://sf.net/projects/vsextcomp. The plugin will wrap cl.exe, to locate the proper target compiler, and convert compiler options -accordingly. +accordingly. The project files require atleast version 0.8. Building for AMD64 ------------------ diff --git a/PCbuild/select.vcproj b/PCbuild/select.vcproj index 12e1f65..15bfe17 100644 --- a/PCbuild/select.vcproj +++ b/PCbuild/select.vcproj @@ -21,7 +21,7 @@ Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="..\Include,..\PC,..\..\select113" + AdditionalIncludeDirectories="..\Include,..\PC" PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS" StringPooling="TRUE" RuntimeLibrary="2" @@ -77,7 +77,7 @@ Date: Sat, 22 Apr 2006 17:37:27 +0000 Subject: Delete test_coercion.py. Coercion is dead, so this test is useless. (Plus, it fails. :-) --- Lib/test/test_coercion.py | 329 ---------------------------------------------- 1 file changed, 329 deletions(-) delete mode 100644 Lib/test/test_coercion.py diff --git a/Lib/test/test_coercion.py b/Lib/test/test_coercion.py deleted file mode 100644 index 964f161..0000000 --- a/Lib/test/test_coercion.py +++ /dev/null @@ -1,329 +0,0 @@ -import copy -import sys -import warnings -import unittest -from test.test_support import run_unittest - -# Fake a number that implements numeric methods through __coerce__ -class CoerceNumber: - def __init__(self, arg): - self.arg = arg - - def __repr__(self): - return '' % repr(self.arg) - - def __coerce__(self, other): - if isinstance(other, CoerceNumber): - return self.arg, other.arg - else: - return (self.arg, other) - -# New-style class version of CoerceNumber -class CoerceTo(object): - def __init__(self, arg): - self.arg = arg - def __coerce__(self, other): - if isinstance(other, CoerceTo): - return self.arg, other.arg - else: - return self.arg, other - - -# Fake a number that implements numeric ops through methods. -class MethodNumber: - def __init__(self,arg): - self.arg = arg - - def __repr__(self): - return '' % repr(self.arg) - - def __add__(self,other): - return self.arg + other - - def __radd__(self,other): - return other + self.arg - - def __sub__(self,other): - return self.arg - other - - def __rsub__(self,other): - return other - self.arg - - def __mul__(self,other): - return self.arg * other - - def __rmul__(self,other): - return other * self.arg - - def __truediv__(self,other): - return self.arg / other - - def __rtruediv__(self,other): - return other / self.arg - - def __truediv__(self,other): - return self.arg / other - - def __rtruediv__(self,other): - return other / self.arg - - def __floordiv__(self,other): - return self.arg // other - - def __rfloordiv__(self,other): - return other // self.arg - - def __pow__(self,other): - return self.arg ** other - - def __rpow__(self,other): - return other ** self.arg - - def __mod__(self,other): - return self.arg % other - - def __rmod__(self,other): - return other % self.arg - - def __cmp__(self, other): - return cmp(self.arg, other) - - -candidates = [2, 2L, 4.0, 2+0j, [1], (2,), None, - MethodNumber(2), CoerceNumber(2)] - -infix_binops = [ '+', '-', '*', '**', '%', '//', '/' ] - -TE = TypeError -# b = both normal and augmented give same result list -# s = single result lists for normal and augmented -# e = equals other results -# result lists: ['+', '-', '*', '**', '%', '//', ('classic /', 'new /')] -# ^^^^^^^^^^^^^^^^^^^^^^ -# 2-tuple if results differ -# else only one value -infix_results = { - # 2 - (0,0): ('b', [4, 0, 4, 4, 0, 1, (1, 1.0)]), - (0,1): ('e', (0,0)), - (0,2): ('b', [6.0, -2.0, 8.0, 16.0, 2.0, 0.0, 0.5]), - (0,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), - (0,4): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), - (0,5): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), - (0,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (0,7): ('e', (0,0)), - (0,8): ('e', (0,0)), - - # 2L - (1,0): ('e', (0,0)), - (1,1): ('e', (0,1)), - (1,2): ('e', (0,2)), - (1,3): ('e', (0,3)), - (1,4): ('e', (0,4)), - (1,5): ('e', (0,5)), - (1,6): ('e', (0,6)), - (1,7): ('e', (0,7)), - (1,8): ('e', (0,8)), - - # 4.0 - (2,0): ('b', [6.0, 2.0, 8.0, 16.0, 0.0, 2.0, 2.0]), - (2,1): ('e', (2,0)), - (2,2): ('b', [8.0, 0.0, 16.0, 256.0, 0.0, 1.0, 1.0]), - (2,3): ('b', [6+0j, 2+0j, 8+0j, 16+0j, 0+0j, 2+0j, 2+0j]), - (2,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (2,5): ('e', (2,4)), - (2,6): ('e', (2,4)), - (2,7): ('e', (2,0)), - (2,8): ('e', (2,0)), - - # (2+0j) - (3,0): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), - (3,1): ('e', (3,0)), - (3,2): ('b', [6+0j, -2+0j, 8+0j, 16+0j, 2+0j, 0+0j, 0.5+0j]), - (3,3): ('b', [4+0j, 0+0j, 4+0j, 4+0j, 0+0j, 1+0j, 1+0j]), - (3,4): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (3,5): ('e', (3,4)), - (3,6): ('e', (3,4)), - (3,7): ('e', (3,0)), - (3,8): ('e', (3,0)), - - # [1] - (4,0): ('b', [TE, TE, [1, 1], TE, TE, TE, TE]), - (4,1): ('e', (4,0)), - (4,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (4,3): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (4,4): ('b', [[1, 1], TE, TE, TE, TE, TE, TE]), - (4,5): ('s', [TE, TE, TE, TE, TE, TE, TE], [[1, 2], TE, TE, TE, TE, TE, TE]), - (4,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (4,7): ('e', (4,0)), - (4,8): ('e', (4,0)), - - # (2,) - (5,0): ('b', [TE, TE, (2, 2), TE, TE, TE, TE]), - (5,1): ('e', (5,0)), - (5,2): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (5,3): ('e', (5,2)), - (5,4): ('e', (5,2)), - (5,5): ('b', [(2, 2), TE, TE, TE, TE, TE, TE]), - (5,6): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (5,7): ('e', (5,0)), - (5,8): ('e', (5,0)), - - # None - (6,0): ('b', [TE, TE, TE, TE, TE, TE, TE]), - (6,1): ('e', (6,0)), - (6,2): ('e', (6,0)), - (6,3): ('e', (6,0)), - (6,4): ('e', (6,0)), - (6,5): ('e', (6,0)), - (6,6): ('e', (6,0)), - (6,7): ('e', (6,0)), - (6,8): ('e', (6,0)), - - # MethodNumber(2) - (7,0): ('e', (0,0)), - (7,1): ('e', (0,1)), - (7,2): ('e', (0,2)), - (7,3): ('e', (0,3)), - (7,4): ('e', (0,4)), - (7,5): ('e', (0,5)), - (7,6): ('e', (0,6)), - (7,7): ('e', (0,7)), - (7,8): ('e', (0,8)), - - # CoerceNumber(2) - (8,0): ('e', (0,0)), - (8,1): ('e', (0,1)), - (8,2): ('e', (0,2)), - (8,3): ('e', (0,3)), - (8,4): ('e', (0,4)), - (8,5): ('e', (0,5)), - (8,6): ('e', (0,6)), - (8,7): ('e', (0,7)), - (8,8): ('e', (0,8)), -} - -def process_infix_results(): - for key in sorted(infix_results): - val = infix_results[key] - if val[0] == 'e': - infix_results[key] = infix_results[val[1]] - else: - if val[0] == 's': - res = (val[1], val[2]) - elif val[0] == 'b': - res = (val[1], val[1]) - for i in range(1): - if isinstance(res[i][6], tuple): - if 1/2 == 0: - # testing with classic (floor) division - res[i][6] = res[i][6][0] - else: - # testing with -Qnew - res[i][6] = res[i][6][1] - infix_results[key] = res - - - -process_infix_results() -# now infix_results has two lists of results for every pairing. - -prefix_binops = [ 'divmod' ] -prefix_results = [ - [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)], - [(1L,0L), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1L,0L)], - [(2.0,0.0), (2.0,0.0), (1.0,0.0), ((2+0j),0j), TE, TE, TE, TE, (2.0,0.0)], - [((1+0j),0j), ((1+0j),0j), (0j,(2+0j)), ((1+0j),0j), TE, TE, TE, TE, ((1+0j),0j)], - [TE, TE, TE, TE, TE, TE, TE, TE, TE], - [TE, TE, TE, TE, TE, TE, TE, TE, TE], - [TE, TE, TE, TE, TE, TE, TE, TE, TE], - [TE, TE, TE, TE, TE, TE, TE, TE, TE], - [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)] -] - -def format_float(value): - if abs(value) < 0.01: - return '0.0' - else: - return '%.1f' % value - -# avoid testing platform fp quirks -def format_result(value): - if isinstance(value, complex): - return '(%s + %sj)' % (format_float(value.real), - format_float(value.imag)) - elif isinstance(value, float): - return format_float(value) - return str(value) - -class CoercionTest(unittest.TestCase): - def test_infix_binops(self): - for ia, a in enumerate(candidates): - for ib, b in enumerate(candidates): - results = infix_results[(ia, ib)] - for op, res, ires in zip(infix_binops, results[0], results[1]): - if res is TE: - self.assertRaises(TypeError, eval, - 'a %s b' % op, {'a': a, 'b': b}) - else: - self.assertEquals(format_result(res), - format_result(eval('a %s b' % op)), - '%s %s %s == %s failed' % (a, op, b, res)) - try: - z = copy.copy(a) - except copy.Error: - z = a # assume it has no inplace ops - if ires is TE: - try: - exec 'z %s= b' % op - except TypeError: - pass - else: - self.fail("TypeError not raised") - else: - exec('z %s= b' % op) - self.assertEquals(ires, z) - - def test_prefix_binops(self): - for ia, a in enumerate(candidates): - for ib, b in enumerate(candidates): - for op in prefix_binops: - res = prefix_results[ia][ib] - if res is TE: - self.assertRaises(TypeError, eval, - '%s(a, b)' % op, {'a': a, 'b': b}) - else: - self.assertEquals(format_result(res), - format_result(eval('%s(a, b)' % op)), - '%s(%s, %s) == %s failed' % (op, a, b, res)) - - def test_cmptypes(self): - # Built-in tp_compare slots expect their arguments to have the - # same type, but a user-defined __coerce__ doesn't have to obey. - # SF #980352 - evil_coercer = CoerceTo(42) - # Make sure these don't crash any more - self.assertNotEquals(cmp(u'fish', evil_coercer), 0) - self.assertNotEquals(cmp(slice(1), evil_coercer), 0) - # ...but that this still works - class WackyComparer(object): - def __cmp__(slf, other): - self.assert_(other == 42, 'expected evil_coercer, got %r' % other) - return 0 - self.assertEquals(cmp(WackyComparer(), evil_coercer), 0) - # ...and classic classes too, since that code path is a little different - class ClassicWackyComparer: - def __cmp__(slf, other): - self.assert_(other == 42, 'expected evil_coercer, got %r' % other) - return 0 - self.assertEquals(cmp(ClassicWackyComparer(), evil_coercer), 0) - -def test_main(): - warnings.filterwarnings("ignore", - r'complex divmod\(\), // and % are deprecated', - DeprecationWarning, - r'test.test_coercion$') - run_unittest(CoercionTest) - -if __name__ == "__main__": - test_main() -- cgit v0.12 From 4dfe8a1131b551687659b9339eaee163a24f82f1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 22 Apr 2006 23:28:04 +0000 Subject: Here is a bytes type. It's very minimal but it's a start. --- Include/Python.h | 1 + Include/bytesobject.h | 47 +++++++ Lib/test/test_bytes.py | 109 +++++++++++++++ Makefile.pre.in | 2 + Objects/bytesobject.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++ Objects/object.c | 3 + Python/bltinmodule.c | 1 + 7 files changed, 536 insertions(+) create mode 100644 Include/bytesobject.h create mode 100644 Lib/test/test_bytes.py create mode 100644 Objects/bytesobject.c diff --git a/Include/Python.h b/Include/Python.h index 161c838..fffc688 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -78,6 +78,7 @@ #include "pydebug.h" +#include "bytesobject.h" #include "unicodeobject.h" #include "intobject.h" #include "boolobject.h" diff --git a/Include/bytesobject.h b/Include/bytesobject.h new file mode 100644 index 0000000..9c11624 --- /dev/null +++ b/Include/bytesobject.h @@ -0,0 +1,47 @@ +/* Bytes object interface */ + +#ifndef Py_BYTESOBJECT_H +#define Py_BYTESOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Type PyBytesObject represents a mutable array of bytes. + * The Python API is that of a sequence; + * the bytes are mapped to ints in [0, 256). + * Bytes are not characters; they may be used to encode characters. + * The only way to go between bytes and str/unicode is via encoding + * and decoding. + * For the concenience of C programmers, the bytes type is considered + * to contain a char pointer, not an unsigned char pointer. + */ + +/* Object layout */ +typedef struct { + PyObject_VAR_HEAD + char *ob_sval; +} PyBytesObject; + +/* Type object */ +PyAPI_DATA(PyTypeObject) PyBytes_Type; + +/* Type check macros */ +#define PyBytes_Check(self) PyObject_TypeCheck(self, &PyBytes_Type) +#define PyBytes_CheckExact(self) ((self)->ob_type == &PyBytes_Type) + +/* Direct API functions */ +PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t); +PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *); +PyAPI_FUNC(char *) PyBytes_AsString(PyObject *); +PyAPI_FUNC(int) PyBytes_Resize(PyObject *, Py_ssize_t); + +/* Macros, trading safety for speed */ +#define PyBytes_AS_STRING(self) (((PyBytesObject *)(self))->ob_sval) +#define PyBytes_GET_SIZE(self) (((PyBytesObject *)(self))->ob_size) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BYTESOBJECT_H */ diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py new file mode 100644 index 0000000..ce224c2 --- /dev/null +++ b/Lib/test/test_bytes.py @@ -0,0 +1,109 @@ +"""Unit tests for the bytes type.""" + +import sys +import unittest +import test.test_support + + +class BytesTest(unittest.TestCase): + + def test_basics(self): + b = bytes() + self.assertEqual(type(b), bytes) + self.assertEqual(b.__class__, bytes) + + def test_empty_sequence(self): + b = bytes() + self.assertEqual(len(b), 0) + self.assertRaises(IndexError, lambda: b[0]) + self.assertRaises(IndexError, lambda: b[1]) + self.assertRaises(IndexError, lambda: b[sys.maxint]) + self.assertRaises(IndexError, lambda: b[sys.maxint+1]) + self.assertRaises(IndexError, lambda: b[10**100]) + self.assertRaises(IndexError, lambda: b[-1]) + self.assertRaises(IndexError, lambda: b[-2]) + self.assertRaises(IndexError, lambda: b[-sys.maxint]) + self.assertRaises(IndexError, lambda: b[-sys.maxint-1]) + self.assertRaises(IndexError, lambda: b[-sys.maxint-2]) + self.assertRaises(IndexError, lambda: b[-10**100]) + + def test_from_list(self): + ints = list(range(256)) + b = bytes(i for i in ints) + self.assertEqual(len(b), 256) + self.assertEqual(list(b), ints) + + def test_from_index(self): + class C: + def __init__(self, i=0): + self.i = i + def __index__(self): + return self.i + b = bytes([C(), C(1), C(254), C(255)]) + self.assertEqual(list(b), [0, 1, 254, 255]) + self.assertRaises(ValueError, lambda: bytes([C(-1)])) + self.assertRaises(ValueError, lambda: bytes([C(256)])) + + def test_constructor_type_errors(self): + class C: + pass + self.assertRaises(TypeError, lambda: bytes(["0"])) + self.assertRaises(TypeError, lambda: bytes([0.0])) + self.assertRaises(TypeError, lambda: bytes([None])) + self.assertRaises(TypeError, lambda: bytes([C()])) + + def test_constructor_value_errors(self): + self.assertRaises(ValueError, lambda: bytes([-1])) + self.assertRaises(ValueError, lambda: bytes([-sys.maxint])) + self.assertRaises(ValueError, lambda: bytes([-sys.maxint-1])) + self.assertRaises(ValueError, lambda: bytes([-sys.maxint-2])) + self.assertRaises(ValueError, lambda: bytes([-10**100])) + self.assertRaises(ValueError, lambda: bytes([256])) + self.assertRaises(ValueError, lambda: bytes([257])) + self.assertRaises(ValueError, lambda: bytes([sys.maxint])) + self.assertRaises(ValueError, lambda: bytes([sys.maxint+1])) + self.assertRaises(ValueError, lambda: bytes([10**100])) + + def test_repr(self): + self.assertEqual(repr(bytes()), "bytes()") + self.assertEqual(repr(bytes([0])), "bytes([0x00])") + self.assertEqual(repr(bytes([0, 1, 254, 255])), "bytes([0x00, 0x01, 0xfe, 0xff])") + + def test_compare(self): + b1 = bytes([1, 2, 3]) + b2 = bytes([1, 2, 3]) + b3 = bytes([1, 3]) + + self.failUnless(b1 == b2) + self.failUnless(b2 != b3) + self.failUnless(b1 <= b2) + self.failUnless(b1 <= b3) + self.failUnless(b1 < b3) + self.failUnless(b1 >= b2) + self.failUnless(b3 >= b2) + self.failUnless(b3 > b2) + + self.failIf(b1 != b2) + self.failIf(b2 == b3) + self.failIf(b1 > b2) + self.failIf(b1 > b3) + self.failIf(b1 >= b3) + self.failIf(b1 < b2) + self.failIf(b3 < b2) + self.failIf(b3 <= b2) + + def test_nohash(self): + self.assertRaises(TypeError, hash, bytes()) + + def test_doc(self): + self.failUnless(bytes.__doc__ != None) + self.failUnless(bytes.__doc__.startswith("bytes(")) + + +def test_main(): + test.test_support.run_unittest(XrangeTest) + + +if __name__ == "__main__": + ##test_main() + unittest.main() diff --git a/Makefile.pre.in b/Makefile.pre.in index f8a7481..c3af94c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -278,6 +278,7 @@ OBJECT_OBJS= \ Objects/abstract.o \ Objects/boolobject.o \ Objects/bufferobject.o \ + Objects/bytesobject.o \ Objects/cellobject.o \ Objects/classobject.o \ Objects/cobject.o \ @@ -494,6 +495,7 @@ PYTHON_HEADERS= \ Include/abstract.h \ Include/boolobject.h \ Include/bufferobject.h \ + Include/bytesobject.h \ Include/ceval.h \ Include/classobject.h \ Include/cobject.h \ diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c new file mode 100644 index 0000000..8fc089b --- /dev/null +++ b/Objects/bytesobject.c @@ -0,0 +1,373 @@ +/* Bytes object implementation */ + +/* XXX TO DO: optimizations */ + +#define PY_SSIZE_T_CLEAN +#include "Python.h" + +/* Direct API functions */ + +PyObject * +PyBytes_FromStringAndSize(const char *sval, Py_ssize_t size) +{ + PyBytesObject *new; + + if (size != 0) { + assert(sval != NULL); + assert(size > 0); + } + + new = PyObject_New(PyBytesObject, &PyBytes_Type); + if (new == NULL) + return NULL; + + if (size > 0) { + new->ob_sval = PyMem_Malloc(size); + if (new->ob_sval == NULL) { + Py_DECREF(new); + return NULL; + } + memcpy(new->ob_sval, sval, size); + new->ob_size = size; + } + + return (PyObject *)new; +} + +Py_ssize_t +PyBytes_Size(PyObject *self) +{ + assert(self != NULL); + assert(PyBytes_Check(self)); + + return ((PyBytesObject *)self)->ob_size; +} + +char * +PyBytes_AsString(PyObject *self) +{ + assert(self != NULL); + assert(PyBytes_Check(self)); + + return ((PyBytesObject *)self)->ob_sval; +} + +int +PyBytes_Resize(PyObject *self, Py_ssize_t size) +{ + void *sval; + + assert(self != NULL); + assert(PyBytes_Check(self)); + assert(size >= 0); + + sval = PyMem_Realloc(((PyBytesObject *)self)->ob_sval, size); + if (sval == NULL) { + PyErr_NoMemory(); + return -1; + } + + ((PyBytesObject *)self)->ob_sval = sval; + ((PyBytesObject *)self)->ob_size = size; + + return 0; +} + +/* Functions stuffed into the type object */ + +static Py_ssize_t +bytes_length(PyBytesObject *self) +{ + return self->ob_size; +} + +static PyObject * +bytes_getitem(PyBytesObject *self, Py_ssize_t i) +{ + if (i < 0) + i += self->ob_size; + if (i < 0 || i >= self->ob_size) { + PyErr_SetString(PyExc_IndexError, "bytes index out of range"); + return NULL; + } + return PyInt_FromLong((unsigned char)(self->ob_sval[i])); +} + +static long +bytes_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "bytes objects are unhashable"); + return -1; +} + +static int +bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"sequence", 0}; + PyObject *arg = NULL; + PyObject *it; /* iter(arg) */ + PyObject *(*iternext)(PyObject *); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bytes", kwlist, &arg)) + return -1; + + /* Verify list invariants established by PyType_GenericAlloc() */ + if (self->ob_size != 0) { + assert(self->ob_sval != NULL); + assert(self->ob_size > 0); + } + + /* Empty previous contents */ + if (PyBytes_Resize((PyObject *)self, 0) < 0) + return -1; + + /* Quick check if we're done */ + if (arg == 0) + return 0; + + /* XXX Optimize this if the arguments is a list, tuple, or bytes */ + + /* Get the iterator */ + it = PyObject_GetIter(arg); + if (it == NULL) + return 0; + iternext = *it->ob_type->tp_iternext; + + /* Run the iterator to exhaustion */ + for (;;) { + PyObject *item; + Py_ssize_t value; + + /* Get the next item */ + item = iternext(it); + if (item == NULL) { + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + goto error; + PyErr_Clear(); + } + break; + } + + /* Interpret it as an int (__index__) */ + value = PyNumber_Index(item); + if (value == -1 && PyErr_Occurred()) + goto error; + + /* Range check */ + if (value < 0 || value >= 256) { + PyErr_SetString(PyExc_ValueError, "bytes must be in range(0, 256)"); + goto error; + } + + /* Append the byte */ + /* XXX Speed this up */ + if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) + goto error; + self->ob_sval[self->ob_size-1] = value; + } + + /* Clean up and return success */ + Py_DECREF(it); + return 0; + + error: + /* Error handling when it != NULL */ + Py_DECREF(it); + return -1; +} + +static PyObject * +bytes_repr(PyBytesObject *self) +{ + PyObject *list; + PyObject *str; + PyObject *result; + int err; + int i; + + if (self->ob_size == 0) + return PyString_FromString("bytes()"); + + list = PyList_New(0); + if (list == NULL) + return NULL; + + str = PyString_FromString("bytes(["); + if (str == NULL) + goto error; + + err = PyList_Append(list, str); + Py_DECREF(str); + if (err < 0) + goto error; + + for (i = 0; i < self->ob_size; i++) { + char buffer[20]; + sprintf(buffer, ", 0x%02x", (unsigned char) (self->ob_sval[i])); + str = PyString_FromString((i == 0) ? buffer+2 : buffer); + if (str == NULL) + goto error; + err = PyList_Append(list, str); + Py_DECREF(str); + if (err < 0) + goto error; + } + + str = PyString_FromString("])"); + if (str == NULL) + goto error; + + err = PyList_Append(list, str); + Py_DECREF(str); + if (err < 0) + goto error; + + str = PyString_FromString(""); + if (str == NULL) + goto error; + + result = _PyString_Join(str, list); + Py_DECREF(str); + Py_DECREF(list); + return result; + + error: + /* Error handling when list != NULL */ + Py_DECREF(list); + return NULL; +} + +static PyObject * +bytes_richcompare(PyBytesObject *self, PyBytesObject *other, int op) +{ + PyObject *res; + int minsize; + int cmp; + + if (!PyBytes_Check(self) || !PyBytes_Check(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + if (self->ob_size != other->ob_size && (op == Py_EQ || op == Py_NE)) { + /* Shortcut: if the lengths differ, the objects differ */ + cmp = (op == Py_NE); + } + else { + minsize = self->ob_size; + if (other->ob_size < minsize) + minsize = other->ob_size; + + cmp = memcmp(self->ob_sval, other->ob_sval, minsize); + /* In ISO C, memcmp() guarantees to use unsigned bytes! */ + + if (cmp == 0) { + if (self->ob_size < other->ob_size) + cmp = -1; + else if (self->ob_size > other->ob_size) + cmp = 1; + } + + switch (op) { + case Py_LT: cmp = cmp < 0; break; + case Py_LE: cmp = cmp <= 0; break; + case Py_EQ: cmp = cmp == 0; break; + case Py_NE: cmp = cmp != 0; break; + case Py_GT: cmp = cmp > 0; break; + case Py_GE: cmp = cmp >= 0; break; + } + } + + res = cmp ? Py_True : Py_False; + Py_INCREF(res); + return res; +} + +static void +bytes_dealloc(PyBytesObject *self) +{ + if (self->ob_sval != 0) { + PyMem_Free(self->ob_sval); + } + self->ob_type->tp_free((PyObject *)self); +} + +static PySequenceMethods bytes_as_sequence = { + (lenfunc)bytes_length, /*sq_length*/ + (binaryfunc)0, /*sq_concat*/ + (ssizeargfunc)0, /*sq_repeat*/ + (ssizeargfunc)bytes_getitem, /*sq_item*/ + (ssizessizeargfunc)0, /*sq_slice*/ + 0, /*sq_ass_item*/ + 0, /*sq_ass_slice*/ + (objobjproc)0, /*sq_contains*/ +}; + +static PyMappingMethods bytes_as_mapping = { + (lenfunc)bytes_length, + (binaryfunc)0, + 0, +}; + +static PyBufferProcs bytes_as_buffer = { +/* + (readbufferproc)bytes_buffer_getreadbuf, + (writebufferproc)bytes_buffer_getwritebuf, + (segcountproc)bytes_buffer_getsegcount, + (charbufferproc)bytes_buffer_getcharbuf, +*/ +}; + +static PyMethodDef +bytes_methods[] = { + {NULL, NULL} +}; + +PyDoc_STRVAR(bytes_doc, +"bytes([iterable]) -> new array of bytes.\n\ +\n\ +If an argument is given it must be an iterable yielding ints in range(256)."); + +PyTypeObject PyBytes_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "bytes", + sizeof(PyBytesObject), + 0, + (destructor)bytes_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)bytes_repr, /* tp_repr */ + 0, /* tp_as_number */ + &bytes_as_sequence, /* tp_as_sequence */ + &bytes_as_mapping, /* tp_as_mapping */ + bytes_nohash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &bytes_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* bytes is 'final' or 'sealed' */ + bytes_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)bytes_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + bytes_methods, /* 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 */ + (initproc)bytes_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_Del, /* tp_free */ +}; diff --git a/Objects/object.c b/Objects/object.c index a75c14e..9a451d2 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1881,6 +1881,9 @@ _Py_ReadyTypes(void) if (PyType_Ready(&PyBool_Type) < 0) Py_FatalError("Can't initialize 'bool'"); + if (PyType_Ready(&PyBytes_Type) < 0) + Py_FatalError("Can't initialize 'bytes'"); + if (PyType_Ready(&PyString_Type) < 0) Py_FatalError("Can't initialize 'str'"); diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 914e0d1..54e8fe8 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2139,6 +2139,7 @@ _PyBuiltin_Init(void) SETBUILTIN("basestring", &PyBaseString_Type); SETBUILTIN("bool", &PyBool_Type); SETBUILTIN("buffer", &PyBuffer_Type); + SETBUILTIN("bytes", &PyBytes_Type); SETBUILTIN("classmethod", &PyClassMethod_Type); #ifndef WITHOUT_COMPLEX SETBUILTIN("complex", &PyComplex_Type); -- cgit v0.12 From 5f6f27de4d9f7cb260e243cf517cec629e54e985 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sun, 23 Apr 2006 00:19:58 +0000 Subject: Fix test_main function so test_bytes can be run by regrtest.py. --- Lib/test/test_bytes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index ce224c2..fc911ac 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -101,7 +101,7 @@ class BytesTest(unittest.TestCase): def test_main(): - test.test_support.run_unittest(XrangeTest) + test.test_support.run_unittest(BytesTest) if __name__ == "__main__": -- cgit v0.12 From e06b6b8ff5a380f5e107f2d28f23853bfe20021e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 23 Apr 2006 07:43:54 +0000 Subject: Fix a leak and a buglet discovered by Thomas. Get rid of silly lambdas in the unit test suite. Add a TODO list to the unit test suite (TDD style). --- Lib/test/test_bytes.py | 64 +++++++++++++++++++++++++++++++++++++------------- Objects/bytesobject.c | 3 ++- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index fc911ac..1ba5e11 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -41,28 +41,29 @@ class BytesTest(unittest.TestCase): return self.i b = bytes([C(), C(1), C(254), C(255)]) self.assertEqual(list(b), [0, 1, 254, 255]) - self.assertRaises(ValueError, lambda: bytes([C(-1)])) - self.assertRaises(ValueError, lambda: bytes([C(256)])) + self.assertRaises(ValueError, bytes, [C(-1)]) + self.assertRaises(ValueError, bytes, [C(256)]) def test_constructor_type_errors(self): + self.assertRaises(TypeError, bytes, 0) class C: pass - self.assertRaises(TypeError, lambda: bytes(["0"])) - self.assertRaises(TypeError, lambda: bytes([0.0])) - self.assertRaises(TypeError, lambda: bytes([None])) - self.assertRaises(TypeError, lambda: bytes([C()])) + self.assertRaises(TypeError, bytes, ["0"]) + self.assertRaises(TypeError, bytes, [0.0]) + self.assertRaises(TypeError, bytes, [None]) + self.assertRaises(TypeError, bytes, [C()]) def test_constructor_value_errors(self): - self.assertRaises(ValueError, lambda: bytes([-1])) - self.assertRaises(ValueError, lambda: bytes([-sys.maxint])) - self.assertRaises(ValueError, lambda: bytes([-sys.maxint-1])) - self.assertRaises(ValueError, lambda: bytes([-sys.maxint-2])) - self.assertRaises(ValueError, lambda: bytes([-10**100])) - self.assertRaises(ValueError, lambda: bytes([256])) - self.assertRaises(ValueError, lambda: bytes([257])) - self.assertRaises(ValueError, lambda: bytes([sys.maxint])) - self.assertRaises(ValueError, lambda: bytes([sys.maxint+1])) - self.assertRaises(ValueError, lambda: bytes([10**100])) + self.assertRaises(ValueError, bytes, [-1]) + self.assertRaises(ValueError, bytes, [-sys.maxint]) + self.assertRaises(ValueError, bytes, [-sys.maxint-1]) + self.assertRaises(ValueError, bytes, [-sys.maxint-2]) + self.assertRaises(ValueError, bytes, [-10**100]) + self.assertRaises(ValueError, bytes, [256]) + self.assertRaises(ValueError, bytes, [257]) + self.assertRaises(ValueError, bytes, [sys.maxint]) + self.assertRaises(ValueError, bytes, [sys.maxint+1]) + self.assertRaises(ValueError, bytes, [10**100]) def test_repr(self): self.assertEqual(repr(bytes()), "bytes()") @@ -99,6 +100,37 @@ class BytesTest(unittest.TestCase): self.failUnless(bytes.__doc__ != None) self.failUnless(bytes.__doc__.startswith("bytes(")) + # XXX More stuff to test and build (TDD): + # constructor from str: bytes() == bytes(map(ord, ))? + # encoding constructor: bytes([, [, ]]) + # default encoding Latin-1? (Matching ord) + # slicing + # extended slicing? + # item assignment + # slice assignment + # extended slice assignment? + # __contains__ with simple int arg + # __contains__ with another bytes arg? + # find/index? (int or bytes arg?) + # count? (int arg) + # concatenation (+) + # repeat? + # extend? + # append? + # insert? + # pop? + # __reversed__? + # reverse? (inplace) + # NOT sort! + # __iter__? (optimization) + # __str__? (could return "".join(map(chr, self)) + # decode + # buffer API + # check that regexp searches work + # (I suppose re.sub() returns a string) + # file.readinto + # file.write + def test_main(): test.test_support.run_unittest(BytesTest) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 8fc089b..f221395 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -130,7 +130,7 @@ bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) /* Get the iterator */ it = PyObject_GetIter(arg); if (it == NULL) - return 0; + return -1; iternext = *it->ob_type->tp_iternext; /* Run the iterator to exhaustion */ @@ -151,6 +151,7 @@ bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) /* Interpret it as an int (__index__) */ value = PyNumber_Index(item); + Py_DECREF(item); if (value == -1 && PyErr_Occurred()) goto error; -- cgit v0.12 From d624f18a2167daeabc85c1fef7e71dbc93daf5b5 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 24 Apr 2006 13:47:05 +0000 Subject: Added much functionality to the bytes type. Change file.readinto() to require binary mode. --- Include/bytesobject.h | 5 +- Lib/test/test_bytes.py | 264 ++++++++++++++++++--- Lib/test/test_file.py | 19 +- Objects/bytesobject.c | 630 ++++++++++++++++++++++++++++++++++++------------- Objects/fileobject.c | 5 + 5 files changed, 717 insertions(+), 206 deletions(-) diff --git a/Include/bytesobject.h b/Include/bytesobject.h index 9c11624..72ca076 100644 --- a/Include/bytesobject.h +++ b/Include/bytesobject.h @@ -21,7 +21,7 @@ extern "C" { /* Object layout */ typedef struct { PyObject_VAR_HEAD - char *ob_sval; + char *ob_bytes; } PyBytesObject; /* Type object */ @@ -32,13 +32,14 @@ PyAPI_DATA(PyTypeObject) PyBytes_Type; #define PyBytes_CheckExact(self) ((self)->ob_type == &PyBytes_Type) /* Direct API functions */ +PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *); PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t); PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *); PyAPI_FUNC(char *) PyBytes_AsString(PyObject *); PyAPI_FUNC(int) PyBytes_Resize(PyObject *, Py_ssize_t); /* Macros, trading safety for speed */ -#define PyBytes_AS_STRING(self) (((PyBytesObject *)(self))->ob_sval) +#define PyBytes_AS_STRING(self) (((PyBytesObject *)(self))->ob_bytes) #define PyBytes_GET_SIZE(self) (((PyBytesObject *)(self))->ob_size) #ifdef __cplusplus diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 1ba5e11..cf5cd5a 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1,6 +1,9 @@ """Unit tests for the bytes type.""" +import os +import re import sys +import tempfile import unittest import test.test_support @@ -45,7 +48,7 @@ class BytesTest(unittest.TestCase): self.assertRaises(ValueError, bytes, [C(256)]) def test_constructor_type_errors(self): - self.assertRaises(TypeError, bytes, 0) + self.assertRaises(TypeError, bytes, 0.0) class C: pass self.assertRaises(TypeError, bytes, ["0"]) @@ -100,36 +103,233 @@ class BytesTest(unittest.TestCase): self.failUnless(bytes.__doc__ != None) self.failUnless(bytes.__doc__.startswith("bytes(")) - # XXX More stuff to test and build (TDD): - # constructor from str: bytes() == bytes(map(ord, ))? - # encoding constructor: bytes([, [, ]]) - # default encoding Latin-1? (Matching ord) - # slicing - # extended slicing? - # item assignment - # slice assignment - # extended slice assignment? - # __contains__ with simple int arg - # __contains__ with another bytes arg? - # find/index? (int or bytes arg?) - # count? (int arg) - # concatenation (+) - # repeat? - # extend? - # append? - # insert? - # pop? - # __reversed__? - # reverse? (inplace) - # NOT sort! + def test_buffer_api(self): + short_sample = "Hello world\n" + sample = short_sample + "x"*(20 - len(short_sample)) + tfn = tempfile.mktemp() + try: + # Prepare + with open(tfn, "wb") as f: + f.write(short_sample) + # Test readinto + with open(tfn, "rb") as f: + b = bytes([ord('x')]*20) + n = f.readinto(b) + self.assertEqual(n, len(short_sample)) + self.assertEqual(list(b), map(ord, sample)) + # Test writing in binary mode + with open(tfn, "wb") as f: + f.write(b) + with open(tfn, "rb") as f: + self.assertEqual(f.read(), sample) + # Test writing in text mode + with open(tfn, "w") as f: + f.write(b) + with open(tfn, "r") as f: + self.assertEqual(f.read(), sample) + # Can't use readinto in text mode + with open(tfn, "r") as f: + self.assertRaises(TypeError, f.readinto, b) + finally: + try: + os.remove(tfn) + except os.error: + pass + + def test_reversed(self): + input = map(ord, "Hello") + b = bytes(input) + output = list(reversed(b)) + input.reverse() + self.assertEqual(output, input) + + def test_getslice(self): + def by(s): + return bytes(map(ord, s)) + b = by("Hello, world") + + self.assertEqual(b[:5], by("Hello")) + self.assertEqual(b[1:5], by("ello")) + self.assertEqual(b[5:7], by(", ")) + self.assertEqual(b[7:], by("world")) + self.assertEqual(b[7:12], by("world")) + self.assertEqual(b[7:100], by("world")) + + self.assertEqual(b[:-7], by("Hello")) + self.assertEqual(b[-11:-7], by("ello")) + self.assertEqual(b[-7:-5], by(", ")) + self.assertEqual(b[-5:], by("world")) + self.assertEqual(b[-5:12], by("world")) + self.assertEqual(b[-5:100], by("world")) + self.assertEqual(b[-100:5], by("Hello")) + + def test_regexps(self): + def by(s): + return bytes(map(ord, s)) + b = by("Hello, world") + self.assertEqual(re.findall(r"\w+", b), [by("Hello"), by("world")]) + + def test_setitem(self): + b = bytes([1, 2, 3]) + b[1] = 100 + self.assertEqual(b, bytes([1, 100, 3])) + b[-1] = 200 + self.assertEqual(b, bytes([1, 100, 200])) + class C: + def __init__(self, i=0): + self.i = i + def __index__(self): + return self.i + b[0] = C(10) + self.assertEqual(b, bytes([10, 100, 200])) + try: + b[3] = 0 + self.fail("Didn't raise IndexError") + except IndexError: + pass + try: + b[-10] = 0 + self.fail("Didn't raise IndexError") + except IndexError: + pass + try: + b[0] = 256 + self.fail("Didn't raise ValueError") + except ValueError: + pass + try: + b[0] = C(-1) + self.fail("Didn't raise ValueError") + except ValueError: + pass + try: + b[0] = None + self.fail("Didn't raise TypeError") + except TypeError: + pass + + def test_delitem(self): + b = bytes(range(10)) + del b[0] + self.assertEqual(b, bytes(range(1, 10))) + del b[-1] + self.assertEqual(b, bytes(range(1, 9))) + del b[4] + self.assertEqual(b, bytes([1, 2, 3, 4, 6, 7, 8])) + + def test_setslice(self): + b = bytes(range(10)) + self.assertEqual(list(b), list(range(10))) + + b[0:5] = bytes([1, 1, 1, 1, 1]) + self.assertEqual(b, bytes([1, 1, 1, 1, 1, 5, 6, 7, 8, 9])) + + del b[0:-5] + self.assertEqual(b, bytes([5, 6, 7, 8, 9])) + + b[0:0] = bytes([0, 1, 2, 3, 4]) + self.assertEqual(b, bytes(range(10))) + + b[-7:-3] = bytes([100, 101]) + self.assertEqual(b, bytes([0, 1, 2, 100, 101, 7, 8, 9])) + + b[3:5] = [3, 4, 5, 6] + self.assertEqual(b, bytes(range(10))) + + def test_setslice_trap(self): + # This test verifies that we correctly handle assigning self + # to a slice of self (the old Lambert Meertens trap). + b = bytes(range(256)) + b[8:] = b + self.assertEqual(b, bytes(list(range(8)) + list(range(256)))) + + def test_encoding(self): + sample = u"Hello world\n\u1234\u5678\u9abc\udef0" + for enc in ("utf8", "utf16"): + b = bytes(sample, enc) + self.assertEqual(b, bytes(map(ord, sample.encode(enc)))) + self.assertRaises(UnicodeEncodeError, bytes, sample, "latin1") + b = bytes(sample, "latin1", "ignore") + self.assertEqual(b, bytes(sample[:-4])) + + def test_decode(self): + sample = u"Hello world\n\u1234\u5678\u9abc\def0\def0" + for enc in ("utf8", "utf16"): + b = bytes(sample, enc) + self.assertEqual(b.decode(enc), sample) + sample = u"Hello world\n\x80\x81\xfe\xff" + b = bytes(sample, "latin1") + self.assertRaises(UnicodeDecodeError, b.decode, "utf8") + self.assertEqual(b.decode("utf8", "ignore"), "Hello world\n") + + def test_from_buffer(self): + sample = "Hello world\n\x80\x81\xfe\xff" + buf = buffer(sample) + b = bytes(buf) + self.assertEqual(b, bytes(map(ord, sample))) + + def test_to_str(self): + sample = "Hello world\n\x80\x81\xfe\xff" + b = bytes(sample) + self.assertEqual(str(b), sample) + + def test_from_int(self): + b = bytes(0) + self.assertEqual(b, bytes()) + b = bytes(10) + self.assertEqual(b, bytes([0]*10)) + b = bytes(10000) + self.assertEqual(b, bytes([0]*10000)) + + def test_concat(self): + b1 = bytes("abc") + b2 = bytes("def") + self.assertEqual(b1 + b2, bytes("abcdef")) + self.assertRaises(TypeError, lambda: b1 + "def") + self.assertRaises(TypeError, lambda: "abc" + b2) + + def test_repeat(self): + b = bytes("abc") + self.assertEqual(b * 3, bytes("abcabcabc")) + self.assertEqual(b * 0, bytes()) + self.assertEqual(b * -1, bytes()) + self.assertRaises(TypeError, lambda: b * 3.14) + self.assertRaises(TypeError, lambda: 3.14 * b) + self.assertRaises(MemoryError, lambda: b * sys.maxint) + self.assertEqual(bytes('x')*100, bytes('x'*100)) + + # Optimizations: # __iter__? (optimization) - # __str__? (could return "".join(map(chr, self)) - # decode - # buffer API - # check that regexp searches work - # (I suppose re.sub() returns a string) - # file.readinto - # file.write + # __reversed__? (optimization) + + # XXX Some list methods? + # extended slicing + # extended slice assignment + # extend (same as b[len(b):] = src) + # reverse (in-place) + # remove + # pop + # NOT sort! + # With int arg: + # __contains__ + # index + # count + # append + # insert + + # XXX Some string methods? (Those that don't use character properties) + # startswith + # endswidth + # find, rfind + # __contains__ (bytes arg) + # index, rindex (bytes arg) + # join + # replace + # translate + # split, rsplit + # lstrip, rstrip, strip?? + + # XXX pickle and marshal support? def test_main(): @@ -137,5 +337,5 @@ def test_main(): if __name__ == "__main__": - ##test_main() - unittest.main() + test_main() + ##unittest.main() diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index ab3da86..cfc1019 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -67,6 +67,17 @@ n = f.readinto(a) f.close() verify(buf == a.tostring()[:n]) +# verify readinto refuses text files +a = array('c', 'x'*10) +f = open(TESTFN, 'r') +try: + f.readinto(a) + raise TestFailed("readinto shouldn't work in text mode") +except TypeError: + pass +finally: + f.close() + # verify writelines with integers f = open(TESTFN, 'wb') try: @@ -261,13 +272,13 @@ methods = [("readline", ()), ("read", ()), ("readlines", ()), try: # Prepare the testfile - bag = open(TESTFN, "w") + bag = open(TESTFN, "wb") bag.write(filler * nchunks) bag.writelines(testlines) bag.close() # Test for appropriate errors mixing read* and iteration for methodname, args in methods: - f = open(TESTFN) + f = open(TESTFN, 'rb') if f.next() != filler: raise TestFailed, "Broken testfile" meth = getattr(f, methodname) @@ -286,7 +297,7 @@ try: # Each line in the bag o' ham is 4 bytes ("h", "a", "m", "\n"), so # 4096 lines of that should get us exactly on the buffer boundary for # any power-of-2 buffersize between 4 and 16384 (inclusive). - f = open(TESTFN) + f = open(TESTFN, 'rb') for i in range(nchunks): f.next() testline = testlines.pop(0) @@ -328,7 +339,7 @@ try: raise TestFailed("readlines() after next() with empty buffer " "failed. Got %r, expected %r" % (line, testline)) # Reading after iteration hit EOF shouldn't hurt either - f = open(TESTFN) + f = open(TESTFN, 'rb') try: for line in f: pass diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index f221395..36b4424 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -8,27 +8,34 @@ /* Direct API functions */ PyObject * -PyBytes_FromStringAndSize(const char *sval, Py_ssize_t size) +PyBytes_FromObject(PyObject *input) +{ + return PyObject_CallFunctionObjArgs((PyObject *)&PyBytes_Type, + input, NULL); +} + +PyObject * +PyBytes_FromStringAndSize(const char *bytes, Py_ssize_t size) { PyBytesObject *new; - if (size != 0) { - assert(sval != NULL); - assert(size > 0); - } + assert(size >= 0); new = PyObject_New(PyBytesObject, &PyBytes_Type); if (new == NULL) - return NULL; - - if (size > 0) { - new->ob_sval = PyMem_Malloc(size); - if (new->ob_sval == NULL) { - Py_DECREF(new); - return NULL; - } - memcpy(new->ob_sval, sval, size); - new->ob_size = size; + return NULL; + + new->ob_size = size; + if (size == 0) + new->ob_bytes = NULL; + else { + new->ob_bytes = PyMem_Malloc(size); + if (new->ob_bytes == NULL) { + Py_DECREF(new); + return NULL; + } + if (bytes != NULL) + memcpy(new->ob_bytes, bytes, size); } return (PyObject *)new; @@ -49,7 +56,7 @@ PyBytes_AsString(PyObject *self) assert(self != NULL); assert(PyBytes_Check(self)); - return ((PyBytesObject *)self)->ob_sval; + return ((PyBytesObject *)self)->ob_bytes; } int @@ -61,13 +68,13 @@ PyBytes_Resize(PyObject *self, Py_ssize_t size) assert(PyBytes_Check(self)); assert(size >= 0); - sval = PyMem_Realloc(((PyBytesObject *)self)->ob_sval, size); + sval = PyMem_Realloc(((PyBytesObject *)self)->ob_bytes, size); if (sval == NULL) { - PyErr_NoMemory(); - return -1; + PyErr_NoMemory(); + return -1; } - ((PyBytesObject *)self)->ob_sval = sval; + ((PyBytesObject *)self)->ob_bytes = sval; ((PyBytesObject *)self)->ob_size = size; return 0; @@ -82,15 +89,178 @@ bytes_length(PyBytesObject *self) } static PyObject * +bytes_concat(PyBytesObject *self, PyObject *other) +{ + PyBytesObject *result; + Py_ssize_t mysize; + Py_ssize_t size; + + if (!PyBytes_Check(other)) { + PyErr_Format(PyExc_TypeError, + "can't concat bytes to %.100s", other->ob_type->tp_name); + return NULL; + } + + mysize = self->ob_size; + size = mysize + ((PyBytesObject *)other)->ob_size; + if (size < 0) + return PyErr_NoMemory(); + result = (PyBytesObject *) PyBytes_FromStringAndSize(NULL, size); + if (result != NULL) { + memcpy(result->ob_bytes, self->ob_bytes, self->ob_size); + memcpy(result->ob_bytes + self->ob_size, + ((PyBytesObject *)other)->ob_bytes, + ((PyBytesObject *)other)->ob_size); + } + return (PyObject *)result; +} + +static PyObject * +bytes_repeat(PyBytesObject *self, Py_ssize_t count) +{ + PyBytesObject *result; + Py_ssize_t mysize; + Py_ssize_t size; + + if (count < 0) + count = 0; + mysize = self->ob_size; + size = mysize * count; + if (count != 0 && size / count != mysize) + return PyErr_NoMemory(); + result = (PyBytesObject *)PyBytes_FromStringAndSize(NULL, size); + if (result != NULL && size != 0) { + if (mysize == 1) + memset(result->ob_bytes, self->ob_bytes[0], size); + else { + int i; + for (i = 0; i < count; i++) + memcpy(result->ob_bytes + i*mysize, self->ob_bytes, mysize); + } + } + return (PyObject *)result; +} + +static PyObject * bytes_getitem(PyBytesObject *self, Py_ssize_t i) { if (i < 0) - i += self->ob_size; + i += self->ob_size; if (i < 0 || i >= self->ob_size) { - PyErr_SetString(PyExc_IndexError, "bytes index out of range"); - return NULL; + PyErr_SetString(PyExc_IndexError, "bytes index out of range"); + return NULL; } - return PyInt_FromLong((unsigned char)(self->ob_sval[i])); + return PyInt_FromLong((unsigned char)(self->ob_bytes[i])); +} + +static PyObject * +bytes_getslice(PyBytesObject *self, Py_ssize_t lo, Py_ssize_t hi) +{ + if (lo < 0) + lo = 0; + if (hi > self->ob_size) + hi = self->ob_size; + if (lo >= hi) + lo = hi = 0; + return PyBytes_FromStringAndSize(self->ob_bytes + lo, hi - lo); +} + +static int +bytes_setslice(PyBytesObject *self, Py_ssize_t lo, Py_ssize_t hi, + PyObject *values) +{ + int avail; + int needed; + char *bytes; + + if (values == NULL) { + bytes = NULL; + needed = 0; + } + else if (values == (PyObject *)self || !PyBytes_Check(values)) { + /* Make a copy an call this function recursively */ + int err; + values = PyBytes_FromObject(values); + if (values == NULL) + return -1; + err = bytes_setslice(self, lo, hi, values); + Py_DECREF(values); + return err; + } + else { + assert(PyBytes_Check(values)); + bytes = ((PyBytesObject *)values)->ob_bytes; + needed = ((PyBytesObject *)values)->ob_size; + } + + if (lo < 0) + lo = 0; + if (hi > self->ob_size) + hi = self->ob_size; + + avail = hi - lo; + if (avail < 0) + lo = hi = avail = 0; + + if (avail != needed) { + if (avail > needed) { + /* + 0 lo hi old_size + | |<----avail----->|<-----tomove------>| + | |<-needed->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + lo + needed, self->ob_bytes + hi, + self->ob_size - hi); + } + if (PyBytes_Resize((PyObject *)self, + self->ob_size + needed - avail) < 0) + return -1; + if (avail < needed) { + /* + 0 lo hi old_size + | |<-avail->|<-----tomove------>| + | |<----needed---->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + lo + needed, self->ob_bytes + hi, + self->ob_size - lo - needed); + } + } + + if (needed > 0) + memcpy(self->ob_bytes + lo, bytes, needed); + + return 0; +} + +static int +bytes_setitem(PyBytesObject *self, Py_ssize_t i, PyObject *value) +{ + Py_ssize_t ival; + + if (i < 0) + i += self->ob_size; + + if (i < 0 || i >= self->ob_size) { + PyErr_SetString(PyExc_IndexError, "bytes index out of range"); + return -1; + } + + if (value == NULL) + return bytes_setslice(self, i, i+1, NULL); + + ival = PyNumber_Index(value); + if (ival == -1 && PyErr_Occurred()) + return -1; + + if (ival < 0 || ival >= 256) { + PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); + return -1; + } + + self->ob_bytes[i] = ival; + return 0; } static long @@ -103,69 +273,138 @@ bytes_nohash(PyObject *self) static int bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"sequence", 0}; + static char *kwlist[] = {"source", "encoding", "errors", 0}; PyObject *arg = NULL; - PyObject *it; /* iter(arg) */ + const char *encoding = NULL; + const char *errors = NULL; + Py_ssize_t count; + PyObject *it; PyObject *(*iternext)(PyObject *); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bytes", kwlist, &arg)) - return -1; + /* Empty previous contents (yes, do this first of all!) */ + if (PyBytes_Resize((PyObject *)self, 0) < 0) + return -1; + + /* Parse arguments */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, + &arg, &encoding, &errors)) + return -1; + + /* Make a quick exit if no first argument */ + if (arg == NULL) { + if (encoding != NULL || errors != NULL) { + PyErr_SetString(PyExc_TypeError, + "encoding or errors without sequence argument"); + return -1; + } + return 0; + } - /* Verify list invariants established by PyType_GenericAlloc() */ - if (self->ob_size != 0) { - assert(self->ob_sval != NULL); - assert(self->ob_size > 0); + if (PyUnicode_Check(arg)) { + /* Encode via the codec registry */ + PyObject *encoded; + char *bytes; + Py_ssize_t size; + if (encoding == NULL) + encoding = PyUnicode_GetDefaultEncoding(); + encoded = PyCodec_Encode(arg, encoding, errors); + if (encoded == NULL) + return -1; + if (!PyString_Check(encoded)) { + PyErr_Format(PyExc_TypeError, + "encoder did not return a string object (type=%.400s)", + encoded->ob_type->tp_name); + Py_DECREF(encoded); + return -1; + } + bytes = PyString_AS_STRING(encoded); + size = PyString_GET_SIZE(encoded); + if (PyBytes_Resize((PyObject *)self, size) < 0) { + Py_DECREF(encoded); + return -1; + } + memcpy(self->ob_bytes, bytes, size); + Py_DECREF(encoded); + return 0; } - /* Empty previous contents */ - if (PyBytes_Resize((PyObject *)self, 0) < 0) - return -1; + /* If it's not unicode, there can't be encoding or errors */ + if (encoding != NULL || errors != NULL) { + PyErr_SetString(PyExc_TypeError, + "encoding or errors without a string argument"); + return -1; + } - /* Quick check if we're done */ - if (arg == 0) - return 0; + /* Is it an int? */ + count = PyNumber_Index(arg); + if (count == -1 && PyErr_Occurred()) + PyErr_Clear(); + else { + if (count < 0) { + PyErr_SetString(PyExc_ValueError, "negative count"); + return -1; + } + if (count > 0) { + if (PyBytes_Resize((PyObject *)self, count)) + return -1; + memset(self->ob_bytes, 0, count); + } + return 0; + } + + if (PyObject_CheckReadBuffer(arg)) { + const void *bytes; + Py_ssize_t size; + if (PyObject_AsReadBuffer(arg, &bytes, &size) < 0) + return -1; + if (PyBytes_Resize((PyObject *)self, size) < 0) + return -1; + memcpy(self->ob_bytes, bytes, size); + return 0; + } - /* XXX Optimize this if the arguments is a list, tuple, or bytes */ + /* XXX Optimize this if the arguments is a list, tuple */ /* Get the iterator */ it = PyObject_GetIter(arg); if (it == NULL) - return -1; + return -1; iternext = *it->ob_type->tp_iternext; /* Run the iterator to exhaustion */ for (;;) { - PyObject *item; - Py_ssize_t value; - - /* Get the next item */ - item = iternext(it); - if (item == NULL) { - if (PyErr_Occurred()) { - if (!PyErr_ExceptionMatches(PyExc_StopIteration)) - goto error; - PyErr_Clear(); - } - break; - } - - /* Interpret it as an int (__index__) */ - value = PyNumber_Index(item); - Py_DECREF(item); - if (value == -1 && PyErr_Occurred()) - goto error; - - /* Range check */ - if (value < 0 || value >= 256) { - PyErr_SetString(PyExc_ValueError, "bytes must be in range(0, 256)"); - goto error; - } - - /* Append the byte */ - /* XXX Speed this up */ - if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) - goto error; - self->ob_sval[self->ob_size-1] = value; + PyObject *item; + Py_ssize_t value; + + /* Get the next item */ + item = iternext(it); + if (item == NULL) { + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + goto error; + PyErr_Clear(); + } + break; + } + + /* Interpret it as an int (__index__) */ + value = PyNumber_Index(item); + Py_DECREF(item); + if (value == -1 && PyErr_Occurred()) + goto error; + + /* Range check */ + if (value < 0 || value >= 256) { + PyErr_SetString(PyExc_ValueError, + "bytes must be in range(0, 256)"); + goto error; + } + + /* Append the byte */ + /* XXX Speed this up */ + if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) + goto error; + self->ob_bytes[self->ob_size-1] = value; } /* Clean up and return success */ @@ -188,45 +427,45 @@ bytes_repr(PyBytesObject *self) int i; if (self->ob_size == 0) - return PyString_FromString("bytes()"); + return PyString_FromString("bytes()"); list = PyList_New(0); if (list == NULL) - return NULL; + return NULL; str = PyString_FromString("bytes(["); if (str == NULL) - goto error; + goto error; err = PyList_Append(list, str); Py_DECREF(str); if (err < 0) - goto error; + goto error; for (i = 0; i < self->ob_size; i++) { - char buffer[20]; - sprintf(buffer, ", 0x%02x", (unsigned char) (self->ob_sval[i])); - str = PyString_FromString((i == 0) ? buffer+2 : buffer); - if (str == NULL) - goto error; - err = PyList_Append(list, str); - Py_DECREF(str); - if (err < 0) - goto error; + char buffer[20]; + sprintf(buffer, ", 0x%02x", (unsigned char) (self->ob_bytes[i])); + str = PyString_FromString((i == 0) ? buffer+2 : buffer); + if (str == NULL) + goto error; + err = PyList_Append(list, str); + Py_DECREF(str); + if (err < 0) + goto error; } str = PyString_FromString("])"); if (str == NULL) - goto error; + goto error; err = PyList_Append(list, str); Py_DECREF(str); if (err < 0) - goto error; + goto error; str = PyString_FromString(""); if (str == NULL) - goto error; + goto error; result = _PyString_Join(str, list); Py_DECREF(str); @@ -240,6 +479,12 @@ bytes_repr(PyBytesObject *self) } static PyObject * +bytes_str(PyBytesObject *self) +{ + return PyString_FromStringAndSize(self->ob_bytes, self->ob_size); +} + +static PyObject * bytes_richcompare(PyBytesObject *self, PyBytesObject *other, int op) { PyObject *res; @@ -247,37 +492,37 @@ bytes_richcompare(PyBytesObject *self, PyBytesObject *other, int op) int cmp; if (!PyBytes_Check(self) || !PyBytes_Check(other)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } if (self->ob_size != other->ob_size && (op == Py_EQ || op == Py_NE)) { - /* Shortcut: if the lengths differ, the objects differ */ - cmp = (op == Py_NE); + /* Shortcut: if the lengths differ, the objects differ */ + cmp = (op == Py_NE); } else { - minsize = self->ob_size; - if (other->ob_size < minsize) - minsize = other->ob_size; - - cmp = memcmp(self->ob_sval, other->ob_sval, minsize); - /* In ISO C, memcmp() guarantees to use unsigned bytes! */ - - if (cmp == 0) { - if (self->ob_size < other->ob_size) - cmp = -1; - else if (self->ob_size > other->ob_size) - cmp = 1; - } - - switch (op) { - case Py_LT: cmp = cmp < 0; break; - case Py_LE: cmp = cmp <= 0; break; - case Py_EQ: cmp = cmp == 0; break; - case Py_NE: cmp = cmp != 0; break; - case Py_GT: cmp = cmp > 0; break; - case Py_GE: cmp = cmp >= 0; break; - } + minsize = self->ob_size; + if (other->ob_size < minsize) + minsize = other->ob_size; + + cmp = memcmp(self->ob_bytes, other->ob_bytes, minsize); + /* In ISO C, memcmp() guarantees to use unsigned bytes! */ + + if (cmp == 0) { + if (self->ob_size < other->ob_size) + cmp = -1; + else if (self->ob_size > other->ob_size) + cmp = 1; + } + + switch (op) { + case Py_LT: cmp = cmp < 0; break; + case Py_LE: cmp = cmp <= 0; break; + case Py_EQ: cmp = cmp == 0; break; + case Py_NE: cmp = cmp != 0; break; + case Py_GT: cmp = cmp > 0; break; + case Py_GE: cmp = cmp >= 0; break; + } } res = cmp ? Py_True : Py_False; @@ -288,41 +533,89 @@ bytes_richcompare(PyBytesObject *self, PyBytesObject *other, int op) static void bytes_dealloc(PyBytesObject *self) { - if (self->ob_sval != 0) { - PyMem_Free(self->ob_sval); + if (self->ob_bytes != 0) { + PyMem_Free(self->ob_bytes); } self->ob_type->tp_free((PyObject *)self); } +static Py_ssize_t +bytes_getbuffer(PyBytesObject *self, Py_ssize_t index, const void **ptr) +{ + if (index != 0) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent string segment"); + return -1; + } + *ptr = (void *)self->ob_bytes; + return self->ob_size; +} + +static Py_ssize_t +bytes_getsegcount(PyStringObject *self, Py_ssize_t *lenp) +{ + if (lenp) + *lenp = self->ob_size; + return 1; +} + +PyDoc_STRVAR(decode_doc, +"B.decode([encoding[,errors]]) -> unicode obect.\n\ +\n\ +Decodes B using the codec registered for encoding. encoding defaults\n\ +to the default encoding. errors may be given to set a different error\n\ +handling scheme. Default is 'strict' meaning that encoding errors raise\n\ +a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n\ +as well as any other name registerd with codecs.register_error that is\n\ +able to handle UnicodeDecodeErrors."); + +static PyObject * +bytes_decode(PyObject *self, PyObject *args) +{ + const char *encoding = NULL; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "|ss:decode", &encoding, &errors)) + return NULL; + if (encoding == NULL) + encoding = PyUnicode_GetDefaultEncoding(); + return PyCodec_Decode(self, encoding, errors); +} + static PySequenceMethods bytes_as_sequence = { - (lenfunc)bytes_length, /*sq_length*/ - (binaryfunc)0, /*sq_concat*/ - (ssizeargfunc)0, /*sq_repeat*/ - (ssizeargfunc)bytes_getitem, /*sq_item*/ - (ssizessizeargfunc)0, /*sq_slice*/ - 0, /*sq_ass_item*/ - 0, /*sq_ass_slice*/ - (objobjproc)0, /*sq_contains*/ + (lenfunc)bytes_length, /*sq_length*/ + (binaryfunc)bytes_concat, /*sq_concat*/ + (ssizeargfunc)bytes_repeat, /*sq_repeat*/ + (ssizeargfunc)bytes_getitem, /*sq_item*/ + (ssizessizeargfunc)bytes_getslice, /*sq_slice*/ + (ssizeobjargproc)bytes_setitem, /*sq_ass_item*/ + (ssizessizeobjargproc)bytes_setslice, /* sq_ass_slice */ +#if 0 + (objobjproc)bytes_contains, /* sq_contains */ + (binaryfunc)bytes_inplace_concat, /* sq_inplace_concat */ + (ssizeargfunc)bytes_inplace_repeat, /* sq_inplace_repeat */ +#endif }; static PyMappingMethods bytes_as_mapping = { - (lenfunc)bytes_length, - (binaryfunc)0, - 0, + (lenfunc)bytes_length, + (binaryfunc)0, + 0, }; static PyBufferProcs bytes_as_buffer = { -/* - (readbufferproc)bytes_buffer_getreadbuf, - (writebufferproc)bytes_buffer_getwritebuf, - (segcountproc)bytes_buffer_getsegcount, - (charbufferproc)bytes_buffer_getcharbuf, -*/ + (readbufferproc)bytes_getbuffer, + (writebufferproc)bytes_getbuffer, + (segcountproc)bytes_getsegcount, + /* XXX Bytes are not characters! But we need to implement + bf_getcharbuffer() so we can be used as 't#' argument to codecs. */ + (charbufferproc)bytes_getbuffer, }; static PyMethodDef bytes_methods[] = { - {NULL, NULL} + {"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_doc}, + {NULL, NULL} }; PyDoc_STRVAR(bytes_doc, @@ -336,39 +629,40 @@ PyTypeObject PyBytes_Type = { "bytes", sizeof(PyBytesObject), 0, - (destructor)bytes_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)bytes_repr, /* tp_repr */ - 0, /* tp_as_number */ - &bytes_as_sequence, /* tp_as_sequence */ - &bytes_as_mapping, /* tp_as_mapping */ - bytes_nohash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - &bytes_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* bytes is 'final' or 'sealed' */ - bytes_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)bytes_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bytes_methods, /* 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 */ - (initproc)bytes_init, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - PyObject_Del, /* tp_free */ + (destructor)bytes_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)bytes_repr, /* tp_repr */ + 0, /* tp_as_number */ + &bytes_as_sequence, /* tp_as_sequence */ + &bytes_as_mapping, /* tp_as_mapping */ + bytes_nohash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)bytes_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &bytes_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + /* bytes is 'final' or 'sealed' */ + bytes_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)bytes_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + bytes_methods, /* 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 */ + (initproc)bytes_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_Del, /* tp_free */ }; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 632ab04..ab2616d 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -880,6 +880,11 @@ file_readinto(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->f_binary) { + PyErr_SetString(PyExc_TypeError, + "readinto() requires binary mode"); + return NULL; + } /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && -- cgit v0.12 From 1b283c5bcce08dcda96a0fc86af5b10682f3baa3 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 24 Apr 2006 20:01:24 +0000 Subject: Change C code to use 4-space indents. Highlighting tab indents is off at the moment since that would be rather noisy, but should be turned on in the future when conversion to the new indentation style has been done. --- Misc/Vim/vimrc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Misc/Vim/vimrc b/Misc/Vim/vimrc index 7cd33db..2d70699 100644 --- a/Misc/Vim/vimrc +++ b/Misc/Vim/vimrc @@ -19,9 +19,8 @@ " Number of spaces to use for an indent. " This will affect Ctrl-T and 'autoindent'. " Python: 4 spaces -" C: tab (8 spaces) -au BufRead,BufNewFile *.py,*pyw set shiftwidth=4 -au BufRead,BufNewFile *.c,*.h set shiftwidth=8 +" C: 4 spaces +au BufRead,BufNewFile *.py,*pyw,*.c,*.h set shiftwidth=4 " Number of spaces that a pre-existing tab is equal to. " For the amount of space used for a new tab use shiftwidth. @@ -32,16 +31,17 @@ au BufRead,BufNewFile *py,*pyw,*.c,*.h set tabstop=8 " Replace tabs with the equivalent number of spaces. " Also have an autocmd for Makefiles since they require hard tabs. " Python: yes -" C: no +" C: yes " Makefile: no -au BufRead,BufNewFile *.py,*.pyw set expandtab -au BufRead,BufNewFile *.c,*.h set noexpandtab +au BufRead,BufNewFile *.py,*.pyw,*.c,*.h set expandtab au BufRead,BufNewFile Makefile* set noexpandtab " Use the below highlight group when displaying bad whitespace is desired highlight BadWhitespace ctermbg=red guibg=red " Display tabs at the beginning of a line in Python mode as bad +" Should be done for C code, but not until all code has been moved to 4-space +" indents. au BufRead,BufNewFile *.py,*.pyw match BadWhitespace /^\t\+/ " Wrap text after a certain number of characters -- cgit v0.12 From 13e57219d3143e4bae976a90846d6902e0514006 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 27 Apr 2006 22:54:26 +0000 Subject: Implement bytes += bytes, bytes *= int, int in bytes, bytes in bytes. --- Lib/test/test_bytes.py | 51 +++++++++++++++++++++++++- Objects/bytesobject.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 143 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index cf5cd5a..94524d4 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -296,8 +296,57 @@ class BytesTest(unittest.TestCase): self.assertRaises(TypeError, lambda: b * 3.14) self.assertRaises(TypeError, lambda: 3.14 * b) self.assertRaises(MemoryError, lambda: b * sys.maxint) + + def test_repeat_1char(self): self.assertEqual(bytes('x')*100, bytes('x'*100)) + def test_iconcat(self): + b = bytes("abc") + b1 = b + b += bytes("def") + self.assertEqual(b, bytes("abcdef")) + self.assertEqual(b, b1) + self.failUnless(b is b1) + + def test_irepeat(self): + b = bytes("abc") + b1 = b + b *= 3 + self.assertEqual(b, bytes("abcabcabc")) + self.assertEqual(b, b1) + self.failUnless(b is b1) + + def test_irepeat_1char(self): + b = bytes("x") + b1 = b + b *= 100 + self.assertEqual(b, bytes("x"*100)) + self.assertEqual(b, b1) + self.failUnless(b is b1) + + def test_contains(self): + b = bytes("abc") + self.failUnless(ord('a') in b) + self.failUnless(long(ord('a')) in b) + self.failIf(200 in b) + self.failIf(200L in b) + self.assertRaises(ValueError, lambda: 300 in b) + self.assertRaises(ValueError, lambda: -1 in b) + self.assertRaises(TypeError, lambda: None in b) + self.assertRaises(TypeError, lambda: float(ord('a')) in b) + self.assertRaises(TypeError, lambda: "a" in b) + self.failUnless(bytes("") in b) + self.failUnless(bytes("a") in b) + self.failUnless(bytes("b") in b) + self.failUnless(bytes("c") in b) + self.failUnless(bytes("ab") in b) + self.failUnless(bytes("bc") in b) + self.failUnless(bytes("abc") in b) + self.failIf(bytes("ac") in b) + self.failIf(bytes("d") in b) + self.failIf(bytes("dab") in b) + self.failIf(bytes("abd") in b) + # Optimizations: # __iter__? (optimization) # __reversed__? (optimization) @@ -311,7 +360,6 @@ class BytesTest(unittest.TestCase): # pop # NOT sort! # With int arg: - # __contains__ # index # count # append @@ -321,7 +369,6 @@ class BytesTest(unittest.TestCase): # startswith # endswidth # find, rfind - # __contains__ (bytes arg) # index, rindex (bytes arg) # join # replace diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 36b4424..c4f9eec 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -116,6 +116,31 @@ bytes_concat(PyBytesObject *self, PyObject *other) } static PyObject * +bytes_iconcat(PyBytesObject *self, PyObject *other) +{ + Py_ssize_t mysize; + Py_ssize_t osize; + Py_ssize_t size; + + if (!PyBytes_Check(other)) { + PyErr_Format(PyExc_TypeError, + "can't concat bytes to %.100s", other->ob_type->tp_name); + return NULL; + } + + mysize = self->ob_size; + osize = ((PyBytesObject *)other)->ob_size; + size = mysize + osize; + if (size < 0) + return PyErr_NoMemory(); + if (PyBytes_Resize((PyObject *)self, size) < 0) + return NULL; + memcpy(self->ob_bytes + mysize, ((PyBytesObject *)other)->ob_bytes, osize); + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * bytes_repeat(PyBytesObject *self, Py_ssize_t count) { PyBytesObject *result; @@ -133,7 +158,7 @@ bytes_repeat(PyBytesObject *self, Py_ssize_t count) if (mysize == 1) memset(result->ob_bytes, self->ob_bytes[0], size); else { - int i; + Py_ssize_t i; for (i = 0; i < count; i++) memcpy(result->ob_bytes + i*mysize, self->ob_bytes, mysize); } @@ -142,6 +167,72 @@ bytes_repeat(PyBytesObject *self, Py_ssize_t count) } static PyObject * +bytes_irepeat(PyBytesObject *self, Py_ssize_t count) +{ + Py_ssize_t mysize; + Py_ssize_t size; + + if (count < 0) + count = 0; + mysize = self->ob_size; + size = mysize * count; + if (count != 0 && size / count != mysize) + return PyErr_NoMemory(); + if (PyBytes_Resize((PyObject *)self, size) < 0) + return NULL; + + if (mysize == 1) + memset(self->ob_bytes, self->ob_bytes[0], size); + else { + Py_ssize_t i; + for (i = 1; i < count; i++) + memcpy(self->ob_bytes + i*mysize, self->ob_bytes, mysize); + } + + Py_INCREF(self); + return (PyObject *)self; +} + +static int +bytes_substring(PyBytesObject *self, PyBytesObject *other) +{ + Py_ssize_t i; + + if (other->ob_size == 1) { + return memchr(self->ob_bytes, other->ob_bytes[0], + self->ob_size) != NULL; + } + if (other->ob_size == 0) + return 1; /* Edge case */ + for (i = 0; i + other->ob_size <= self->ob_size; i++) { + /* XXX Yeah, yeah, lots of optimizations possible... */ + if (memcmp(self->ob_bytes + i, other->ob_bytes, other->ob_size) == 0) + return 1; + } + return 0; +} + +static int +bytes_contains(PyBytesObject *self, PyObject *value) +{ + Py_ssize_t ival; + + if (PyBytes_Check(value)) + return bytes_substring(self, (PyBytesObject *)value); + + ival = PyNumber_Index(value); + if (ival == -1 && PyErr_Occurred()) + return -1; + + if (ival < 0 || ival >= 256) { + PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); + return -1; + } + + return memchr(self->ob_bytes, ival, self->ob_size) != NULL; +} + +static PyObject * bytes_getitem(PyBytesObject *self, Py_ssize_t i) { if (i < 0) @@ -590,11 +681,9 @@ static PySequenceMethods bytes_as_sequence = { (ssizessizeargfunc)bytes_getslice, /*sq_slice*/ (ssizeobjargproc)bytes_setitem, /*sq_ass_item*/ (ssizessizeobjargproc)bytes_setslice, /* sq_ass_slice */ -#if 0 (objobjproc)bytes_contains, /* sq_contains */ - (binaryfunc)bytes_inplace_concat, /* sq_inplace_concat */ - (ssizeargfunc)bytes_inplace_repeat, /* sq_inplace_repeat */ -#endif + (binaryfunc)bytes_iconcat, /* sq_inplace_concat */ + (ssizeargfunc)bytes_irepeat, /* sq_inplace_repeat */ }; static PyMappingMethods bytes_as_mapping = { -- cgit v0.12 From cab109779881ee4996a895499fce08380a63f315 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 30 Apr 2006 02:31:56 +0000 Subject: __coerce__ is gone, there's nothing to test. This test now passes after removing refs to __coerce__. --- Lib/test/test_compare.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Lib/test/test_compare.py b/Lib/test/test_compare.py index 2fde614..7c81194 100644 --- a/Lib/test/test_compare.py +++ b/Lib/test/test_compare.py @@ -6,19 +6,6 @@ class Empty: def __repr__(self): return '' -class Coerce: - def __init__(self, arg): - self.arg = arg - - def __repr__(self): - return '' % self.arg - - def __coerce__(self, other): - if isinstance(other, Coerce): - return self.arg, other.arg - else: - return self.arg, other - class Cmp: def __init__(self,arg): self.arg = arg @@ -30,7 +17,7 @@ class Cmp: return cmp(self.arg, other) class ComparisonTest(unittest.TestCase): - set1 = [2, 2.0, 2L, 2+0j, Coerce(2), Cmp(2.0)] + set1 = [2, 2.0, 2L, 2+0j, Cmp(2.0)] set2 = [[1], (3,), None, Empty()] candidates = set1 + set2 -- cgit v0.12 From 5584245f584edc88d02bdebf7864e31f086622c6 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 30 Apr 2006 02:32:45 +0000 Subject: Some of the test cases were failing due to the busted (relative) import. This fixes some of the failures, but there are still more. --- Lib/xmlcore/etree/ElementInclude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/xmlcore/etree/ElementInclude.py b/Lib/xmlcore/etree/ElementInclude.py index 974cc21..d7f85b3 100644 --- a/Lib/xmlcore/etree/ElementInclude.py +++ b/Lib/xmlcore/etree/ElementInclude.py @@ -49,7 +49,7 @@ ## import copy -import ElementTree +from . import ElementTree XINCLUDE = "{http://www.w3.org/2001/XInclude}" -- cgit v0.12 From a0867f79bbdd2b38add23fced8e5ae071ce09f70 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 5 May 2006 04:34:18 +0000 Subject: Optimizations for bytes reallocation. This uses up to 12.5% overallocation, not entirely unlike list_resize(). Could probably use more tweaks for odd allocation patterns, TBD. Also add __alloc__() method which returns the actually allocated size. PS. I'm now convinced that we need something like "".join(); later. --- Include/bytesobject.h | 1 + Objects/bytesobject.c | 64 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Include/bytesobject.h b/Include/bytesobject.h index 72ca076..849078d 100644 --- a/Include/bytesobject.h +++ b/Include/bytesobject.h @@ -21,6 +21,7 @@ extern "C" { /* Object layout */ typedef struct { PyObject_VAR_HEAD + Py_ssize_t ob_alloc; /* How many bytes allocated */ char *ob_bytes; } PyBytesObject; diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index c4f9eec..d1ebc16 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -4,6 +4,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "structmember.h" /* Direct API functions */ @@ -25,7 +26,6 @@ PyBytes_FromStringAndSize(const char *bytes, Py_ssize_t size) if (new == NULL) return NULL; - new->ob_size = size; if (size == 0) new->ob_bytes = NULL; else { @@ -37,6 +37,7 @@ PyBytes_FromStringAndSize(const char *bytes, Py_ssize_t size) if (bytes != NULL) memcpy(new->ob_bytes, bytes, size); } + new->ob_size = new->ob_alloc = size; return (PyObject *)new; } @@ -63,12 +64,31 @@ int PyBytes_Resize(PyObject *self, Py_ssize_t size) { void *sval; + Py_ssize_t alloc = ((PyBytesObject *)self)->ob_alloc; assert(self != NULL); assert(PyBytes_Check(self)); assert(size >= 0); - sval = PyMem_Realloc(((PyBytesObject *)self)->ob_bytes, size); + if (size < alloc / 2) { + /* Major downsize; resize down to exact size */ + alloc = size; + } + else if (size <= alloc) { + /* Within allocated size; quick exit */ + ((PyBytesObject *)self)->ob_size = size; + return 0; + } + else if (size <= alloc * 1.125) { + /* Moderate upsize; overallocate similar to list_resize() */ + alloc = size + (size >> 3) + (size < 9 ? 3 : 6); + } + else { + /* Major upsize; resize up to exact size */ + alloc = size; + } + + sval = PyMem_Realloc(((PyBytesObject *)self)->ob_bytes, alloc); if (sval == NULL) { PyErr_NoMemory(); return -1; @@ -76,6 +96,7 @@ PyBytes_Resize(PyObject *self, Py_ssize_t size) ((PyBytesObject *)self)->ob_bytes = sval; ((PyBytesObject *)self)->ob_size = size; + ((PyBytesObject *)self)->ob_alloc = alloc; return 0; } @@ -133,7 +154,9 @@ bytes_iconcat(PyBytesObject *self, PyObject *other) size = mysize + osize; if (size < 0) return PyErr_NoMemory(); - if (PyBytes_Resize((PyObject *)self, size) < 0) + if (size <= self->ob_alloc) + self->ob_size = size; + else if (PyBytes_Resize((PyObject *)self, size) < 0) return NULL; memcpy(self->ob_bytes + mysize, ((PyBytesObject *)other)->ob_bytes, osize); Py_INCREF(self); @@ -178,7 +201,9 @@ bytes_irepeat(PyBytesObject *self, Py_ssize_t count) size = mysize * count; if (count != 0 && size / count != mysize) return PyErr_NoMemory(); - if (PyBytes_Resize((PyObject *)self, size) < 0) + if (size <= self->ob_alloc) + self->ob_size = size; + else if (PyBytes_Resize((PyObject *)self, size) < 0) return NULL; if (mysize == 1) @@ -372,9 +397,11 @@ bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) PyObject *it; PyObject *(*iternext)(PyObject *); - /* Empty previous contents (yes, do this first of all!) */ - if (PyBytes_Resize((PyObject *)self, 0) < 0) - return -1; + if (self->ob_size != 0) { + /* Empty previous contents (yes, do this first of all!) */ + if (PyBytes_Resize((PyObject *)self, 0) < 0) + return -1; + } /* Parse arguments */ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, @@ -410,7 +437,9 @@ bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) } bytes = PyString_AS_STRING(encoded); size = PyString_GET_SIZE(encoded); - if (PyBytes_Resize((PyObject *)self, size) < 0) { + if (size <= self->ob_alloc) + self->ob_size = size; + else if (PyBytes_Resize((PyObject *)self, size) < 0) { Py_DECREF(encoded); return -1; } @@ -492,8 +521,9 @@ bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) } /* Append the byte */ - /* XXX Speed this up */ - if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) + if (self->ob_size < self->ob_alloc) + self->ob_size++; + else if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) goto error; self->ob_bytes[self->ob_size-1] = value; } @@ -673,6 +703,17 @@ bytes_decode(PyObject *self, PyObject *args) return PyCodec_Decode(self, encoding, errors); } +PyDoc_STRVAR(alloc_doc, +"B.__alloc__() -> int\n\ +\n\ +Returns the number of bytes actually allocated."); + +static PyObject * +bytes_alloc(PyBytesObject *self) +{ + return PyInt_FromSsize_t(self->ob_alloc); +} + static PySequenceMethods bytes_as_sequence = { (lenfunc)bytes_length, /*sq_length*/ (binaryfunc)bytes_concat, /*sq_concat*/ @@ -704,7 +745,8 @@ static PyBufferProcs bytes_as_buffer = { static PyMethodDef bytes_methods[] = { {"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_doc}, - {NULL, NULL} + {"__alloc__", (PyCFunction)bytes_alloc, METH_NOARGS, alloc_doc}, + {NULL} }; PyDoc_STRVAR(bytes_doc, -- cgit v0.12 From 2018831b2b2106499a43b37e49e24f7f14154d35 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 5 May 2006 15:15:40 +0000 Subject: Adding bytes.join() -- a class methods that concatenates an iterable of bytes. The name and API are questionable, but the functionality isn't. --- Lib/test/test_bytes.py | 24 ++++++++++++++++++ Objects/bytesobject.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 94524d4..051773d 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -347,6 +347,30 @@ class BytesTest(unittest.TestCase): self.failIf(bytes("dab") in b) self.failIf(bytes("abd") in b) + def test_alloc(self): + b = bytes() + alloc = b.__alloc__() + self.assert_(alloc >= 0) + seq = [alloc] + for i in range(100): + b += bytes("x") + alloc = b.__alloc__() + self.assert_(alloc >= len(b)) + if alloc not in seq: + seq.append(alloc) + print seq + + def test_join(self): + self.assertEqual(bytes.join([]), bytes()) + self.assertEqual(bytes.join([bytes()]), bytes()) + for part in [("abc",), ("a", "bc"), ("ab", "c"), ("a", "b", "c")]: + lst = map(bytes, part) + self.assertEqual(bytes.join(lst), bytes("abc")) + self.assertEqual(bytes.join(tuple(lst)), bytes("abc")) + self.assertEqual(bytes.join(iter(lst)), bytes("abc")) + # XXX more... + + # Optimizations: # __iter__? (optimization) # __reversed__? (optimization) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index d1ebc16..f335abe 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -48,7 +48,7 @@ PyBytes_Size(PyObject *self) assert(self != NULL); assert(PyBytes_Check(self)); - return ((PyBytesObject *)self)->ob_size; + return PyBytes_GET_SIZE(self); } char * @@ -57,7 +57,7 @@ PyBytes_AsString(PyObject *self) assert(self != NULL); assert(PyBytes_Check(self)); - return ((PyBytesObject *)self)->ob_bytes; + return PyBytes_AS_STRING(self); } int @@ -714,6 +714,68 @@ bytes_alloc(PyBytesObject *self) return PyInt_FromSsize_t(self->ob_alloc); } +PyDoc_STRVAR(join_doc, +"bytes.join(iterable_of_bytes) -> bytes\n\ +\n\ +Concatenates any number of bytes objects. Example:\n\ +bytes.join([bytes('ab'), bytes('pq'), bytes('rs')]) -> bytes('abpqrs')."); + +static PyObject * +bytes_join(PyObject *cls, PyObject *it) +{ + PyObject *seq; + Py_ssize_t i; + Py_ssize_t n; + PyObject **items; + Py_ssize_t totalsize = 0; + PyObject *result; + char *dest; + + seq = PySequence_Fast(it, "can only join an iterable"); + if (seq == NULL) + return NULL; + n = PySequence_Fast_GET_SIZE(seq); + items = PySequence_Fast_ITEMS(seq); + + /* Compute the total size, and check that they are all bytes */ + for (i = 0; i < n; i++) { + PyObject *obj = items[i]; + if (!PyBytes_Check(obj)) { + PyErr_Format(PyExc_TypeError, + "can only join an iterable of bytes " + "(item %d has type '%.100s')", + i, obj->ob_type->tp_name); + goto error; + } + totalsize += PyBytes_GET_SIZE(obj); + if (totalsize < 0) { + PyErr_NoMemory(); + goto error; + } + } + + /* Allocate the result, and copy the bytes */ + result = PyBytes_FromStringAndSize(NULL, totalsize); + if (result == NULL) + goto error; + dest = PyBytes_AS_STRING(result); + for (i = 0; i < n; i++) { + PyObject *obj = items[i]; + Py_ssize_t size = PyBytes_GET_SIZE(obj); + memcpy(dest, PyBytes_AS_STRING(obj), size); + dest += size; + } + + /* Done */ + Py_DECREF(seq); + return result; + + /* Error handling */ + error: + Py_DECREF(seq); + return NULL; +} + static PySequenceMethods bytes_as_sequence = { (lenfunc)bytes_length, /*sq_length*/ (binaryfunc)bytes_concat, /*sq_concat*/ @@ -746,6 +808,7 @@ static PyMethodDef bytes_methods[] = { {"decode", (PyCFunction)bytes_decode, METH_VARARGS, decode_doc}, {"__alloc__", (PyCFunction)bytes_alloc, METH_NOARGS, alloc_doc}, + {"join", (PyCFunction)bytes_join, METH_O|METH_CLASS, join_doc}, {NULL} }; -- cgit v0.12 From 65810fee5e961af07fb964252b794c67162f98ee Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 26 May 2006 19:12:38 +0000 Subject: SF patch 1495675: Remove types.InstanceType and new.instance (Collin Winter) --- Doc/lib/libnew.tex | 8 ----- Doc/lib/libtypes.tex | 4 --- Lib/copy.py | 43 --------------------------- Lib/dis.py | 2 -- Lib/new.py | 1 - Lib/pickle.py | 40 ------------------------- Lib/pickletools.py | 76 +++++++++++++++++++++++++++++------------------- Lib/test/output/test_new | 1 - Lib/test/test_new.py | 14 ++------- Lib/types.py | 6 ++-- Lib/xmlrpclib.py | 1 - 11 files changed, 50 insertions(+), 146 deletions(-) diff --git a/Doc/lib/libnew.tex b/Doc/lib/libnew.tex index 5edc95da..e3f2a49 100644 --- a/Doc/lib/libnew.tex +++ b/Doc/lib/libnew.tex @@ -16,14 +16,6 @@ interpreter when the object is used. The \module{new} module defines the following functions: -\begin{funcdesc}{instance}{class\optional{, dict}} -This function creates an instance of \var{class} with dictionary -\var{dict} without calling the \method{__init__()} constructor. If -\var{dict} is omitted or \code{None}, a new, empty dictionary is -created for the new instance. Note that there are no guarantees that -the object will be in a consistent state. -\end{funcdesc} - \begin{funcdesc}{instancemethod}{function, instance, class} This function will return a method object, bound to \var{instance}, or unbound if \var{instance} is \code{None}. \var{function} must be diff --git a/Doc/lib/libtypes.tex b/Doc/lib/libtypes.tex index 19d2faa..f8f557d 100644 --- a/Doc/lib/libtypes.tex +++ b/Doc/lib/libtypes.tex @@ -122,10 +122,6 @@ The type for code objects such as returned by The type of user-defined classes. \end{datadesc} -\begin{datadesc}{InstanceType} -The type of instances of user-defined classes. -\end{datadesc} - \begin{datadesc}{MethodType} The type of methods of user-defined class instances. \end{datadesc} diff --git a/Lib/copy.py b/Lib/copy.py index 35c666f..f9e403d 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -119,26 +119,6 @@ def _copy_with_copy_method(x): if PyStringMap is not None: d[PyStringMap] = _copy_with_copy_method -def _copy_inst(x): - if hasattr(x, '__copy__'): - return x.__copy__() - if hasattr(x, '__getinitargs__'): - args = x.__getinitargs__() - y = x.__class__(*args) - else: - y = _EmptyClass() - y.__class__ = x.__class__ - if hasattr(x, '__getstate__'): - state = x.__getstate__() - else: - state = x.__dict__ - if hasattr(y, '__setstate__'): - y.__setstate__(state) - else: - y.__dict__.update(state) - return y -d[types.InstanceType] = _copy_inst - del d def deepcopy(x, memo=None, _nil=[]): @@ -273,29 +253,6 @@ def _keep_alive(x, memo): # aha, this is the first one :-) memo[id(memo)]=[x] -def _deepcopy_inst(x, memo): - if hasattr(x, '__deepcopy__'): - return x.__deepcopy__(memo) - if hasattr(x, '__getinitargs__'): - args = x.__getinitargs__() - args = deepcopy(args, memo) - y = x.__class__(*args) - else: - y = _EmptyClass() - y.__class__ = x.__class__ - memo[id(x)] = y - if hasattr(x, '__getstate__'): - state = x.__getstate__() - else: - state = x.__dict__ - state = deepcopy(state, memo) - if hasattr(y, '__setstate__'): - y.__setstate__(state) - else: - y.__dict__.update(state) - return y -d[types.InstanceType] = _deepcopy_inst - def _reconstruct(x, info, deep, memo=None): if isinstance(info, str): return x diff --git a/Lib/dis.py b/Lib/dis.py index 5a74b3a..89caf20 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -18,8 +18,6 @@ def dis(x=None): if x is None: distb() return - if type(x) is types.InstanceType: - x = x.__class__ if hasattr(x, 'im_func'): x = x.im_func if hasattr(x, 'func_code'): diff --git a/Lib/new.py b/Lib/new.py index 99a1c3f..bee11ed 100644 --- a/Lib/new.py +++ b/Lib/new.py @@ -6,7 +6,6 @@ Objects of most types can now be created by calling the type object. from types import ClassType as classobj from types import FunctionType as function -from types import InstanceType as instance from types import MethodType as instancemethod from types import ModuleType as module diff --git a/Lib/pickle.py b/Lib/pickle.py index 02a1b1d..ccda3e7 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -687,46 +687,6 @@ class Pickler: write(SETITEM) # else tmp is empty, and we're done - def save_inst(self, obj): - cls = obj.__class__ - - memo = self.memo - write = self.write - save = self.save - - if hasattr(obj, '__getinitargs__'): - args = obj.__getinitargs__() - len(args) # XXX Assert it's a sequence - _keep_alive(args, memo) - else: - args = () - - write(MARK) - - if self.bin: - save(cls) - for arg in args: - save(arg) - write(OBJ) - else: - for arg in args: - save(arg) - write(INST + cls.__module__ + '\n' + cls.__name__ + '\n') - - self.memoize(obj) - - try: - getstate = obj.__getstate__ - except AttributeError: - stuff = obj.__dict__ - else: - stuff = getstate() - _keep_alive(stuff, memo) - save(stuff) - write(BUILD) - - dispatch[InstanceType] = save_inst - def save_global(self, obj, name=None, pack=struct.pack): write = self.write memo = self.memo diff --git a/Lib/pickletools.py b/Lib/pickletools.py index 98f80f1..ab5e247 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -2071,42 +2071,58 @@ highest protocol among opcodes = 0 0: ( MARK 1: l LIST (MARK at 0) 2: p PUT 0 - 5: ( MARK - 6: i INST 'pickletools _Example' (MARK at 5) - 28: p PUT 1 - 31: ( MARK - 32: d DICT (MARK at 31) - 33: p PUT 2 - 36: S STRING 'value' - 45: p PUT 3 - 48: I INT 42 - 52: s SETITEM - 53: b BUILD - 54: a APPEND - 55: g GET 1 - 58: a APPEND - 59: . STOP + 5: c GLOBAL 'copy_reg _reconstructor' + 30: p PUT 1 + 33: ( MARK + 34: c GLOBAL 'pickletools _Example' + 56: p PUT 2 + 59: c GLOBAL '__builtin__ object' + 79: p PUT 3 + 82: N NONE + 83: t TUPLE (MARK at 33) + 84: p PUT 4 + 87: R REDUCE + 88: p PUT 5 + 91: ( MARK + 92: d DICT (MARK at 91) + 93: p PUT 6 + 96: S STRING 'value' + 105: p PUT 7 + 108: I INT 42 + 112: s SETITEM + 113: b BUILD + 114: a APPEND + 115: g GET 5 + 118: a APPEND + 119: . STOP highest protocol among opcodes = 0 >>> dis(pickle.dumps(x, 1)) 0: ] EMPTY_LIST 1: q BINPUT 0 3: ( MARK - 4: ( MARK - 5: c GLOBAL 'pickletools _Example' - 27: q BINPUT 1 - 29: o OBJ (MARK at 4) - 30: q BINPUT 2 - 32: } EMPTY_DICT - 33: q BINPUT 3 - 35: U SHORT_BINSTRING 'value' - 42: q BINPUT 4 - 44: K BININT1 42 - 46: s SETITEM - 47: b BUILD - 48: h BINGET 2 - 50: e APPENDS (MARK at 3) - 51: . STOP + 4: c GLOBAL 'copy_reg _reconstructor' + 29: q BINPUT 1 + 31: ( MARK + 32: c GLOBAL 'pickletools _Example' + 54: q BINPUT 2 + 56: c GLOBAL '__builtin__ object' + 76: q BINPUT 3 + 78: N NONE + 79: t TUPLE (MARK at 31) + 80: q BINPUT 4 + 82: R REDUCE + 83: q BINPUT 5 + 85: } EMPTY_DICT + 86: q BINPUT 6 + 88: U SHORT_BINSTRING 'value' + 95: q BINPUT 7 + 97: K BININT1 42 + 99: s SETITEM + 100: b BUILD + 101: h BINGET 5 + 103: e APPENDS (MARK at 3) + 104: . STOP highest protocol among opcodes = 1 Try "the canonical" recursive-object test. diff --git a/Lib/test/output/test_new b/Lib/test/output/test_new index b7f2ed9..fd225f3 100644 --- a/Lib/test/output/test_new +++ b/Lib/test/output/test_new @@ -1,7 +1,6 @@ test_new new.module() new.classobj() -new.instance() new.instancemethod() new.function() new.code() diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py index 4aab1e2..2819923 100644 --- a/Lib/test/test_new.py +++ b/Lib/test/test_new.py @@ -21,22 +21,12 @@ print 'new.classobj()' C = new.classobj('Spam', (Spam.Eggs,), {'get_more_yolks': get_more_yolks}) if verbose: print C -print 'new.instance()' -c = new.instance(C, {'yolks': 3}) -if verbose: - print c -o = new.instance(C) -verify(o.__dict__ == {}, - "new __dict__ should be empty") -del o -o = new.instance(C, None) -verify(o.__dict__ == {}, - "new __dict__ should be empty") -del o def break_yolks(self): self.yolks = self.yolks - 2 print 'new.instancemethod()' +c = C() +c.yolks = 3 im = new.instancemethod(break_yolks, c, C) if verbose: print im diff --git a/Lib/types.py b/Lib/types.py index 39812ac..db63c96 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -56,9 +56,7 @@ class _C: def _m(self): pass ClassType = type(_C) UnboundMethodType = type(_C._m) # Same as MethodType -_x = _C() -InstanceType = type(_x) -MethodType = type(_x._m) +MethodType = type(_C()._m) BuiltinFunctionType = type(len) BuiltinMethodType = type([].append) # Same as BuiltinFunctionType @@ -86,4 +84,4 @@ EllipsisType = type(Ellipsis) DictProxyType = type(TypeType.__dict__) NotImplementedType = type(NotImplemented) -del sys, _f, _g, _C, _x # Not for export +del sys, _f, _g, _C # Not for export diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py index bac0a9f..2938f29 100644 --- a/Lib/xmlrpclib.py +++ b/Lib/xmlrpclib.py @@ -748,7 +748,6 @@ class Marshaller: else: # store instance attributes as a struct (really?) self.dump_struct(value.__dict__, write) - dispatch[InstanceType] = dump_instance dispatch[DateTime] = dump_instance dispatch[Binary] = dump_instance -- cgit v0.12 From 08e8b7a295e04a62bfb0d7eb2529f6531ca28b75 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 26 May 2006 19:16:09 +0000 Subject: Comment out debug print. --- Lib/test/test_bytes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 051773d..d45ff64 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -358,7 +358,7 @@ class BytesTest(unittest.TestCase): self.assert_(alloc >= len(b)) if alloc not in seq: seq.append(alloc) - print seq + #print seq def test_join(self): self.assertEqual(bytes.join([]), bytes()) -- cgit v0.12 -- cgit v0.12 From 477c8d5e70240744d24631b18341ad892c8a8e1c Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 27 May 2006 19:21:47 +0000 Subject: Much-needed merge (using svnmerge.py this time) of trunk changes into p3yk. Inherits test_gzip/test_tarfile failures on 64-bit platforms from the trunk, but I don't want the merge to hang around too long (even though the regular p3yk-contributors are/have been busy with other things.) Merged revisions 45621-46490 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r45621 | george.yoshida | 2006-04-21 18:34:17 +0200 (Fri, 21 Apr 2006) | 2 lines Correct the grammar ........ r45622 | tim.peters | 2006-04-21 18:34:54 +0200 (Fri, 21 Apr 2006) | 2 lines Whitespace normalization. ........ r45624 | thomas.heller | 2006-04-21 18:48:56 +0200 (Fri, 21 Apr 2006) | 1 line Merge in changes from ctypes 0.9.9.6 upstream version. ........ r45625 | thomas.heller | 2006-04-21 18:51:04 +0200 (Fri, 21 Apr 2006) | 1 line Merge in changes from ctypes 0.9.9.6 upstream version. ........ r45630 | thomas.heller | 2006-04-21 20:29:17 +0200 (Fri, 21 Apr 2006) | 8 lines Documentation for ctypes. I think that 'generic operating system services' is the best category. Note that the Doc/lib/libctypes.latex file is generated from reST sources. You are welcome to make typo fixes, and I'll try to keep the reST sources in sync, but markup changes would be lost - they should be fixed in the tool that creates the latex file. The conversion script is external/ctypes/docs/manual/mkpydoc.py. ........ r45631 | tim.peters | 2006-04-21 23:18:10 +0200 (Fri, 21 Apr 2006) | 24 lines SF bug #1473760 TempFile can hang on Windows. Python 2.4 changed ntpath.abspath to do an import inside the function. As a result, due to Python's import lock, anything calling abspath on Windows (directly, or indirectly like tempfile.TemporaryFile) hung when it was called from a thread spawned as a side effect of importing a module. This is a depressingly frequent problem, and deserves a more general fix. I'm settling for a micro-fix here because this specific one accounts for a report of Zope Corp's ZEO hanging on Windows, and it was an odd way to change abspath to begin with (ntpath needs a different implementation depending on whether we're actually running on Windows, and the _obvious_ way to arrange for that is not to bury a possibly-failing import _inside_ the function). Note that if/when other micro-fixes of this kind get made, the new Lib/test/threaded_import_hangers.py is a convenient place to add tests for them. ........ r45634 | phillip.eby | 2006-04-21 23:53:37 +0200 (Fri, 21 Apr 2006) | 2 lines Guido wrote contextlib, not me, but thanks anyway. ;) ........ r45636 | andrew.kuchling | 2006-04-22 03:51:41 +0200 (Sat, 22 Apr 2006) | 1 line Typo fixes ........ r45638 | andrew.kuchling | 2006-04-22 03:58:40 +0200 (Sat, 22 Apr 2006) | 1 line Fix comment typo ........ r45639 | andrew.kuchling | 2006-04-22 04:06:03 +0200 (Sat, 22 Apr 2006) | 8 lines Make copy of test_mailbox.py. We'll still want to check the backward compatibility classes in the new mailbox.py that I'll be committing in a few minutes. One change has been made: the tests use len(mbox) instead of len(mbox.boxes). The 'boxes' attribute was never documented and contains some internal state that seems unlikely to have been useful. ........ r45640 | andrew.kuchling | 2006-04-22 04:32:43 +0200 (Sat, 22 Apr 2006) | 16 lines Add Gregory K. Johnson's revised version of mailbox.py (funded by the 2005 Summer of Code). The revision adds a number of new mailbox classes that support adding and removing messages; these classes also support mailbox locking and default to using email.Message instead of rfc822.Message. The old mailbox classes are largely left alone for backward compatibility. The exception is the Maildir class, which was present in the old module and now inherits from the new classes. The Maildir class's interface is pretty simple, though, so I think it'll be compatible with existing code. (The change to the NEWS file also adds a missing word to a different news item, which unfortunately required rewrapping the line.) ........ r45641 | tim.peters | 2006-04-22 07:52:59 +0200 (Sat, 22 Apr 2006) | 2 lines Whitespace normalization. ........ r45642 | neal.norwitz | 2006-04-22 08:07:46 +0200 (Sat, 22 Apr 2006) | 1 line Add libctypes as a dep ........ r45643 | martin.v.loewis | 2006-04-22 13:15:41 +0200 (Sat, 22 Apr 2006) | 1 line Fix more ssize_t problems. ........ r45644 | martin.v.loewis | 2006-04-22 13:40:03 +0200 (Sat, 22 Apr 2006) | 1 line Fix more ssize_t issues. ........ r45645 | george.yoshida | 2006-04-22 17:10:49 +0200 (Sat, 22 Apr 2006) | 2 lines Typo fixes ........ r45647 | martin.v.loewis | 2006-04-22 17:19:54 +0200 (Sat, 22 Apr 2006) | 1 line Port to Python 2.5. Drop .DEF file. Change output file names to .pyd. ........ r45648 | george.yoshida | 2006-04-22 17:27:14 +0200 (Sat, 22 Apr 2006) | 3 lines - add versionadded tag - make arbitrary arguments come last ........ r45649 | hyeshik.chang | 2006-04-22 17:48:15 +0200 (Sat, 22 Apr 2006) | 3 lines Remove $CJKCodecs$ RCS tags. The CJKCodecs isn't maintained outside anymore. ........ r45654 | greg.ward | 2006-04-23 05:47:58 +0200 (Sun, 23 Apr 2006) | 2 lines Update optparse to Optik 1.5.1. ........ r45658 | george.yoshida | 2006-04-23 11:27:10 +0200 (Sun, 23 Apr 2006) | 2 lines wrap SyntaxError with \exception{} ........ r45660 | ronald.oussoren | 2006-04-23 13:59:25 +0200 (Sun, 23 Apr 2006) | 6 lines Patch 1471925 - Weak linking support for OSX This patch causes several symbols in the socket and posix module to be weakly linked on OSX and disables usage of ftime on OSX. These changes make it possible to use a binary build on OSX 10.4 on a 10.3 system. ........ r45661 | ronald.oussoren | 2006-04-23 14:36:23 +0200 (Sun, 23 Apr 2006) | 5 lines Patch 1471761 - test for broken poll at runtime This patch checks if poll is broken when the select module is loaded instead of doing so at configure-time. This functionality is only active on Mac OS X. ........ r45662 | nick.coghlan | 2006-04-23 17:13:32 +0200 (Sun, 23 Apr 2006) | 1 line Add a Context Types section to parallel the Iterator Types section (uses the same terminology as the 2.5a1 implementation) ........ r45663 | nick.coghlan | 2006-04-23 17:14:37 +0200 (Sun, 23 Apr 2006) | 1 line Update contextlib documentation to use the same terminology as the module implementation ........ r45664 | gerhard.haering | 2006-04-23 17:24:26 +0200 (Sun, 23 Apr 2006) | 2 lines Updated the sqlite3 module to the external pysqlite 2.2.2 version. ........ r45666 | nick.coghlan | 2006-04-23 17:39:16 +0200 (Sun, 23 Apr 2006) | 1 line Update with statement documentation to use same terminology as 2.5a1 implementation ........ r45667 | nick.coghlan | 2006-04-23 18:05:04 +0200 (Sun, 23 Apr 2006) | 1 line Add a (very) brief mention of the with statement to the end of chapter 8 ........ r45668 | nick.coghlan | 2006-04-23 18:35:19 +0200 (Sun, 23 Apr 2006) | 1 line Take 2 on mentioning the with statement, this time without inadvertently killing the Unicode examples ........ r45669 | nick.coghlan | 2006-04-23 19:04:07 +0200 (Sun, 23 Apr 2006) | 1 line Backdated NEWS entry to record the implementation of PEP 338 for alpha 1 ........ r45670 | tim.peters | 2006-04-23 20:13:45 +0200 (Sun, 23 Apr 2006) | 2 lines Whitespace normalization. ........ r45671 | skip.montanaro | 2006-04-23 21:14:27 +0200 (Sun, 23 Apr 2006) | 1 line first cut at trace module doc ........ r45672 | skip.montanaro | 2006-04-23 21:26:33 +0200 (Sun, 23 Apr 2006) | 1 line minor tweak ........ r45673 | skip.montanaro | 2006-04-23 21:30:50 +0200 (Sun, 23 Apr 2006) | 1 line it's always helpful if the example works... ........ r45674 | skip.montanaro | 2006-04-23 21:32:14 +0200 (Sun, 23 Apr 2006) | 1 line correct example ........ r45675 | andrew.kuchling | 2006-04-23 23:01:04 +0200 (Sun, 23 Apr 2006) | 1 line Edits to the PEP 343 section ........ r45676 | andrew.kuchling | 2006-04-23 23:51:10 +0200 (Sun, 23 Apr 2006) | 1 line Add two items ........ r45677 | tim.peters | 2006-04-24 04:03:16 +0200 (Mon, 24 Apr 2006) | 5 lines Bug #1337990: clarified that `doctest` does not support examples requiring both expected output and an exception. I'll backport to 2.4 next. ........ r45679 | nick.coghlan | 2006-04-24 05:04:43 +0200 (Mon, 24 Apr 2006) | 1 line Note changes made to PEP 343 related documentation ........ r45681 | nick.coghlan | 2006-04-24 06:17:02 +0200 (Mon, 24 Apr 2006) | 1 line Change PEP 343 related documentation to use the term context specifier instead of context object ........ r45682 | nick.coghlan | 2006-04-24 06:32:47 +0200 (Mon, 24 Apr 2006) | 1 line Add unit tests for the -m and -c command line switches ........ r45683 | nick.coghlan | 2006-04-24 06:37:15 +0200 (Mon, 24 Apr 2006) | 1 line Fix contextlib.nested to cope with exit methods raising and handling exceptions ........ r45685 | nick.coghlan | 2006-04-24 06:59:28 +0200 (Mon, 24 Apr 2006) | 1 line Fix broken contextlib test from last checkin (I'd've sworn I tested that before checking it in. . .) ........ r45686 | nick.coghlan | 2006-04-24 07:24:26 +0200 (Mon, 24 Apr 2006) | 1 line Back out new command line tests (broke buildbot) ........ r45687 | nick.coghlan | 2006-04-24 07:52:15 +0200 (Mon, 24 Apr 2006) | 1 line More reliable version of new command line tests that just checks the exit codes ........ r45688 | thomas.wouters | 2006-04-24 13:37:13 +0200 (Mon, 24 Apr 2006) | 4 lines Stop test_tcl's testLoadTk from leaking the Tk commands 'loadtk' registers. ........ r45690 | andrew.kuchling | 2006-04-24 16:30:47 +0200 (Mon, 24 Apr 2006) | 2 lines Edits, using the new term 'context specifier' in a few places ........ r45697 | phillip.eby | 2006-04-24 22:53:13 +0200 (Mon, 24 Apr 2006) | 2 lines Revert addition of setuptools ........ r45698 | tim.peters | 2006-04-25 00:45:13 +0200 (Tue, 25 Apr 2006) | 2 lines Whitespace normalization. ........ r45700 | trent.mick | 2006-04-25 02:34:50 +0200 (Tue, 25 Apr 2006) | 4 lines Put break at correct level so *all* root HKEYs acutally get checked for an installed VC6. Otherwise only the first such tree gets checked and this warning doesn't get displayed. ........ r45701 | tim.peters | 2006-04-25 05:31:36 +0200 (Tue, 25 Apr 2006) | 3 lines Patch #1475231: add a new SKIP doctest option, thanks to Edward Loper. ........ r45702 | neal.norwitz | 2006-04-25 07:04:35 +0200 (Tue, 25 Apr 2006) | 1 line versionadded for SKIP ........ r45703 | neal.norwitz | 2006-04-25 07:05:03 +0200 (Tue, 25 Apr 2006) | 1 line Restore Walters name ........ r45704 | neal.norwitz | 2006-04-25 07:49:42 +0200 (Tue, 25 Apr 2006) | 1 line Revert previous change, SKIP had a versionadded elsewhere ........ r45706 | nick.coghlan | 2006-04-25 12:56:51 +0200 (Tue, 25 Apr 2006) | 31 lines Move the PEP 343 documentation and implementation closer to the terminology in the alpha 1 documentation. - "context manager" reverts to its alpha 1 definition - the term "context specifier" goes away entirely - contextlib.GeneratorContextManager is renamed GeneratorContext There are still a number of changes relative to alpha 1: - the expression in the with statement is explicitly called the "context expression" in the language reference - the terms 'with statement context', 'context object' or 'with statement context' are used in several places instead of a bare 'context'. The aim of this is to avoid ambiguity in relation to the runtime context set up when the block is executed, and the context objects that already exist in various application domains (such as decimal.Context) - contextlib.contextmanager is renamed to contextfactory This best reflects the nature of the function resulting from the use of that decorator - decimal.ContextManager is renamed to WithStatementContext Simple dropping the 'Manager' part wasn't possible due to the fact that decimal.Context already exists and means something different. WithStatementContext is ugly but workable. A technically unrelated change snuck into this commit: contextlib.closing now avoids the overhead of creating a generator, since it's trivial to implement that particular context manager directly. ........ r45707 | nick.coghlan | 2006-04-25 13:05:56 +0200 (Tue, 25 Apr 2006) | 1 line Fix latex typo ........ r45708 | thomas.wouters | 2006-04-25 14:28:56 +0200 (Tue, 25 Apr 2006) | 4 lines Fix markup glitch in unittest docs. Will backport. ........ r45710 | andrew.kuchling | 2006-04-25 14:31:38 +0200 (Tue, 25 Apr 2006) | 1 line Add two items; easy_install is now off the table, though pkgutil still is ........ r45711 | andrew.kuchling | 2006-04-25 14:47:25 +0200 (Tue, 25 Apr 2006) | 1 line Rework context terminology ........ r45712 | thomas.wouters | 2006-04-25 15:53:23 +0200 (Tue, 25 Apr 2006) | 9 lines SF bug/patch #1433877: string parameter to ioctl not null terminated The new char-array used in ioctl calls wasn't explicitly NUL-terminated; quite probably the cause for the test_pty failures on Solaris that we circumvented earlier. (I wasn't able to reproduce it with this patch, but it has been somewhat elusive to start with.) ........ r45713 | george.yoshida | 2006-04-25 16:09:58 +0200 (Tue, 25 Apr 2006) | 2 lines minor tweak ........ r45714 | thomas.wouters | 2006-04-25 17:08:10 +0200 (Tue, 25 Apr 2006) | 7 lines Fix SF bug #1476111: SystemError in socket sendto. The AF_INET6 and AF_PACKET cases in getsockaddrarg were missing their own checks for tuple-ness of the address argument, which means a confusing SystemError was raised by PyArg_ParseTuple instead. ........ r45715 | thomas.wouters | 2006-04-25 17:29:46 +0200 (Tue, 25 Apr 2006) | 10 lines Define MAXPATHLEN to be at least PATH_MAX, if that's defined. Python uses MAXPATHLEN-sized buffers for various output-buffers (like to realpath()), and that's correct on BSD platforms, but not Linux (which uses PATH_MAX, and does not define MAXPATHLEN.) Cursory googling suggests Linux is following a newer standard than BSD, but in cases like this, who knows. Using the greater of PATH_MAX and 1024 as a fallback for MAXPATHLEN seems to be the most portable solution. ........ r45717 | thomas.heller | 2006-04-25 20:26:08 +0200 (Tue, 25 Apr 2006) | 3 lines Fix compiler warnings on Darwin. Patch by Brett Canon, see https://sourceforge.net/tracker/?func=detail&atid=532156&aid=1475959&group_id=71702 ........ r45718 | guido.van.rossum | 2006-04-25 22:12:45 +0200 (Tue, 25 Apr 2006) | 4 lines Implement MvL's improvement on __context__ in Condition; this can just call __context__ on the underlying lock. (The same change for Semaphore does *not* work!) ........ r45721 | tim.peters | 2006-04-26 03:15:53 +0200 (Wed, 26 Apr 2006) | 13 lines Rev 45706 renamed stuff in contextlib.py, but didn't rename uses of it in test_with.py. As a result, test_with has been skipped (due to failing imports) on all buildbot boxes since. Alas, that's not a test failure -- you have to pay attention to the 1 skip unexpected on PLATFORM: test_with kinds of output at the ends of test runs to notice that this got broken. It's likely that more renaming in test_with.py would be desirable. ........ r45722 | fred.drake | 2006-04-26 07:15:41 +0200 (Wed, 26 Apr 2006) | 1 line markup fixes, cleanup ........ r45723 | fred.drake | 2006-04-26 07:19:39 +0200 (Wed, 26 Apr 2006) | 1 line minor adjustment suggested by Peter Gephardt ........ r45724 | neal.norwitz | 2006-04-26 07:34:03 +0200 (Wed, 26 Apr 2006) | 10 lines Patch from Aldo Cortesi (OpenBSD buildbot owner). After the patch (45590) to add extra debug stats to the gc module, Python was crashing on OpenBSD due to: Fatal Python error: Interpreter not initialized (version mismatch?) This seems to occur due to calling collect() when initialized (in pythonrun.c) is set to 0. Now, the import will occur in the init function which shouldn't suffer this problem. ........ r45725 | neal.norwitz | 2006-04-26 08:26:12 +0200 (Wed, 26 Apr 2006) | 3 lines Fix this test on Solaris. There can be embedded \r, so don't just replace the one at the end. ........ r45727 | nick.coghlan | 2006-04-26 13:50:04 +0200 (Wed, 26 Apr 2006) | 1 line Fix an error in the last contextlib.closing example ........ r45728 | andrew.kuchling | 2006-04-26 14:21:06 +0200 (Wed, 26 Apr 2006) | 1 line [Bug #1475080] Fix example ........ r45729 | andrew.kuchling | 2006-04-26 14:23:39 +0200 (Wed, 26 Apr 2006) | 1 line Add labels to all sections ........ r45730 | thomas.wouters | 2006-04-26 17:53:30 +0200 (Wed, 26 Apr 2006) | 7 lines The result of SF patch #1471578: big-memory tests for strings, lists and tuples. Lots to be added, still, but this will give big-memory people something to play with in 2.5 alpha 2, and hopefully get more people to write these tests. ........ r45731 | tim.peters | 2006-04-26 19:11:16 +0200 (Wed, 26 Apr 2006) | 2 lines Whitespace normalization. ........ r45732 | martin.v.loewis | 2006-04-26 19:19:44 +0200 (Wed, 26 Apr 2006) | 1 line Use GS- and bufferoverlowU.lib where appropriate, for AMD64. ........ r45733 | thomas.wouters | 2006-04-26 20:46:01 +0200 (Wed, 26 Apr 2006) | 5 lines Add tests for += and *= on strings, and fix the memory-use estimate for the list.extend tests (they were estimating half the actual use.) ........ r45734 | thomas.wouters | 2006-04-26 21:14:46 +0200 (Wed, 26 Apr 2006) | 5 lines Some more test-size-estimate fixes: test_append and test_insert trigger a list resize, which overallocates. ........ r45735 | hyeshik.chang | 2006-04-26 21:20:26 +0200 (Wed, 26 Apr 2006) | 3 lines Fix build on MIPS for libffi. I haven't tested this yet because I don't have an access on MIPS machines. Will be tested by buildbot. :) ........ r45737 | fred.drake | 2006-04-27 01:40:32 +0200 (Thu, 27 Apr 2006) | 1 line one more place to use the current Python version ........ r45738 | fred.drake | 2006-04-27 02:02:24 +0200 (Thu, 27 Apr 2006) | 3 lines - update version numbers in file names again, until we have a better way - elaborate instructions for Cygwin support (closes SF #839709) ........ r45739 | fred.drake | 2006-04-27 02:20:14 +0200 (Thu, 27 Apr 2006) | 1 line add missing word ........ r45740 | anthony.baxter | 2006-04-27 04:11:24 +0200 (Thu, 27 Apr 2006) | 2 lines 2.5a2 ........ r45741 | anthony.baxter | 2006-04-27 04:13:13 +0200 (Thu, 27 Apr 2006) | 1 line 2.5a2 ........ r45749 | andrew.kuchling | 2006-04-27 14:22:37 +0200 (Thu, 27 Apr 2006) | 1 line Now that 2.5a2 is out, revert to the current date ........ r45750 | andrew.kuchling | 2006-04-27 14:23:07 +0200 (Thu, 27 Apr 2006) | 1 line Bump document version ........ r45751 | andrew.kuchling | 2006-04-27 14:34:39 +0200 (Thu, 27 Apr 2006) | 6 lines [Bug #1477102] Add necessary import to example This may be a useful style question for the docs -- should examples show the necessary imports, or should it be assumed that the reader will figure it out? In the What's New, I'm not consistent but usually opt for omitting the imports. ........ r45753 | andrew.kuchling | 2006-04-27 14:38:35 +0200 (Thu, 27 Apr 2006) | 1 line [Bug #1477140] Import Error base class ........ r45754 | andrew.kuchling | 2006-04-27 14:42:54 +0200 (Thu, 27 Apr 2006) | 1 line Mention the xmlrpclib.Error base class, which is used in one of the examples ........ r45756 | george.yoshida | 2006-04-27 15:41:07 +0200 (Thu, 27 Apr 2006) | 2 lines markup fix ........ r45757 | thomas.wouters | 2006-04-27 15:46:59 +0200 (Thu, 27 Apr 2006) | 4 lines Some more size-estimate fixes, for large-list-tests. ........ r45758 | thomas.heller | 2006-04-27 17:50:42 +0200 (Thu, 27 Apr 2006) | 3 lines Rerun the libffi configuration if any of the files used for that are newer then fficonfig.py. ........ r45766 | thomas.wouters | 2006-04-28 00:37:50 +0200 (Fri, 28 Apr 2006) | 6 lines Some style fixes and size-calculation fixes. Also do the small-memory run using a prime number, rather than a convenient power-of-2-and-multiple-of-5, so incorrect testing algorithms fail more easily. ........ r45767 | thomas.wouters | 2006-04-28 00:38:32 +0200 (Fri, 28 Apr 2006) | 6 lines Do the small-memory run of big-meormy tests using a prime number, rather than a convenient power-of-2-and-multiple-of-5, so incorrect testing algorithms fail more easily. ........ r45768 | david.goodger | 2006-04-28 00:53:05 +0200 (Fri, 28 Apr 2006) | 1 line Added SVN access for Steven Bethard and Talin, for PEP updating. ........ r45770 | thomas.wouters | 2006-04-28 01:13:20 +0200 (Fri, 28 Apr 2006) | 16 lines - Add new Warning class, ImportWarning - Warn-raise ImportWarning when importing would have picked up a directory as package, if only it'd had an __init__.py. This swaps two tests (for case-ness and __init__-ness), but case-test is not really more expensive, and it's not in a speed-critical section. - Test for the new warning by importing a common non-package directory on sys.path: site-packages - In regrtest.py, silence warnings generated by the build-environment because Modules/ (which is added to sys.path for Setup-created modules) has 'zlib' and '_ctypes' directories without __init__.py's. ........ r45771 | thomas.wouters | 2006-04-28 01:41:27 +0200 (Fri, 28 Apr 2006) | 6 lines Add more ignores of ImportWarnings; these are all just potential triggers (since they won't trigger if zlib is already sucessfully imported); they were found by grepping .py files, instead of looking at warning output :) ........ r45773 | neal.norwitz | 2006-04-28 06:32:20 +0200 (Fri, 28 Apr 2006) | 1 line Add some whitespace to be more consistent. ........ r45774 | neal.norwitz | 2006-04-28 06:34:43 +0200 (Fri, 28 Apr 2006) | 5 lines Try to really fix the slow buildbots this time. Printing to stdout, doesn't mean the data was actually written. It depends on the buffering, so we need to flush. This will hopefully really fix the buildbots getting killed due to no output on the slow bots. ........ r45775 | neal.norwitz | 2006-04-28 07:28:05 +0200 (Fri, 28 Apr 2006) | 1 line Fix some warnings on Mac OS X 10.4 ........ r45776 | neal.norwitz | 2006-04-28 07:28:30 +0200 (Fri, 28 Apr 2006) | 1 line Fix a warning on alpha ........ r45777 | neal.norwitz | 2006-04-28 07:28:54 +0200 (Fri, 28 Apr 2006) | 1 line Fix a warning on ppc (debian) ........ r45778 | george.yoshida | 2006-04-28 18:09:45 +0200 (Fri, 28 Apr 2006) | 2 lines fix markup glitch ........ r45780 | georg.brandl | 2006-04-28 18:31:17 +0200 (Fri, 28 Apr 2006) | 3 lines Add SeaMonkey to the list of Mozilla browsers. ........ r45781 | georg.brandl | 2006-04-28 18:36:55 +0200 (Fri, 28 Apr 2006) | 2 lines Bug #1475009: clarify ntpath.join behavior with absolute components ........ r45783 | george.yoshida | 2006-04-28 18:40:14 +0200 (Fri, 28 Apr 2006) | 2 lines correct a dead link ........ r45785 | georg.brandl | 2006-04-28 18:54:25 +0200 (Fri, 28 Apr 2006) | 4 lines Bug #1472949: stringify IOErrors in shutil.copytree when appending them to the Error errors list. ........ r45786 | georg.brandl | 2006-04-28 18:58:52 +0200 (Fri, 28 Apr 2006) | 3 lines Bug #1478326: don't allow '/' in distutils.util.get_platform machine names since this value is used to name the build directory. ........ r45788 | thomas.heller | 2006-04-28 19:02:18 +0200 (Fri, 28 Apr 2006) | 1 line Remove a duplicated test (the same test is in test_incomplete.py). ........ r45792 | georg.brandl | 2006-04-28 21:09:24 +0200 (Fri, 28 Apr 2006) | 3 lines Bug #1478429: make datetime.datetime.fromtimestamp accept every float, possibly "rounding up" to the next whole second. ........ r45796 | george.yoshida | 2006-04-29 04:43:30 +0200 (Sat, 29 Apr 2006) | 2 lines grammar fix ........ r45800 | ronald.oussoren | 2006-04-29 13:31:35 +0200 (Sat, 29 Apr 2006) | 2 lines Patch 1471883: --enable-universalsdk on Mac OS X ........ r45801 | andrew.kuchling | 2006-04-29 13:53:15 +0200 (Sat, 29 Apr 2006) | 1 line Add item ........ r45802 | andrew.kuchling | 2006-04-29 14:10:28 +0200 (Sat, 29 Apr 2006) | 1 line Make case of 'ZIP' consistent ........ r45803 | andrew.kuchling | 2006-04-29 14:10:43 +0200 (Sat, 29 Apr 2006) | 1 line Add item ........ r45808 | martin.v.loewis | 2006-04-29 14:37:25 +0200 (Sat, 29 Apr 2006) | 3 lines Further changes for #1471883: Edit Misc/NEWS, and add expat_config.h. ........ r45809 | brett.cannon | 2006-04-29 23:29:50 +0200 (Sat, 29 Apr 2006) | 2 lines Fix docstring for contextfactory; mentioned old contextmanager name. ........ r45810 | gerhard.haering | 2006-04-30 01:12:41 +0200 (Sun, 30 Apr 2006) | 3 lines This is the start of documentation for the sqlite3 module. Please feel free to find a better place for the link to it than alongside bsddb & friends. ........ r45811 | andrew.kuchling | 2006-04-30 03:07:09 +0200 (Sun, 30 Apr 2006) | 1 line Add two items ........ r45814 | george.yoshida | 2006-04-30 05:49:56 +0200 (Sun, 30 Apr 2006) | 2 lines Use \versionchanged instead of \versionadded for new parameter support. ........ r45815 | georg.brandl | 2006-04-30 09:06:11 +0200 (Sun, 30 Apr 2006) | 2 lines Patch #1470846: fix urllib2 ProxyBasicAuthHandler. ........ r45817 | georg.brandl | 2006-04-30 10:57:35 +0200 (Sun, 30 Apr 2006) | 3 lines In stdlib, use hashlib instead of deprecated md5 and sha modules. ........ r45819 | georg.brandl | 2006-04-30 11:23:59 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1470976: don't NLST files when retrieving over FTP. ........ r45821 | georg.brandl | 2006-04-30 13:13:56 +0200 (Sun, 30 Apr 2006) | 6 lines Bug #1473625: stop cPickle making float dumps locale dependent in protocol 0. On the way, add a decorator to test_support to facilitate running single test functions in different locales with automatic cleanup. ........ r45822 | phillip.eby | 2006-04-30 17:59:26 +0200 (Sun, 30 Apr 2006) | 2 lines Fix infinite regress when inspecting or frames. ........ r45824 | georg.brandl | 2006-04-30 19:42:26 +0200 (Sun, 30 Apr 2006) | 3 lines Fix another problem in inspect: if the module for an object cannot be found, don't try to give its __dict__ to linecache. ........ r45825 | georg.brandl | 2006-04-30 20:14:54 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1472854: make the rlcompleter.Completer class usable on non- UNIX platforms. ........ r45826 | georg.brandl | 2006-04-30 21:34:19 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1479438: add \keyword markup for "with". ........ r45827 | andrew.kuchling | 2006-04-30 23:19:31 +0200 (Sun, 30 Apr 2006) | 1 line Add urllib2 HOWTO from Michael Foord ........ r45828 | andrew.kuchling | 2006-04-30 23:19:49 +0200 (Sun, 30 Apr 2006) | 1 line Add item ........ r45830 | barry.warsaw | 2006-05-01 05:03:02 +0200 (Mon, 01 May 2006) | 11 lines Port forward from 2.4 branch: Patch #1464708 from William McVey: fixed handling of nested comments in mail addresses. E.g. "Foo ((Foo Bar)) " Fixes for both rfc822.py and email package. This patch needs to be back ported to Python 2.3 for email 2.5. ........ r45832 | fred.drake | 2006-05-01 08:25:58 +0200 (Mon, 01 May 2006) | 4 lines - minor clarification in section title - markup adjustments (there is clearly much to be done in this section) ........ r45833 | martin.v.loewis | 2006-05-01 08:28:01 +0200 (Mon, 01 May 2006) | 2 lines Work around deadlock risk. Will backport. ........ r45836 | andrew.kuchling | 2006-05-01 14:45:02 +0200 (Mon, 01 May 2006) | 1 line Some ElementTree fixes: import from xml, not xmlcore; fix case of module name; mention list() instead of getchildren() ........ r45837 | gerhard.haering | 2006-05-01 17:14:48 +0200 (Mon, 01 May 2006) | 3 lines Further integration of the documentation for the sqlite3 module. There's still quite some content to move over from the pysqlite manual, but it's a start now. ........ r45838 | martin.v.loewis | 2006-05-01 17:56:03 +0200 (Mon, 01 May 2006) | 2 lines Rename uisample to text, drop all non-text tables. ........ r45839 | martin.v.loewis | 2006-05-01 18:12:44 +0200 (Mon, 01 May 2006) | 2 lines Add msilib documentation. ........ r45840 | martin.v.loewis | 2006-05-01 18:14:16 +0200 (Mon, 01 May 2006) | 4 lines Rename parameters to match the documentation (which in turn matches Microsoft's documentation). Drop unused parameter in CAB.append. ........ r45841 | fred.drake | 2006-05-01 18:28:54 +0200 (Mon, 01 May 2006) | 1 line add dependency ........ r45842 | andrew.kuchling | 2006-05-01 18:30:25 +0200 (Mon, 01 May 2006) | 1 line Markup fixes; add some XXX comments noting problems ........ r45843 | andrew.kuchling | 2006-05-01 18:32:49 +0200 (Mon, 01 May 2006) | 1 line Add item ........ r45844 | andrew.kuchling | 2006-05-01 19:06:54 +0200 (Mon, 01 May 2006) | 1 line Markup fixes ........ r45850 | neal.norwitz | 2006-05-02 06:43:14 +0200 (Tue, 02 May 2006) | 3 lines SF #1479181: split open() and file() from being aliases for each other. ........ r45852 | neal.norwitz | 2006-05-02 08:23:22 +0200 (Tue, 02 May 2006) | 1 line Try to fix breakage caused by patch #1479181, r45850 ........ r45853 | fred.drake | 2006-05-02 08:53:59 +0200 (Tue, 02 May 2006) | 3 lines SF #1479988: add methods to allow access to weakrefs for the weakref.WeakKeyDictionary and weakref.WeakValueDictionary ........ r45854 | neal.norwitz | 2006-05-02 09:27:47 +0200 (Tue, 02 May 2006) | 5 lines Fix breakage from patch 1471883 (r45800 & r45808) on OSF/1. The problem was that pyconfig.h was being included before some system headers which caused redefinitions and other breakage. This moves system headers after expat_config.h which includes pyconfig.h. ........ r45855 | vinay.sajip | 2006-05-02 10:35:36 +0200 (Tue, 02 May 2006) | 1 line Replaced my dumb way of calculating seconds to midnight with Tim Peters' much more sensible suggestion. What was I thinking ?!? ........ r45856 | andrew.kuchling | 2006-05-02 13:30:03 +0200 (Tue, 02 May 2006) | 1 line Provide encoding as keyword argument; soften warning paragraph about encodings ........ r45858 | guido.van.rossum | 2006-05-02 19:36:09 +0200 (Tue, 02 May 2006) | 2 lines Fix the formatting of KeyboardInterrupt -- a bad issubclass() call. ........ r45862 | guido.van.rossum | 2006-05-02 21:47:52 +0200 (Tue, 02 May 2006) | 7 lines Get rid of __context__, per the latest changes to PEP 343 and python-dev discussion. There are two places of documentation that still mention __context__: Doc/lib/libstdtypes.tex -- I wasn't quite sure how to rewrite that without spending a whole lot of time thinking about it; and whatsnew, which Andrew usually likes to change himself. ........ r45863 | armin.rigo | 2006-05-02 21:52:32 +0200 (Tue, 02 May 2006) | 4 lines Documentation bug: PySet_Pop() returns a new reference (because the caller becomes the owner of that reference). ........ r45864 | guido.van.rossum | 2006-05-02 22:47:36 +0200 (Tue, 02 May 2006) | 4 lines Hopefully this will fix the spurious failures of test_mailbox.py that I'm experiencing. (This code and mailbox.py itself are full of calls to file() that should be calls to open() -- but I'm not fixing those.) ........ r45865 | andrew.kuchling | 2006-05-02 23:44:33 +0200 (Tue, 02 May 2006) | 1 line Use open() instead of file() ........ r45866 | andrew.kuchling | 2006-05-03 00:47:49 +0200 (Wed, 03 May 2006) | 1 line Update context manager section for removal of __context__ ........ r45867 | fred.drake | 2006-05-03 03:46:52 +0200 (Wed, 03 May 2006) | 1 line remove unnecessary assignment ........ r45868 | fred.drake | 2006-05-03 03:48:24 +0200 (Wed, 03 May 2006) | 4 lines tell LaTeX2HTML to: - use UTF-8 output - not mess with the >>> prompt! ........ r45869 | fred.drake | 2006-05-03 04:04:40 +0200 (Wed, 03 May 2006) | 3 lines avoid ugly markup based on the unfortunate conversions of ">>" and "<<" to guillemets; no need for magic here ........ r45870 | fred.drake | 2006-05-03 04:12:47 +0200 (Wed, 03 May 2006) | 1 line at least comment on why curly-quotes are not enabled ........ r45871 | fred.drake | 2006-05-03 04:27:40 +0200 (Wed, 03 May 2006) | 1 line one more place to avoid extra markup ........ r45872 | fred.drake | 2006-05-03 04:29:09 +0200 (Wed, 03 May 2006) | 1 line one more place to avoid extra markup (how many will there be?) ........ r45873 | fred.drake | 2006-05-03 04:29:39 +0200 (Wed, 03 May 2006) | 1 line fix up whitespace in prompt strings ........ r45876 | tim.peters | 2006-05-03 06:46:14 +0200 (Wed, 03 May 2006) | 2 lines Whitespace normalization. ........ r45877 | martin.v.loewis | 2006-05-03 06:52:04 +0200 (Wed, 03 May 2006) | 2 lines Correct some formulations, fix XXX comments. ........ r45879 | georg.brandl | 2006-05-03 07:05:02 +0200 (Wed, 03 May 2006) | 2 lines Patch #1480067: don't redirect HTTP digest auth in urllib2 ........ r45881 | georg.brandl | 2006-05-03 07:15:10 +0200 (Wed, 03 May 2006) | 3 lines Move network tests from test_urllib2 to test_urllib2net. ........ r45887 | nick.coghlan | 2006-05-03 15:02:47 +0200 (Wed, 03 May 2006) | 1 line Finish bringing SVN into line with latest version of PEP 343 by getting rid of all remaining references to context objects that I could find. Without a __context__() method context objects no longer exist. Also get test_with working again, and adopt a suggestion from Neal for decimal.Context.get_manager() ........ r45888 | nick.coghlan | 2006-05-03 15:17:49 +0200 (Wed, 03 May 2006) | 1 line Get rid of a couple more context object references, fix some markup and clarify what happens when a generator context function swallows an exception. ........ r45889 | georg.brandl | 2006-05-03 19:46:13 +0200 (Wed, 03 May 2006) | 3 lines Add seamonkey to list of Windows browsers too. ........ r45890 | georg.brandl | 2006-05-03 20:03:22 +0200 (Wed, 03 May 2006) | 3 lines RFE #1472176: In httplib, don't encode the netloc and hostname with "idna" if not necessary. ........ r45891 | georg.brandl | 2006-05-03 20:12:33 +0200 (Wed, 03 May 2006) | 2 lines Bug #1472191: convert breakpoint indices to ints before comparing them to ints ........ r45893 | georg.brandl | 2006-05-03 20:18:32 +0200 (Wed, 03 May 2006) | 3 lines Bug #1385040: don't allow "def foo(a=1, b): pass" in the compiler package. ........ r45894 | thomas.heller | 2006-05-03 20:35:39 +0200 (Wed, 03 May 2006) | 1 line Don't fail the tests when libglut.so or libgle.so cannot be loaded. ........ r45895 | georg.brandl | 2006-05-04 07:08:10 +0200 (Thu, 04 May 2006) | 2 lines Bug #1481530: allow "from os.path import ..." with imputil ........ r45897 | martin.v.loewis | 2006-05-04 07:51:03 +0200 (Thu, 04 May 2006) | 2 lines Patch #1475845: Raise IndentationError for unexpected indent. ........ r45898 | martin.v.loewis | 2006-05-04 12:08:42 +0200 (Thu, 04 May 2006) | 1 line Implement os.{chdir,rename,rmdir,remove} using Win32 directly. ........ r45899 | martin.v.loewis | 2006-05-04 14:04:27 +0200 (Thu, 04 May 2006) | 2 lines Drop now-unnecessary arguments to posix_2str. ........ r45900 | martin.v.loewis | 2006-05-04 16:27:52 +0200 (Thu, 04 May 2006) | 1 line Update checks to consider Windows error numbers. ........ r45913 | thomas.heller | 2006-05-05 20:42:14 +0200 (Fri, 05 May 2006) | 2 lines Export the 'free' standard C function for use in the test suite. ........ r45914 | thomas.heller | 2006-05-05 20:43:24 +0200 (Fri, 05 May 2006) | 3 lines Fix memory leaks in the ctypes test suite, reported by valgrind, by free()ing the memory we allocate. ........ r45915 | thomas.heller | 2006-05-05 20:46:27 +0200 (Fri, 05 May 2006) | 1 line oops - the function is exported as 'my_free', not 'free'. ........ r45916 | thomas.heller | 2006-05-05 21:14:24 +0200 (Fri, 05 May 2006) | 2 lines Clean up. ........ r45920 | george.yoshida | 2006-05-06 15:09:45 +0200 (Sat, 06 May 2006) | 2 lines describe optional arguments for DocFileSuite ........ r45924 | george.yoshida | 2006-05-06 16:16:51 +0200 (Sat, 06 May 2006) | 2 lines Use \versionchanged for the feature change ........ r45925 | martin.v.loewis | 2006-05-06 18:32:54 +0200 (Sat, 06 May 2006) | 1 line Port access, chmod, parts of getcwdu, mkdir, and utime to direct Win32 API. ........ r45926 | martin.v.loewis | 2006-05-06 22:04:08 +0200 (Sat, 06 May 2006) | 2 lines Handle ERROR_ALREADY_EXISTS. ........ r45931 | andrew.kuchling | 2006-05-07 19:12:12 +0200 (Sun, 07 May 2006) | 1 line [Patch #1479977] Revised version of urllib2 HOWTO, edited by John J. Lee ........ r45932 | andrew.kuchling | 2006-05-07 19:14:53 +0200 (Sun, 07 May 2006) | 1 line Minor language edit ........ r45934 | georg.brandl | 2006-05-07 22:44:34 +0200 (Sun, 07 May 2006) | 3 lines Patch #1483395: add new TLDs to cookielib ........ r45936 | martin.v.loewis | 2006-05-08 07:25:56 +0200 (Mon, 08 May 2006) | 2 lines Add missing PyMem_Free. ........ r45938 | georg.brandl | 2006-05-08 19:28:47 +0200 (Mon, 08 May 2006) | 3 lines Add test for rev. 45934. ........ r45939 | georg.brandl | 2006-05-08 19:36:08 +0200 (Mon, 08 May 2006) | 3 lines Patch #1479302: Make urllib2 digest auth and basic auth play together. ........ r45940 | georg.brandl | 2006-05-08 19:48:01 +0200 (Mon, 08 May 2006) | 3 lines Patch #1478993: take advantage of BaseException/Exception split in cookielib ........ r45941 | neal.norwitz | 2006-05-09 07:38:56 +0200 (Tue, 09 May 2006) | 5 lines Micro optimization. In the first case, we know that frame->f_exc_type is NULL, so there's no reason to do anything with it. In the second case, we know frame->f_exc_type is not NULL, so we can just do an INCREF. ........ r45943 | thomas.heller | 2006-05-09 22:20:15 +0200 (Tue, 09 May 2006) | 2 lines Disable a test that is unreliable. ........ r45944 | tim.peters | 2006-05-10 04:43:01 +0200 (Wed, 10 May 2006) | 4 lines Variant of patch #1478292. doctest.register_optionflag(name) shouldn't create a new flag when `name` is already the name of an option flag. ........ r45947 | neal.norwitz | 2006-05-10 08:57:58 +0200 (Wed, 10 May 2006) | 14 lines Fix problems found by Coverity. longobject.c: also fix an ssize_t problem could have been NULL, so hoist the size calc to not use . _ssl.c: under fail: self is DECREF'd, but it would have been NULL. _elementtree.c: delete self if there was an error. _csv.c: I'm not sure if lineterminator could have been anything other than a string. However, other string method calls are checked, so check this one too. ........ r45948 | thomas.wouters | 2006-05-10 17:04:11 +0200 (Wed, 10 May 2006) | 4 lines Ignore reflog.txt, too. ........ r45949 | georg.brandl | 2006-05-10 17:59:06 +0200 (Wed, 10 May 2006) | 3 lines Bug #1482988: indicate more prominently that the Stats class is in the pstats module. ........ r45950 | georg.brandl | 2006-05-10 18:09:03 +0200 (Wed, 10 May 2006) | 2 lines Bug #1485447: subprocess: document that the "cwd" parameter isn't used to find the executable. Misc. other markup fixes. ........ r45952 | georg.brandl | 2006-05-10 18:11:44 +0200 (Wed, 10 May 2006) | 2 lines Bug #1484978: curses.panel: clarify that Panel objects are destroyed on garbage collection. ........ r45954 | georg.brandl | 2006-05-10 18:26:03 +0200 (Wed, 10 May 2006) | 4 lines Patch #1484695: Update the tarfile module to version 0.8. This fixes a couple of issues, notably handling of long file names using the GNU LONGNAME extension. ........ r45955 | georg.brandl | 2006-05-10 19:13:20 +0200 (Wed, 10 May 2006) | 4 lines Patch #721464: pdb.Pdb instances can now be given explicit stdin and stdout arguments, making it possible to redirect input and output for remote debugging. ........ r45956 | andrew.kuchling | 2006-05-10 19:19:04 +0200 (Wed, 10 May 2006) | 1 line Clarify description of exception handling ........ r45957 | georg.brandl | 2006-05-10 22:09:23 +0200 (Wed, 10 May 2006) | 2 lines Fix two small errors in argument lists. ........ r45960 | brett.cannon | 2006-05-11 07:11:33 +0200 (Thu, 11 May 2006) | 5 lines Detect if %zd is supported by printf() during configure and sets PY_FORMAT_SIZE_T appropriately. Removes warnings on OS X under gcc 4.0.1 when PY_FORMAT_SIZE_T is set to "" instead of "z" as is needed. ........ r45963 | neal.norwitz | 2006-05-11 09:51:59 +0200 (Thu, 11 May 2006) | 1 line Don't mask a no memory error with a less meaningful one as discussed on python-checkins ........ r45964 | martin.v.loewis | 2006-05-11 15:28:43 +0200 (Thu, 11 May 2006) | 3 lines Change WindowsError to carry the Win32 error code in winerror, and the DOS error code in errno. Revert changes where WindowsError catch blocks unnecessarily special-case OSError. ........ r45965 | george.yoshida | 2006-05-11 17:53:27 +0200 (Thu, 11 May 2006) | 2 lines Grammar fix ........ r45967 | andrew.kuchling | 2006-05-11 18:32:24 +0200 (Thu, 11 May 2006) | 1 line typo fix ........ r45968 | tim.peters | 2006-05-11 18:37:42 +0200 (Thu, 11 May 2006) | 5 lines BaseThreadedTestCase.setup(): stop special-casing WindowsError. Rev 45964 fiddled with WindowsError, and broke test_bsddb3 on all the Windows buildbot slaves as a result. This should repair it. ........ r45969 | georg.brandl | 2006-05-11 21:57:09 +0200 (Thu, 11 May 2006) | 2 lines Typo fix. ........ r45970 | tim.peters | 2006-05-12 03:57:59 +0200 (Fri, 12 May 2006) | 5 lines SF patch #1473132: Improve docs for tp_clear and tp_traverse, by Collin Winter. Bugfix candidate (but I'm not going to bother). ........ r45974 | martin.v.loewis | 2006-05-12 14:27:28 +0200 (Fri, 12 May 2006) | 4 lines Dynamically allocate path name buffer for Unicode path name in listdir. Fixes #1431582. Stop overallocating MAX_PATH characters for ANSI path names. Stop assigning to errno. ........ r45975 | martin.v.loewis | 2006-05-12 15:57:36 +0200 (Fri, 12 May 2006) | 1 line Move icon files into DLLs dir. Fixes #1477968. ........ r45976 | george.yoshida | 2006-05-12 18:40:11 +0200 (Fri, 12 May 2006) | 2 lines At first there were 6 steps, but one was removed after that. ........ r45977 | martin.v.loewis | 2006-05-12 19:22:04 +0200 (Fri, 12 May 2006) | 1 line Fix alignment error on Itanium. ........ r45978 | george.yoshida | 2006-05-12 19:25:26 +0200 (Fri, 12 May 2006) | 3 lines Duplicated description about the illegal continue usage can be found in nearly the same place. They are same, so keep the original one and remove the later-added one. ........ r45980 | thomas.heller | 2006-05-12 20:16:03 +0200 (Fri, 12 May 2006) | 2 lines Add missing svn properties. ........ r45981 | thomas.heller | 2006-05-12 20:47:35 +0200 (Fri, 12 May 2006) | 1 line set svn properties ........ r45982 | thomas.heller | 2006-05-12 21:31:46 +0200 (Fri, 12 May 2006) | 1 line add svn:eol-style native svn:keywords Id ........ r45987 | gerhard.haering | 2006-05-13 01:49:49 +0200 (Sat, 13 May 2006) | 3 lines Integrated the rest of the pysqlite reference manual into the Python documentation. Ready to be reviewed and improved upon. ........ r45988 | george.yoshida | 2006-05-13 08:53:31 +0200 (Sat, 13 May 2006) | 2 lines Add \exception markup ........ r45990 | martin.v.loewis | 2006-05-13 15:34:04 +0200 (Sat, 13 May 2006) | 2 lines Revert 43315: Printing of %zd must be signed. ........ r45992 | tim.peters | 2006-05-14 01:28:20 +0200 (Sun, 14 May 2006) | 11 lines Teach PyString_FromFormat, PyErr_Format, and PyString_FromFormatV about "%u", "%lu" and "%zu" formats. Since PyString_FromFormat and PyErr_Format have exactly the same rules (both inherited from PyString_FromFormatV), it would be good if someone with more LaTeX Fu changed one of them to just point to the other. Their docs were way out of synch before this patch, and I just did a mass copy+paste to repair that. Not a backport candidate (this is a new feature). ........ r45993 | tim.peters | 2006-05-14 01:31:05 +0200 (Sun, 14 May 2006) | 2 lines Typo repair. ........ r45994 | tim.peters | 2006-05-14 01:33:19 +0200 (Sun, 14 May 2006) | 2 lines Remove lie in new comment. ........ r45995 | ronald.oussoren | 2006-05-14 21:56:34 +0200 (Sun, 14 May 2006) | 11 lines Rework the build system for osx applications: * Don't use xcodebuild for building PythonLauncher, but use a normal unix makefile. This makes it a lot easier to use the same build flags as for the rest of python (e.g. make a universal version of python launcher) * Convert the mac makefile-s to makefile.in-s and use configure to set makefile variables instead of forwarding them as command-line arguments * Add a C version of pythonw, that we you can use '#!/usr/local/bin/pythonw' * Build IDLE.app using bundlebuilder instead of BuildApplet, that will allow easier modification of the bundle contents later on. ........ r45996 | ronald.oussoren | 2006-05-14 22:35:41 +0200 (Sun, 14 May 2006) | 6 lines A first cut at replacing the icons on MacOS X. This replaces all icons by icons based on the new python.org logo. These are also the first icons that are "proper" OSX icons. These icons were created by Jacob Rus. ........ r45997 | ronald.oussoren | 2006-05-14 23:07:41 +0200 (Sun, 14 May 2006) | 3 lines I missed one small detail in my rewrite of the osx build files: the path to the Python.app template. ........ r45998 | martin.v.loewis | 2006-05-15 07:51:36 +0200 (Mon, 15 May 2006) | 2 lines Fix memory leak. ........ r45999 | neal.norwitz | 2006-05-15 08:48:14 +0200 (Mon, 15 May 2006) | 1 line Move items implemented after a2 into the new a3 section ........ r46000 | neal.norwitz | 2006-05-15 09:04:36 +0200 (Mon, 15 May 2006) | 5 lines - Bug #1487966: Fix SystemError with conditional expression in assignment Most of the test_syntax changes are just updating the numbers. ........ r46001 | neal.norwitz | 2006-05-15 09:17:23 +0200 (Mon, 15 May 2006) | 1 line Patch #1488312, Fix memory alignment problem on SPARC in unicode. Will backport ........ r46003 | martin.v.loewis | 2006-05-15 11:22:27 +0200 (Mon, 15 May 2006) | 3 lines Remove bogus DECREF of self. Change __str__() functions to METH_O. Change WindowsError__str__ to use PyTuple_Pack. ........ r46005 | georg.brandl | 2006-05-15 21:30:35 +0200 (Mon, 15 May 2006) | 3 lines [ 1488881 ] tarfile.py: support for file-objects and bz2 (cp. #1488634) ........ r46007 | tim.peters | 2006-05-15 22:44:10 +0200 (Mon, 15 May 2006) | 9 lines ReadDetectFileobjTest: repair Windows disasters by opening the file object in binary mode. The Windows buildbot slaves shouldn't swap themselves to death anymore. However, test_tarfile may still fail because of a temp directory left behind from a previous failing run. Windows buildbot owners may need to remove that directory by hand. ........ r46009 | tim.peters | 2006-05-15 23:32:25 +0200 (Mon, 15 May 2006) | 3 lines test_directory(): Remove the leftover temp directory that's making the Windows buildbots fail test_tarfile. ........ r46010 | martin.v.loewis | 2006-05-16 09:05:37 +0200 (Tue, 16 May 2006) | 4 lines - Test for sys/statvfs.h before including it, as statvfs is present on some OSX installation, but its header file is not. Will backport to 2.4 ........ r46012 | georg.brandl | 2006-05-16 09:38:27 +0200 (Tue, 16 May 2006) | 3 lines Patch #1435422: zlib's compress and decompress objects now have a copy() method. ........ r46015 | andrew.kuchling | 2006-05-16 18:11:54 +0200 (Tue, 16 May 2006) | 1 line Add item ........ r46016 | andrew.kuchling | 2006-05-16 18:27:31 +0200 (Tue, 16 May 2006) | 3 lines PEP 243 has been withdrawn, so don't refer to it any more. The PyPI upload material has been moved into the section on PEP314. ........ r46017 | george.yoshida | 2006-05-16 19:42:16 +0200 (Tue, 16 May 2006) | 2 lines Update for 'ImportWarning' ........ r46018 | george.yoshida | 2006-05-16 20:07:00 +0200 (Tue, 16 May 2006) | 4 lines Mention that Exception is now a subclass of BaseException. Remove a sentence that says that BaseException inherits from BaseException. (I guess this is just a copy & paste mistake.) ........ r46019 | george.yoshida | 2006-05-16 20:26:10 +0200 (Tue, 16 May 2006) | 2 lines Document ImportWarning ........ r46020 | tim.peters | 2006-05-17 01:22:20 +0200 (Wed, 17 May 2006) | 2 lines Whitespace normalization. ........ r46021 | tim.peters | 2006-05-17 01:24:08 +0200 (Wed, 17 May 2006) | 2 lines Text files missing the SVN eol-style property. ........ r46022 | tim.peters | 2006-05-17 03:30:11 +0200 (Wed, 17 May 2006) | 2 lines PyZlib_copy(), PyZlib_uncopy(): Repair leaks on the normal-case path. ........ r46023 | georg.brandl | 2006-05-17 16:06:07 +0200 (Wed, 17 May 2006) | 3 lines Remove misleading comment about type-class unification. ........ r46024 | georg.brandl | 2006-05-17 16:11:36 +0200 (Wed, 17 May 2006) | 3 lines Apply patch #1489784 from Michael Foord. ........ r46025 | georg.brandl | 2006-05-17 16:18:20 +0200 (Wed, 17 May 2006) | 3 lines Fix typo in os.utime docstring (patch #1490189) ........ r46026 | georg.brandl | 2006-05-17 16:26:50 +0200 (Wed, 17 May 2006) | 3 lines Patch #1490224: set time.altzone correctly on Cygwin. ........ r46027 | georg.brandl | 2006-05-17 16:45:06 +0200 (Wed, 17 May 2006) | 4 lines Add global debug flag to cookielib to avoid heavy dependency on the logging module. Resolves #1484758. ........ r46028 | georg.brandl | 2006-05-17 16:56:04 +0200 (Wed, 17 May 2006) | 3 lines Patch #1486962: Several bugs in the turtle Tk demo module were fixed and several features added, such as speed and geometry control. ........ r46029 | georg.brandl | 2006-05-17 17:17:00 +0200 (Wed, 17 May 2006) | 4 lines Delay-import some large modules to speed up urllib2 import. (fixes #1484793). ........ r46030 | georg.brandl | 2006-05-17 17:51:16 +0200 (Wed, 17 May 2006) | 3 lines Patch #1180296: improve locale string formatting functions ........ r46032 | tim.peters | 2006-05-18 04:06:40 +0200 (Thu, 18 May 2006) | 2 lines Whitespace normalization. ........ r46033 | georg.brandl | 2006-05-18 08:11:19 +0200 (Thu, 18 May 2006) | 3 lines Amendments to patch #1484695. ........ r46034 | georg.brandl | 2006-05-18 08:18:06 +0200 (Thu, 18 May 2006) | 3 lines Remove unused import. ........ r46035 | georg.brandl | 2006-05-18 08:33:27 +0200 (Thu, 18 May 2006) | 3 lines Fix test_locale for platforms without a default thousands separator. ........ r46036 | neal.norwitz | 2006-05-18 08:51:46 +0200 (Thu, 18 May 2006) | 1 line Little cleanup ........ r46037 | georg.brandl | 2006-05-18 09:01:27 +0200 (Thu, 18 May 2006) | 4 lines Bug #1462152: file() now checks more thoroughly for invalid mode strings and removes a possible "U" before passing the mode to the C library function. ........ r46038 | georg.brandl | 2006-05-18 09:20:05 +0200 (Thu, 18 May 2006) | 3 lines Bug #1490688: properly document %e, %f, %g format subtleties. ........ r46039 | vinay.sajip | 2006-05-18 09:28:58 +0200 (Thu, 18 May 2006) | 1 line Changed status from "beta" to "production"; since logging has been part of the stdlib since 2.3, it should be safe to make this assertion ;-) ........ r46040 | ronald.oussoren | 2006-05-18 11:04:15 +0200 (Thu, 18 May 2006) | 2 lines Fix some minor issues with the generated application bundles on MacOSX ........ r46041 | andrew.kuchling | 2006-05-19 02:03:55 +0200 (Fri, 19 May 2006) | 1 line Typo fix; add clarifying word ........ r46044 | neal.norwitz | 2006-05-19 08:31:23 +0200 (Fri, 19 May 2006) | 3 lines Fix #132 from Coverity, retval could have been derefed if a continue inside a try failed. ........ r46045 | neal.norwitz | 2006-05-19 08:43:50 +0200 (Fri, 19 May 2006) | 2 lines Fix #1474677, non-keyword argument following keyword. ........ r46046 | neal.norwitz | 2006-05-19 09:00:58 +0200 (Fri, 19 May 2006) | 4 lines Bug/Patch #1481770: Use .so extension for shared libraries on HP-UX for ia64. I suppose this could be backported if anyone cares. ........ r46047 | neal.norwitz | 2006-05-19 09:05:01 +0200 (Fri, 19 May 2006) | 7 lines Oops, I forgot to include this file in the last commit (46046): Bug/Patch #1481770: Use .so extension for shared libraries on HP-UX for ia64. I suppose this could be backported if anyone cares. ........ r46050 | ronald.oussoren | 2006-05-19 20:17:31 +0200 (Fri, 19 May 2006) | 6 lines * Change working directory to the users home directory, that makes the file open/save dialogs more useable. * Don't use argv emulator, its not needed for idle. ........ r46052 | tim.peters | 2006-05-19 21:16:34 +0200 (Fri, 19 May 2006) | 2 lines Whitespace normalization. ........ r46054 | ronald.oussoren | 2006-05-20 08:17:01 +0200 (Sat, 20 May 2006) | 9 lines Fix bug #1000914 (again). This patches a file that is generated by bgen, however the code is now the same as a current copy of bgen would generate. Without this patch most types in the Carbon.CF module are unusable. I haven't managed to coax bgen into generating a complete copy of _CFmodule.c yet :-(, hence the manual patching. ........ r46055 | george.yoshida | 2006-05-20 17:36:19 +0200 (Sat, 20 May 2006) | 3 lines - markup fix - add clarifying words ........ r46057 | george.yoshida | 2006-05-20 18:29:14 +0200 (Sat, 20 May 2006) | 3 lines - Add 'as' and 'with' as new keywords in 2.5. - Regenerate keyword lists with reswords.py. ........ r46058 | george.yoshida | 2006-05-20 20:07:26 +0200 (Sat, 20 May 2006) | 2 lines Apply patch #1492147 from Mike Foord. ........ r46059 | andrew.kuchling | 2006-05-20 21:25:16 +0200 (Sat, 20 May 2006) | 1 line Minor edits ........ r46061 | george.yoshida | 2006-05-21 06:22:59 +0200 (Sun, 21 May 2006) | 2 lines Fix the TeX compile error. ........ r46062 | george.yoshida | 2006-05-21 06:40:32 +0200 (Sun, 21 May 2006) | 2 lines Apply patch #1492255 from Mike Foord. ........ r46063 | martin.v.loewis | 2006-05-22 10:48:14 +0200 (Mon, 22 May 2006) | 1 line Patch 1490384: New Icons for the PC build. ........ r46064 | martin.v.loewis | 2006-05-22 11:15:18 +0200 (Mon, 22 May 2006) | 1 line Patch #1492356: Port to Windows CE (patch set 1). ........ r46065 | tim.peters | 2006-05-22 13:29:41 +0200 (Mon, 22 May 2006) | 4 lines Define SIZEOF_{DOUBLE,FLOAT} on Windows. Else Michael Hudson's nice gimmicks for IEEE special values (infinities, NaNs) don't work. ........ r46070 | bob.ippolito | 2006-05-22 16:31:24 +0200 (Mon, 22 May 2006) | 2 lines GzipFile.readline performance improvement (~30-40%), patch #1281707 ........ r46071 | bob.ippolito | 2006-05-22 17:22:46 +0200 (Mon, 22 May 2006) | 1 line Revert gzip readline performance patch #1281707 until a more generic performance improvement can be found ........ r46073 | fredrik.lundh | 2006-05-22 17:35:12 +0200 (Mon, 22 May 2006) | 4 lines docstring tweaks: count counts non-overlapping substrings, not total number of occurences ........ r46075 | bob.ippolito | 2006-05-22 17:59:12 +0200 (Mon, 22 May 2006) | 1 line Apply revised patch for GzipFile.readline performance #1281707 ........ r46076 | fredrik.lundh | 2006-05-22 18:29:30 +0200 (Mon, 22 May 2006) | 3 lines needforspeed: speed up unicode repeat, unicode string copy ........ r46079 | fredrik.lundh | 2006-05-22 19:12:58 +0200 (Mon, 22 May 2006) | 4 lines needforspeed: use memcpy for "long" strings; use a better algorithm for long repeats. ........ r46084 | tim.peters | 2006-05-22 21:17:04 +0200 (Mon, 22 May 2006) | 7 lines PyUnicode_Join(): Recent code changes introduced new compiler warnings on Windows (signed vs unsigned mismatch in comparisons). Cleaned that up by switching more locals to Py_ssize_t. Simplified overflow checking (it can _be_ simpler because while these things are declared as Py_ssize_t, then should in fact never be negative). ........ r46085 | tim.peters | 2006-05-23 07:47:16 +0200 (Tue, 23 May 2006) | 3 lines unicode_repeat(): Change type of local to Py_ssize_t, since that's what it should be. ........ r46094 | fredrik.lundh | 2006-05-23 12:10:57 +0200 (Tue, 23 May 2006) | 3 lines needforspeed: check first *and* last character before doing a full memcmp ........ r46095 | fredrik.lundh | 2006-05-23 12:12:21 +0200 (Tue, 23 May 2006) | 4 lines needforspeed: fixed unicode "in" operator to use same implementation approach as find/index ........ r46096 | richard.jones | 2006-05-23 12:37:38 +0200 (Tue, 23 May 2006) | 7 lines Merge from rjones-funccall branch. Applied patch zombie-frames-2.diff from sf patch 876206 with updates for Python 2.5 and also modified to retain the free_list to avoid the 67% slow-down in pybench recursion test. 5% speed up in function call pybench. ........ r46098 | ronald.oussoren | 2006-05-23 13:04:24 +0200 (Tue, 23 May 2006) | 2 lines Avoid creating a mess when installing a framework for the second time. ........ r46101 | georg.brandl | 2006-05-23 13:17:21 +0200 (Tue, 23 May 2006) | 3 lines PyErr_NewException now accepts a tuple of base classes as its "base" parameter. ........ r46103 | ronald.oussoren | 2006-05-23 13:47:16 +0200 (Tue, 23 May 2006) | 3 lines Disable linking extensions with -lpython2.5 for darwin. This should fix bug #1487105. ........ r46104 | ronald.oussoren | 2006-05-23 14:01:11 +0200 (Tue, 23 May 2006) | 6 lines Patch #1488098. This patchs makes it possible to create a universal build on OSX 10.4 and use the result to build extensions on 10.3. It also makes it possible to override the '-arch' and '-isysroot' compiler arguments for specific extensions. ........ r46108 | andrew.kuchling | 2006-05-23 14:44:36 +0200 (Tue, 23 May 2006) | 1 line Add some items; mention the sprint ........ r46109 | andrew.kuchling | 2006-05-23 14:47:01 +0200 (Tue, 23 May 2006) | 1 line Mention string improvements ........ r46110 | andrew.kuchling | 2006-05-23 14:49:35 +0200 (Tue, 23 May 2006) | 4 lines Use 'speed' instead of 'performance', because I agree with the argument at http://zestyping.livejournal.com/193260.html that 'erformance' really means something more general. ........ r46113 | ronald.oussoren | 2006-05-23 17:09:57 +0200 (Tue, 23 May 2006) | 2 lines An improved script for building the binary distribution on MacOSX. ........ r46128 | richard.jones | 2006-05-23 20:28:17 +0200 (Tue, 23 May 2006) | 3 lines Applied patch 1337051 by Neal Norwitz, saving 4 ints on frame objects. ........ r46129 | richard.jones | 2006-05-23 20:32:11 +0200 (Tue, 23 May 2006) | 1 line fix broken merge ........ r46130 | bob.ippolito | 2006-05-23 20:41:17 +0200 (Tue, 23 May 2006) | 1 line Update Misc/NEWS for gzip patch #1281707 ........ r46131 | bob.ippolito | 2006-05-23 20:43:47 +0200 (Tue, 23 May 2006) | 1 line Update Misc/NEWS for gzip patch #1281707 ........ r46132 | fredrik.lundh | 2006-05-23 20:44:25 +0200 (Tue, 23 May 2006) | 7 lines needforspeed: use append+reverse for rsplit, use "bloom filters" to speed up splitlines and strip with charsets; etc. rsplit is now as fast as split in all our tests (reverse takes no time at all), and splitlines() is nearly as fast as a plain split("\n") in our tests. and we're not done yet... ;-) ........ r46133 | tim.peters | 2006-05-23 20:45:30 +0200 (Tue, 23 May 2006) | 38 lines Bug #1334662 / patch #1335972: int(string, base) wrong answers. In rare cases of strings specifying true values near sys.maxint, and oddball bases (not decimal or a power of 2), int(string, base) could deliver insane answers. This repairs all such problems, and also speeds string->int significantly. On my box, here are % speedups for decimal strings of various lengths: length speedup ------ ------- 1 12.4% 2 15.7% 3 20.6% 4 28.1% 5 33.2% 6 37.5% 7 41.9% 8 46.3% 9 51.2% 10 19.5% 11 19.9% 12 23.9% 13 23.7% 14 23.3% 15 24.9% 16 25.3% 17 28.3% 18 27.9% 19 35.7% Note that the difference between 9 and 10 is the difference between short and long Python ints on a 32-bit box. The patch doesn't actually do anything to speed conversion to long: the speedup is due to detecting "unsigned long" overflow more quickly. This is a bugfix candidate, but it's a non-trivial patch and it would be painful to separate the "bug fix" from the "speed up" parts. ........ r46134 | bob.ippolito | 2006-05-23 20:46:41 +0200 (Tue, 23 May 2006) | 1 line Patch #1493701: performance enhancements for struct module. ........ r46136 | andrew.kuchling | 2006-05-23 21:00:45 +0200 (Tue, 23 May 2006) | 1 line Remove duplicate item ........ r46141 | bob.ippolito | 2006-05-23 21:09:51 +0200 (Tue, 23 May 2006) | 1 line revert #1493701 ........ r46142 | bob.ippolito | 2006-05-23 21:11:34 +0200 (Tue, 23 May 2006) | 1 line patch #1493701: performance enhancements for struct module ........ r46144 | bob.ippolito | 2006-05-23 21:12:41 +0200 (Tue, 23 May 2006) | 1 line patch #1493701: performance enhancements for struct module ........ r46148 | bob.ippolito | 2006-05-23 21:25:52 +0200 (Tue, 23 May 2006) | 1 line fix linking issue, warnings, in struct ........ r46149 | andrew.kuchling | 2006-05-23 21:29:38 +0200 (Tue, 23 May 2006) | 1 line Add two items ........ r46150 | bob.ippolito | 2006-05-23 21:31:23 +0200 (Tue, 23 May 2006) | 1 line forward declaration for PyStructType ........ r46151 | bob.ippolito | 2006-05-23 21:32:25 +0200 (Tue, 23 May 2006) | 1 line fix typo in _struct ........ r46152 | andrew.kuchling | 2006-05-23 21:32:35 +0200 (Tue, 23 May 2006) | 1 line Add item ........ r46153 | tim.peters | 2006-05-23 21:34:37 +0200 (Tue, 23 May 2006) | 3 lines Get the Windows build working again (recover from `struct` module changes). ........ r46155 | fredrik.lundh | 2006-05-23 21:47:35 +0200 (Tue, 23 May 2006) | 3 lines return 0 on misses, not -1. ........ r46156 | tim.peters | 2006-05-23 23:51:35 +0200 (Tue, 23 May 2006) | 4 lines test_struct grew weird behavior under regrtest.py -R, due to a module-level cache. Clearing the cache should make it stop showing up in refleak reports. ........ r46157 | tim.peters | 2006-05-23 23:54:23 +0200 (Tue, 23 May 2006) | 2 lines Whitespace normalization. ........ r46158 | tim.peters | 2006-05-23 23:55:53 +0200 (Tue, 23 May 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46161 | fredrik.lundh | 2006-05-24 12:20:36 +0200 (Wed, 24 May 2006) | 3 lines use Py_ssize_t for string indexes (thanks, neal!) ........ r46173 | fredrik.lundh | 2006-05-24 16:28:11 +0200 (Wed, 24 May 2006) | 14 lines needforspeed: use "fastsearch" for count and findstring helpers. this results in a 2.5x speedup on the stringbench count tests, and a 20x (!) speedup on the stringbench search/find/contains test, compared to 2.5a2. for more on the algorithm, see: http://effbot.org/zone/stringlib.htm if you get weird results, you can disable the new algoritm by undefining USE_FAST in Objects/unicodeobject.c. enjoy /F ........ r46182 | fredrik.lundh | 2006-05-24 17:11:01 +0200 (Wed, 24 May 2006) | 3 lines needforspeedindeed: use fastsearch also for __contains__ ........ r46184 | bob.ippolito | 2006-05-24 17:32:06 +0200 (Wed, 24 May 2006) | 1 line refactor unpack, add unpack_from ........ r46189 | fredrik.lundh | 2006-05-24 18:35:18 +0200 (Wed, 24 May 2006) | 4 lines needforspeed: refactored the replace code slightly; special-case constant-length changes; use fastsearch to locate the first match. ........ r46198 | andrew.dalke | 2006-05-24 20:55:37 +0200 (Wed, 24 May 2006) | 10 lines Added a slew of test for string replace, based various corner cases from the Need For Speed sprint coding. Includes commented out overflow tests which will be uncommented once the code is fixed. This test will break the 8-bit string tests because "".replace("", "A") == "" when it should == "A" We have a fix for it, which should be added tomorrow. ........ r46200 | tim.peters | 2006-05-24 22:27:18 +0200 (Wed, 24 May 2006) | 2 lines We can't leave the checked-in tests broken. ........ r46201 | tim.peters | 2006-05-24 22:29:44 +0200 (Wed, 24 May 2006) | 2 lines Whitespace normalization. ........ r46202 | tim.peters | 2006-05-24 23:00:45 +0200 (Wed, 24 May 2006) | 4 lines Disable the damn empty-string replace test -- it can't be make to pass now for unicode if it passes for str, or vice versa. ........ r46203 | tim.peters | 2006-05-24 23:10:40 +0200 (Wed, 24 May 2006) | 58 lines Heavily fiddled variant of patch #1442927: PyLong_FromString optimization. ``long(str, base)`` is now up to 6x faster for non-power-of-2 bases. The largest speedup is for inputs with about 1000 decimal digits. Conversion from non-power-of-2 bases remains quadratic-time in the number of input digits (it was and remains linear-time for bases 2, 4, 8, 16 and 32). Speedups at various lengths for decimal inputs, comparing 2.4.3 with current trunk. Note that it's actually a bit slower for 1-digit strings: len speedup ---- ------- 1 -4.5% 2 4.6% 3 8.3% 4 12.7% 5 16.9% 6 28.6% 7 35.5% 8 44.3% 9 46.6% 10 55.3% 11 65.7% 12 77.7% 13 73.4% 14 75.3% 15 85.2% 16 103.0% 17 95.1% 18 112.8% 19 117.9% 20 128.3% 30 174.5% 40 209.3% 50 236.3% 60 254.3% 70 262.9% 80 295.8% 90 297.3% 100 324.5% 200 374.6% 300 403.1% 400 391.1% 500 388.7% 600 440.6% 700 468.7% 800 498.0% 900 507.2% 1000 501.2% 2000 450.2% 3000 463.2% 4000 452.5% 5000 440.6% 6000 439.6% 7000 424.8% 8000 418.1% 9000 417.7% ........ r46204 | andrew.kuchling | 2006-05-25 02:23:03 +0200 (Thu, 25 May 2006) | 1 line Minor edits; add an item ........ r46205 | fred.drake | 2006-05-25 04:42:25 +0200 (Thu, 25 May 2006) | 3 lines fix broken links in PDF (SF patch #1281291, contributed by Rory Yorke) ........ r46208 | walter.doerwald | 2006-05-25 10:53:28 +0200 (Thu, 25 May 2006) | 2 lines Replace tab inside comment with space. ........ r46209 | thomas.wouters | 2006-05-25 13:25:51 +0200 (Thu, 25 May 2006) | 4 lines Fix #1488915, Multiple dots in relative import statement raise SyntaxError. ........ r46210 | thomas.wouters | 2006-05-25 13:26:25 +0200 (Thu, 25 May 2006) | 5 lines Update graminit.c for the fix for #1488915, Multiple dots in relative import statement raise SyntaxError, and add testcase. ........ r46211 | andrew.kuchling | 2006-05-25 14:27:59 +0200 (Thu, 25 May 2006) | 1 line Add entry; and fix a typo ........ r46214 | fredrik.lundh | 2006-05-25 17:22:03 +0200 (Thu, 25 May 2006) | 7 lines needforspeed: speed up upper and lower for 8-bit string objects. (the unicode versions of these are still 2x faster on windows, though...) based on work by Andrew Dalke, with tweaks by yours truly. ........ r46216 | fredrik.lundh | 2006-05-25 17:49:45 +0200 (Thu, 25 May 2006) | 5 lines needforspeed: make new upper/lower work properly for single-character strings too... (thanks to georg brandl for spotting the exact problem faster than anyone else) ........ r46217 | kristjan.jonsson | 2006-05-25 17:53:30 +0200 (Thu, 25 May 2006) | 1 line Added a new macro, Py_IS_FINITE(X). On windows there is an intrinsic for this and it is more efficient than to use !Py_IS_INFINITE(X) && !Py_IS_NAN(X). No change on other platforms ........ r46219 | fredrik.lundh | 2006-05-25 18:10:12 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: _toupper/_tolower is a SUSv2 thing; fall back on ISO C versions if they're not defined. ........ r46220 | andrew.kuchling | 2006-05-25 18:23:15 +0200 (Thu, 25 May 2006) | 1 line Fix comment typos ........ r46221 | andrew.dalke | 2006-05-25 18:30:52 +0200 (Thu, 25 May 2006) | 2 lines Added tests for implementation error we came up with in the need for speed sprint. ........ r46222 | andrew.kuchling | 2006-05-25 18:34:54 +0200 (Thu, 25 May 2006) | 1 line Fix another typo ........ r46223 | kristjan.jonsson | 2006-05-25 18:39:27 +0200 (Thu, 25 May 2006) | 1 line Fix incorrect documentation for the Py_IS_FINITE(X) macro. ........ r46224 | fredrik.lundh | 2006-05-25 18:46:54 +0200 (Thu, 25 May 2006) | 3 lines needforspeed: check for overflow in replace (from Andrew Dalke) ........ r46226 | fredrik.lundh | 2006-05-25 19:08:14 +0200 (Thu, 25 May 2006) | 5 lines needforspeed: new replace implementation by Andrew Dalke. replace is now about 3x faster on my machine, for the replace tests from string- bench. ........ r46227 | tim.peters | 2006-05-25 19:34:03 +0200 (Thu, 25 May 2006) | 5 lines A new table to help string->integer conversion was added yesterday to both mystrtoul.c and longobject.c. Share the table instead. Also cut its size by 64 entries (they had been used for an inscrutable trick originally, but the code no longer tries to use that trick). ........ r46229 | andrew.dalke | 2006-05-25 19:53:00 +0200 (Thu, 25 May 2006) | 11 lines Fixed problem identified by Georg. The special-case in-place code for replace made a copy of the string using PyString_FromStringAndSize(s, n) and modify the copied string in-place. However, 1 (and 0) character strings are shared from a cache. This cause "A".replace("A", "a") to change the cached version of "A" -- used by everyone. Now may the copy with NULL as the string and do the memcpy manually. I've added regression tests to check if this happens in the future. Perhaps there should be a PyString_Copy for this case? ........ r46230 | fredrik.lundh | 2006-05-25 19:55:31 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: use "fastsearch" for count. this results in a 3x speedup for the related stringbench tests. ........ r46231 | andrew.dalke | 2006-05-25 20:03:25 +0200 (Thu, 25 May 2006) | 4 lines Code had returned an ssize_t, upcast to long, then converted with PyInt_FromLong. Now using PyInt_FromSsize_t. ........ r46233 | andrew.kuchling | 2006-05-25 20:11:16 +0200 (Thu, 25 May 2006) | 1 line Comment typo ........ r46234 | andrew.dalke | 2006-05-25 20:18:39 +0200 (Thu, 25 May 2006) | 4 lines Added overflow test for adding two (very) large strings where the new string is over max Py_ssize_t. I have no way to test it on my box or any box I have access to. At least it doesn't break anything. ........ r46235 | bob.ippolito | 2006-05-25 20:20:23 +0200 (Thu, 25 May 2006) | 1 line Faster path for PyLong_FromLongLong, using PyLong_FromLong algorithm ........ r46238 | georg.brandl | 2006-05-25 20:44:09 +0200 (Thu, 25 May 2006) | 3 lines Guard the _active.remove() call to avoid errors when there is no _active list. ........ r46239 | fredrik.lundh | 2006-05-25 20:44:29 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: use fastsearch also for find/index and contains. the related tests are now about 10x faster. ........ r46240 | bob.ippolito | 2006-05-25 20:44:50 +0200 (Thu, 25 May 2006) | 1 line Struct now unpacks to PY_LONG_LONG directly when possible, also include #ifdef'ed out code that will return int instead of long when in bounds (not active since it's an API and doc change) ........ r46241 | jack.diederich | 2006-05-25 20:47:15 +0200 (Thu, 25 May 2006) | 1 line * eliminate warning by reverting tmp_s type to 'const char*' ........ r46242 | bob.ippolito | 2006-05-25 21:03:19 +0200 (Thu, 25 May 2006) | 1 line Fix Cygwin compiler issue ........ r46243 | bob.ippolito | 2006-05-25 21:15:27 +0200 (Thu, 25 May 2006) | 1 line fix a struct regression where long would be returned for short unsigned integers ........ r46244 | georg.brandl | 2006-05-25 21:15:31 +0200 (Thu, 25 May 2006) | 4 lines Replace PyObject_CallFunction calls with only object args with PyObject_CallFunctionObjArgs, which is 30% faster. ........ r46245 | fredrik.lundh | 2006-05-25 21:19:05 +0200 (Thu, 25 May 2006) | 3 lines needforspeed: use insert+reverse instead of append ........ r46246 | bob.ippolito | 2006-05-25 21:33:38 +0200 (Thu, 25 May 2006) | 1 line Use LONG_MIN and LONG_MAX to check Python integer bounds instead of the incorrect INT_MIN and INT_MAX ........ r46248 | bob.ippolito | 2006-05-25 21:56:56 +0200 (Thu, 25 May 2006) | 1 line Use faster struct pack/unpack functions for the endian table that matches the host's ........ r46249 | bob.ippolito | 2006-05-25 21:59:56 +0200 (Thu, 25 May 2006) | 1 line enable darwin/x86 support for libffi and hence ctypes (doesn't yet support --enable-universalsdk) ........ r46252 | georg.brandl | 2006-05-25 22:28:10 +0200 (Thu, 25 May 2006) | 4 lines Someone seems to just have copy-pasted the docs of tp_compare to tp_richcompare ;) ........ r46253 | brett.cannon | 2006-05-25 22:44:08 +0200 (Thu, 25 May 2006) | 2 lines Swap out bare malloc()/free() use for PyMem_MALLOC()/PyMem_FREE() . ........ r46254 | bob.ippolito | 2006-05-25 22:52:38 +0200 (Thu, 25 May 2006) | 1 line squelch gcc4 darwin/x86 compiler warnings ........ r46255 | bob.ippolito | 2006-05-25 23:09:45 +0200 (Thu, 25 May 2006) | 1 line fix test_float regression and 64-bit size mismatch issue ........ r46256 | georg.brandl | 2006-05-25 23:11:56 +0200 (Thu, 25 May 2006) | 3 lines Add a x-ref to newer calling APIs. ........ r46257 | ronald.oussoren | 2006-05-25 23:30:54 +0200 (Thu, 25 May 2006) | 2 lines Fix minor typo in prep_cif.c ........ r46259 | brett.cannon | 2006-05-25 23:33:11 +0200 (Thu, 25 May 2006) | 4 lines Change test_values so that it compares the lowercasing of group names since getgrall() can return all lowercase names while getgrgid() returns proper casing. Discovered on Ubuntu 5.04 (custom). ........ r46261 | tim.peters | 2006-05-25 23:50:17 +0200 (Thu, 25 May 2006) | 7 lines Some Win64 pre-release in 2000 didn't support QueryPerformanceCounter(), but we believe Win64 does support it now. So use in time.clock(). It would be peachy if someone with a Win64 box tried this ;-) ........ r46262 | tim.peters | 2006-05-25 23:52:19 +0200 (Thu, 25 May 2006) | 2 lines Whitespace normalization. ........ r46263 | bob.ippolito | 2006-05-25 23:58:05 +0200 (Thu, 25 May 2006) | 1 line Add missing files from x86 darwin ctypes patch ........ r46264 | brett.cannon | 2006-05-26 00:00:14 +0200 (Fri, 26 May 2006) | 2 lines Move over to use of METH_O and METH_NOARGS. ........ r46265 | tim.peters | 2006-05-26 00:25:25 +0200 (Fri, 26 May 2006) | 3 lines Repair idiot typo, and complete the job of trying to use the Windows time.clock() implementation on Win64. ........ r46266 | tim.peters | 2006-05-26 00:28:46 +0200 (Fri, 26 May 2006) | 9 lines Patch #1494387: SVN longobject.c compiler warnings The SIGCHECK macro defined here has always been bizarre, but it apparently causes compiler warnings on "Sun Studio 11". I believe the warnings are bogus, but it doesn't hurt to make the macro definition saner. Bugfix candidate (but I'm not going to bother). ........ r46268 | fredrik.lundh | 2006-05-26 01:27:53 +0200 (Fri, 26 May 2006) | 8 lines needforspeed: partition for 8-bit strings. for some simple tests, this is on par with a corresponding find, and nearly twice as fast as split(sep, 1) full tests, a unicode version, and documentation will follow to- morrow. ........ r46271 | andrew.kuchling | 2006-05-26 03:46:22 +0200 (Fri, 26 May 2006) | 1 line Add Soc student ........ r46272 | ronald.oussoren | 2006-05-26 10:41:25 +0200 (Fri, 26 May 2006) | 3 lines Without this patch OSX users couldn't add new help sources because the code tried to update one item in a tuple. ........ r46273 | fredrik.lundh | 2006-05-26 10:54:28 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: partition implementation, part two. feel free to improve the documentation and the docstrings. ........ r46274 | georg.brandl | 2006-05-26 11:05:54 +0200 (Fri, 26 May 2006) | 3 lines Clarify docs for str.partition(). ........ r46278 | fredrik.lundh | 2006-05-26 11:46:59 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: use METH_O for argument handling, which made partition some ~15% faster for the current tests (which is noticable faster than a corre- sponding find call). thanks to neal-who-never-sleeps for the tip. ........ r46280 | fredrik.lundh | 2006-05-26 12:27:17 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: use Py_ssize_t for the fastsearch counter and skip length (thanks, neal!). and yes, I've verified that this doesn't slow things down ;-) ........ r46285 | andrew.dalke | 2006-05-26 13:11:38 +0200 (Fri, 26 May 2006) | 2 lines Added a few more test cases for whitespace split. These strings have leading whitespace. ........ r46286 | jack.diederich | 2006-05-26 13:15:17 +0200 (Fri, 26 May 2006) | 1 line use Py_ssize_t in places that may need it ........ r46287 | andrew.dalke | 2006-05-26 13:15:22 +0200 (Fri, 26 May 2006) | 2 lines Added split whitespace checks for characters other than space. ........ r46288 | ronald.oussoren | 2006-05-26 13:17:55 +0200 (Fri, 26 May 2006) | 2 lines Fix buglet in postinstall script, it would generate an invalid .cshrc file. ........ r46290 | georg.brandl | 2006-05-26 13:26:11 +0200 (Fri, 26 May 2006) | 3 lines Add "partition" to UserString. ........ r46291 | fredrik.lundh | 2006-05-26 13:29:39 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: added Py_LOCAL macro, based on the LOCAL macro used for SRE and others. applied Py_LOCAL to relevant portion of ceval, which gives a 1-2% speedup on my machine. ymmv. ........ r46292 | jack.diederich | 2006-05-26 13:37:20 +0200 (Fri, 26 May 2006) | 1 line when generating python code prefer to generate valid python code ........ r46293 | fredrik.lundh | 2006-05-26 13:38:15 +0200 (Fri, 26 May 2006) | 3 lines use Py_LOCAL also for string and unicode objects ........ r46294 | ronald.oussoren | 2006-05-26 13:38:39 +0200 (Fri, 26 May 2006) | 12 lines - Search the sqlite specific search directories after the normal include directories when looking for the version of sqlite to use. - On OSX: * Extract additional include and link directories from the CFLAGS and LDFLAGS, if the user has bothered to specify them we might as wel use them. * Add '-Wl,-search_paths_first' to the extra_link_args for readline and sqlite. This makes it possible to use a static library to override the system provided dynamic library. ........ r46295 | ronald.oussoren | 2006-05-26 13:43:26 +0200 (Fri, 26 May 2006) | 6 lines Integrate installing a framework in the 'make install' target. Until now users had to use 'make frameworkinstall' to install python when it is configured with '--enable-framework'. This tends to confuse users that don't hunt for readme files hidden in platform specific directories :-) ........ r46297 | fredrik.lundh | 2006-05-26 13:54:04 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: added PY_LOCAL_AGGRESSIVE macro to enable "aggressive" LOCAL inlining; also added some missing whitespace ........ r46298 | andrew.kuchling | 2006-05-26 14:01:44 +0200 (Fri, 26 May 2006) | 1 line Typo fixes ........ r46299 | fredrik.lundh | 2006-05-26 14:01:49 +0200 (Fri, 26 May 2006) | 4 lines Py_LOCAL shouldn't be used for data; it works for some .NET 2003 compilers, but Trent's copy thinks that it's an anachronism... ........ r46300 | martin.blais | 2006-05-26 14:03:27 +0200 (Fri, 26 May 2006) | 12 lines Support for buffer protocol for socket and struct. * Added socket.recv_buf() and socket.recvfrom_buf() methods, that use the buffer protocol (send and sendto already did). * Added struct.pack_to(), that is the corresponding buffer compatible method to unpack_from(). * Fixed minor typos in arraymodule. ........ r46302 | ronald.oussoren | 2006-05-26 14:23:20 +0200 (Fri, 26 May 2006) | 6 lines - Remove previous version of the binary distribution script for OSX - Some small bugfixes for the IDLE.app wrapper - Tweaks to build-installer to ensure that python gets build in the right way, including sqlite3. - Updated readme files ........ r46305 | tim.peters | 2006-05-26 14:26:21 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46307 | andrew.dalke | 2006-05-26 14:28:15 +0200 (Fri, 26 May 2006) | 7 lines I like tests. The new split functions use a preallocated list. Added tests which exceed the preallocation size, to exercise list appends/resizes. Also added more edge case tests. ........ r46308 | andrew.dalke | 2006-05-26 14:31:00 +0200 (Fri, 26 May 2006) | 2 lines Test cases for off-by-one errors in string split with multicharacter pattern. ........ r46309 | tim.peters | 2006-05-26 14:31:20 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46313 | andrew.kuchling | 2006-05-26 14:39:48 +0200 (Fri, 26 May 2006) | 1 line Add str.partition() ........ r46314 | bob.ippolito | 2006-05-26 14:52:53 +0200 (Fri, 26 May 2006) | 1 line quick hack to fix busted binhex test ........ r46316 | andrew.dalke | 2006-05-26 15:05:55 +0200 (Fri, 26 May 2006) | 2 lines Added more rstrip tests, including for prealloc'ed arrays ........ r46320 | bob.ippolito | 2006-05-26 15:15:44 +0200 (Fri, 26 May 2006) | 1 line fix #1229380 No struct.pack exception for some out of range integers ........ r46325 | tim.peters | 2006-05-26 15:39:17 +0200 (Fri, 26 May 2006) | 2 lines Use open() to open files (was using file()). ........ r46327 | andrew.dalke | 2006-05-26 16:00:45 +0200 (Fri, 26 May 2006) | 37 lines Changes to string.split/rsplit on whitespace to preallocate space in the results list. Originally it allocated 0 items and used the list growth during append. Now it preallocates 12 items so the first few appends don't need list reallocs. ("Here are some words ."*2).split(None, 1) is 7% faster ("Here are some words ."*2).split() is is 15% faster (Your milage may vary, see dealership for details.) File parsing like this for line in f: count += len(line.split()) is also about 15% faster. There is a slowdown of about 3% for large strings because of the additional overhead of checking if the append is to a preallocated region of the list or not. This will be the rare case. It could be improved with special case code but we decided it was not useful enough. There is a cost of 12*sizeof(PyObject *) bytes per list. For the normal case of file parsing this is not a problem because of the lists have a short lifetime. We have not come up with cases where this is a problem in real life. I chose 12 because human text averages about 11 words per line in books, one of my data sets averages 6.2 words with a final peak at 11 words per line, and I work with a tab delimited data set with 8 tabs per line (or 9 words per line). 12 encompasses all of these. Also changed the last rstrip code to append then reverse, rather than doing insert(0). The strip() and rstrip() times are now comparable. ........ r46328 | tim.peters | 2006-05-26 16:02:05 +0200 (Fri, 26 May 2006) | 5 lines Explicitly close files. I'm trying to stop the frequent spurious test_tarfile failures on Windows buildbots, but it's hard to know how since the regrtest failure output is useless here, and it never fails when a buildbot slave runs test_tarfile the second time in verbose mode. ........ r46329 | andrew.kuchling | 2006-05-26 16:03:41 +0200 (Fri, 26 May 2006) | 1 line Add buffer support for struct, socket ........ r46330 | andrew.kuchling | 2006-05-26 16:04:19 +0200 (Fri, 26 May 2006) | 1 line Typo fix ........ r46331 | bob.ippolito | 2006-05-26 16:07:23 +0200 (Fri, 26 May 2006) | 1 line Fix distutils so that libffi will cross-compile between darwin/x86 and darwin/ppc ........ r46333 | bob.ippolito | 2006-05-26 16:23:21 +0200 (Fri, 26 May 2006) | 1 line Fix _struct typo that broke some 64-bit platforms ........ r46335 | bob.ippolito | 2006-05-26 16:29:35 +0200 (Fri, 26 May 2006) | 1 line Enable PY_USE_INT_WHEN_POSSIBLE in struct ........ r46343 | andrew.dalke | 2006-05-26 17:21:01 +0200 (Fri, 26 May 2006) | 2 lines Eeked out another 3% or so performance in split whitespace by cleaning up the algorithm. ........ r46352 | andrew.dalke | 2006-05-26 18:22:52 +0200 (Fri, 26 May 2006) | 3 lines Test for more edge strip cases; leading and trailing separator gets removed even with strip(..., 0) ........ r46354 | bob.ippolito | 2006-05-26 18:23:28 +0200 (Fri, 26 May 2006) | 1 line fix signed/unsigned mismatch in struct ........ r46355 | steve.holden | 2006-05-26 18:27:59 +0200 (Fri, 26 May 2006) | 5 lines Add -t option to allow easy test selection. Action verbose option correctly. Tweak operation counts. Add empty and new instances tests. Enable comparisons across different warp factors. Change version. ........ r46356 | fredrik.lundh | 2006-05-26 18:32:42 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: use Py_LOCAL on a few more locals in stringobject.c ........ r46357 | thomas.heller | 2006-05-26 18:42:44 +0200 (Fri, 26 May 2006) | 4 lines For now, I gave up with automatic conversion of reST to Python-latex, so I'm writing this in latex now. Skeleton for the ctypes reference. ........ r46358 | tim.peters | 2006-05-26 18:49:28 +0200 (Fri, 26 May 2006) | 3 lines Repair Windows compiler warnings about mixing signed and unsigned integral types in comparisons. ........ r46359 | tim.peters | 2006-05-26 18:52:04 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46360 | tim.peters | 2006-05-26 18:53:04 +0200 (Fri, 26 May 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46362 | fredrik.lundh | 2006-05-26 19:04:58 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring (in progress) ........ r46363 | thomas.heller | 2006-05-26 19:18:33 +0200 (Fri, 26 May 2006) | 1 line Write some docs. ........ r46364 | fredrik.lundh | 2006-05-26 19:22:38 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring (in progress) ........ r46366 | fredrik.lundh | 2006-05-26 19:26:39 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: cleanup ........ r46367 | fredrik.lundh | 2006-05-26 19:31:41 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: remove remaining USE_FAST macros; if fastsearch was broken, someone would have noticed by now ;-) ........ r46368 | steve.holden | 2006-05-26 19:41:32 +0200 (Fri, 26 May 2006) | 5 lines Use minimum calibration time rather than avergae to avoid the illusion of negative run times. Halt with an error if run times go below 10 ms, indicating that results will be unreliable. ........ r46370 | thomas.heller | 2006-05-26 19:47:40 +0200 (Fri, 26 May 2006) | 2 lines Reordered, and wrote more docs. ........ r46372 | georg.brandl | 2006-05-26 20:03:31 +0200 (Fri, 26 May 2006) | 9 lines Need for speed: Patch #921466 : sys.path_importer_cache is now used to cache valid and invalid file paths for the built-in import machinery which leads to fewer open calls on startup. Also fix issue with PEP 302 style import hooks which lead to more open() calls than necessary. ........ r46373 | fredrik.lundh | 2006-05-26 20:05:34 +0200 (Fri, 26 May 2006) | 3 lines removed unnecessary include ........ r46377 | fredrik.lundh | 2006-05-26 20:15:38 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: added rpartition implementation ........ r46380 | fredrik.lundh | 2006-05-26 20:24:15 +0200 (Fri, 26 May 2006) | 5 lines needspeed: rpartition documentation, tests, and a bug fixes. feel free to add more tests and improve the documentation. ........ r46381 | steve.holden | 2006-05-26 20:26:21 +0200 (Fri, 26 May 2006) | 4 lines Revert tests to MAL's original round sizes to retiain comparability from long ago and far away. Stop calling this pybench 1.4 because it isn't. Remove the empty test, which was a bad idea. ........ r46387 | andrew.kuchling | 2006-05-26 20:41:18 +0200 (Fri, 26 May 2006) | 1 line Add rpartition() and path caching ........ r46388 | andrew.dalke | 2006-05-26 21:02:09 +0200 (Fri, 26 May 2006) | 10 lines substring split now uses /F's fast string matching algorithm. (If compiled without FAST search support, changed the pre-memcmp test to check the last character as well as the first. This gave a 25% speedup for my test case.) Rewrote the split algorithms so they stop when maxsplit gets to 0. Previously they did a string match first then checked if the maxsplit was reached. The new way prevents a needless string search. ........ r46391 | brett.cannon | 2006-05-26 21:04:47 +0200 (Fri, 26 May 2006) | 2 lines Change C spacing to 4 spaces by default to match PEP 7 for new C files. ........ r46392 | georg.brandl | 2006-05-26 21:04:47 +0200 (Fri, 26 May 2006) | 3 lines Exception isn't the root of all exception classes anymore. ........ r46397 | fredrik.lundh | 2006-05-26 21:23:21 +0200 (Fri, 26 May 2006) | 3 lines added rpartition method to UserString class ........ r46398 | fredrik.lundh | 2006-05-26 21:24:53 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: stringlib refactoring, continued. added count and find helpers; updated unicodeobject to use stringlib_count ........ r46400 | fredrik.lundh | 2006-05-26 21:29:05 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: stringlib refactoring: use stringlib/find for unicode find ........ r46403 | fredrik.lundh | 2006-05-26 21:33:03 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: use a macro to fix slice indexes ........ r46404 | thomas.heller | 2006-05-26 21:43:45 +0200 (Fri, 26 May 2006) | 1 line Write more docs. ........ r46406 | fredrik.lundh | 2006-05-26 21:48:07 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring: use stringlib/find for string find ........ r46407 | andrew.kuchling | 2006-05-26 21:51:10 +0200 (Fri, 26 May 2006) | 1 line Comment typo ........ r46409 | georg.brandl | 2006-05-26 22:04:44 +0200 (Fri, 26 May 2006) | 3 lines Replace Py_BuildValue("OO") by PyTuple_Pack. ........ r46411 | georg.brandl | 2006-05-26 22:14:47 +0200 (Fri, 26 May 2006) | 2 lines Patch #1492218: document None being a constant. ........ r46415 | georg.brandl | 2006-05-26 22:22:50 +0200 (Fri, 26 May 2006) | 3 lines Simplify calling. ........ r46416 | andrew.dalke | 2006-05-26 22:25:22 +0200 (Fri, 26 May 2006) | 4 lines Added limits to the replace code so it does not count all of the matching patterns in a string, only the number needed by the max limit. ........ r46417 | bob.ippolito | 2006-05-26 22:25:23 +0200 (Fri, 26 May 2006) | 1 line enable all of the struct tests, use ssize_t, fix some whitespace ........ r46418 | tim.peters | 2006-05-26 22:56:56 +0200 (Fri, 26 May 2006) | 2 lines Record Iceland sprint attendees. ........ r46421 | tim.peters | 2006-05-26 23:51:13 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46422 | steve.holden | 2006-05-27 00:17:54 +0200 (Sat, 27 May 2006) | 2 lines Add Richard Tew to developers ........ r46423 | steve.holden | 2006-05-27 00:33:20 +0200 (Sat, 27 May 2006) | 2 lines Update help text and documentaition. ........ r46424 | steve.holden | 2006-05-27 00:39:27 +0200 (Sat, 27 May 2006) | 2 lines Blasted typos ... ........ r46425 | andrew.dalke | 2006-05-27 00:49:03 +0200 (Sat, 27 May 2006) | 2 lines Added description of why splitlines doesn't use the prealloc strategy ........ r46426 | tim.peters | 2006-05-27 01:14:37 +0200 (Sat, 27 May 2006) | 19 lines Patch 1145039. set_exc_info(), reset_exc_info(): By exploiting the likely (who knows?) invariant that when an exception's `type` is NULL, its `value` and `traceback` are also NULL, save some cycles in heavily-executed code. This is a "a kronar saved is a kronar earned" patch: the speedup isn't reliably measurable, but it obviously does reduce the operation count in the normal (no exception raised) path through PyEval_EvalFrameEx(). The tim-exc_sanity branch tries to push this harder, but is still blowing up (at least in part due to pre-existing subtle bugs that appear to have no other visible consequences!). Not a bugfix candidate. ........ r46429 | steve.holden | 2006-05-27 02:51:52 +0200 (Sat, 27 May 2006) | 2 lines Reinstate new-style object tests. ........ r46430 | neal.norwitz | 2006-05-27 07:18:57 +0200 (Sat, 27 May 2006) | 1 line Fix compiler warning (and whitespace) on Mac OS 10.4. (A lot of this code looked duplicated, I wonder if a utility function could help reduce the duplication here.) ........ r46431 | neal.norwitz | 2006-05-27 07:21:30 +0200 (Sat, 27 May 2006) | 4 lines Fix Coverity warnings. - Check the correct variable (str_obj, not str) for NULL - sep_len was already verified it wasn't 0 ........ r46432 | martin.v.loewis | 2006-05-27 10:36:52 +0200 (Sat, 27 May 2006) | 2 lines Patch 1494554: Update numeric properties to Unicode 4.1. ........ r46433 | martin.v.loewis | 2006-05-27 10:54:29 +0200 (Sat, 27 May 2006) | 2 lines Explain why 'consumed' is initialized. ........ r46436 | fredrik.lundh | 2006-05-27 12:05:10 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: more stringlib refactoring ........ r46438 | fredrik.lundh | 2006-05-27 12:39:48 +0200 (Sat, 27 May 2006) | 5 lines needforspeed: backed out the Py_LOCAL-isation of ceval; the massive in- lining killed performance on certain Intel boxes, and the "aggressive" macro itself gives most of the benefits on others. ........ r46439 | andrew.dalke | 2006-05-27 13:04:36 +0200 (Sat, 27 May 2006) | 2 lines fixed typo ........ r46440 | martin.v.loewis | 2006-05-27 13:07:49 +0200 (Sat, 27 May 2006) | 2 lines Revert bogus change committed in 46432 to this file. ........ r46444 | andrew.kuchling | 2006-05-27 13:26:33 +0200 (Sat, 27 May 2006) | 1 line Add Py_LOCAL macros ........ r46450 | bob.ippolito | 2006-05-27 13:47:12 +0200 (Sat, 27 May 2006) | 1 line Remove the range checking and int usage #defines from _struct and strip out the now-dead code ........ r46454 | bob.ippolito | 2006-05-27 14:11:36 +0200 (Sat, 27 May 2006) | 1 line Fix up struct docstrings, add struct.pack_to function for symmetry ........ r46456 | richard.jones | 2006-05-27 14:29:24 +0200 (Sat, 27 May 2006) | 2 lines Conversion of exceptions over from faked-up classes to new-style C types. ........ r46457 | georg.brandl | 2006-05-27 14:30:25 +0200 (Sat, 27 May 2006) | 3 lines Add news item for new-style exception class branch merge. ........ r46458 | tim.peters | 2006-05-27 14:36:53 +0200 (Sat, 27 May 2006) | 3 lines More random thrashing trying to understand spurious Windows failures. Who's keeping a bz2 file open? ........ r46460 | andrew.kuchling | 2006-05-27 15:44:37 +0200 (Sat, 27 May 2006) | 1 line Mention new-style exceptions ........ r46461 | richard.jones | 2006-05-27 15:50:42 +0200 (Sat, 27 May 2006) | 1 line credit where credit is due ........ r46462 | georg.brandl | 2006-05-27 16:02:03 +0200 (Sat, 27 May 2006) | 3 lines Always close BZ2Proxy object. Remove unnecessary struct usage. ........ r46463 | tim.peters | 2006-05-27 16:13:13 +0200 (Sat, 27 May 2006) | 2 lines The cheery optimism of old age. ........ r46464 | andrew.dalke | 2006-05-27 16:16:40 +0200 (Sat, 27 May 2006) | 2 lines cleanup - removed trailing whitespace ........ r46465 | georg.brandl | 2006-05-27 16:41:55 +0200 (Sat, 27 May 2006) | 3 lines Remove spurious semicolons after macro invocations. ........ r46468 | fredrik.lundh | 2006-05-27 16:58:20 +0200 (Sat, 27 May 2006) | 4 lines needforspeed: replace improvements, changed to Py_LOCAL_INLINE where appropriate ........ r46469 | fredrik.lundh | 2006-05-27 17:20:22 +0200 (Sat, 27 May 2006) | 4 lines needforspeed: stringlib refactoring: changed find_obj to find_slice, to enable use from stringobject ........ r46470 | fredrik.lundh | 2006-05-27 17:26:19 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: stringlib refactoring: use find_slice for stringobject ........ r46472 | kristjan.jonsson | 2006-05-27 17:41:31 +0200 (Sat, 27 May 2006) | 1 line Add a PCBuild8 build directory for building with Visual Studio .NET 2005. Contains a special project to perform profile guided optimizations on the pythoncore.dll, by instrumenting and running pybench.py ........ r46473 | jack.diederich | 2006-05-27 17:44:34 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: use PyObject_MALLOC instead of system malloc for small allocations. Use PyMem_MALLOC for larger (1k+) chunks. 1%-2% speedup. ........ r46474 | bob.ippolito | 2006-05-27 17:53:49 +0200 (Sat, 27 May 2006) | 1 line fix struct regression on 64-bit platforms ........ r46475 | richard.jones | 2006-05-27 18:07:28 +0200 (Sat, 27 May 2006) | 1 line doc string additions and tweaks ........ r46477 | richard.jones | 2006-05-27 18:15:11 +0200 (Sat, 27 May 2006) | 1 line move semicolons ........ r46478 | george.yoshida | 2006-05-27 18:32:44 +0200 (Sat, 27 May 2006) | 2 lines minor markup nits ........ r46488 | george.yoshida | 2006-05-27 18:51:43 +0200 (Sat, 27 May 2006) | 3 lines End of Ch.3 is now about "with statement". Avoid obsolescence by directly referring to the section. ........ r46489 | george.yoshida | 2006-05-27 19:09:17 +0200 (Sat, 27 May 2006) | 2 lines fix typo ........ --- Doc/ACKS | 1 + Doc/Makefile.deps | 6 +- Doc/api/abstract.tex | 6 +- Doc/api/concrete.tex | 44 +- Doc/api/exceptions.tex | 47 +- Doc/api/newtypes.tex | 96 +- Doc/api/refcounts.dat | 2 +- Doc/commontex/boilerplate.tex | 2 +- Doc/dist/dist.tex | 8 +- Doc/ext/windows.tex | 2 +- Doc/howto/unicode.rst | 4 +- Doc/howto/urllib2.rst | 598 ++ Doc/inst/inst.tex | 22 +- Doc/lib/lib.tex | 6 +- Doc/lib/libcodecs.tex | 6 +- Doc/lib/libcodeop.tex | 2 +- Doc/lib/libcollections.tex | 4 +- Doc/lib/libcontextlib.tex | 84 +- Doc/lib/libctypes.tex | 1226 +++ Doc/lib/libctypesref.tex | 457 + Doc/lib/libcursespanel.tex | 3 + Doc/lib/libdecimal.tex | 6 +- Doc/lib/libdis.tex | 2 +- Doc/lib/libdoctest.tex | 42 +- Doc/lib/libexcs.tex | 13 +- Doc/lib/libfuncs.tex | 20 +- Doc/lib/libgetpass.tex | 2 +- Doc/lib/libhtmlparser.tex | 2 +- Doc/lib/liblocale.tex | 61 +- Doc/lib/libmailbox.tex | 1353 ++- Doc/lib/libmsilib.tex | 485 ++ Doc/lib/liboperator.tex | 4 +- Doc/lib/liboptparse.tex | 403 +- Doc/lib/libpdb.tex | 4 +- Doc/lib/libposixpath.tex | 5 +- Doc/lib/libprofile.tex | 9 +- Doc/lib/librlcompleter.tex | 16 +- Doc/lib/librunpy.tex | 2 +- Doc/lib/libsqlite3.tex | 503 ++ Doc/lib/libstdtypes.tex | 153 +- Doc/lib/libsubprocess.tex | 15 +- Doc/lib/libsys.tex | 2 +- Doc/lib/libtarfile.tex | 8 +- Doc/lib/libthread.tex | 4 +- Doc/lib/libtokenize.tex | 2 +- Doc/lib/libtrace.tex | 125 + Doc/lib/libunittest.tex | 2 +- Doc/lib/liburllib2.tex | 16 +- Doc/lib/libweakref.tex | 33 + Doc/lib/libxmlrpclib.tex | 9 +- Doc/lib/libzlib.tex | 13 + Doc/lib/sqlite3/adapter_datetime.py | 14 + Doc/lib/sqlite3/adapter_point_1.py | 16 + Doc/lib/sqlite3/adapter_point_2.py | 17 + Doc/lib/sqlite3/collation_reverse.py | 15 + Doc/lib/sqlite3/complete_statement.py | 30 + Doc/lib/sqlite3/connect_db_1.py | 3 + Doc/lib/sqlite3/connect_db_2.py | 3 + Doc/lib/sqlite3/converter_point.py | 47 + Doc/lib/sqlite3/countcursors.py | 15 + Doc/lib/sqlite3/createdb.py | 28 + Doc/lib/sqlite3/execsql_fetchonerow.py | 17 + Doc/lib/sqlite3/execsql_printall_1.py | 13 + Doc/lib/sqlite3/execute_1.py | 11 + Doc/lib/sqlite3/execute_2.py | 12 + Doc/lib/sqlite3/execute_3.py | 12 + Doc/lib/sqlite3/executemany_1.py | 24 + Doc/lib/sqlite3/executemany_2.py | 15 + Doc/lib/sqlite3/executescript.py | 24 + Doc/lib/sqlite3/insert_more_people.py | 16 + Doc/lib/sqlite3/md5func.py | 11 + Doc/lib/sqlite3/mysumaggr.py | 20 + Doc/lib/sqlite3/parse_colnames.py | 8 + Doc/lib/sqlite3/pysqlite_datetime.py | 20 + Doc/lib/sqlite3/row_factory.py | 13 + Doc/lib/sqlite3/rowclass.py | 12 + Doc/lib/sqlite3/shared_cache.py | 6 + Doc/lib/sqlite3/shortcut_methods.py | 21 + Doc/lib/sqlite3/simple_tableprinter.py | 26 + Doc/lib/sqlite3/text_factory.py | 42 + Doc/mac/scripting.tex | 4 +- Doc/perl/l2hinit.perl | 19 +- Doc/perl/python.perl | 1 - Doc/ref/ref2.tex | 30 +- Doc/ref/ref3.tex | 78 +- Doc/ref/ref4.tex | 2 +- Doc/ref/ref5.tex | 11 +- Doc/ref/ref6.tex | 11 +- Doc/ref/ref7.tex | 27 +- Doc/texinputs/python.sty | 24 +- Doc/tut/glossary.tex | 2 +- Doc/tut/tut.tex | 42 +- Doc/whatsnew/whatsnew20.tex | 6 +- Doc/whatsnew/whatsnew23.tex | 2 +- Doc/whatsnew/whatsnew25.tex | 560 +- Grammar/Grammar | 2 +- Include/Python.h | 2 + Include/code.h | 1 + Include/frameobject.h | 13 +- Include/longobject.h | 1 + Include/osdefs.h | 4 + Include/pyerrors.h | 69 +- Include/pyport.h | 58 + Include/unicodeobject.h | 48 +- Lib/UserString.py | 4 + Lib/_LWPCookieJar.py | 22 +- Lib/_MozillaCookieJar.py | 16 +- Lib/bdb.py | 16 +- Lib/binhex.py | 6 +- Lib/bsddb/test/test_thread.py | 6 + Lib/calendar.py | 3 - Lib/codeop.py | 10 +- Lib/compiler/pycodegen.py | 2 - Lib/compiler/transformer.py | 12 +- Lib/contextlib.py | 28 +- Lib/cookielib.py | 176 +- Lib/ctypes/__init__.py | 36 +- Lib/ctypes/_loader.py | 262 - Lib/ctypes/test/test_bitfields.py | 2 +- Lib/ctypes/test/test_byteswap.py | 2 +- Lib/ctypes/test/test_callbacks.py | 2 +- Lib/ctypes/test/test_cast.py | 27 +- Lib/ctypes/test/test_cfuncs.py | 2 +- Lib/ctypes/test/test_checkretval.py | 2 +- Lib/ctypes/test/test_find.py | 104 + Lib/ctypes/test/test_funcptr.py | 2 +- Lib/ctypes/test/test_functions.py | 4 +- Lib/ctypes/test/test_libc.py | 2 +- Lib/ctypes/test/test_loading.py | 38 +- Lib/ctypes/test/test_pointers.py | 29 +- Lib/ctypes/test/test_posix.py | 40 - Lib/ctypes/test/test_prototypes.py | 2 +- Lib/ctypes/test/test_python_api.py | 27 +- Lib/ctypes/test/test_refcounts.py | 2 +- Lib/ctypes/test/test_returnfuncptrs.py | 4 +- Lib/ctypes/test/test_slicing.py | 12 +- Lib/ctypes/test/test_stringptr.py | 2 +- Lib/ctypes/test/test_structures.py | 8 +- Lib/ctypes/test/test_unicode.py | 4 +- Lib/ctypes/test/test_values.py | 4 +- Lib/ctypes/test/test_win32.py | 2 +- Lib/ctypes/util.py | 122 + Lib/decimal.py | 4 +- Lib/distutils/ccompiler.py | 6 +- Lib/distutils/command/bdist_msi.py | 16 +- Lib/distutils/command/build_ext.py | 5 + Lib/distutils/command/upload.py | 2 +- Lib/distutils/msvccompiler.py | 2 +- Lib/distutils/sysconfig.py | 19 +- Lib/distutils/unixccompiler.py | 64 +- Lib/distutils/util.py | 49 + Lib/doctest.py | 16 +- Lib/dummy_thread.py | 3 - Lib/easy_install.py | 5 - Lib/email/_parseaddr.py | 1 + Lib/email/test/test_email.py | 6 + Lib/email/test/test_email_renamed.py | 6 + Lib/gzip.py | 37 +- Lib/httplib.py | 17 +- Lib/idlelib/NEWS.txt | 5 + Lib/idlelib/configHelpSourceEdit.py | 1 + Lib/idlelib/idlever.py | 2 +- Lib/imputil.py | 7 +- Lib/inspect.py | 14 +- Lib/lib-tk/turtle.py | 583 +- Lib/locale.py | 170 +- Lib/logging/__init__.py | 2 +- Lib/logging/handlers.py | 10 +- Lib/mailbox.py | 2035 ++++- Lib/msilib/__init__.py | 15 +- Lib/msilib/text.py | 129 + Lib/msilib/uisample.py | 1399 ---- Lib/ntpath.py | 43 +- Lib/optparse.py | 204 +- Lib/pdb.py | 199 +- Lib/pkg_resources.py | 2377 ------ Lib/pkgutil.py | 30 +- Lib/plat-mac/bundlebuilder.py | 23 +- Lib/plat-mac/pimp.py | 4 +- Lib/popen2.py | 5 +- Lib/poplib.py | 4 +- Lib/rfc822.py | 1 + Lib/rlcompleter.py | 8 +- Lib/setuptools.egg-info/PKG-INFO | 89 - Lib/setuptools.egg-info/entry_points.txt | 51 - Lib/setuptools.egg-info/top_level.txt | 3 - Lib/setuptools.egg-info/zip-safe | 0 Lib/setuptools/__init__.py | 64 - Lib/setuptools/archive_util.py | 200 - Lib/setuptools/cli.exe | Bin 6144 -> 0 bytes Lib/setuptools/command/__init__.py | 19 - Lib/setuptools/command/alias.py | 79 - Lib/setuptools/command/bdist_egg.py | 449 - Lib/setuptools/command/bdist_rpm.py | 37 - Lib/setuptools/command/build_ext.py | 285 - Lib/setuptools/command/build_py.py | 192 - Lib/setuptools/command/develop.py | 116 - Lib/setuptools/command/easy_install.py | 1555 ---- Lib/setuptools/command/egg_info.py | 365 - Lib/setuptools/command/install.py | 101 - Lib/setuptools/command/install_egg_info.py | 81 - Lib/setuptools/command/install_lib.py | 76 - Lib/setuptools/command/install_scripts.py | 56 - Lib/setuptools/command/rotate.py | 57 - Lib/setuptools/command/saveopts.py | 24 - Lib/setuptools/command/sdist.py | 163 - Lib/setuptools/command/setopt.py | 158 - Lib/setuptools/command/test.py | 119 - Lib/setuptools/command/upload.py | 178 - Lib/setuptools/depends.py | 239 - Lib/setuptools/dist.py | 798 -- Lib/setuptools/extension.py | 35 - Lib/setuptools/gui.exe | Bin 6144 -> 0 bytes Lib/setuptools/package_index.py | 674 -- Lib/setuptools/sandbox.py | 203 - Lib/setuptools/site-patch.py | 74 - Lib/setuptools/tests/__init__.py | 364 - Lib/setuptools/tests/api_tests.txt | 330 - Lib/setuptools/tests/test_resources.py | 483 -- Lib/shutil.py | 2 +- Lib/socket.py | 6 +- Lib/sqlite3/dbapi2.py | 68 +- Lib/sqlite3/test/hooks.py | 4 +- Lib/sqlite3/test/regression.py | 27 +- Lib/sqlite3/test/userfunctions.py | 14 + Lib/struct.py | 99 + Lib/subprocess.py | 4 +- Lib/tarfile.py | 539 +- Lib/test/exception_hierarchy.txt | 3 +- Lib/test/output/test_logging | 6 +- Lib/test/pickletester.py | 8 +- Lib/test/regrtest.py | 30 +- Lib/test/string_tests.py | 307 + Lib/test/test_bigmem.py | 964 +++ Lib/test/test_builtin.py | 189 +- Lib/test/test_cmd_line.py | 39 +- Lib/test/test_codeccallbacks.py | 73 - Lib/test/test_codecencodings_cn.py | 1 - Lib/test/test_codecencodings_hk.py | 1 - Lib/test/test_codecencodings_jp.py | 1 - Lib/test/test_codecencodings_kr.py | 1 - Lib/test/test_codecencodings_tw.py | 1 - Lib/test/test_codecmaps_cn.py | 1 - Lib/test/test_codecmaps_hk.py | 1 - Lib/test/test_codecmaps_jp.py | 1 - Lib/test/test_codecmaps_kr.py | 1 - Lib/test/test_codecmaps_tw.py | 1 - Lib/test/test_compiler.py | 4 + Lib/test/test_contextlib.py | 31 +- Lib/test/test_cookielib.py | 16 + Lib/test/test_datetime.py | 6 + Lib/test/test_doctest.py | 39 + Lib/test/test_exceptions.py | 93 +- Lib/test/test_file.py | 2 +- Lib/test/test_grp.py | 5 +- Lib/test/test_import.py | 17 + Lib/test/test_importhooks.py | 11 +- Lib/test/test_locale.py | 28 +- Lib/test/test_logging.py | 19 +- Lib/test/test_mailbox.py | 1676 +++- Lib/test/test_multibytecodec.py | 1 - Lib/test/test_old_mailbox.py | 120 + Lib/test/test_optparse.py | 200 +- Lib/test/test_os.py | 30 +- Lib/test/test_pty.py | 13 +- Lib/test/test_rfc822.py | 10 + Lib/test/test_setuptools.py | 16 - Lib/test/test_shutil.py | 4 +- Lib/test/test_socket.py | 33 +- Lib/test/test_sqlite.py | 5 +- Lib/test/test_stringprep.py | 6 +- Lib/test/test_struct.py | 103 +- Lib/test/test_subprocess.py | 4 +- Lib/test/test_sundry.py | 6 +- Lib/test/test_support.py | 106 +- Lib/test/test_syntax.py | 58 +- Lib/test/test_tarfile.py | 118 +- Lib/test/test_tcl.py | 1 + Lib/test/test_threaded_import.py | 21 +- Lib/test/test_traceback.py | 6 + Lib/test/test_unicode.py | 15 +- Lib/test/test_unicodedata.py | 10 +- Lib/test/test_urllib2.py | 372 +- Lib/test/test_urllib2net.py | 179 +- Lib/test/test_weakref.py | 44 + Lib/test/test_with.py | 84 +- Lib/test/test_zlib.py | 57 + Lib/test/testtar.tar | Bin 112640 -> 133120 bytes Lib/test/threaded_import_hangers.py | 42 + Lib/threading.py | 15 +- Lib/trace.py | 20 +- Lib/traceback.py | 2 +- Lib/urllib.py | 7 - Lib/urllib2.py | 79 +- Lib/warnings.py | 2 - Lib/weakref.py | 48 + Lib/webbrowser.py | 5 +- Mac/Modules/cf/_CFmodule.c | 18 +- Mac/Modules/file/_Filemodule.c | 9 +- Mac/Modules/mlte/_Mltemodule.c | 17 - Mac/OSX/BuildScript/README.txt | 35 + Mac/OSX/BuildScript/build-installer.py | 1014 +++ Mac/OSX/BuildScript/ncurses-5.5.patch | 36 + Mac/OSX/BuildScript/resources/ReadMe.txt | 31 + Mac/OSX/BuildScript/resources/Welcome.rtf | 15 + Mac/OSX/BuildScript/resources/background.jpg | Bin 0 -> 45421 bytes .../BuildScript/scripts/postflight.documentation | 12 + Mac/OSX/BuildScript/scripts/postflight.framework | 33 + .../BuildScript/scripts/postflight.patch-profile | 71 + Mac/OSX/Dist/README.txt | 50 - Mac/OSX/Dist/build | 164 - Mac/OSX/Dist/example-pimp-database.plist | 51 - Mac/OSX/Dist/makedmg | 95 - Mac/OSX/Dist/resources/ReadMe.txt | 31 - Mac/OSX/Dist/resources/Welcome.rtf | 15 - Mac/OSX/Dist/resources/postflight | 92 - Mac/OSX/Extras.ReadMe.txt | 9 - Mac/OSX/Extras.install.py | 1 + Mac/OSX/IDLE/Info.plist | 55 + Mac/OSX/IDLE/Makefile.in | 52 + Mac/OSX/IDLE/idlemain.py | 27 + Mac/OSX/Icons/Disk Image.icns | Bin 0 -> 50703 bytes Mac/OSX/Icons/IDLE.icns | Bin 0 -> 53456 bytes Mac/OSX/Icons/Python Folder.icns | Bin 0 -> 133608 bytes Mac/OSX/Icons/PythonCompiled.icns | Bin 0 -> 60777 bytes Mac/OSX/Icons/PythonLauncher.icns | Bin 0 -> 42658 bytes Mac/OSX/Icons/PythonSource.icns | Bin 0 -> 54522 bytes Mac/OSX/Icons/ReadMe.txt | 3 + Mac/OSX/Makefile | 273 - Mac/OSX/Makefile.in | 241 + .../PythonLauncher/English.lproj/InfoPlist.strings | Bin 618 -> 0 bytes .../English.lproj/PreferenceWindow.nib/classes.nib | 26 + .../English.lproj/PreferenceWindow.nib/info.nib | 16 + .../English.lproj/PreferenceWindow.nib/objects.nib | Bin 0 -> 5882 bytes Mac/OSX/PythonLauncher/Info.plist | 65 + Mac/OSX/PythonLauncher/Makefile.in | 78 + Mac/OSX/PythonLauncher/PythonCompiled.icns | Bin 57125 -> 0 bytes Mac/OSX/PythonLauncher/PythonInterpreter.icns | Bin 45913 -> 0 bytes .../PythonLauncher.pbproj/project.pbxproj | 681 -- Mac/OSX/PythonLauncher/PythonSource.icns | Bin 50112 -> 0 bytes Mac/OSX/PythonLauncher/PythonWSource.icns | Bin 49891 -> 0 bytes Mac/OSX/README | 156 +- Mac/OSX/Tools/pythonw.c | 17 + Mac/OSX/fixversions.py | 69 - Mac/OSX/sample_sitecustomize.py | 6 - Mac/OSXResources/app/Resources/PythonApplet.icns | Bin 36565 -> 63136 bytes .../app/Resources/PythonInterpreter.icns | Bin 45913 -> 42658 bytes Mac/OSXResources/framework/Info.plist | 4 +- Mac/scripts/BuildApplet.icns | Bin 35833 -> 120107 bytes Makefile.pre.in | 61 +- Misc/ACKS | 8 + Misc/NEWS | 59 - Misc/RPM/python-2.5.spec | 2 +- Misc/Vim/python.vim | 6 +- Misc/Vim/syntax_test.py | 2 - Misc/Vim/vimrc | 5 +- Misc/developers.txt | 33 +- Modules/Setup.dist | 2 +- Modules/_codecsmodule.c | 18 +- Modules/_csv.c | 2 + Modules/_ctypes/_ctypes.c | 19 +- Modules/_ctypes/_ctypes_test.c | 10 +- Modules/_ctypes/libffi/configure | 22 +- Modules/_ctypes/libffi/configure.ac | 36 +- Modules/_ctypes/libffi/fficonfig.h.in | 15 +- Modules/_ctypes/libffi/fficonfig.py.in | 15 +- Modules/_ctypes/libffi/src/darwin/ffitarget.h | 25 + Modules/_ctypes/libffi/src/powerpc/darwin.S | 2 + .../_ctypes/libffi/src/powerpc/darwin_closure.S | 2 + Modules/_ctypes/libffi/src/powerpc/ffi_darwin.c | 12 +- Modules/_ctypes/libffi/src/prep_cif.c | 46 +- Modules/_ctypes/libffi/src/x86/darwin.S | 195 + Modules/_ctypes/libffi/src/x86/ffi_darwin.c | 610 ++ Modules/_ctypes/libffi/src/x86/ffitarget.h | 2 +- Modules/_ctypes/libffi_msvc/ffi.c | 85 +- Modules/_ctypes/libffi_msvc/ffi.h | 4 - Modules/_ctypes/libffi_msvc/ffi_common.h | 19 +- Modules/_ctypes/libffi_msvc/ffitarget.h | 20 +- Modules/_ctypes/libffi_msvc/mingwin32.S | 228 + Modules/_ctypes/libffi_msvc/prep_cif.c | 2 +- Modules/_ctypes/libffi_msvc/win32.S | 20 +- Modules/_elementtree.c | 4 +- Modules/_hotshot.c | 2 +- Modules/_localemodule.c | 5 +- Modules/_sqlite/adapters.c | 40 - Modules/_sqlite/adapters.h | 33 - Modules/_sqlite/cache.c | 23 +- Modules/_sqlite/cache.h | 14 +- Modules/_sqlite/connection.c | 197 +- Modules/_sqlite/connection.h | 25 +- Modules/_sqlite/converters.c | 40 - Modules/_sqlite/converters.h | 33 - Modules/_sqlite/cursor.c | 62 +- Modules/_sqlite/cursor.h | 2 +- Modules/_sqlite/microprotocols.h | 4 +- Modules/_sqlite/module.c | 8 +- Modules/_sqlite/module.h | 4 +- Modules/_sqlite/statement.c | 9 +- Modules/_sqlite/statement.h | 1 + Modules/_sre.c | 8 +- Modules/_ssl.c | 6 +- Modules/_struct.c | 1660 ++++ Modules/_testcapimodule.c | 45 +- Modules/_tkinter.c | 18 +- Modules/arraymodule.c | 6 +- Modules/cPickle.c | 10 +- Modules/cjkcodecs/multibytecodec.c | 2 +- Modules/datetimemodule.c | 7 + Modules/expat/expat_config.h | 19 + Modules/expat/xmlparse.c | 8 +- Modules/expat/xmlrole.c | 4 +- Modules/expat/xmltok.c | 4 +- Modules/fcntlmodule.c | 15 +- Modules/gcmodule.c | 23 +- Modules/grpmodule.c | 36 +- Modules/itertoolsmodule.c | 58 +- Modules/main.c | 2 + Modules/operator.c | 10 +- Modules/parsermodule.c | 4 +- Modules/posixmodule.c | 653 +- Modules/rotatingtree.h | 2 +- Modules/selectmodule.c | 83 +- Modules/sha512module.c | 198 +- Modules/socketmodule.c | 504 +- Modules/structmodule.c | 1293 --- Modules/threadmodule.c | 9 - Modules/timemodule.c | 30 +- Modules/unicodedata.c | 4 +- Modules/zlibmodule.c | 106 + Objects/classobject.c | 27 +- Objects/codeobject.c | 3 + Objects/exceptions.c | 2141 +++++ Objects/fileobject.c | 92 +- Objects/floatobject.c | 7 +- Objects/frameobject.c | 220 +- Objects/longobject.c | 269 +- Objects/stringlib/README.txt | 34 + Objects/stringlib/count.h | 34 + Objects/stringlib/fastsearch.h | 104 + Objects/stringlib/find.h | 112 + Objects/stringlib/partition.h | 111 + Objects/stringobject.c | 1408 +++- Objects/typeobject.c | 10 +- Objects/unicodectype.c | 333 +- Objects/unicodeobject.c | 772 +- Objects/weakrefobject.c | 2 +- PC/config.c | 4 +- PC/errmap.h | 78 + PC/errmap.mak | 5 + PC/example_nt/example.c | 2 +- PC/example_nt/example.def | 2 - PC/example_nt/example.vcproj | 15 +- PC/generrmap.c | 20 + PC/py.ico | Bin 766 -> 23558 bytes PC/pyc.ico | Bin 766 -> 23558 bytes PC/pycon.ico | Bin 766 -> 23558 bytes PC/pyconfig.h | 51 +- PCbuild/_bsddb.vcproj | 2 +- PCbuild/_elementtree.vcproj | 2 +- PCbuild/_msi.vcproj | 4 +- PCbuild/_socket.vcproj | 2 +- PCbuild/_sqlite3.vcproj | 6 - PCbuild/_testcapi.vcproj | 2 +- PCbuild/_tkinter.vcproj | 2 +- PCbuild/bz2.vcproj | 2 +- PCbuild/installer.bmp | Bin 50324 -> 58806 bytes PCbuild/pythoncore.vcproj | 10 +- PCbuild/unicodedata.vcproj | 2 +- PCbuild/winsound.vcproj | 2 +- PCbuild8/Uninstal.wse | 514 ++ PCbuild8/_bsddb.vcproj | 385 + PCbuild8/_ctypes.vcproj | 408 + PCbuild8/_ctypes_test.vcproj | 367 + PCbuild8/_elementtree.vcproj | 390 + PCbuild8/_msi.vcproj | 375 + PCbuild8/_socket.vcproj | 381 + PCbuild8/_sqlite3.vcproj | 414 + PCbuild8/_ssl.mak | 21 + PCbuild8/_ssl.vcproj | 121 + PCbuild8/_testcapi.vcproj | 374 + PCbuild8/_tkinter.vcproj | 389 + PCbuild8/build_ssl.py | 163 + PCbuild8/bz2.vcproj | 390 + PCbuild8/db.build | 10 + PCbuild8/field3.py | 35 + PCbuild8/installer.bmp | Bin 0 -> 58806 bytes PCbuild8/make_buildinfo.c | 92 + PCbuild8/make_buildinfo.vcproj | 188 + PCbuild8/make_versioninfo.vcproj | 207 + PCbuild8/pcbuild.sln | 185 + PCbuild8/pyexpat.vcproj | 393 + PCbuild8/python.build | 21 + PCbuild8/python.iss | 346 + PCbuild8/python.vcproj | 400 + PCbuild8/python20.wse | 3135 +++++++ PCbuild8/pythoncore.vcproj | 1103 +++ PCbuild8/pythoncore_link.txt | 311 + PCbuild8/pythoncore_pgo.vcproj | 781 ++ PCbuild8/pythoncore_pgo_link.txt | 311 + PCbuild8/pythonw.vcproj | 386 + PCbuild8/readme.txt | 423 + PCbuild8/rmpyc.py | 25 + PCbuild8/rt.bat | 52 + PCbuild8/select.vcproj | 382 + PCbuild8/unicodedata.vcproj | 371 + PCbuild8/w9xpopen.vcproj | 185 + PCbuild8/winsound.vcproj | 375 + Parser/parsetok.c | 4 +- Python/ast.c | 12 +- Python/bltinmodule.c | 18 +- Python/ceval.c | 172 +- Python/codecs.c | 4 +- Python/compile.c | 19 +- Python/dynload_hpux.c | 4 +- Python/errors.c | 51 +- Python/exceptions.c | 1943 ----- Python/getcwd.c | 4 + Python/graminit.c | 35 +- Python/import.c | 75 +- Python/mystrtoul.c | 238 +- Python/pystrtod.c | 4 +- Python/pythonrun.c | 12 +- Python/structmember.c | 11 +- Python/thread_nt.h | 4 +- README | 7 +- Tools/msi/msi.py | 10 +- Tools/pybench/Arithmetic.py | 1 - Tools/pybench/Calls.py | 1 - Tools/pybench/CommandLine.py | 32 +- Tools/pybench/Constructs.py | 1 - Tools/pybench/Dict.py | 71 +- Tools/pybench/Exceptions.py | 4 +- Tools/pybench/Imports.py | 5 +- Tools/pybench/Instances.py | 2 - Tools/pybench/Lists.py | 31 +- Tools/pybench/Lookups.py | 1 - Tools/pybench/NewInstances.py | 66 + Tools/pybench/Numbers.py | 216 +- Tools/pybench/README | 11 + Tools/pybench/Setup.py | 4 + Tools/pybench/Strings.py | 88 +- Tools/pybench/Tuples.py | 3 +- Tools/pybench/Unicode.py | 16 +- Tools/pybench/pybench.py | 217 +- Tools/unicode/gencodec.py | 6 +- configure | 8722 +++++++++++--------- configure.in | 116 +- pyconfig.h.in | 26 +- setup.py | 78 +- 549 files changed, 47591 insertions(+), 25481 deletions(-) create mode 100644 Doc/howto/urllib2.rst create mode 100755 Doc/lib/libctypes.tex create mode 100644 Doc/lib/libctypesref.tex create mode 100644 Doc/lib/libmsilib.tex create mode 100644 Doc/lib/libsqlite3.tex create mode 100644 Doc/lib/libtrace.tex create mode 100644 Doc/lib/sqlite3/adapter_datetime.py create mode 100644 Doc/lib/sqlite3/adapter_point_1.py create mode 100644 Doc/lib/sqlite3/adapter_point_2.py create mode 100644 Doc/lib/sqlite3/collation_reverse.py create mode 100644 Doc/lib/sqlite3/complete_statement.py create mode 100644 Doc/lib/sqlite3/connect_db_1.py create mode 100644 Doc/lib/sqlite3/connect_db_2.py create mode 100644 Doc/lib/sqlite3/converter_point.py create mode 100644 Doc/lib/sqlite3/countcursors.py create mode 100644 Doc/lib/sqlite3/createdb.py create mode 100644 Doc/lib/sqlite3/execsql_fetchonerow.py create mode 100644 Doc/lib/sqlite3/execsql_printall_1.py create mode 100644 Doc/lib/sqlite3/execute_1.py create mode 100644 Doc/lib/sqlite3/execute_2.py create mode 100644 Doc/lib/sqlite3/execute_3.py create mode 100644 Doc/lib/sqlite3/executemany_1.py create mode 100644 Doc/lib/sqlite3/executemany_2.py create mode 100644 Doc/lib/sqlite3/executescript.py create mode 100644 Doc/lib/sqlite3/insert_more_people.py create mode 100644 Doc/lib/sqlite3/md5func.py create mode 100644 Doc/lib/sqlite3/mysumaggr.py create mode 100644 Doc/lib/sqlite3/parse_colnames.py create mode 100644 Doc/lib/sqlite3/pysqlite_datetime.py create mode 100644 Doc/lib/sqlite3/row_factory.py create mode 100644 Doc/lib/sqlite3/rowclass.py create mode 100644 Doc/lib/sqlite3/shared_cache.py create mode 100644 Doc/lib/sqlite3/shortcut_methods.py create mode 100644 Doc/lib/sqlite3/simple_tableprinter.py create mode 100644 Doc/lib/sqlite3/text_factory.py delete mode 100644 Lib/ctypes/_loader.py create mode 100644 Lib/ctypes/test/test_find.py delete mode 100644 Lib/ctypes/test/test_posix.py create mode 100644 Lib/ctypes/util.py delete mode 100644 Lib/easy_install.py create mode 100644 Lib/msilib/text.py delete mode 100644 Lib/msilib/uisample.py delete mode 100644 Lib/pkg_resources.py delete mode 100644 Lib/setuptools.egg-info/PKG-INFO delete mode 100755 Lib/setuptools.egg-info/entry_points.txt delete mode 100644 Lib/setuptools.egg-info/top_level.txt delete mode 100644 Lib/setuptools.egg-info/zip-safe delete mode 100644 Lib/setuptools/__init__.py delete mode 100755 Lib/setuptools/archive_util.py delete mode 100755 Lib/setuptools/cli.exe delete mode 100644 Lib/setuptools/command/__init__.py delete mode 100755 Lib/setuptools/command/alias.py delete mode 100644 Lib/setuptools/command/bdist_egg.py delete mode 100755 Lib/setuptools/command/bdist_rpm.py delete mode 100644 Lib/setuptools/command/build_ext.py delete mode 100644 Lib/setuptools/command/build_py.py delete mode 100755 Lib/setuptools/command/develop.py delete mode 100755 Lib/setuptools/command/easy_install.py delete mode 100755 Lib/setuptools/command/egg_info.py delete mode 100644 Lib/setuptools/command/install.py delete mode 100755 Lib/setuptools/command/install_egg_info.py delete mode 100644 Lib/setuptools/command/install_lib.py delete mode 100755 Lib/setuptools/command/install_scripts.py delete mode 100755 Lib/setuptools/command/rotate.py delete mode 100755 Lib/setuptools/command/saveopts.py delete mode 100755 Lib/setuptools/command/sdist.py delete mode 100755 Lib/setuptools/command/setopt.py delete mode 100644 Lib/setuptools/command/test.py delete mode 100755 Lib/setuptools/command/upload.py delete mode 100644 Lib/setuptools/depends.py delete mode 100644 Lib/setuptools/dist.py delete mode 100644 Lib/setuptools/extension.py delete mode 100755 Lib/setuptools/gui.exe delete mode 100755 Lib/setuptools/package_index.py delete mode 100755 Lib/setuptools/sandbox.py delete mode 100755 Lib/setuptools/site-patch.py delete mode 100644 Lib/setuptools/tests/__init__.py delete mode 100755 Lib/setuptools/tests/api_tests.txt delete mode 100644 Lib/setuptools/tests/test_resources.py create mode 100644 Lib/struct.py create mode 100644 Lib/test/test_bigmem.py create mode 100644 Lib/test/test_old_mailbox.py delete mode 100644 Lib/test/test_setuptools.py create mode 100644 Lib/test/threaded_import_hangers.py create mode 100644 Mac/OSX/BuildScript/README.txt create mode 100755 Mac/OSX/BuildScript/build-installer.py create mode 100644 Mac/OSX/BuildScript/ncurses-5.5.patch create mode 100644 Mac/OSX/BuildScript/resources/ReadMe.txt create mode 100644 Mac/OSX/BuildScript/resources/Welcome.rtf create mode 100644 Mac/OSX/BuildScript/resources/background.jpg create mode 100755 Mac/OSX/BuildScript/scripts/postflight.documentation create mode 100755 Mac/OSX/BuildScript/scripts/postflight.framework create mode 100755 Mac/OSX/BuildScript/scripts/postflight.patch-profile delete mode 100644 Mac/OSX/Dist/README.txt delete mode 100755 Mac/OSX/Dist/build delete mode 100644 Mac/OSX/Dist/example-pimp-database.plist delete mode 100755 Mac/OSX/Dist/makedmg delete mode 100644 Mac/OSX/Dist/resources/ReadMe.txt delete mode 100644 Mac/OSX/Dist/resources/Welcome.rtf delete mode 100755 Mac/OSX/Dist/resources/postflight create mode 100644 Mac/OSX/IDLE/Info.plist create mode 100644 Mac/OSX/IDLE/Makefile.in create mode 100644 Mac/OSX/IDLE/idlemain.py create mode 100644 Mac/OSX/Icons/Disk Image.icns create mode 100644 Mac/OSX/Icons/IDLE.icns create mode 100644 Mac/OSX/Icons/Python Folder.icns create mode 100644 Mac/OSX/Icons/PythonCompiled.icns create mode 100644 Mac/OSX/Icons/PythonLauncher.icns create mode 100644 Mac/OSX/Icons/PythonSource.icns create mode 100644 Mac/OSX/Icons/ReadMe.txt delete mode 100644 Mac/OSX/Makefile create mode 100644 Mac/OSX/Makefile.in delete mode 100644 Mac/OSX/PythonLauncher/English.lproj/InfoPlist.strings create mode 100644 Mac/OSX/PythonLauncher/English.lproj/PreferenceWindow.nib/classes.nib create mode 100644 Mac/OSX/PythonLauncher/English.lproj/PreferenceWindow.nib/info.nib create mode 100644 Mac/OSX/PythonLauncher/English.lproj/PreferenceWindow.nib/objects.nib create mode 100644 Mac/OSX/PythonLauncher/Info.plist create mode 100644 Mac/OSX/PythonLauncher/Makefile.in delete mode 100644 Mac/OSX/PythonLauncher/PythonCompiled.icns delete mode 100644 Mac/OSX/PythonLauncher/PythonInterpreter.icns delete mode 100755 Mac/OSX/PythonLauncher/PythonLauncher.pbproj/project.pbxproj delete mode 100644 Mac/OSX/PythonLauncher/PythonSource.icns delete mode 100644 Mac/OSX/PythonLauncher/PythonWSource.icns create mode 100644 Mac/OSX/Tools/pythonw.c delete mode 100644 Mac/OSX/fixversions.py delete mode 100644 Mac/OSX/sample_sitecustomize.py create mode 100644 Modules/_ctypes/libffi/src/darwin/ffitarget.h create mode 100644 Modules/_ctypes/libffi/src/x86/darwin.S create mode 100644 Modules/_ctypes/libffi/src/x86/ffi_darwin.c create mode 100644 Modules/_ctypes/libffi_msvc/mingwin32.S delete mode 100644 Modules/_sqlite/adapters.c delete mode 100644 Modules/_sqlite/adapters.h delete mode 100644 Modules/_sqlite/converters.c delete mode 100644 Modules/_sqlite/converters.h create mode 100644 Modules/_struct.c create mode 100644 Modules/expat/expat_config.h delete mode 100644 Modules/structmodule.c create mode 100644 Objects/exceptions.c create mode 100644 Objects/stringlib/README.txt create mode 100644 Objects/stringlib/count.h create mode 100644 Objects/stringlib/fastsearch.h create mode 100644 Objects/stringlib/find.h create mode 100644 Objects/stringlib/partition.h create mode 100644 PC/errmap.h create mode 100644 PC/errmap.mak delete mode 100644 PC/example_nt/example.def create mode 100644 PC/generrmap.c create mode 100644 PCbuild8/Uninstal.wse create mode 100644 PCbuild8/_bsddb.vcproj create mode 100644 PCbuild8/_ctypes.vcproj create mode 100644 PCbuild8/_ctypes_test.vcproj create mode 100644 PCbuild8/_elementtree.vcproj create mode 100644 PCbuild8/_msi.vcproj create mode 100644 PCbuild8/_socket.vcproj create mode 100644 PCbuild8/_sqlite3.vcproj create mode 100644 PCbuild8/_ssl.mak create mode 100644 PCbuild8/_ssl.vcproj create mode 100644 PCbuild8/_testcapi.vcproj create mode 100644 PCbuild8/_tkinter.vcproj create mode 100644 PCbuild8/build_ssl.py create mode 100644 PCbuild8/bz2.vcproj create mode 100644 PCbuild8/db.build create mode 100644 PCbuild8/field3.py create mode 100644 PCbuild8/installer.bmp create mode 100644 PCbuild8/make_buildinfo.c create mode 100644 PCbuild8/make_buildinfo.vcproj create mode 100644 PCbuild8/make_versioninfo.vcproj create mode 100644 PCbuild8/pcbuild.sln create mode 100644 PCbuild8/pyexpat.vcproj create mode 100644 PCbuild8/python.build create mode 100644 PCbuild8/python.iss create mode 100644 PCbuild8/python.vcproj create mode 100644 PCbuild8/python20.wse create mode 100644 PCbuild8/pythoncore.vcproj create mode 100644 PCbuild8/pythoncore_link.txt create mode 100644 PCbuild8/pythoncore_pgo.vcproj create mode 100644 PCbuild8/pythoncore_pgo_link.txt create mode 100644 PCbuild8/pythonw.vcproj create mode 100644 PCbuild8/readme.txt create mode 100644 PCbuild8/rmpyc.py create mode 100644 PCbuild8/rt.bat create mode 100644 PCbuild8/select.vcproj create mode 100644 PCbuild8/unicodedata.vcproj create mode 100644 PCbuild8/w9xpopen.vcproj create mode 100644 PCbuild8/winsound.vcproj delete mode 100644 Python/exceptions.c create mode 100755 Tools/pybench/NewInstances.py diff --git a/Doc/ACKS b/Doc/ACKS index 14a5ca8..bbb3241 100644 --- a/Doc/ACKS +++ b/Doc/ACKS @@ -195,6 +195,7 @@ Dan Wolfe Steven Work Thomas Wouters Ka-Ping Yee +Rory Yorke Moshe Zadka Milan Zamazal Cheng Zhang diff --git a/Doc/Makefile.deps b/Doc/Makefile.deps index 20c0688..11c6de0 100644 --- a/Doc/Makefile.deps +++ b/Doc/Makefile.deps @@ -155,6 +155,7 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ lib/required_2.py \ lib/libtempfile.tex \ lib/liberrno.tex \ + lib/libctypes.tex \ lib/libsomeos.tex \ lib/libsignal.tex \ lib/libsocket.tex \ @@ -179,6 +180,7 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ lib/libprofile.tex \ lib/libhotshot.tex \ lib/libtimeit.tex \ + lib/libtrace.tex \ lib/libcgi.tex \ lib/libcgitb.tex \ lib/liburllib.tex \ @@ -306,6 +308,7 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ lib/libgetpass.tex \ lib/libshutil.tex \ lib/librepr.tex \ + lib/libmsilib.tex \ lib/libmsvcrt.tex \ lib/libwinreg.tex \ lib/libwinsound.tex \ @@ -348,7 +351,8 @@ LIBFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ lib/libturtle.tex \ lib/libtarfile.tex \ lib/libcsv.tex \ - lib/libcfgparser.tex + lib/libcfgparser.tex \ + lib/libsqlite3.tex # LaTeX source files for Macintosh Library Modules. MACFILES= $(HOWTOSTYLES) $(INDEXSTYLES) $(COMMONTEX) \ diff --git a/Doc/api/abstract.tex b/Doc/api/abstract.tex index f50ebc4..7c742a0 100644 --- a/Doc/api/abstract.tex +++ b/Doc/api/abstract.tex @@ -255,6 +255,8 @@ determination. \NULL, indicating that no arguments are provided. Returns the result of the call on success, or \NULL{} on failure. This is the equivalent of the Python expression \samp{\var{callable}(*\var{args})}. + Note that if you only pass \ctype{PyObject *} args, + \cfunction{PyObject_CallFunctionObjArgs} is a faster alternative. \end{cfuncdesc} @@ -268,6 +270,8 @@ determination. indicating that no arguments are provided. Returns the result of the call on success, or \NULL{} on failure. This is the equivalent of the Python expression \samp{\var{o}.\var{method}(\var{args})}. + Note that if you only pass \ctype{PyObject *} args, + \cfunction{PyObject_CallMethodObjArgs} is a faster alternative. \end{cfuncdesc} @@ -624,7 +628,7 @@ determination. Returns the result of right shifting \var{o1} by \var{o2} on success, or \NULL{} on failure. The operation is done \emph{in-place} when \var{o1} supports it. This is the equivalent - of the Python statement \samp{\var{o1} >\code{>=} \var{o2}}. + of the Python statement \samp{\var{o1} >>= \var{o2}}. \end{cfuncdesc} diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex index 1982bae..10247ab 100644 --- a/Doc/api/concrete.tex +++ b/Doc/api/concrete.tex @@ -245,7 +245,7 @@ booleans. The following macros are available, however. \end{csimplemacrodesc} \begin{cfuncdesc}{PyObject*}{PyBool_FromLong}{long v} - Return a new reference to \constant{Py_True} or \constant{Py_False} + Return a new reference to \constant{Py_True} or \constant{Py_False} depending on the truth value of \var{v}. \versionadded{2.3} \end{cfuncdesc} @@ -618,12 +618,24 @@ parameter and are called with a non-string parameter. exactly to the format characters in the \var{format} string. The following format characters are allowed: + % This should be exactly the same as the table in PyErr_Format. + % One should just refer to the other. + + % The descriptions for %zd and %zu are wrong, but the truth is complicated + % because not all compilers support the %z width modifier -- we fake it + % when necessary via interpolating PY_FORMAT_SIZE_T. + + % %u, %lu, %zu should have "new in Python 2.5" blurbs. + \begin{tableiii}{l|l|l}{member}{Format Characters}{Type}{Comment} \lineiii{\%\%}{\emph{n/a}}{The literal \% character.} \lineiii{\%c}{int}{A single character, represented as an C int.} \lineiii{\%d}{int}{Exactly equivalent to \code{printf("\%d")}.} + \lineiii{\%u}{unsigned int}{Exactly equivalent to \code{printf("\%u")}.} \lineiii{\%ld}{long}{Exactly equivalent to \code{printf("\%ld")}.} - \lineiii{\%zd}{long}{Exactly equivalent to \code{printf("\%zd")}.} + \lineiii{\%lu}{unsigned long}{Exactly equivalent to \code{printf("\%lu")}.} + \lineiii{\%zd}{Py_ssize_t}{Exactly equivalent to \code{printf("\%zd")}.} + \lineiii{\%zu}{size_t}{Exactly equivalent to \code{printf("\%zu")}.} \lineiii{\%i}{int}{Exactly equivalent to \code{printf("\%i")}.} \lineiii{\%x}{int}{Exactly equivalent to \code{printf("\%x")}.} \lineiii{\%s}{char*}{A null-terminated C character array.} @@ -632,6 +644,10 @@ parameter and are called with a non-string parameter. guaranteed to start with the literal \code{0x} regardless of what the platform's \code{printf} yields.} \end{tableiii} + + An unrecognized format character causes all the rest of the format + string to be copied as-is to the result string, and any extra + arguments discarded. \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyString_FromFormatV}{const char *format, @@ -687,7 +703,7 @@ parameter and are called with a non-string parameter. \var{size})}. It must not be deallocated. If \var{string} is a Unicode object, this function computes the default encoding of \var{string} and operates on that. If \var{string} is not a string - object at all, \cfunction{PyString_AsStringAndSize()} returns + object at all, \cfunction{PyString_AsStringAndSize()} returns \code{-1} and raises \exception{TypeError}. \end{cfuncdesc} @@ -949,7 +965,7 @@ These APIs can be used for fast direct character conversions: \end{cfuncdesc} \begin{cfuncdesc}{double}{Py_UNICODE_TONUMERIC}{Py_UNICODE ch} - Return the character \var{ch} converted to a (positive) double. + Return the character \var{ch} converted to a double. Return \code{-1.0} if this is not possible. This macro does not raise exceptions. \end{cfuncdesc} @@ -1393,7 +1409,7 @@ The following codec API is special in that maps Unicode to Unicode. The \var{mapping} table must map Unicode ordinal integers to Unicode ordinal integers or None (causing deletion of the character). - Mapping tables need only provide the method{__getitem__()} + Mapping tables need only provide the \method{__getitem__()} interface; dictionaries and sequences work well. Unmapped character ordinals (ones which cause a \exception{LookupError}) are left untouched and are copied as-is. @@ -1494,7 +1510,7 @@ They all return \NULL{} or \code{-1} if an exception occurs. Return 1 if \var{substr} matches \var{str}[\var{start}:\var{end}] at the given tail end (\var{direction} == -1 means to do a prefix match, \var{direction} == 1 a suffix match), 0 otherwise. - Return \code{-1} if an error occurred. + Return \code{-1} if an error occurred. \end{cfuncdesc} \begin{cfuncdesc}{Py_ssize_t}{PyUnicode_Find}{PyObject *str, @@ -3013,7 +3029,7 @@ Macros for the convenience of modules implementing the DB API: \subsection{Set Objects \label{setObjects}} -\sectionauthor{Raymond D. Hettinger}{python@rcn.com} +\sectionauthor{Raymond D. Hettinger}{python@rcn.com} \obindex{set} \obindex{frozenset} @@ -3022,8 +3038,8 @@ Macros for the convenience of modules implementing the DB API: This section details the public API for \class{set} and \class{frozenset} objects. Any functionality not listed below is best accessed using the either the abstract object protocol (including -\cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()}, -\cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()}, +\cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()}, +\cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()}, \cfunction{PyObject_IsTrue()}, \cfunction{PyObject_Print()}, and \cfunction{PyObject_GetIter()}) or the abstract number protocol (including @@ -3040,7 +3056,7 @@ or the abstract number protocol (including block of memory for medium and large sized sets (much like list storage). None of the fields of this structure should be considered public and are subject to change. All access should be done through the - documented API rather than by manipulating the values in the structure. + documented API rather than by manipulating the values in the structure. \end{ctypedesc} @@ -3059,7 +3075,7 @@ The following type check macros work on pointers to any Python object. Likewise, the constructor functions work with any iterable Python object. \begin{cfuncdesc}{int}{PyAnySet_Check}{PyObject *p} - Return true if \var{p} is a \class{set} object, a \class{frozenset} + Return true if \var{p} is a \class{set} object, a \class{frozenset} object, or an instance of a subtype. \end{cfuncdesc} @@ -3112,7 +3128,7 @@ The following functions and macros are available for instances of function does not automatically convert unhashable sets into temporary frozensets. Raise a \exception{TypeError} if the \var{key} is unhashable. Raise \exception{PyExc_SystemError} if \var{anyset} is not a \class{set}, - \class{frozenset}, or an instance of a subtype. + \class{frozenset}, or an instance of a subtype. \end{cfuncdesc} The following functions are available for instances of \class{set} or @@ -3134,7 +3150,7 @@ its subtypes but not for instances of \class{frozenset} or its subtypes. unhashable. Unlike the Python \method{discard()} method, this function does not automatically convert unhashable sets into temporary frozensets. Raise \exception{PyExc_SystemError} if \var{set} is an not an instance - of \class{set} or its subtype. + of \class{set} or its subtype. \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PySet_Pop}{PyObject *set} @@ -3142,7 +3158,7 @@ its subtypes but not for instances of \class{frozenset} or its subtypes. and removes the object from the \var{set}. Return \NULL{} on failure. Raise \exception{KeyError} if the set is empty. Raise a \exception{SystemError} if \var{set} is an not an instance - of \class{set} or its subtype. + of \class{set} or its subtype. \end{cfuncdesc} \begin{cfuncdesc}{int}{PySet_Clear}{PyObject *set} diff --git a/Doc/api/exceptions.tex b/Doc/api/exceptions.tex index 62f713b..6dbe818 100644 --- a/Doc/api/exceptions.tex +++ b/Doc/api/exceptions.tex @@ -132,13 +132,32 @@ error indicator for each thread. codes, similar to \cfunction{printf()}. The \code{width.precision} before a format code is parsed, but the width part is ignored. - \begin{tableii}{c|l}{character}{Character}{Meaning} - \lineii{c}{Character, as an \ctype{int} parameter} - \lineii{d}{Number in decimal, as an \ctype{int} parameter} - \lineii{x}{Number in hexadecimal, as an \ctype{int} parameter} - \lineii{s}{A string, as a \ctype{char *} parameter} - \lineii{p}{A hex pointer, as a \ctype{void *} parameter} - \end{tableii} + % This should be exactly the same as the table in PyString_FromFormat. + % One should just refer to the other. + + % The descriptions for %zd and %zu are wrong, but the truth is complicated + % because not all compilers support the %z width modifier -- we fake it + % when necessary via interpolating PY_FORMAT_SIZE_T. + + % %u, %lu, %zu should have "new in Python 2.5" blurbs. + + \begin{tableiii}{l|l|l}{member}{Format Characters}{Type}{Comment} + \lineiii{\%\%}{\emph{n/a}}{The literal \% character.} + \lineiii{\%c}{int}{A single character, represented as an C int.} + \lineiii{\%d}{int}{Exactly equivalent to \code{printf("\%d")}.} + \lineiii{\%u}{unsigned int}{Exactly equivalent to \code{printf("\%u")}.} + \lineiii{\%ld}{long}{Exactly equivalent to \code{printf("\%ld")}.} + \lineiii{\%lu}{unsigned long}{Exactly equivalent to \code{printf("\%lu")}.} + \lineiii{\%zd}{Py_ssize_t}{Exactly equivalent to \code{printf("\%zd")}.} + \lineiii{\%zu}{size_t}{Exactly equivalent to \code{printf("\%zu")}.} + \lineiii{\%i}{int}{Exactly equivalent to \code{printf("\%i")}.} + \lineiii{\%x}{int}{Exactly equivalent to \code{printf("\%x")}.} + \lineiii{\%s}{char*}{A null-terminated C character array.} + \lineiii{\%p}{void*}{The hex representation of a C pointer. + Mostly equivalent to \code{printf("\%p")} except that it is + guaranteed to start with the literal \code{0x} regardless of + what the platform's \code{printf} yields.} + \end{tableiii} An unrecognized format character causes all the rest of the format string to be copied as-is to the result string, and any extra @@ -272,8 +291,8 @@ error indicator for each thread. command line documentation. There is no C API for warning control. \end{cfuncdesc} -\begin{cfuncdesc}{int}{PyErr_WarnExplicit}{PyObject *category, - const char *message, const char *filename, int lineno, +\begin{cfuncdesc}{int}{PyErr_WarnExplicit}{PyObject *category, + const char *message, const char *filename, int lineno, const char *module, PyObject *registry} Issue a warning message with explicit control over all warning attributes. This is a straightforward wrapper around the Python @@ -314,12 +333,14 @@ error indicator for each thread. The \var{name} argument must be the name of the new exception, a C string of the form \code{module.class}. The \var{base} and \var{dict} arguments are normally \NULL. This creates a class - object derived from the root for all exceptions, the built-in name - \exception{Exception} (accessible in C as \cdata{PyExc_Exception}). + object derived from \exception{Exception} (accessible in C as + \cdata{PyExc_Exception}). + The \member{__module__} attribute of the new class is set to the first part (up to the last dot) of the \var{name} argument, and the class name is set to the last part (after the last dot). The - \var{base} argument can be used to specify an alternate base class. + \var{base} argument can be used to specify alternate base classes; + it can either be only one class or a tuple of classes. The \var{dict} argument can be used to specify a dictionary of class variables and methods. \end{cfuncdesc} @@ -399,5 +420,5 @@ are derived from \exception{BaseException}. \withsubitem{(built-in exception)}{\ttindex{BaseException}} String exceptions are still supported in the interpreter to allow -existing code to run unmodified, but this will also change in a future +existing code to run unmodified, but this will also change in a future release. diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index 2d758b0..28f77f7 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -883,8 +883,39 @@ The following three fields only exist if the \begin{cmemberdesc}{PyTypeObject}{traverseproc}{tp_traverse} An optional pointer to a traversal function for the garbage collector. This is only used if the \constant{Py_TPFLAGS_HAVE_GC} - flag bit is set. More information in section - \ref{supporting-cycle-detection} about garbage collection. + flag bit is set. More information about Python's garbage collection + scheme can be found in section \ref{supporting-cycle-detection}. + + The \member{tp_traverse} pointer is used by the garbage collector + to detect reference cycles. A typical implementation of a + \member{tp_traverse} function simply calls \cfunction{Py_VISIT()} on + each of the instance's members that are Python objects. For exampe, this + is function \cfunction{local_traverse} from the \module{thread} extension + module: + + \begin{verbatim} + static int + local_traverse(localobject *self, visitproc visit, void *arg) + { + Py_VISIT(self->args); + Py_VISIT(self->kw); + Py_VISIT(self->dict); + return 0; + } + \end{verbatim} + + Note that \cfunction{Py_VISIT()} is called only on those members that can + participate in reference cycles. Although there is also a + \samp{self->key} member, it can only be \NULL{} or a Python string and + therefore cannot be part of a reference cycle. + + On the other hand, even if you know a member can never be part of a cycle, + as a debugging aid you may want to visit it anyway just so the + \module{gc} module's \function{get_referents()} function will include it. + + Note that \cfunction{Py_VISIT()} requires the \var{visit} and \var{arg} + parameters to \cfunction{local_traverse} to have these specific names; + don't name them just anything. This field is inherited by subtypes together with \member{tp_clear} and the \constant{Py_TPFLAGS_HAVE_GC} flag bit: the flag bit, @@ -896,8 +927,57 @@ The following three fields only exist if the \begin{cmemberdesc}{PyTypeObject}{inquiry}{tp_clear} An optional pointer to a clear function for the garbage collector. This is only used if the \constant{Py_TPFLAGS_HAVE_GC} flag bit is - set. More information in section - \ref{supporting-cycle-detection} about garbage collection. + set. + + The \member{tp_clear} member function is used to break reference + cycles in cyclic garbage detected by the garbage collector. Taken + together, all \member{tp_clear} functions in the system must combine to + break all reference cycles. This is subtle, and if in any doubt supply a + \member{tp_clear} function. For example, the tuple type does not + implement a \member{tp_clear} function, because it's possible to prove + that no reference cycle can be composed entirely of tuples. Therefore + the \member{tp_clear} functions of other types must be sufficient to + break any cycle containing a tuple. This isn't immediately obvious, and + there's rarely a good reason to avoid implementing \member{tp_clear}. + + Implementations of \member{tp_clear} should drop the instance's + references to those of its members that may be Python objects, and set + its pointers to those members to \NULL{}, as in the following example: + + \begin{verbatim} + static int + local_clear(localobject *self) + { + Py_CLEAR(self->key); + Py_CLEAR(self->args); + Py_CLEAR(self->kw); + Py_CLEAR(self->dict); + return 0; + } + \end{verbatim} + + The \cfunction{Py_CLEAR()} macro should be used, because clearing + references is delicate: the reference to the contained object must not be + decremented until after the pointer to the contained object is set to + \NULL{}. This is because decrementing the reference count may cause + the contained object to become trash, triggering a chain of reclamation + activity that may include invoking arbitrary Python code (due to + finalizers, or weakref callbacks, associated with the contained object). + If it's possible for such code to reference \var{self} again, it's + important that the pointer to the contained object be \NULL{} at that + time, so that \var{self} knows the contained object can no longer be + used. The \cfunction{Py_CLEAR()} macro performs the operations in a + safe order. + + Because the goal of \member{tp_clear} functions is to break reference + cycles, it's not necessary to clear contained objects like Python strings + or Python integers, which can't participate in reference cycles. + On the other hand, it may be convenient to clear all contained Python + objects, and write the type's \member{tp_dealloc} function to + invoke \member{tp_clear}. + + More information about Python's garbage collection + scheme can be found in section \ref{supporting-cycle-detection}. This field is inherited by subtypes together with \member{tp_clear} and the \constant{Py_TPFLAGS_HAVE_GC} flag bit: the flag bit, @@ -910,10 +990,10 @@ The following three fields only exist if the An optional pointer to the rich comparison function. The signature is the same as for \cfunction{PyObject_RichCompare()}. - The function should return \code{1} if the requested comparison - returns true, \code{0} if it returns false. It should return - \code{-1} and set an exception condition when an error occurred - during the comparison. + The function should return the result of the comparison (usually + \code{Py_True} or \code{Py_False}). If the comparison is undefined, + it must return \code{Py_NotImplemented}, if another error occurred + it must return \code{NULL} and set an exception condition. This field is inherited by subtypes together with \member{tp_compare} and \member{tp_hash}: a subtype inherits all diff --git a/Doc/api/refcounts.dat b/Doc/api/refcounts.dat index 7bba011..ab6d865 100644 --- a/Doc/api/refcounts.dat +++ b/Doc/api/refcounts.dat @@ -1147,7 +1147,7 @@ PySet_Discard:PyObject*:key:-1:no effect if key not found PySet_New:PyObject*::+1: PySet_New:PyObject*:iterable:0: -PySet_Pop:PyObject*::0:or returns NULL and raises KeyError if set is empty +PySet_Pop:PyObject*::+1:or returns NULL and raises KeyError if set is empty PySet_Pop:PyObject*:set:0: PySet_Size:int::: diff --git a/Doc/commontex/boilerplate.tex b/Doc/commontex/boilerplate.tex index 55a4184..b4c9f48 100644 --- a/Doc/commontex/boilerplate.tex +++ b/Doc/commontex/boilerplate.tex @@ -5,5 +5,5 @@ Email: \email{docs@python.org} } -\date{5th April 2006} % XXX update before final release! +\date{\today} % XXX update before final release! \input{patchlevel} % include Python version information diff --git a/Doc/dist/dist.tex b/Doc/dist/dist.tex index 3ba51d0..9970ec2 100644 --- a/Doc/dist/dist.tex +++ b/Doc/dist/dist.tex @@ -1760,16 +1760,16 @@ 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). -You can use the \programopt{--sign} option to tell \command{upload} to +You can use the \longprogramopt{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 \programopt{--identity=\var{name}} option. +using the \longprogramopt{identity=\var{name}} option. Other \command{upload} options include -\programopt{--repository=\var{url}} (which lets you override the +\longprogramopt{repository=\var{url}} (which lets you override the repository setting from \file{\$HOME/.pypirc}), and -\programopt{--show-response} (which displays the full response text +\longprogramopt{show-response} (which displays the full response text from the PyPI server for help in debugging upload problems). \chapter{Examples} diff --git a/Doc/ext/windows.tex b/Doc/ext/windows.tex index a821094..ca18a1e 100644 --- a/Doc/ext/windows.tex +++ b/Doc/ext/windows.tex @@ -88,7 +88,7 @@ described here are distributed with the Python sources in the Once the Debug build has succeeded, bring up a DOS box, and change to the \file{example_nt\textbackslash Debug} directory. You should now be able to repeat the following session (\code{C>} is - the DOS prompt, \code{>\code{>}>} is the Python prompt; note that + the DOS prompt, \code{>>>} is the Python prompt; note that build information and various debug output from Python may not match this screen dump exactly): diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index 0946bdc..f92471a 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -158,7 +158,7 @@ that are more efficient and convenient. Encodings don't have to handle every possible Unicode character, and most encodings don't. For example, Python's default encoding is the 'ascii' encoding. The rules for converting a Unicode string into the -ASCII encoding are are simple; for each code point: +ASCII encoding are simple; for each code point: 1. If the code point is <128, each byte is the same as the value of the code point. @@ -721,7 +721,7 @@ Revision History and Acknowledgements Thanks to the following people who have noted errors or offered suggestions on this article: Nicholas Bastin, Marius Gedminas, Kent Johnson, Ken Krugler, -Marc-André Lemburg, Martin von Löwis. +Marc-André Lemburg, Martin von Löwis, Chad Whitacre. Version 1.0: posted August 5 2005. diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst new file mode 100644 index 0000000..69ce508 --- /dev/null +++ b/Doc/howto/urllib2.rst @@ -0,0 +1,598 @@ +============================================== + HOWTO Fetch Internet Resources Using urllib2 +============================================== +---------------------------- + Fetching URLs With Python +---------------------------- + + +.. note:: + + There is an French translation of an earlier revision of this + HOWTO, available at `urllib2 - Le Manuel manquant + `_. + +.. contents:: urllib2 Tutorial + + +Introduction +============ + +.. sidebar:: Related Articles + + You may also find useful the following article on fetching web + resources with Python : + + * `Basic Authentication `_ + + A tutorial on *Basic Authentication*, with examples in Python. + + This HOWTO is written by `Michael Foord + `_. + +**urllib2** is a `Python `_ module for fetching URLs +(Uniform Resource Locators). It offers a very simple interface, in the form of +the *urlopen* function. This is capable of fetching URLs using a variety +of different protocols. It also offers a slightly more complex +interface for handling common situations - like basic authentication, +cookies, proxies and so on. These are provided by objects called +handlers and openers. + +urllib2 supports fetching URLs for many "URL schemes" (identified by the string +before the ":" in URL - for example "ftp" is the URL scheme of +"ftp://python.org/") using their associated network protocols (e.g. FTP, HTTP). +This tutorial focuses on the most common case, HTTP. + +For straightforward situations *urlopen* is very easy to use. But as +soon as you encounter errors or non-trivial cases when opening HTTP +URLs, you will need some understanding of the HyperText Transfer +Protocol. The most comprehensive and authoritative reference to HTTP +is :RFC:`2616`. This is a technical document and not intended to be +easy to read. This HOWTO aims to illustrate using *urllib2*, with +enough detail about HTTP to help you through. It is not intended to +replace the `urllib2 docs `_ , +but is supplementary to them. + + +Fetching URLs +============= + +The simplest way to use urllib2 is as follows : :: + + import urllib2 + response = urllib2.urlopen('http://python.org/') + html = response.read() + +Many uses of urllib2 will be that simple (note that instead of an +'http:' URL we could have used an URL starting with 'ftp:', 'file:', +etc.). However, it's the purpose of this tutorial to explain the more +complicated cases, concentrating on HTTP. + +HTTP is based on requests and responses - the client makes requests +and servers send responses. urllib2 mirrors this with a ``Request`` +object which represents the HTTP request you are making. In its +simplest form you create a Request object that specifies the URL you +want to fetch. Calling ``urlopen`` with this Request object returns a +response object for the URL requested. This response is a file-like +object, which means you can for example call .read() on the response : +:: + + import urllib2 + + req = urllib2.Request('http://www.voidspace.org.uk') + response = urllib2.urlopen(req) + the_page = response.read() + +Note that urllib2 makes use of the same Request interface to handle +all URL schemes. For example, you can make an FTP request like so: :: + + req = urllib2.Request('ftp://example.com/') + +In the case of HTTP, there are two extra things that Request objects +allow you to do: First, you can pass data to be sent to the server. +Second, you can pass extra information ("metadata") *about* the data +or the about request itself, to the server - this information is sent +as HTTP "headers". Let's look at each of these in turn. + +Data +---- + +Sometimes you want to send data to a URL (often the URL will refer to +a CGI (Common Gateway Interface) script [#]_ or other web +application). With HTTP, this is often done using what's known as a +**POST** request. This is often what your browser does when you submit +a HTML form that you filled in on the web. Not all POSTs have to come +from forms: you can use a POST to transmit arbitrary data to your own +application. In the common case of HTML forms, the data needs to be +encoded in a standard way, and then passed to the Request object as +the ``data`` argument. The encoding is done using a function from the +``urllib`` library *not* from ``urllib2``. :: + + import urllib + import urllib2 + + url = 'http://www.someserver.com/cgi-bin/register.cgi' + values = {'name' : 'Michael Foord', + 'location' : 'Northampton', + 'language' : 'Python' } + + data = urllib.urlencode(values) + req = urllib2.Request(url, data) + response = urllib2.urlopen(req) + the_page = response.read() + +Note that other encodings are sometimes required (e.g. for file upload +from HTML forms - see +`HTML Specification, Form Submission `_ +for more details). + +If you do not pass the ``data`` argument, urllib2 uses a **GET** +request. One way in which GET and POST requests differ is that POST +requests often have "side-effects": they change the state of the +system in some way (for example by placing an order with the website +for a hundredweight of tinned spam to be delivered to your door). +Though the HTTP standard makes it clear that POSTs are intended to +*always* cause side-effects, and GET requests *never* to cause +side-effects, nothing prevents a GET request from having side-effects, +nor a POST requests from having no side-effects. Data can also be +passed in an HTTP GET request by encoding it in the URL itself. + +This is done as follows:: + + >>> import urllib2 + >>> import urllib + >>> data = {} + >>> data['name'] = 'Somebody Here' + >>> data['location'] = 'Northampton' + >>> data['language'] = 'Python' + >>> url_values = urllib.urlencode(data) + >>> print url_values + name=Somebody+Here&language=Python&location=Northampton + >>> url = 'http://www.example.com/example.cgi' + >>> full_url = url + '?' + url_values + >>> data = urllib2.open(full_url) + +Notice that the full URL is created by adding a ``?`` to the URL, followed by +the encoded values. + +Headers +------- + +We'll discuss here one particular HTTP header, to illustrate how to +add headers to your HTTP request. + +Some websites [#]_ dislike being browsed by programs, or send +different versions to different browsers [#]_ . By default urllib2 +identifies itself as ``Python-urllib/x.y`` (where ``x`` and ``y`` are +the major and minor version numbers of the Python release, +e.g. ``Python-urllib/2.5``), which may confuse the site, or just plain +not work. The way a browser identifies itself is through the +``User-Agent`` header [#]_. When you create a Request object you can +pass a dictionary of headers in. The following example makes the same +request as above, but identifies itself as a version of Internet +Explorer [#]_. :: + + import urllib + import urllib2 + + url = 'http://www.someserver.com/cgi-bin/register.cgi' + user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' + values = {'name' : 'Michael Foord', + 'location' : 'Northampton', + 'language' : 'Python' } + headers = { 'User-Agent' : user_agent } + + data = urllib.urlencode(values) + req = urllib2.Request(url, data, headers) + response = urllib2.urlopen(req) + the_page = response.read() + +The response also has two useful methods. See the section on `info and +geturl`_ which comes after we have a look at what happens when things +go wrong. + + +Handling Exceptions +=================== + +*urlopen* raises ``URLError`` when it cannot handle a response (though +as usual with Python APIs, builtin exceptions such as ValueError, +TypeError etc. may also be raised). + +``HTTPError`` is the subclass of ``URLError`` raised in the specific +case of HTTP URLs. + +URLError +-------- + +Often, URLError is raised because there is no network connection (no +route to the specified server), or the specified server doesn't exist. +In this case, the exception raised will have a 'reason' attribute, +which is a tuple containing an error code and a text error message. + +e.g. :: + + >>> req = urllib2.Request('http://www.pretend_server.org') + >>> try: urllib2.urlopen(req) + >>> except URLError, e: + >>> print e.reason + >>> + (4, 'getaddrinfo failed') + + +HTTPError +--------- + +Every HTTP response from the server contains a numeric "status +code". Sometimes the status code indicates that the server is unable +to fulfil the request. The default handlers will handle some of these +responses for you (for example, if the response is a "redirection" +that requests the client fetch the document from a different URL, +urllib2 will handle that for you). For those it can't handle, urlopen +will raise an ``HTTPError``. Typical errors include '404' (page not +found), '403' (request forbidden), and '401' (authentication +required). + +See section 10 of RFC 2616 for a reference on all the HTTP error +codes. + +The ``HTTPError`` instance raised will have an integer 'code' +attribute, which corresponds to the error sent by the server. + +Error Codes +~~~~~~~~~~~ + +Because the default handlers handle redirects (codes in the 300 +range), and codes in the 100-299 range indicate success, you will +usually only see error codes in the 400-599 range. + +``BaseHTTPServer.BaseHTTPRequestHandler.responses`` is a useful +dictionary of response codes in that shows all the response codes used +by RFC 2616. The dictionary is reproduced here for convenience :: + + # Table mapping response codes to messages; entries have the + # form {code: (shortmessage, longmessage)}. + responses = { + 100: ('Continue', 'Request received, please continue'), + 101: ('Switching Protocols', + 'Switching to new protocol; obey Upgrade header'), + + 200: ('OK', 'Request fulfilled, document follows'), + 201: ('Created', 'Document created, URL follows'), + 202: ('Accepted', + 'Request accepted, processing continues off-line'), + 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), + 204: ('No Content', 'Request fulfilled, nothing follows'), + 205: ('Reset Content', 'Clear input form for further input.'), + 206: ('Partial Content', 'Partial content follows.'), + + 300: ('Multiple Choices', + 'Object has several resources -- see URI list'), + 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), + 302: ('Found', 'Object moved temporarily -- see URI list'), + 303: ('See Other', 'Object moved -- see Method and URL list'), + 304: ('Not Modified', + 'Document has not changed since given time'), + 305: ('Use Proxy', + 'You must use proxy specified in Location to access this ' + 'resource.'), + 307: ('Temporary Redirect', + 'Object moved temporarily -- see URI list'), + + 400: ('Bad Request', + 'Bad request syntax or unsupported method'), + 401: ('Unauthorized', + 'No permission -- see authorization schemes'), + 402: ('Payment Required', + 'No payment -- see charging schemes'), + 403: ('Forbidden', + 'Request forbidden -- authorization will not help'), + 404: ('Not Found', 'Nothing matches the given URI'), + 405: ('Method Not Allowed', + 'Specified method is invalid for this server.'), + 406: ('Not Acceptable', 'URI not available in preferred format.'), + 407: ('Proxy Authentication Required', 'You must authenticate with ' + 'this proxy before proceeding.'), + 408: ('Request Timeout', 'Request timed out; try again later.'), + 409: ('Conflict', 'Request conflict.'), + 410: ('Gone', + 'URI no longer exists and has been permanently removed.'), + 411: ('Length Required', 'Client must specify Content-Length.'), + 412: ('Precondition Failed', 'Precondition in headers is false.'), + 413: ('Request Entity Too Large', 'Entity is too large.'), + 414: ('Request-URI Too Long', 'URI is too long.'), + 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), + 416: ('Requested Range Not Satisfiable', + 'Cannot satisfy request range.'), + 417: ('Expectation Failed', + 'Expect condition could not be satisfied.'), + + 500: ('Internal Server Error', 'Server got itself in trouble'), + 501: ('Not Implemented', + 'Server does not support this operation'), + 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), + 503: ('Service Unavailable', + 'The server cannot process the request due to a high load'), + 504: ('Gateway Timeout', + 'The gateway server did not receive a timely response'), + 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), + } + +When an error is raised the server responds by returning an HTTP error +code *and* an error page. You can use the ``HTTPError`` instance as a +response on the page returned. This means that as well as the code +attribute, it also has read, geturl, and info, methods. :: + + >>> req = urllib2.Request('http://www.python.org/fish.html') + >>> try: + >>> urllib2.urlopen(req) + >>> except URLError, e: + >>> print e.code + >>> print e.read() + >>> + 404 + + + Error 404: File Not Found + ...... etc... + +Wrapping it Up +-------------- + +So if you want to be prepared for ``HTTPError`` *or* ``URLError`` +there are two basic approaches. I prefer the second approach. + +Number 1 +~~~~~~~~ + +:: + + + from urllib2 import Request, urlopen, URLError, HTTPError + req = Request(someurl) + try: + response = urlopen(req) + except HTTPError, e: + print 'The server couldn\'t fulfill the request.' + print 'Error code: ', e.code + except URLError, e: + print 'We failed to reach a server.' + print 'Reason: ', e.reason + else: + # everything is fine + + +.. note:: + + The ``except HTTPError`` *must* come first, otherwise ``except URLError`` + will *also* catch an ``HTTPError``. + +Number 2 +~~~~~~~~ + +:: + + from urllib2 import Request, urlopen, URLError + req = Request(someurl) + try: + response = urlopen(req) + except URLError, e: + if hasattr(e, 'reason'): + print 'We failed to reach a server.' + print 'Reason: ', e.reason + elif hasattr(e, 'code'): + print 'The server couldn\'t fulfill the request.' + print 'Error code: ', e.code + else: + # everything is fine + + +info and geturl +=============== + +The response returned by urlopen (or the ``HTTPError`` instance) has +two useful methods ``info`` and ``geturl``. + +**geturl** - this returns the real URL of the page fetched. This is +useful because ``urlopen`` (or the opener object used) may have +followed a redirect. The URL of the page fetched may not be the same +as the URL requested. + +**info** - this returns a dictionary-like object that describes the +page fetched, particularly the headers sent by the server. It is +currently an ``httplib.HTTPMessage`` instance. + +Typical headers include 'Content-length', 'Content-type', and so +on. See the +`Quick Reference to HTTP Headers `_ +for a useful listing of HTTP headers with brief explanations of their meaning +and use. + + +Openers and Handlers +==================== + +When you fetch a URL you use an opener (an instance of the perhaps +confusingly-named ``urllib2.OpenerDirector``). Normally we have been using +the default opener - via ``urlopen`` - but you can create custom +openers. Openers use handlers. All the "heavy lifting" is done by the +handlers. Each handler knows how to open URLs for a particular URL +scheme (http, ftp, etc.), or how to handle an aspect of URL opening, +for example HTTP redirections or HTTP cookies. + +You will want to create openers if you want to fetch URLs with +specific handlers installed, for example to get an opener that handles +cookies, or to get an opener that does not handle redirections. + +To create an opener, instantiate an OpenerDirector, and then call +.add_handler(some_handler_instance) repeatedly. + +Alternatively, you can use ``build_opener``, which is a convenience +function for creating opener objects with a single function call. +``build_opener`` adds several handlers by default, but provides a +quick way to add more and/or override the default handlers. + +Other sorts of handlers you might want to can handle proxies, +authentication, and other common but slightly specialised +situations. + +``install_opener`` can be used to make an ``opener`` object the +(global) default opener. This means that calls to ``urlopen`` will use +the opener you have installed. + +Opener objects have an ``open`` method, which can be called directly +to fetch urls in the same way as the ``urlopen`` function: there's no +need to call ``install_opener``, except as a convenience. + + +Basic Authentication +==================== + +To illustrate creating and installing a handler we will use the +``HTTPBasicAuthHandler``. For a more detailed discussion of this +subject - including an explanation of how Basic Authentication works - +see the `Basic Authentication Tutorial `_. + +When authentication is required, the server sends a header (as well as +the 401 error code) requesting authentication. This specifies the +authentication scheme and a 'realm'. The header looks like : +``Www-authenticate: SCHEME realm="REALM"``. + +e.g. :: + + Www-authenticate: Basic realm="cPanel Users" + + +The client should then retry the request with the appropriate name and +password for the realm included as a header in the request. This is +'basic authentication'. In order to simplify this process we can +create an instance of ``HTTPBasicAuthHandler`` and an opener to use +this handler. + +The ``HTTPBasicAuthHandler`` uses an object called a password manager +to handle the mapping of URLs and realms to passwords and +usernames. If you know what the realm is (from the authentication +header sent by the server), then you can use a +``HTTPPasswordMgr``. Frequently one doesn't care what the realm is. In +that case, it is convenient to use +``HTTPPasswordMgrWithDefaultRealm``. This allows you to specify a +default username and password for a URL. This will be supplied in the +absence of you providing an alternative combination for a specific +realm. We indicate this by providing ``None`` as the realm argument to +the ``add_password`` method. + +The top-level URL is the first URL that requires authentication. URLs +"deeper" than the URL you pass to .add_password() will also match. :: + + # create a password manager + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + + # Add the username and password. + # If we knew the realm, we could use it instead of ``None``. + top_level_url = "http://example.com/foo/" + password_mgr.add_password(None, top_level_url, username, password) + + handler = urllib2.HTTPBasicAuthHandler(password_mgr) + + # create "opener" (OpenerDirector instance) + opener = urllib2.build_opener(handler) + + # use the opener to fetch a URL + opener.open(a_url) + + # Install the opener. + # Now all calls to urllib2.urlopen use our opener. + urllib2.install_opener(opener) + +.. note:: + + In the above example we only supplied our ``HHTPBasicAuthHandler`` + to ``build_opener``. By default openers have the handlers for + normal situations - ``ProxyHandler``, ``UnknownHandler``, + ``HTTPHandler``, ``HTTPDefaultErrorHandler``, + ``HTTPRedirectHandler``, ``FTPHandler``, ``FileHandler``, + ``HTTPErrorProcessor``. + +top_level_url is in fact *either* a full URL (including the 'http:' +scheme component and the hostname and optionally the port number) +e.g. "http://example.com/" *or* an "authority" (i.e. the hostname, +optionally including the port number) e.g. "example.com" or +"example.com:8080" (the latter example includes a port number). The +authority, if present, must NOT contain the "userinfo" component - for +example "joe@password:example.com" is not correct. + + +Proxies +======= + +**urllib2** will auto-detect your proxy settings and use those. This +is through the ``ProxyHandler`` which is part of the normal handler +chain. Normally that's a good thing, but there are occasions when it +may not be helpful [#]_. One way to do this is to setup our own +``ProxyHandler``, with no proxies defined. This is done using similar +steps to setting up a `Basic Authentication`_ handler : :: + + >>> proxy_support = urllib2.ProxyHandler({}) + >>> opener = urllib2.build_opener(proxy_support) + >>> urllib2.install_opener(opener) + +.. note:: + + Currently ``urllib2`` *does not* support fetching of ``https`` + locations through a proxy. This can be a problem. + +Sockets and Layers +================== + +The Python support for fetching resources from the web is +layered. urllib2 uses the httplib library, which in turn uses the +socket library. + +As of Python 2.3 you can specify how long a socket should wait for a +response before timing out. This can be useful in applications which +have to fetch web pages. By default the socket module has *no timeout* +and can hang. Currently, the socket timeout is not exposed at the +httplib or urllib2 levels. However, you can set the default timeout +globally for all sockets using : :: + + import socket + import urllib2 + + # timeout in seconds + timeout = 10 + socket.setdefaulttimeout(timeout) + + # this call to urllib2.urlopen now uses the default timeout + # we have set in the socket module + req = urllib2.Request('http://www.voidspace.org.uk') + response = urllib2.urlopen(req) + + +------- + + +Footnotes +========= + +This document was reviewed and revised by John Lee. + +.. [#] For an introduction to the CGI protocol see + `Writing Web Applications in Python `_. +.. [#] Like Google for example. The *proper* way to use google from a program + is to use `PyGoogle `_ of course. See + `Voidspace Google `_ + for some examples of using the Google API. +.. [#] Browser sniffing is a very bad practise for website design - building + sites using web standards is much more sensible. Unfortunately a lot of + sites still send different versions to different browsers. +.. [#] The user agent for MSIE 6 is + *'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)'* +.. [#] For details of more HTTP request headers, see + `Quick Reference to HTTP Headers`_. +.. [#] In my case I have to use a proxy to access the internet at work. If you + attempt to fetch *localhost* URLs through this proxy it blocks them. IE + is set to use the proxy, which urllib2 picks up on. In order to test + scripts with a localhost server, I have to prevent urllib2 from using + the proxy. diff --git a/Doc/inst/inst.tex b/Doc/inst/inst.tex index 4961a1a..676f8ae 100644 --- a/Doc/inst/inst.tex +++ b/Doc/inst/inst.tex @@ -726,8 +726,8 @@ There are two environment variables that can modify \code{sys.path}. \envvar{PYTHONHOME} sets an alternate value for the prefix of the Python installation. For example, if \envvar{PYTHONHOME} is set to \samp{/www/python}, the search path will be set to \code{['', -'/www/python/lib/python2.2/', '/www/python/lib/python2.3/plat-linux2', -...]}. +'/www/python/lib/python\shortversion/', +'/www/python/lib/python\shortversion/plat-linux2', ...]}. The \envvar{PYTHONPATH} variable can be set to a list of paths that will be added to the beginning of \code{sys.path}. For example, if @@ -981,15 +981,15 @@ 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 \Cpp, which uses COFF as the object file format.) For this reason you have to convert Python's library -\file{python24.lib} into the Borland format. You can do this as +\file{python25.lib} into the Borland format. You can do this as follows: \begin{verbatim} -coff2omf python24.lib python24_bcpp.lib +coff2omf python25.lib python25_bcpp.lib \end{verbatim} The \file{coff2omf} program comes with the Borland compiler. The file -\file{python24.lib} is in the \file{Libs} directory of your Python +\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. @@ -1053,17 +1053,23 @@ First you have to create a list of symbols which the Python DLL exports. PExports 0.42h there.) \begin{verbatim} -pexports python24.dll >python24.def +pexports python25.dll >python25.def \end{verbatim} +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. \begin{verbatim} -dlltool --dllname python24.dll --def python24.def --output-lib libpython24.a +/cygwin/bin/dlltool --dllname python25.dll --def python25.def --output-lib libpython25.a \end{verbatim} The resulting library has to be placed in the same directory as -\file{python24.lib}. (Should be the \file{libs} directory under your +\file{python25.lib}. (Should be the \file{libs} directory under your Python installation directory.) If your extension uses other libraries (zlib,...) you might diff --git a/Doc/lib/lib.tex b/Doc/lib/lib.tex index eac35de..cf657c3 100644 --- a/Doc/lib/lib.tex +++ b/Doc/lib/lib.tex @@ -224,6 +224,7 @@ and how to embed it in other applications. \input{libdbhash} \input{libbsddb} \input{libdumbdbm} +\input{libsqlite3} % ============= @@ -243,6 +244,8 @@ and how to embed it in other applications. \input{libcursespanel} \input{libplatform} \input{liberrno} +\input{libctypes} +\input{libctypesref} \input{libsomeos} % Optional Operating System Services \input{libselect} @@ -359,7 +362,7 @@ and how to embed it in other applications. \input{libprofile} % The Python Profiler \input{libhotshot} % unmaintained C profiler \input{libtimeit} - +\input{libtrace} % ============= % PYTHON ENGINE @@ -444,6 +447,7 @@ and how to embed it in other applications. \input{libsunaudio} \input{windows} % MS Windows ONLY +\input{libmsilib} \input{libmsvcrt} \input{libwinreg} \input{libwinsound} diff --git a/Doc/lib/libcodecs.tex b/Doc/lib/libcodecs.tex index 6e0bc8d..05c0375 100644 --- a/Doc/lib/libcodecs.tex +++ b/Doc/lib/libcodecs.tex @@ -161,7 +161,7 @@ directly. \end{funcdesc} \begin{funcdesc}{lookup_error}{name} -Return the error handler previously register under the name \var{name}. +Return the error handler previously registered under the name \var{name}. Raises a \exception{LookupError} in case the handler cannot be found. \end{funcdesc} @@ -366,7 +366,7 @@ steps. It defines the following methods which every incremental encoder must define in order to be compatible with the Python codec registry. \begin{classdesc}{IncrementalEncoder}{\optional{errors}} - Constructor for a \class{IncrementalEncoder} instance. + Constructor for an \class{IncrementalEncoder} instance. All incremental encoders must provide this constructor interface. They are free to add additional keyword arguments, but only the ones defined @@ -413,7 +413,7 @@ steps. It defines the following methods which every incremental decoder must define in order to be compatible with the Python codec registry. \begin{classdesc}{IncrementalDecoder}{\optional{errors}} - Constructor for a \class{IncrementalDecoder} instance. + Constructor for an \class{IncrementalDecoder} instance. All incremental decoders must provide this constructor interface. They are free to add additional keyword arguments, but only the ones defined diff --git a/Doc/lib/libcodeop.tex b/Doc/lib/libcodeop.tex index 7d6153e..6972b6f 100644 --- a/Doc/lib/libcodeop.tex +++ b/Doc/lib/libcodeop.tex @@ -19,7 +19,7 @@ There are two parts to this job: \begin{enumerate} \item Being able to tell if a line of input completes a Python statement: in short, telling whether to print - `\code{>\code{>}>~}' or `\code{...~}' next. + `\code{>>>~}' or `\code{...~}' next. \item Remembering which future statements the user has entered, so subsequent input can be compiled with these in effect. \end{enumerate} diff --git a/Doc/lib/libcollections.tex b/Doc/lib/libcollections.tex index d9bfa39..3e56a3e 100644 --- a/Doc/lib/libcollections.tex +++ b/Doc/lib/libcollections.tex @@ -59,12 +59,12 @@ Deque objects support the following methods: \begin{methoddesc}{pop}{} Remove and return an element from the right side of the deque. - If no elements are present, raises a \exception{IndexError}. + If no elements are present, raises an \exception{IndexError}. \end{methoddesc} \begin{methoddesc}{popleft}{} Remove and return an element from the left side of the deque. - If no elements are present, raises a \exception{IndexError}. + If no elements are present, raises an \exception{IndexError}. \end{methoddesc} \begin{methoddesc}{remove}{value} diff --git a/Doc/lib/libcontextlib.tex b/Doc/lib/libcontextlib.tex index 46f9cdd..72bf537 100644 --- a/Doc/lib/libcontextlib.tex +++ b/Doc/lib/libcontextlib.tex @@ -12,11 +12,13 @@ This module provides utilities for common tasks involving the Functions provided: \begin{funcdesc}{contextmanager}{func} -This function is a decorator that can be used to define context managers -for use with the \keyword{with} statement, without needing to create a -class or separate \method{__enter__()} and \method{__exit__()} methods. +This function is a decorator that can be used to define a factory +function for \keyword{with} statement context managers, without +needing to create a class or separate \method{__enter__()} and +\method{__exit__()} methods. -A simple example: +A simple example (this is not recommended as a real way of +generating HTML!): \begin{verbatim} from __future__ import with_statement @@ -36,9 +38,10 @@ foo \end{verbatim} -When called, the decorated function must return a generator-iterator. -This iterator must yield exactly one value, which will be bound to the -targets in the \keyword{with} statement's \keyword{as} clause, if any. +The function being decorated must return a generator-iterator when +called. This iterator must yield exactly one value, which will be +bound to the targets in the \keyword{with} statement's \keyword{as} +clause, if any. At the point where the generator yields, the block nested in the \keyword{with} statement is executed. The generator is then resumed @@ -46,37 +49,16 @@ after the block is exited. If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a \keyword{try}...\keyword{except}...\keyword{finally} statement to trap -the error (if any), or ensure that some cleanup takes place. - -Note that you can use \code{@contextmanager} to define a context -manager's \method{__context__} method. This is usually more convenient -than creating another class just to serve as a context. For example: - -\begin{verbatim} -from __future__ import with_statement -from contextlib import contextmanager - -class Tag: - def __init__(self, name): - self.name = name - - @contextmanager - def __context__(self): - print "<%s>" % self.name - yield self - print "" % self.name - -h1 = Tag("h1") - ->>> with h1 as me: -... print "hello from", me -

-hello from <__main__.Tag instance at 0x402ce8ec> -

-\end{verbatim} +the error (if any), or ensure that some cleanup takes place. If an +exception is trapped merely in order to log it or to perform some +action (rather than to suppress it entirely), the generator must +reraise that exception. Otherwise the generator context manager will +indicate to the \keyword{with} statement that the exception has been +handled, and execution will resume with the statement immediately +following the \keyword{with} statement. \end{funcdesc} -\begin{funcdesc}{nested}{ctx1\optional{, ctx2\optional{, ...}}} +\begin{funcdesc}{nested}{mgr1\optional{, mgr2\optional{, ...}}} Combine multiple context managers into a single nested context manager. Code like this: @@ -97,18 +79,22 @@ with A as X: do_something() \end{verbatim} -Note that if one of the nested contexts' \method{__exit__()} method -raises an exception, any previous exception state will be lost; the new -exception will be passed to the outer contexts' \method{__exit__()} -method(s), if any. In general, \method{__exit__()} methods should avoid -raising exceptions, and in particular they should not re-raise a +Note that if the \method{__exit__()} method of one of the nested +context managers indicates an exception should be suppressed, no +exception information will be passed to any remaining outer context +managers. Similarly, if the \method{__exit__()} method of one of the +nested managers raises an exception, any previous exception state will +be lost; the new exception will be passed to the +\method{__exit__()} methods of any remaining outer context managers. +In general, \method{__exit__()} methods should avoid raising +exceptions, and in particular they should not re-raise a passed-in exception. \end{funcdesc} \label{context-closing} \begin{funcdesc}{closing}{thing} -Return a context manager that closes \var{thing} upon completion of the -block. This is basically equivalent to: +Return a context manager that closes \var{thing} upon completion of +the block. This is basically equivalent to: \begin{verbatim} from contextlib import contextmanager @@ -127,14 +113,14 @@ from __future__ import with_statement from contextlib import closing import codecs -with closing(codecs.open("foo", encoding="utf8")) as f: - for line in f: - print line.encode("latin1") +with closing(urllib.urlopen('http://www.python.org')) as page: + for line in page: + print line \end{verbatim} -without needing to explicitly close \code{f}. Even if an error occurs, -\code{f.close()} will be called when the \keyword{with} block is exited. - +without needing to explicitly close \code{page}. Even if an error +occurs, \code{page.close()} will be called when the \keyword{with} +block is exited. \end{funcdesc} \begin{seealso} diff --git a/Doc/lib/libctypes.tex b/Doc/lib/libctypes.tex new file mode 100755 index 0000000..dc37749 --- /dev/null +++ b/Doc/lib/libctypes.tex @@ -0,0 +1,1226 @@ +\newlength{\locallinewidth} +\setlength{\locallinewidth}{\linewidth} +\section{\module{ctypes} --- A foreign function library for Python.} +\declaremodule{standard}{ctypes} +\moduleauthor{Thomas Heller}{theller@python.net} +\modulesynopsis{A foreign function library for Python.} +\versionadded{2.5} + +\code{ctypes} is a foreign function library for Python. + + +\subsection{ctypes tutorial\label{ctypes-ctypes-tutorial}} + +This tutorial describes version 0.9.9 of \code{ctypes}. + +Note: The code samples in this tutorial uses \code{doctest} to make sure +that they actually work. Since some code samples behave differently +under Linux, Windows, or Mac OS X, they contain doctest directives in +comments. + +Note: Quite some code samples references the ctypes \class{c{\_}int} type. +This type is an alias to the \class{c{\_}long} type on 32-bit systems. So, +you should not be confused if \class{c{\_}long} is printed if you would +expect \class{c{\_}int} - they are actually the same type. + + +\subsubsection{Loading dynamic link libraries\label{ctypes-loading-dynamic-link-libraries}} + +\code{ctypes} exports the \var{cdll}, and on Windows also \var{windll} and +\var{oledll} objects to load dynamic link libraries. + +You load libraries by accessing them as attributes of these objects. +\var{cdll} loads libraries which export functions using the standard +\code{cdecl} calling convention, while \var{windll} libraries call +functions using the \code{stdcall} calling convention. \var{oledll} also +uses the \code{stdcall} calling convention, and assumes the functions +return a Windows \class{HRESULT} error code. The error code is used to +automatically raise \class{WindowsError} Python exceptions when the +function call fails. + +Here are some examples for Windows, note that \code{msvcrt} is the MS +standard C library containing most standard C functions, and uses the +cdecl calling convention: +\begin{verbatim} +>>> from ctypes import * +>>> print windll.kernel32 # doctest: +WINDOWS + +>>> print cdll.msvcrt # doctest: +WINDOWS + +>>> libc = cdll.msvcrt # doctest: +WINDOWS +>>> +\end{verbatim} + +Windows appends the usual '.dll' file suffix automatically. + +On Linux, it is required to specify the filename \emph{including} the +extension to load a library, so attribute access does not work. +Either the \method{LoadLibrary} method of the dll loaders should be used, +or you should load the library by creating an instance of CDLL by +calling the constructor: +\begin{verbatim} +>>> cdll.LoadLibrary("libc.so.6") # doctest: +LINUX + +>>> libc = CDLL("libc.so.6") # doctest: +LINUX +>>> libc # doctest: +LINUX + +>>> +\end{verbatim} + +XXX Add section for Mac OS X. + + +\subsubsection{Accessing functions from loaded dlls\label{ctypes-accessing-functions-from-loaded-dlls}} + +Functions are accessed as attributes of dll objects: +\begin{verbatim} +>>> from ctypes import * +>>> libc.printf +<_FuncPtr object at 0x...> +>>> print windll.kernel32.GetModuleHandleA # doctest: +WINDOWS +<_FuncPtr object at 0x...> +>>> print windll.kernel32.MyOwnFunction # doctest: +WINDOWS +Traceback (most recent call last): + File "", line 1, in ? + File "ctypes.py", line 239, in __getattr__ + func = _StdcallFuncPtr(name, self) +AttributeError: function 'MyOwnFunction' not found +>>> +\end{verbatim} + +Note that win32 system dlls like \code{kernel32} and \code{user32} often +export ANSI as well as UNICODE versions of a function. The UNICODE +version is exported with an \code{W} appended to the name, while the ANSI +version is exported with an \code{A} appended to the name. The win32 +\code{GetModuleHandle} function, which returns a \emph{module handle} for a +given module name, has the following C prototype, and a macro is used +to expose one of them as \code{GetModuleHandle} depending on whether +UNICODE is defined or not: +\begin{verbatim} +/* ANSI version */ +HMODULE GetModuleHandleA(LPCSTR lpModuleName); +/* UNICODE version */ +HMODULE GetModuleHandleW(LPCWSTR lpModuleName); +\end{verbatim} + +\var{windll} does not try to select one of them by magic, you must +access the version you need by specifying \code{GetModuleHandleA} or +\code{GetModuleHandleW} explicitely, and then call it with normal strings +or unicode strings respectively. + +Sometimes, dlls export functions with names which aren't valid Python +identifiers, like \code{"??2@YAPAXI@Z"}. In this case you have to use +\code{getattr} to retrieve the function: +\begin{verbatim} +>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") # doctest: +WINDOWS +<_FuncPtr object at 0x...> +>>> +\end{verbatim} + +On Windows, some dlls export functions not by name but by ordinal. +These functions can be accessed by indexing the dll object with the +odinal number: +\begin{verbatim} +>>> cdll.kernel32[1] # doctest: +WINDOWS +<_FuncPtr object at 0x...> +>>> cdll.kernel32[0] # doctest: +WINDOWS +Traceback (most recent call last): + File "", line 1, in ? + File "ctypes.py", line 310, in __getitem__ + func = _StdcallFuncPtr(name, self) +AttributeError: function ordinal 0 not found +>>> +\end{verbatim} + + +\subsubsection{Calling functions\label{ctypes-calling-functions}} + +You can call these functions like any other Python callable. This +example uses the \code{time()} function, which returns system time in +seconds since the \UNIX{} epoch, and the \code{GetModuleHandleA()} function, +which returns a win32 module handle. + +This example calls both functions with a NULL pointer (\code{None} should +be used as the NULL pointer): +\begin{verbatim} +>>> print libc.time(None) +114... +>>> print hex(windll.kernel32.GetModuleHandleA(None)) # doctest: +WINDOWS +0x1d000000 +>>> +\end{verbatim} + +\code{ctypes} tries to protect you from calling functions with the wrong +number of arguments. Unfortunately this only works on Windows. It +does this by examining the stack after the function returns: +\begin{verbatim} +>>> windll.kernel32.GetModuleHandleA() # doctest: +WINDOWS +Traceback (most recent call last): + File "", line 1, in ? +ValueError: Procedure probably called with not enough arguments (4 bytes missing) +>>> windll.kernel32.GetModuleHandleA(0, 0) # doctest: +WINDOWS +Traceback (most recent call last): + File "", line 1, in ? +ValueError: Procedure probably called with too many arguments (4 bytes in excess) +>>> +\end{verbatim} + +On Windows, \code{ctypes} uses win32 structured exception handling to +prevent crashes from general protection faults when functions are +called with invalid argument values: +\begin{verbatim} +>>> windll.kernel32.GetModuleHandleA(32) # doctest: +WINDOWS +Traceback (most recent call last): + File "", line 1, in ? +WindowsError: exception: access violation reading 0x00000020 +>>> +\end{verbatim} + +There are, however, enough ways to crash Python with \code{ctypes}, so +you should be careful anyway. + +Python integers, strings and unicode strings are the only objects that +can directly be used as parameters in these function calls. + +Before we move on calling functions with other parameter types, we +have to learn more about \code{ctypes} data types. + + +\subsubsection{Simple data types\label{ctypes-simple-data-types}} + +\code{ctypes} defines a number of primitive C compatible data types : +\begin{quote} + +\begin{longtable}[c]{|p{0.19\locallinewidth}|p{0.28\locallinewidth}|p{0.14\locallinewidth}|} +\hline +\textbf{ +ctypes type +} & \textbf{ +C type +} & \textbf{ +Python type +} \\ +\hline +\endhead + +\class{c{\_}char} + & +\code{char} + & +character + \\ +\hline + +\class{c{\_}byte} + & +\code{char} + & +integer + \\ +\hline + +\class{c{\_}ubyte} + & +\code{unsigned char} + & +integer + \\ +\hline + +\class{c{\_}short} + & +\code{short} + & +integer + \\ +\hline + +\class{c{\_}ushort} + & +\code{unsigned short} + & +integer + \\ +\hline + +\class{c{\_}int} + & +\code{int} + & +integer + \\ +\hline + +\class{c{\_}uint} + & +\code{unsigned int} + & +integer + \\ +\hline + +\class{c{\_}long} + & +\code{long} + & +integer + \\ +\hline + +\class{c{\_}ulong} + & +\code{unsigned long} + & +long + \\ +\hline + +\class{c{\_}longlong} + & +\code{{\_}{\_}int64} or +\code{long long} + & +long + \\ +\hline + +\class{c{\_}ulonglong} + & +\code{unsigned {\_}{\_}int64} or +\code{unsigned long long} + & +long + \\ +\hline + +\class{c{\_}float} + & +\code{float} + & +float + \\ +\hline + +\class{c{\_}double} + & +\code{double} + & +float + \\ +\hline + +\class{c{\_}char{\_}p} + & +\code{char *} +(NUL terminated) + & +string or +\code{None} + \\ +\hline + +\class{c{\_}wchar{\_}p} + & +\code{wchar{\_}t *} +(NUL terminated) + & +unicode or +\code{None} + \\ +\hline + +\class{c{\_}void{\_}p} + & +\code{void *} + & +integer or +\code{None} + \\ +\hline +\end{longtable} +\end{quote} + +All these types can be created by calling them with an optional +initializer of the correct type and value: +\begin{verbatim} +>>> c_int() +c_long(0) +>>> c_char_p("Hello, World") +c_char_p('Hello, World') +>>> c_ushort(-3) +c_ushort(65533) +>>> +\end{verbatim} + +Since these types are mutable, their value can also be changed +afterwards: +\begin{verbatim} +>>> i = c_int(42) +>>> print i +c_long(42) +>>> print i.value +42 +>>> i.value = -99 +>>> print i.value +-99 +>>> +\end{verbatim} + +Assigning a new value to instances of the pointer types \class{c{\_}char{\_}p}, +\class{c{\_}wchar{\_}p}, and \class{c{\_}void{\_}p} changes the \emph{memory location} they +point to, \emph{not the contents} of the memory block (of course not, +because Python strings are immutable): +\begin{verbatim} +>>> s = "Hello, World" +>>> c_s = c_char_p(s) +>>> print c_s +c_char_p('Hello, World') +>>> c_s.value = "Hi, there" +>>> print c_s +c_char_p('Hi, there') +>>> print s # first string is unchanged +Hello, World +\end{verbatim} + +You should be careful, however, not to pass them to functions +expecting pointers to mutable memory. If you need mutable memory +blocks, ctypes has a \code{create{\_}string{\_}buffer} function which creates +these in various ways. The current memory block contents can be +accessed (or changed) with the \code{raw} property, if you want to access +it as NUL terminated string, use the \code{string} property: +\begin{verbatim} +>>> from ctypes import * +>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes +>>> print sizeof(p), repr(p.raw) +3 '\x00\x00\x00' +>>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string +>>> print sizeof(p), repr(p.raw) +6 'Hello\x00' +>>> print repr(p.value) +'Hello' +>>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer +>>> print sizeof(p), repr(p.raw) +10 'Hello\x00\x00\x00\x00\x00' +>>> p.value = "Hi" +>>> print sizeof(p), repr(p.raw) +10 'Hi\x00lo\x00\x00\x00\x00\x00' +>>> +\end{verbatim} + +The \code{create{\_}string{\_}buffer} function replaces the \code{c{\_}buffer} +function (which is still available as an alias), as well as the +\code{c{\_}string} function from earlier ctypes releases. To create a +mutable memory block containing unicode characters of the C type +\code{wchar{\_}t} use the \code{create{\_}unicode{\_}buffer} function. + + +\subsubsection{Calling functions, continued\label{ctypes-calling-functions-continued}} + +Note that printf prints to the real standard output channel, \emph{not} to +\code{sys.stdout}, so these examples will only work at the console +prompt, not from within \emph{IDLE} or \emph{PythonWin}: +\begin{verbatim} +>>> printf = libc.printf +>>> printf("Hello, %s\n", "World!") +Hello, World! +14 +>>> printf("Hello, %S", u"World!") +Hello, World! +13 +>>> printf("%d bottles of beer\n", 42) +42 bottles of beer +19 +>>> printf("%f bottles of beer\n", 42.5) +Traceback (most recent call last): + File "", line 1, in ? +ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 +>>> +\end{verbatim} + +As has been mentioned before, all Python types except integers, +strings, and unicode strings have to be wrapped in their corresponding +\code{ctypes} type, so that they can be converted to the required C data +type: +\begin{verbatim} +>>> printf("An int %d, a double %f\n", 1234, c_double(3.14)) +Integer 1234, double 3.1400001049 +31 +>>> +\end{verbatim} + + +\subsubsection{Calling functions with your own custom data types\label{ctypes-calling-functions-with-own-custom-data-types}} + +You can also customize \code{ctypes} argument conversion to allow +instances of your own classes be used as function arguments. +\code{ctypes} looks for an \member{{\_}as{\_}parameter{\_}} attribute and uses this as +the function argument. Of course, it must be one of integer, string, +or unicode: +\begin{verbatim} +>>> class Bottles(object): +... def __init__(self, number): +... self._as_parameter_ = number +... +>>> bottles = Bottles(42) +>>> printf("%d bottles of beer\n", bottles) +42 bottles of beer +19 +>>> +\end{verbatim} + +If you don't want to store the instance's data in the +\member{{\_}as{\_}parameter{\_}} instance variable, you could define a \code{property} +which makes the data avaiblable. + + +\subsubsection{Specifying the required argument types (function prototypes)\label{ctypes-specifying-required-argument-types}} + +It is possible to specify the required argument types of functions +exported from DLLs by setting the \member{argtypes} attribute. + +\member{argtypes} must be a sequence of C data types (the \code{printf} +function is probably not a good example here, because it takes a +variable number and different types of parameters depending on the +format string, on the other hand this is quite handy to experiment +with this feature): +\begin{verbatim} +>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] +>>> printf("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2) +String 'Hi', Int 10, Double 2.200000 +37 +>>> +\end{verbatim} + +Specifying a format protects against incompatible argument types (just +as a prototype for a C function), and tries to convert the arguments +to valid types: +\begin{verbatim} +>>> printf("%d %d %d", 1, 2, 3) +Traceback (most recent call last): + File "", line 1, in ? +ArgumentError: argument 2: exceptions.TypeError: wrong type +>>> printf("%s %d %f", "X", 2, 3) +X 2 3.00000012 +12 +>>> +\end{verbatim} + +If you have defined your own classes which you pass to function calls, +you have to implement a \method{from{\_}param} class method for them to be +able to use them in the \member{argtypes} sequence. The \method{from{\_}param} +class method receives the Python object passed to the function call, +it should do a typecheck or whatever is needed to make sure this +object is acceptable, and then return the object itself, it's +\member{{\_}as{\_}parameter{\_}} attribute, or whatever you want to pass as the C +function argument in this case. Again, the result should be an +integer, string, unicode, a \code{ctypes} instance, or something having +the \member{{\_}as{\_}parameter{\_}} attribute. + + +\subsubsection{Return types\label{ctypes-return-types}} + +By default functions are assumed to return integers. Other return +types can be specified by setting the \member{restype} attribute of the +function object. + +Here is a more advanced example, it uses the strchr function, which +expects a string pointer and a char, and returns a pointer to a +string: +\begin{verbatim} +>>> strchr = libc.strchr +>>> strchr("abcdef", ord("d")) # doctest: +SKIP +8059983 +>>> strchr.restype = c_char_p # c_char_p is a pointer to a string +>>> strchr("abcdef", ord("d")) +'def' +>>> print strchr("abcdef", ord("x")) +None +>>> +\end{verbatim} + +If you want to avoid the \code{ord("x")} calls above, you can set the +\member{argtypes} attribute, and the second argument will be converted from +a single character Python string into a C char: +\begin{verbatim} +>>> strchr.restype = c_char_p +>>> strchr.argtypes = [c_char_p, c_char] +>>> strchr("abcdef", "d") +'def' +>>> strchr("abcdef", "def") +Traceback (most recent call last): + File "", line 1, in ? +ArgumentError: argument 2: exceptions.TypeError: one character string expected +>>> print strchr("abcdef", "x") +None +>>> strchr("abcdef", "d") +'def' +>>> +\end{verbatim} + +XXX Mention the \member{errcheck} protocol... + +You can also use a callable Python object (a function or a class for +example) as the \member{restype} attribute. It will be called with the +\code{integer} the C function returns, and the result of this call will +be used as the result of your function call. This is useful to check +for error return values and automatically raise an exception: +\begin{verbatim} +>>> GetModuleHandle = windll.kernel32.GetModuleHandleA # doctest: +WINDOWS +>>> def ValidHandle(value): +... if value == 0: +... raise WinError() +... return value +... +>>> +>>> GetModuleHandle.restype = ValidHandle # doctest: +WINDOWS +>>> GetModuleHandle(None) # doctest: +WINDOWS +486539264 +>>> GetModuleHandle("something silly") # doctest: +WINDOWS +IGNORE_EXCEPTION_DETAIL +Traceback (most recent call last): + File "", line 1, in ? + File "", line 3, in ValidHandle +WindowsError: [Errno 126] The specified module could not be found. +>>> +\end{verbatim} + +\code{WinError} is a function which will call Windows \code{FormatMessage()} +api to get the string representation of an error code, and \emph{returns} +an exception. \code{WinError} takes an optional error code parameter, if +no one is used, it calls \function{GetLastError()} to retrieve it. + + +\subsubsection{Passing pointers (or: passing parameters by reference)\label{ctypes-passing-pointers}} + +Sometimes a C api function expects a \emph{pointer} to a data type as +parameter, probably to write into the corresponding location, or if +the data is too large to be passed by value. This is also known as +\emph{passing parameters by reference}. + +\code{ctypes} exports the \function{byref} function which is used to pass +parameters by reference. The same effect can be achieved with the +\code{pointer} function, although \code{pointer} does a lot more work since +it constructs a real pointer object, so it is faster to use \function{byref} +if you don't need the pointer object in Python itself: +\begin{verbatim} +>>> i = c_int() +>>> f = c_float() +>>> s = create_string_buffer('\000' * 32) +>>> print i.value, f.value, repr(s.value) +0 0.0 '' +>>> libc.sscanf("1 3.14 Hello", "%d %f %s", +... byref(i), byref(f), s) +3 +>>> print i.value, f.value, repr(s.value) +1 3.1400001049 'Hello' +>>> +\end{verbatim} + + +\subsubsection{Structures and unions\label{ctypes-structures-unions}} + +Structures and unions must derive from the \class{Structure} and \class{Union} +base classes which are defined in the \code{ctypes} module. Each subclass +must define a \member{{\_}fields{\_}} attribute. \member{{\_}fields{\_}} must be a list of +\emph{2-tuples}, containing a \emph{field name} and a \emph{field type}. + +The field type must be a \code{ctypes} type like \class{c{\_}int}, or any other +derived \code{ctypes} type: structure, union, array, pointer. + +Here is a simple example of a POINT structure, which contains two +integers named \code{x} and \code{y}, and also shows how to initialize a +structure in the constructor: +\begin{verbatim} +>>> from ctypes import * +>>> class POINT(Structure): +... _fields_ = [("x", c_int), +... ("y", c_int)] +... +>>> point = POINT(10, 20) +>>> print point.x, point.y +10 20 +>>> point = POINT(y=5) +>>> print point.x, point.y +0 5 +>>> POINT(1, 2, 3) +Traceback (most recent call last): + File "", line 1, in ? +ValueError: too many initializers +>>> +\end{verbatim} + +You can, however, build much more complicated structures. Structures +can itself contain other structures by using a structure as a field +type. + +Here is a RECT structure which contains two POINTs named \code{upperleft} +and \code{lowerright} +\begin{verbatim} +>>> class RECT(Structure): +... _fields_ = [("upperleft", POINT), +... ("lowerright", POINT)] +... +>>> rc = RECT(point) +>>> print rc.upperleft.x, rc.upperleft.y +0 5 +>>> print rc.lowerright.x, rc.lowerright.y +0 0 +>>> +\end{verbatim} + +Nested structures can also be initialized in the constructor in +several ways: +\begin{verbatim} +>>> r = RECT(POINT(1, 2), POINT(3, 4)) +>>> r = RECT((1, 2), (3, 4)) +\end{verbatim} + +Fields descriptors can be retrieved from the \emph{class}, they are useful +for debugging because they can provide useful information: +\begin{verbatim} +>>> print POINT.x + +>>> print POINT.y + +>>> +\end{verbatim} + + +\subsubsection{Structure/union alignment and byte order\label{ctypes-structureunion-alignment-byte-order}} + +By default, Structure and Union fields are aligned in the same way the +C compiler does it. It is possible to override this behaviour be +specifying a \member{{\_}pack{\_}} class attribute in the subclass +definition. This must be set to a positive integer and specifies the +maximum alignment for the fields. This is what \code{{\#}pragma pack(n)} +also does in MSVC. + +\code{ctypes} uses the native byte order for Structures and Unions. To +build structures with non-native byte order, you can use one of the +BigEndianStructure, LittleEndianStructure, BigEndianUnion, and +LittleEndianUnion base classes. These classes cannot contain pointer +fields. + + +\subsubsection{Bit fields in structures and unions\label{ctypes-bit-fields-in-structures-unions}} + +It is possible to create structures and unions containing bit fields. +Bit fields are only possible for integer fields, the bit width is +specified as the third item in the \member{{\_}fields{\_}} tuples: +\begin{verbatim} +>>> class Int(Structure): +... _fields_ = [("first_16", c_int, 16), +... ("second_16", c_int, 16)] +... +>>> print Int.first_16 + +>>> print Int.second_16 + +>>> +\end{verbatim} + + +\subsubsection{Arrays\label{ctypes-arrays}} + +Arrays are sequences, containing a fixed number of instances of the +same type. + +The recommended way to create array types is by multiplying a data +type with a positive integer: +\begin{verbatim} +TenPointsArrayType = POINT * 10 +\end{verbatim} + +Here is an example of an somewhat artifical data type, a structure +containing 4 POINTs among other stuff: +\begin{verbatim} +>>> from ctypes import * +>>> class POINT(Structure): +... _fields_ = ("x", c_int), ("y", c_int) +... +>>> class MyStruct(Structure): +... _fields_ = [("a", c_int), +... ("b", c_float), +... ("point_array", POINT * 4)] +>>> +>>> print len(MyStruct().point_array) +4 +\end{verbatim} + +Instances are created in the usual way, by calling the class: +\begin{verbatim} +arr = TenPointsArrayType() +for pt in arr: + print pt.x, pt.y +\end{verbatim} + +The above code print a series of \code{0 0} lines, because the array +contents is initialized to zeros. + +Initializers of the correct type can also be specified: +\begin{verbatim} +>>> from ctypes import * +>>> TenIntegers = c_int * 10 +>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +>>> print ii + +>>> for i in ii: print i, +... +1 2 3 4 5 6 7 8 9 10 +>>> +\end{verbatim} + + +\subsubsection{Pointers\label{ctypes-pointers}} + +Pointer instances are created by calling the \code{pointer} function on a +\code{ctypes} type: +\begin{verbatim} +>>> from ctypes import * +>>> i = c_int(42) +>>> pi = pointer(i) +>>> +\end{verbatim} + +XXX XXX Not correct: use indexing, not the contents atribute + +Pointer instances have a \code{contents} attribute which returns the +ctypes' type pointed to, the \code{c{\_}int(42)} in the above case: +\begin{verbatim} +>>> pi.contents +c_long(42) +>>> +\end{verbatim} + +Assigning another \class{c{\_}int} instance to the pointer's contents +attribute would cause the pointer to point to the memory location +where this is stored: +\begin{verbatim} +>>> pi.contents = c_int(99) +>>> pi.contents +c_long(99) +>>> +\end{verbatim} + +Pointer instances can also be indexed with integers: +\begin{verbatim} +>>> pi[0] +99 +>>> +\end{verbatim} + +XXX What is this??? +Assigning to an integer index changes the pointed to value: +\begin{verbatim} +>>> i2 = pi[0] +>>> i2 +99 +>>> pi[0] = 22 +>>> i2 +99 +>>> +\end{verbatim} + +It is also possible to use indexes different from 0, but you must know +what you're doing when you use this: You access or change arbitrary +memory locations when you do this. Generally you only use this feature +if you receive a pointer from a C function, and you \emph{know} that the +pointer actually points to an array instead of a single item. + + +\subsubsection{Pointer classes/types\label{ctypes-pointer-classestypes}} + +Behind the scenes, the \code{pointer} function does more than simply +create pointer instances, it has to create pointer \emph{types} first. +This is done with the \code{POINTER} function, which accepts any +\code{ctypes} type, and returns a new type: +\begin{verbatim} +>>> PI = POINTER(c_int) +>>> PI + +>>> PI(42) # doctest: +IGNORE_EXCEPTION_DETAIL +Traceback (most recent call last): + File "", line 1, in ? +TypeError: expected c_long instead of int +>>> PI(c_int(42)) + +>>> +\end{verbatim} + + +\subsubsection{Incomplete Types\label{ctypes-incomplete-types}} + +\emph{Incomplete Types} are structures, unions or arrays whose members are +not yet specified. In C, they are specified by forward declarations, which +are defined later: +\begin{verbatim} +struct cell; /* forward declaration */ + +struct { + char *name; + struct cell *next; +} cell; +\end{verbatim} + +The straightforward translation into ctypes code would be this, but it +does not work: +\begin{verbatim} +>>> class cell(Structure): +... _fields_ = [("name", c_char_p), +... ("next", POINTER(cell))] +... +Traceback (most recent call last): + File "", line 1, in ? + File "", line 2, in cell +NameError: name 'cell' is not defined +>>> +\end{verbatim} + +because the new \code{class cell} is not available in the class statement +itself. In \code{ctypes}, we can define the \code{cell} class and set the +\member{{\_}fields{\_}} attribute later, after the class statement: +\begin{verbatim} +>>> from ctypes import * +>>> class cell(Structure): +... pass +... +>>> cell._fields_ = [("name", c_char_p), +... ("next", POINTER(cell))] +>>> +\end{verbatim} + +Lets try it. We create two instances of \code{cell}, and let them point +to each other, and finally follow the pointer chain a few times: +\begin{verbatim} +>>> c1 = cell() +>>> c1.name = "foo" +>>> c2 = cell() +>>> c2.name = "bar" +>>> c1.next = pointer(c2) +>>> c2.next = pointer(c1) +>>> p = c1 +>>> for i in range(8): +... print p.name, +... p = p.next[0] +... +foo bar foo bar foo bar foo bar +>>> +\end{verbatim} + + +\subsubsection{Callback functions\label{ctypes-callback-functions}} + +\code{ctypes} allows to create C callable function pointers from Python +callables. These are sometimes called \emph{callback functions}. + +First, you must create a class for the callback function, the class +knows the calling convention, the return type, and the number and +types of arguments this function will receive. + +The CFUNCTYPE factory function creates types for callback functions +using the normal cdecl calling convention, and, on Windows, the +WINFUNCTYPE factory function creates types for callback functions +using the stdcall calling convention. + +Both of these factory functions are called with the result type as +first argument, and the callback functions expected argument types as +the remaining arguments. + +I will present an example here which uses the standard C library's +\function{qsort} function, this is used to sort items with the help of a +callback function. \function{qsort} will be used to sort an array of +integers: +\begin{verbatim} +>>> IntArray5 = c_int * 5 +>>> ia = IntArray5(5, 1, 7, 33, 99) +>>> qsort = libc.qsort +>>> qsort.restype = None +>>> +\end{verbatim} + +\function{qsort} must be called with a pointer to the data to sort, the +number of items in the data array, the size of one item, and a pointer +to the comparison function, the callback. The callback will then be +called with two pointers to items, and it must return a negative +integer if the first item is smaller than the second, a zero if they +are equal, and a positive integer else. + +So our callback function receives pointers to integers, and must +return an integer. First we create the \code{type} for the callback +function: +\begin{verbatim} +>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) +>>> +\end{verbatim} + +For the first implementation of the callback function, we simply print +the arguments we get, and return 0 (incremental development ;-): +\begin{verbatim} +>>> def py_cmp_func(a, b): +... print "py_cmp_func", a, b +... return 0 +... +>>> +\end{verbatim} + +Create the C callable callback: +\begin{verbatim} +>>> cmp_func = CMPFUNC(py_cmp_func) +>>> +\end{verbatim} + +And we're ready to go: +\begin{verbatim} +>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +WINDOWS +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +py_cmp_func +>>> +\end{verbatim} + +We know how to access the contents of a pointer, so lets redefine our callback: +\begin{verbatim} +>>> def py_cmp_func(a, b): +... print "py_cmp_func", a[0], b[0] +... return 0 +... +>>> cmp_func = CMPFUNC(py_cmp_func) +>>> +\end{verbatim} + +Here is what we get on Windows: +\begin{verbatim} +>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +WINDOWS +py_cmp_func 7 1 +py_cmp_func 33 1 +py_cmp_func 99 1 +py_cmp_func 5 1 +py_cmp_func 7 5 +py_cmp_func 33 5 +py_cmp_func 99 5 +py_cmp_func 7 99 +py_cmp_func 33 99 +py_cmp_func 7 33 +>>> +\end{verbatim} + +It is funny to see that on linux the sort function seems to work much +more efficient, it is doing less comparisons: +\begin{verbatim} +>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) # doctest: +LINUX +py_cmp_func 5 1 +py_cmp_func 33 99 +py_cmp_func 7 33 +py_cmp_func 5 7 +py_cmp_func 1 7 +>>> +\end{verbatim} + +Ah, we're nearly done! The last step is to actually compare the two +items and return a useful result: +\begin{verbatim} +>>> def py_cmp_func(a, b): +... print "py_cmp_func", a[0], b[0] +... return a[0] - b[0] +... +>>> +\end{verbatim} + +Final run on Windows: +\begin{verbatim} +>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) # doctest: +WINDOWS +py_cmp_func 33 7 +py_cmp_func 99 33 +py_cmp_func 5 99 +py_cmp_func 1 99 +py_cmp_func 33 7 +py_cmp_func 1 33 +py_cmp_func 5 33 +py_cmp_func 5 7 +py_cmp_func 1 7 +py_cmp_func 5 1 +>>> +\end{verbatim} + +and on Linux: +\begin{verbatim} +>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) # doctest: +LINUX +py_cmp_func 5 1 +py_cmp_func 33 99 +py_cmp_func 7 33 +py_cmp_func 1 7 +py_cmp_func 5 7 +>>> +\end{verbatim} + +So, our array sorted now: +\begin{verbatim} +>>> for i in ia: print i, +... +1 5 7 33 99 +>>> +\end{verbatim} + +\textbf{Important note for callback functions:} + +Make sure you keep references to CFUNCTYPE objects as long as they are +used from C code. ctypes doesn't, and if you don't, they may be +garbage collected, crashing your program when a callback is made. + + +\subsubsection{Accessing values exported from dlls\label{ctypes-accessing-values-exported-from-dlls}} + +Sometimes, a dll not only exports functions, it also exports +values. An example in the Python library itself is the +\code{Py{\_}OptimizeFlag}, an integer set to 0, 1, or 2, depending on the +\programopt{-O} or \programopt{-OO} flag given on startup. + +\code{ctypes} can access values like this with the \method{in{\_}dll} class +methods of the type. \var{pythonapi} ìs a predefined symbol giving +access to the Python C api: +\begin{verbatim} +>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") +>>> print opt_flag +c_long(0) +>>> +\end{verbatim} + +If the interpreter would have been started with \programopt{-O}, the sample +would have printed \code{c{\_}long(1)}, or \code{c{\_}long(2)} if \programopt{-OO} would have +been specified. + +An extended example which also demonstrates the use of pointers +accesses the \code{PyImport{\_}FrozenModules} pointer exported by Python. + +Quoting the Python docs: \emph{This pointer is initialized to point to an +array of ``struct {\_}frozen`` records, terminated by one whose members +are all NULL or zero. When a frozen module is imported, it is searched +in this table. Third-party code could play tricks with this to provide +a dynamically created collection of frozen modules.} + +So manipulating this pointer could even prove useful. To restrict the +example size, we show only how this table can be read with +\code{ctypes}: +\begin{verbatim} +>>> from ctypes import * +>>> +>>> class struct_frozen(Structure): +... _fields_ = [("name", c_char_p), +... ("code", POINTER(c_ubyte)), +... ("size", c_int)] +... +>>> +\end{verbatim} + +We have defined the \code{struct {\_}frozen} data type, so we can get the +pointer to the table: +\begin{verbatim} +>>> FrozenTable = POINTER(struct_frozen) +>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") +>>> +\end{verbatim} + +Since \code{table} is a \code{pointer} to the array of \code{struct{\_}frozen} +records, we can iterate over it, but we just have to make sure that +our loop terminates, because pointers have no size. Sooner or later it +would probably crash with an access violation or whatever, so it's +better to break out of the loop when we hit the NULL entry: +\begin{verbatim} +>>> for item in table: +... print item.name, item.size +... if item.name is None: +... break +... +__hello__ 104 +__phello__ -104 +__phello__.spam 104 +None 0 +>>> +\end{verbatim} + +The fact that standard Python has a frozen module and a frozen package +(indicated by the negative size member) is not wellknown, it is only +used for testing. Try it out with \code{import {\_}{\_}hello{\_}{\_}} for example. + +XXX Describe how to access the \var{code} member fields, which contain +the byte code for the modules. + + +\subsubsection{Surprises\label{ctypes-surprises}} + +There are some edges in \code{ctypes} where you may be expect something +else than what actually happens. + +Consider the following example: +\begin{verbatim} +>>> from ctypes import * +>>> class POINT(Structure): +... _fields_ = ("x", c_int), ("y", c_int) +... +>>> class RECT(Structure): +... _fields_ = ("a", POINT), ("b", POINT) +... +>>> p1 = POINT(1, 2) +>>> p2 = POINT(3, 4) +>>> rc = RECT(p1, p2) +>>> print rc.a.x, rc.a.y, rc.b.x, rc.b.y +1 2 3 4 +>>> # now swap the two points +>>> rc.a, rc.b = rc.b, rc.a +>>> print rc.a.x, rc.a.y, rc.b.x, rc.b.y +3 4 3 4 +\end{verbatim} + +Hm. We certainly expected the last statement to print \code{3 4 1 2}. +What happended? Here are the steps of the \code{rc.a, rc.b = rc.b, rc.a} +line above: +\begin{verbatim} +>>> temp0, temp1 = rc.b, rc.a +>>> rc.a = temp0 +>>> rc.b = temp1 +\end{verbatim} + +Note that \code{temp0} and \code{temp1} are objects still using the internal +buffer of the \code{rc} object above. So executing \code{rc.a = temp0} +copies the buffer contents of \code{temp0} into \code{rc} 's buffer. This, +in turn, changes the contents of \code{temp1}. So, the last assignment +\code{rc.b = temp1}, doesn't have the expected effect. + +Keep in mind that retrieving subobjects from Structure, Unions, and +Arrays doesn't \emph{copy} the subobject, instead it retrieves a wrapper +object accessing the root-object's underlying buffer. + +Another example that may behave different from what one would expect is this: +\begin{verbatim} +>>> s = c_char_p() +>>> s.value = "abc def ghi" +>>> s.value +'abc def ghi' +>>> s.value is s.value +False +>>> +\end{verbatim} + +Why is it printing \code{False}? ctypes instances are objects containing +a memory block plus some descriptors accessing the contents of the +memory. Storing a Python object in the memory block does not store +the object itself, instead the \code{contents} of the object is stored. +Accessing the contents again constructs a new Python each time! + + +\subsubsection{Bugs, ToDo and non-implemented things\label{ctypes-bugs-todo-non-implemented-things}} + +Enumeration types are not implemented. You can do it easily yourself, +using \class{c{\_}int} as the base class. + +\code{long double} is not implemented. +% Local Variables: +% compile-command: "make.bat" +% End: + diff --git a/Doc/lib/libctypesref.tex b/Doc/lib/libctypesref.tex new file mode 100644 index 0000000..6d950f4 --- /dev/null +++ b/Doc/lib/libctypesref.tex @@ -0,0 +1,457 @@ +\subsection{ctypes reference\label{ctypes-reference}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% functions +\subsubsection{ctypes functions} + +\begin{funcdesc}{addressof}{obj} +Returns the address of the memory buffer as integer. \var{obj} must +be an instance of a ctypes type. +\end{funcdesc} + +\begin{funcdesc}{alignment}{obj_or_type} +Returns the alignment requirements of a ctypes type. +\var{obj_or_type} must be a ctypes type or an instance. +\end{funcdesc} + +\begin{excclassdesc}{ArgumentError}{} +This exception is raised when a foreign function call cannot convert +one of the passed arguments. +\end{excclassdesc} + +\begin{funcdesc}{byref}{obj} +Returns a light-weight pointer to \var{obj}, which must be an instance +of a ctypes type. The returned object can only be used as a foreign +function call parameter. It behaves similar to \code{pointer(obj)}, +but the construction is a lot faster. +\end{funcdesc} + +\begin{funcdesc}{cast}{obj, type} +This function is similar to the cast operator in C. It returns a new +instance of \var{type} which points to the same memory block as +\code{obj}. \code{type} must be a pointer type, and \code{obj} + must be an object that can be interpreted as a pointer. +\end{funcdesc} + +% XXX separate section for CFUNCTYPE, WINFUNCTYPE, PYFUNCTYPE? + +\begin{funcdesc}{CFUNCTYPE}{restype, *argtypes} +This is a factory function that returns a function prototype. The +function prototype describes a function that has a result type of +\code{restype}, and accepts arguments as specified by \code{argtypes}. +The function prototype can be used to construct several kinds of +functions, depending on how the prototype is called. + +The prototypes returned by \code{CFUNCTYPE} or \code{PYFUNCTYPE} +create functions that use the standard C calling convention, +prototypes returned from \code{WINFUNCTYPE} (on Windows) use the +\code{__stdcall} calling convention. + +Functions created by calling the \code{CFUNCTYPE} and +\code{WINFUNCTYPE} prototypes release the Python GIL +before entering the foreign function, and acquire it back after +leaving the function code. + +% XXX differences between CFUNCTYPE / WINFUNCTYPE / PYFUNCTYPE + +\end{funcdesc} + +\begin{funcdesc}{create_string_buffer}{init_or_size\optional{, size}} +This function creates a mutable character buffer. The returned object +is a ctypes array of \code{c_char}. + +\var{init_or_size} must be an integer which specifies the size of the +array, or a string which will be used to initialize the array items. + +If a string is specified as first argument, the buffer is made one +item larger than the length of the string so that the last element in +the array is a NUL termination character. An integer can be passed as +second argument which allows to specify the size of the array if the +length of the string should not be used. + +If the first parameter is a unicode string, it is converted into an +8-bit string according to ctypes conversion rules. +\end{funcdesc} + +\begin{funcdesc}{create_unicode_buffer}{init_or_size\optional{, size}} +This function creates a mutable unicode character buffer. The +returned object is a ctypes array of \code{c_wchar}. + +\var{init_or_size} must be an integer which specifies the size of the +array, or a unicode string which will be used to initialize the array +items. + +If a unicode string is specified as first argument, the buffer is made +one item larger than the length of the string so that the last element +in the array is a NUL termination character. An integer can be passed +as second argument which allows to specify the size of the array if +the length of the string should not be used. + +If the first parameter is a 8-bit string, it is converted into an +unicode string according to ctypes conversion rules. +\end{funcdesc} + +\begin{funcdesc}{DllCanUnloadNow}{} +Windows only: This function is a hook which allows to implement +inprocess COM servers with ctypes. It is called from the +\code{DllCanUnloadNow} function that the \code{_ctypes} +extension dll exports. +\end{funcdesc} + +\begin{funcdesc}{DllGetClassObject}{} +Windows only: This function is a hook which allows to implement +inprocess COM servers with ctypes. It is called from the +\code{DllGetClassObject} function that the \code{_ctypes} +extension dll exports. +\end{funcdesc} + +\begin{funcdesc}{FormatError}{\optional{code}} +Windows only: Returns a textual description of the error code. If no +error code is specified, the last error code is used by calling the +Windows api function \code{GetLastError}. +\end{funcdesc} + +\begin{funcdesc}{GetLastError}{} +Windows only: Returns the last error code set by Windows in the +calling thread. +\end{funcdesc} + +\begin{funcdesc}{memmove}{dst, src, count} +Same as the standard C \code{memmove} library function: copies +\var{count} bytes from \code{src} to \code{dst}. \code{dst} and +\code{src} must be integers or ctypes instances that can be converted to pointers. +\end{funcdesc} + +\begin{funcdesc}{memset}{dst, c, count} +Same as the standard C \code{memset} library function: fills the +memory clock at address \code{dst} with \var{count} bytes of value +\var{c}. \var{dst} must be an integer specifying an address, or a ctypes instance. +\end{funcdesc} + +\begin{funcdesc}{POINTER}{type} +This factory function creates and returns a new ctypes pointer type. +Pointer types are cached an reused internally, so calling this +function repeatedly is cheap. \var{type} must be a ctypes type. +\end{funcdesc} + +\begin{funcdesc}{pointer}{obj} +This function creates a new pointer instance, pointing to \var{obj}. +The returned object is of the type \code{POINTER(type(obj))}. + +Note: If you just want to pass a pointer to an object to a foreign +function call, you should use \code{byref(obj)} which is much faster. +\end{funcdesc} + +\begin{funcdesc}{PYFUNCTYPE}{restype, *argtypes} +\end{funcdesc} + +\begin{funcdesc}{pythonapi}{} +\end{funcdesc} + +\begin{funcdesc}{resize}{obj, size} +This function resizes the internal memory buffer of \var{obj}, which +must be an instance of a ctypes type. It is not possible to make the +buffer smaller than the native size of the objects type, as given by +\code{sizeof(type(obj))}, but it is possible to enlarge the buffer. +\end{funcdesc} + +\begin{funcdesc}{set_conversion_mode}{encoding, errors} +This function sets the rules that ctypes objects use when converting +between 8-bit strings and unicode strings. \var{encoding} must be a +string specifying an encoding, like 'utf-8' or 'mbcs', \var{errors} +must be a string specifying the error handling on encoding/decoding +errors. Examples of possible values are ``strict'', ``replace'', or +``ignore''. + +\code{set_conversion_mode} returns a 2-tuple containing the previous +conversion rules. On windows, the initial conversion rules are +\code{('mbcs', 'ignore')}, on other systems \code{('ascii', 'strict')}. +\end{funcdesc} + +\begin{funcdesc}{sizeof}{obj_or_type} +Returns the size in bytes of a ctypes type or instance memory buffer. +Does the same as the C sizeof() function. +\end{funcdesc} + +\begin{funcdesc}{string_at}{address\optional{size}} +This function returns the string starting at memory address +\var{address}. If \var{size} is specified, it is used as size, +otherwise the string is assumed to be zero-terminated. +\end{funcdesc} + +\begin{funcdesc}{WinError}{code=None, descr=None} +Windows only: this function is probably the worst-named thing in +ctypes. It creates an instance of \code{WindowsError}. If \var{code} +is not specified, \code{GetLastError} is called to determine the error +code. If \var{descr} is not spcified, \var{FormatError} is called to +get a textual description of the error. +\end{funcdesc} + +\begin{funcdesc}{WINFUNCTYPE}{restype, *argtypes} +\end{funcdesc} + +\begin{funcdesc}{wstring_at}{address} +This function returns the wide character string starting at memory +address \var{address} as unicode string. If \var{size} is specified, +it is used as size, otherwise the string is assumed to be +zero-terminated. +\end{funcdesc} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% data types +\subsubsection{data types} + +ctypes defines a lot of C compatible datatypes, and also allows to +define your own types. Among other things, a ctypes type instance +holds a memory block that contains C compatible data. + +\begin{classdesc}{_ctypes._CData}{} +This non-public class is the base class of all ctypes data types. It +is mentioned here because it contains the common methods of the ctypes +data types. +\end{classdesc} + +Common methods of ctypes data types, these are all class methods (to +be exact, they are methods of the metaclass): + +\begin{methoddesc}{from_address}{address} +This method returns a ctypes type instance using the memory specified +by \code{address}. +\end{methoddesc} + +\begin{methoddesc}{from_param}{obj} +This method adapts \code{obj} to a ctypes type. +\end{methoddesc} + +\begin{methoddesc}{in_dll}{name, library} +This method returns a ctypes type instance exported by a shared +library. \var{name} is the name of the symbol that exports the data, +\var{library} is the loaded shared library. +\end{methoddesc} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% simple data types +\subsubsection{simple data types} + +\begin{classdesc}{_ctypes._SimpleCData}{} +This non-public class is the base class of all ctypes data types. It +is mentioned here because it contains the common attributes of the +ctypes data types. +\end{classdesc} + +\begin{memberdesc}{value} +This attribute contains the actual value of the instance. For integer +types, it is an integer. +\end{memberdesc} + +Here are the simple ctypes data types: + +\begin{classdesc}{c_byte}{\optional{value}} +Represents a C \code{signed char} datatype, and interprets the value +as small integer. The constructor accepts an optional integer +initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_char}{\optional{value}} +Represents a C \code{char} datatype, and interprets the value as a +single character. The constructor accepts an optional string +initializer, the length of the string must be exactly one character. +\end{classdesc} + +\begin{classdesc}{c_char_p}{\optional{value}} +Represents a C \code{char *} datatype, which must be a pointer to a +zero-terminated string. The constructor accepts an integer address, +or a string. +% XXX Explain the difference to POINTER(c_char) +\end{classdesc} + +\begin{classdesc}{c_double}{\optional{value}} +Represents a C \code{double} datatype. The constructor accepts an +optional float initializer. +\end{classdesc} + +\begin{classdesc}{c_float}{\optional{value}} +Represents a C \code{double} datatype. The constructor accepts an +optional float initializer. +\end{classdesc} + +\begin{classdesc}{c_int}{\optional{value}} +Represents a C \code{signed int} datatype. The constructor accepts an +optional integer initializer; no overflow checking is done. On +platforms where \code{sizeof(int) == sizeof(long)} \var{c_int} is an +alias to \var{c_long}. +\end{classdesc} + +\begin{classdesc}{c_int16}{\optional{value}} +Represents a C 16-bit \code{signed int} datatype. Usually an alias +for \var{c_short}. +\end{classdesc} + +\begin{classdesc}{c_int32}{\optional{value}} +Represents a C 32-bit \code{signed int} datatype. Usually an alias +for \code{c_int}. +\end{classdesc} + +\begin{classdesc}{c_int64}{\optional{value}} +Represents a C 64-bit \code{signed int} datatype. Usually an alias +for \code{c_longlong}. +\end{classdesc} + +\begin{classdesc}{c_int8}{\optional{value}} +Represents a C 8-bit \code{signed int} datatype. Usually an alias for \code{c_byte}. +\end{classdesc} + +\begin{classdesc}{c_long}{\optional{value}} +Represents a C \code{signed long} datatype. The constructor accepts +an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_longlong}{\optional{value}} +Represents a C \code{signed long long} datatype. The constructor +accepts an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_short}{\optional{value}} +Represents a C \code{signed short} datatype. The constructor accepts +an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_size_t}{\optional{value}} +Represents a C \code{size_t} datatype. +\end{classdesc} + +\begin{classdesc}{c_ubyte}{\optional{value}} +Represents a C \code{unsigned char} datatype, and interprets the value +as small integer. The constructor accepts an optional integer +initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_uint}{\optional{value}} +Represents a C \code{unsigned int} datatype. The constructor accepts +an optional integer initializer; no overflow checking is done. On +platforms where \code{sizeof(int) == sizeof(long)} \var{c_int} is an +alias to \var{c_long}. +\end{classdesc} + +\begin{classdesc}{c_uint16}{\optional{value}} +Represents a C 16-bit \code{unsigned int} datatype. Usually an alias +for \code{c_ushort}. +\end{classdesc} + +\begin{classdesc}{c_uint32}{\optional{value}} +Represents a C 32-bit \code{unsigned int} datatype. Usually an alias +for \code{c_uint}. +\end{classdesc} + +\begin{classdesc}{c_uint64}{\optional{value}} +Represents a C 64-bit \code{unsigned int} datatype. Usually an alias +for \code{c_ulonglong}. +\end{classdesc} + +\begin{classdesc}{c_uint8}{\optional{value}} +Represents a C 8-bit \code{unsigned int} datatype. Usually an alias +for \code{c_ubyte}. +\end{classdesc} + +\begin{classdesc}{c_ulong}{\optional{value}} +Represents a C \code{unsigned long} datatype. The constructor accepts +an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_ulonglong}{\optional{value}} +Represents a C \code{unsigned long long} datatype. The constructor +accepts an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_ushort}{\optional{value}} +Represents a C \code{unsigned short} datatype. The constructor accepts +an optional integer initializer; no overflow checking is done. +\end{classdesc} + +\begin{classdesc}{c_void_p}{\optional{value}} +Represents a C \code{void *} type. The value is represented as +integer. The constructor accepts an optional integer initializer. +\end{classdesc} + +\begin{classdesc}{c_wchar}{\optional{value}} +Represents a C \code{wchar_t} datatype, and interprets the value as a +single character unicode string. The constructor accepts an optional +string initializer, the length of the string must be exactly one +character. +\end{classdesc} + +\begin{classdesc}{c_wchar_p}{\optional{value}} +Represents a C \code{wchar_t *} datatype, which must be a pointer to a +zero-terminated wide character string. The constructor accepts an +integer address, or a string. +% XXX Explain the difference to POINTER(c_wchar) +\end{classdesc} + +\begin{classdesc}{HRESULT}{} +Windows only: Represents a \code{HRESULT} value, which contains +success or error information for a function or method call. +\end{classdesc} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% structured data types +\subsubsection{structured data types} + +\begin{classdesc}{BigEndianStructure}{} +\end{classdesc} + +\begin{classdesc}{LittleEndianStructure}{} +\end{classdesc} + +\begin{classdesc}{Structure}{} +Base class for Structure data types. + +\end{classdesc} + +\begin{classdesc}{Union}{} +\end{classdesc} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% libraries +\subsubsection{libraries} + +\begin{classdesc}{CDLL}{name, mode=RTLD_LOCAL, handle=None} +\end{classdesc} + +\begin{datadesc}{cdll} +\end{datadesc} + +\begin{classdesc}{LibraryLoader}{dlltype} + +\begin{memberdesc}{LoadLibrary}{name, mode=RTLD_LOCAL, handle=None} +\end{memberdesc} + +\end{classdesc} + +\begin{classdesc}{OleDLL}{name, mode=RTLD_LOCAL, handle=None} +\end{classdesc} + +\begin{datadesc}{oledll} +\end{datadesc} + +\begin{classdesc}{py_object}{} +\end{classdesc} + +\begin{classdesc}{PyDLL}{name, mode=RTLD_LOCAL, handle=None} +\end{classdesc} + +\begin{datadesc}{pydll}{} +\end{datadesc} + +\begin{datadesc}{RTLD_GLOBAL} +\end{datadesc} + +\begin{datadesc}{RTLD_LOCAL} +\end{datadesc} + +\begin{classdesc}{WinDLL}{name, mode=RTLD_LOCAL, handle=None} +\end{classdesc} + +\begin{datadesc}{windll} +\end{datadesc} + diff --git a/Doc/lib/libcursespanel.tex b/Doc/lib/libcursespanel.tex index 519091e..1f96717 100644 --- a/Doc/lib/libcursespanel.tex +++ b/Doc/lib/libcursespanel.tex @@ -22,6 +22,9 @@ Returns the bottom panel in the panel stack. \begin{funcdesc}{new_panel}{win} Returns a panel object, associating it with the given window \var{win}. +Be aware that you need to keep the returned panel object referenced +explicitly. If you don't, the panel object is garbage collected and +removed from the panel stack. \end{funcdesc} \begin{funcdesc}{top_panel}{} diff --git a/Doc/lib/libdecimal.tex b/Doc/lib/libdecimal.tex index ffc3363..a0c7bde 100644 --- a/Doc/lib/libdecimal.tex +++ b/Doc/lib/libdecimal.tex @@ -713,8 +713,8 @@ here. \constant{NaN}. \end{methoddesc} -\begin{methoddesc}{sqrt}{} - Return the square root to full precision. +\begin{methoddesc}{sqrt}{x} + Return the square root of \var{x} to full precision. \end{methoddesc} \begin{methoddesc}{subtract}{x, y} @@ -734,7 +734,7 @@ here. or \constant{Rounded}. \end{methoddesc} -\begin{methoddesc}{to_sci_string}{} +\begin{methoddesc}{to_sci_string}{x} Converts a number to a string using scientific notation. \end{methoddesc} diff --git a/Doc/lib/libdis.tex b/Doc/lib/libdis.tex index 19fda5b..27b8a5c 100644 --- a/Doc/lib/libdis.tex +++ b/Doc/lib/libdis.tex @@ -55,7 +55,7 @@ was provided. The output is divided in the following columns: \begin{enumerate} \item the line number, for the first instruction of each line \item the current instruction, indicated as \samp{-->}, -\item a labelled instruction, indicated with \samp{>\code{>}}, +\item a labelled instruction, indicated with \samp{>>}, \item the address of the instruction, \item the operation code name, \item operation parameters, and diff --git a/Doc/lib/libdoctest.tex b/Doc/lib/libdoctest.tex index 0e3a017..73b29ad 100644 --- a/Doc/lib/libdoctest.tex +++ b/Doc/lib/libdoctest.tex @@ -333,8 +333,8 @@ NO!!! \end{verbatim} Any expected output must immediately follow the final -\code{'>\code{>}>~'} or \code{'...~'} line containing the code, and -the expected output (if any) extends to the next \code{'>\code{>}>~'} +\code{'>>>~'} or \code{'...~'} line containing the code, and +the expected output (if any) extends to the next \code{'>>>~'} or all-whitespace line. The fine print: @@ -386,7 +386,7 @@ Backslashes in a raw docstring: m\n \end{verbatim} and as many leading whitespace characters are stripped from the -expected output as appeared in the initial \code{'>\code{>}>~'} line +expected output as appeared in the initial \code{'>>>~'} line that started the example. \end{itemize} @@ -407,10 +407,13 @@ You can force use of your own dict as the execution context by passing \subsubsection{What About Exceptions?\label{doctest-exceptions}} No problem, provided that the traceback is the only output produced by -the example: just paste in the traceback. Since tracebacks contain -details that are likely to change rapidly (for example, exact file paths -and line numbers), this is one case where doctest works hard to be -flexible in what it accepts. +the example: just paste in the traceback.\footnote{Examples containing + both expected output and an exception are not supported. Trying + to guess where one ends and the other begins is too error-prone, + and that also makes for a confusing test.} +Since tracebacks contain details that are likely to change rapidly (for +example, exact file paths and line numbers), this is one case where doctest +works hard to be flexible in what it accepts. Simple example: @@ -613,6 +616,20 @@ TypeError: object doesn't support item assignment \end{datadesc} +\begin{datadesc}{SKIP} + + When specified, do not run the example at all. This can be useful + in contexts where doctest examples serve as both documentation and + test cases, and an example should be included for documentation + purposes, but should not be checked. E.g., the example's output + might be random; or the example might depend on resources which + would be unavailable to the test driver. + + The SKIP flag can also be used for temporarily "commenting out" + examples. + +\end{datadesc} + \begin{datadesc}{COMPARISON_FLAGS} A bitmask or'ing together all the comparison flags above. \end{datadesc} @@ -741,6 +758,7 @@ can be useful. were added; by default \code{} in expected output matches an empty line in actual output; and doctest directives were added]{2.4} +\versionchanged[Constant \constant{SKIP} was added]{2.5} There's also a way to register new option flag names, although this isn't useful unless you intend to extend \refmodule{doctest} internals @@ -1040,7 +1058,11 @@ runner.run(suite) There are two main functions for creating \class{\refmodule{unittest}.TestSuite} instances from text files and modules with doctests: -\begin{funcdesc}{DocFileSuite}{*paths, **kw} +\begin{funcdesc}{DocFileSuite}{\optional{module_relative}\optional{, + package}\optional{, setUp}\optional{, + tearDown}\optional{, globs}\optional{, + optionflags}\optional{, parser}} + Convert doctest tests from one or more text files to a \class{\refmodule{unittest}.TestSuite}. @@ -1108,9 +1130,9 @@ instances from text files and modules with doctests: \versionadded{2.4} - Starting in Python 2.5, the global \code{__file__} was added to the + \versionchanged[The global \code{__file__} was added to the globals provided to doctests loaded from a text file using - \function{DocFileSuite()}. + \function{DocFileSuite()}]{2.5} \end{funcdesc} \begin{funcdesc}{DocTestSuite}{\optional{module}\optional{, diff --git a/Doc/lib/libexcs.tex b/Doc/lib/libexcs.tex index 85058a4..30fe831 100644 --- a/Doc/lib/libexcs.tex +++ b/Doc/lib/libexcs.tex @@ -80,7 +80,6 @@ text message explaining why the exception had been raised. If more data needs to be attached to the exception, attach it through arbitrary attributes on the instance. All arguments are also stored in \member{args} as a tuple, but it will eventually be deprecated and thus its use is discouraged. -\versionchanged[Changed to inherit from \exception{BaseException}]{2.5} \versionadded{2.5} \end{excdesc} @@ -88,6 +87,7 @@ eventually be deprecated and thus its use is discouraged. All built-in, non-system-exiting exceptions are derived from this class. All user-defined exceptions should also be derived from this class. +\versionchanged[Changed to inherit from \exception{BaseException}]{2.5} \end{excdesc} \begin{excdesc}{StandardError} @@ -394,11 +394,15 @@ Raised when an \keyword{assert} statement fails. \begin{excdesc}{WindowsError} Raised when a Windows-specific error occurs or when the error number does not correspond to an \cdata{errno} value. The - \member{errno} and \member{strerror} values are created from the + \member{winerror} and \member{strerror} values are created from the return values of the \cfunction{GetLastError()} and \cfunction{FormatMessage()} functions from the Windows Platform API. + The \member{errno} value maps the \member{winerror} value to + corresponding \code{errno.h} values. This is a subclass of \exception{OSError}. \versionadded{2.0} +\versionchanged[Previous versions put the \cfunction{GetLastError()} +codes into \member{errno}]{2.5} \end{excdesc} \begin{excdesc}{ZeroDivisionError} @@ -442,6 +446,11 @@ Base class for warnings about constructs that will change semantically in the future. \end{excdesc} +\begin{excdesc}{ImportWarning} +Base class for warnings about probable mistakes in module imports. +\versionadded{2.5} +\end{excdesc} + The class hierarchy for built-in exceptions is: \verbatiminput{../../Lib/test/exception_hierarchy.txt} diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex index c0352d3..8904d5f 100644 --- a/Doc/lib/libfuncs.tex +++ b/Doc/lib/libfuncs.tex @@ -418,7 +418,7 @@ class C: that differentiate between binary and text files (else it is ignored). If the file cannot be opened, \exception{IOError} is raised. - + In addition to the standard \cfunction{fopen()} values \var{mode} may be \code{'U'} or \code{'rU'}. If Python is built with universal newline support (the default) the file is opened as a text file, but @@ -434,6 +434,9 @@ class C: have yet been seen), \code{'\e n'}, \code{'\e r'}, \code{'\e r\e n'}, or a tuple containing all the newline types seen. + Python enforces that the mode, after stripping \code{'U'}, begins with + \code{'r'}, \code{'w'} or \code{'a'}. + If \var{mode} is omitted, it defaults to \code{'r'}. When opening a binary file, you should append \code{'b'} to the \var{mode} value for improved portability. (It's useful even on systems which don't @@ -455,12 +458,10 @@ class C: after any I/O has been performed, and there's no reliable way to determine whether this is the case.} - The \function{file()} constructor is new in Python 2.2 and is an - alias for \function{open()}. Both spellings are equivalent. The - intent is for \function{open()} to continue to be preferred for use - as a factory function which returns a new \class{file} object. The - spelling, \class{file} is more suited to type testing (for example, - writing \samp{isinstance(f, file)}). + \versionadded{2.2} + + \versionchanged[Restriction on first letter of mode string + introduced]{2.5} \end{funcdesc} \begin{funcdesc}{filter}{function, list} @@ -708,7 +709,10 @@ class C: \end{funcdesc} \begin{funcdesc}{open}{filename\optional{, mode\optional{, bufsize}}} - An alias for the \function{file()} function above. + A wrapper for the \function{file()} function above. The intent is + for \function{open()} to be preferred for use as a factory function + returning a new \class{file} object. \class{file} is more suited to + type testing (for example, writing \samp{isinstance(f, file)}). \end{funcdesc} \begin{funcdesc}{ord}{c} diff --git a/Doc/lib/libgetpass.tex b/Doc/lib/libgetpass.tex index 1d177d3..a742439 100644 --- a/Doc/lib/libgetpass.tex +++ b/Doc/lib/libgetpass.tex @@ -19,7 +19,7 @@ The \module{getpass} module provides two functions: \code{sys.stdout} (this argument is ignored on Windows). Availability: Macintosh, \UNIX, Windows. - \versionadded[The \var{stream} parameter]{2.5} + \versionchanged[The \var{stream} parameter was added]{2.5} \end{funcdesc} diff --git a/Doc/lib/libhtmlparser.tex b/Doc/lib/libhtmlparser.tex index b85ba56..52f8409 100644 --- a/Doc/lib/libhtmlparser.tex +++ b/Doc/lib/libhtmlparser.tex @@ -132,7 +132,7 @@ implementation does nothing. \begin{methoddesc}{handle_decl}{decl} Method called when an SGML declaration is read by the parser. The \var{decl} parameter will be the entire contents of the declaration -inside the \code{} markup.It is intended to be overridden +inside the \code{} markup. It is intended to be overridden by a derived class; the base class implementation does nothing. \end{methoddesc} diff --git a/Doc/lib/liblocale.tex b/Doc/lib/liblocale.tex index e6ba2c1..688ccb0 100644 --- a/Doc/lib/liblocale.tex +++ b/Doc/lib/liblocale.tex @@ -61,7 +61,7 @@ locale.setlocale(locale.LC_ALL, '') Returns the database of the local conventions as a dictionary. This dictionary has the following strings as keys: - \begin{tableiii}{l|l|p{3in}}{constant}{Key}{Category}{Meaning} + \begin{tableiii}{l|l|p{3in}}{constant}{Category}{Key}{Meaning} \lineiii{LC_NUMERIC}{\code{'decimal_point'}} {Decimal point character.} \lineiii{}{\code{'grouping'}} @@ -76,8 +76,20 @@ locale.setlocale(locale.LC_ALL, '') {International currency symbol.} \lineiii{}{\code{'currency_symbol'}} {Local currency symbol.} + \lineiii{}{\code{'p_cs_precedes/n_cs_precedes'}} + {Whether the currency symbol precedes the value (for positive resp. + negative values).} + \lineiii{}{\code{'p_sep_by_space/n_sep_by_space'}} + {Whether the currency symbol is separated from the value + by a space (for positive resp. negative values).} \lineiii{}{\code{'mon_decimal_point'}} {Decimal point used for monetary values.} + \lineiii{}{\code{'frac_digits'}} + {Number of fractional digits used in local formatting + of monetary values.} + \lineiii{}{\code{'int_frac_digits'}} + {Number of fractional digits used in international + formatting of monetary values.} \lineiii{}{\code{'mon_thousands_sep'}} {Group separator used for monetary values.} \lineiii{}{\code{'mon_grouping'}} @@ -87,13 +99,12 @@ locale.setlocale(locale.LC_ALL, '') {Symbol used to annotate a positive monetary value.} \lineiii{}{\code{'negative_sign'}} {Symbol used to annotate a negative monetary value.} - \lineiii{}{\code{'frac_digits'}} - {Number of fractional digits used in local formatting - of monetary values.} - \lineiii{}{\code{'int_frac_digits'}} - {Number of fractional digits used in international - formatting of monetary values.} + \lineiii{}{\code{'p_sign_posn/n_sign_posn'}} + {The position of the sign (for positive resp. negative values), see below.} \end{tableiii} + + All numeric values can be set to \constant{CHAR_MAX} to indicate that + there is no value specified in this locale. The possible values for \code{'p_sign_posn'} and \code{'n_sign_posn'} are given below. @@ -104,7 +115,7 @@ locale.setlocale(locale.LC_ALL, '') \lineii{2}{The sign should follow the value and currency symbol.} \lineii{3}{The sign should immediately precede the value.} \lineii{4}{The sign should immediately follow the value.} - \lineii{\constant{LC_MAX}}{Nothing is specified in this locale.} + \lineii{\constant{CHAR_MAX}}{Nothing is specified in this locale.} \end{tableii} \end{funcdesc} @@ -206,12 +217,44 @@ for which symbolic constants are available in the locale module. strings. \end{funcdesc} -\begin{funcdesc}{format}{format, val\optional{, grouping}} +\begin{funcdesc}{format}{format, val\optional{, grouping\optional{, monetary}}} Formats a number \var{val} according to the current \constant{LC_NUMERIC} setting. The format follows the conventions of the \code{\%} operator. For floating point values, the decimal point is modified if appropriate. If \var{grouping} is true, also takes the grouping into account. + + If \var{monetary} is true, the conversion uses monetary thousands + separator and grouping strings. + + Please note that this function will only work for exactly one \%char + specifier. For whole format strings, use \function{format_string()}. + + \versionchanged[Added the \var{monetary} parameter]{2.5} +\end{funcdesc} + +\begin{funcdesc}{format_string}{format, val\optional{, grouping}} + Processes formatting specifiers as in \code{format \% val}, + but takes the current locale settings into account. + + \versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{currency}{val\optional{, symbol\optional{, grouping\optional{, international}}}} + Formats a number \var{val} according to the current \constant{LC_MONETARY} + settings. + + The returned string includes the currency symbol if \var{symbol} is true, + which is the default. + If \var{grouping} is true (which is not the default), grouping is done with + the value. + If \var{international} is true (which is not the default), the international + currency symbol is used. + + Note that this function will not work with the `C' locale, so you have to set + a locale via \function{setlocale()} first. + + \versionadded{2.5} \end{funcdesc} \begin{funcdesc}{str}{float} diff --git a/Doc/lib/libmailbox.tex b/Doc/lib/libmailbox.tex index dd18562..0a1f792 100644 --- a/Doc/lib/libmailbox.tex +++ b/Doc/lib/libmailbox.tex @@ -1,12 +1,1253 @@ \section{\module{mailbox} --- - Read various mailbox formats} + Manipulate mailboxes in various formats} -\declaremodule{standard}{mailbox} -\modulesynopsis{Read various mailbox formats.} +\declaremodule{}{mailbox} +\moduleauthor{Gregory K.~Johnson}{gkj@gregorykjohnson.com} +\sectionauthor{Gregory K.~Johnson}{gkj@gregorykjohnson.com} +\modulesynopsis{Manipulate mailboxes in various formats} -This module defines a number of classes that allow easy and uniform -access to mail messages in a (\UNIX) mailbox. +This module defines two classes, \class{Mailbox} and \class{Message}, for +accessing and manipulating on-disk mailboxes and the messages they contain. +\class{Mailbox} offers a dictionary-like mapping from keys to messages. +\class{Message} extends the \module{email.Message} module's \class{Message} +class with format-specific state and behavior. Supported mailbox formats are +Maildir, mbox, MH, Babyl, and MMDF. + +\begin{seealso} + \seemodule{email}{Represent and manipulate messages.} +\end{seealso} + +\subsection{\class{Mailbox} objects} +\label{mailbox-objects} + +\begin{classdesc*}{Mailbox} +A mailbox, which may be inspected and modified. +\end{classdesc*} + +The \class{Mailbox} interface is dictionary-like, with small keys +corresponding to messages. Keys are issued by the \class{Mailbox} instance +with which they will be used and are only meaningful to that \class{Mailbox} +instance. A key continues to identify a message even if the corresponding +message is modified, such as by replacing it with another message. Messages may +be added to a \class{Mailbox} instance using the set-like method +\method{add()} and removed using a \code{del} statement or the set-like methods +\method{remove()} and \method{discard()}. + +\class{Mailbox} interface semantics differ from dictionary semantics in some +noteworthy ways. Each time a message is requested, a new representation +(typically a \class{Message} instance) is generated, based upon the current +state of the mailbox. Similarly, when a message is added to a \class{Mailbox} +instance, the provided message representation's contents are copied. In neither +case is a reference to the message representation kept by the \class{Mailbox} +instance. + +The default \class{Mailbox} iterator iterates over message representations, not +keys as the default dictionary iterator does. Moreover, modification of a +mailbox during iteration is safe and well-defined. Messages added to the +mailbox after an iterator is created will not be seen by the iterator. Messages +removed from the mailbox before the iterator yields them will be silently +skipped, though using a key from an iterator may result in a +\exception{KeyError} exception if the corresponding message is subsequently +removed. + +\class{Mailbox} itself is intended to define an interface and to be inherited +from by format-specific subclasses but is not intended to be instantiated. +Instead, you should instantiate a subclass. + +\class{Mailbox} instances have the following methods: + +\begin{methoddesc}{add}{message} +Add \var{message} to the mailbox and return the key that has been assigned to +it. + +Parameter \var{message} may be a \class{Message} instance, an +\class{email.Message.Message} instance, a string, or a file-like object (which +should be open in text mode). If \var{message} is an instance of the +appropriate format-specific \class{Message} subclass (e.g., if it's an +\class{mboxMessage} instance and this is an \class{mbox} instance), its +format-specific information is used. Otherwise, reasonable defaults for +format-specific information are used. +\end{methoddesc} + +\begin{methoddesc}{remove}{key} +\methodline{__delitem__}{key} +\methodline{discard}{key} +Delete the message corresponding to \var{key} from the mailbox. + +If no such message exists, a \exception{KeyError} exception is raised if the +method was called as \method{remove()} or \method{__delitem__()} but no +exception is raised if the method was called as \method{discard()}. The +behavior of \method{discard()} may be preferred if the underlying mailbox +format supports concurrent modification by other processes. +\end{methoddesc} + +\begin{methoddesc}{__setitem__}{key, message} +Replace the message corresponding to \var{key} with \var{message}. Raise a +\exception{KeyError} exception if no message already corresponds to \var{key}. + +As with \method{add()}, parameter \var{message} may be a \class{Message} +instance, an \class{email.Message.Message} instance, a string, or a file-like +object (which should be open in text mode). If \var{message} is an instance of +the appropriate format-specific \class{Message} subclass (e.g., if it's an +\class{mboxMessage} instance and this is an \class{mbox} instance), its +format-specific information is used. Otherwise, the format-specific information +of the message that currently corresponds to \var{key} is left unchanged. +\end{methoddesc} + +\begin{methoddesc}{iterkeys}{} +\methodline{keys}{} +Return an iterator over all keys if called as \method{iterkeys()} or return a +list of keys if called as \method{keys()}. +\end{methoddesc} + +\begin{methoddesc}{itervalues}{} +\methodline{__iter__}{} +\methodline{values}{} +Return an iterator over representations of all messages if called as +\method{itervalues()} or \method{__iter__()} or return a list of such +representations if called as \method{values()}. The messages are represented as +instances of the appropriate format-specific \class{Message} subclass unless a +custom message factory was specified when the \class{Mailbox} instance was +initialized. \note{The behavior of \method{__iter__()} is unlike that of +dictionaries, which iterate over keys.} +\end{methoddesc} + +\begin{methoddesc}{iteritems}{} +\methodline{items}{} +Return an iterator over (\var{key}, \var{message}) pairs, where \var{key} is a +key and \var{message} is a message representation, if called as +\method{iteritems()} or return a list of such pairs if called as +\method{items()}. The messages are represented as instances of the appropriate +format-specific \class{Message} subclass unless a custom message factory was +specified when the \class{Mailbox} instance was initialized. +\end{methoddesc} + +\begin{methoddesc}{get}{key\optional{, default=None}} +\methodline{__getitem__}{key} +Return a representation of the message corresponding to \var{key}. If no such +message exists, \var{default} is returned if the method was called as +\method{get()} and a \exception{KeyError} exception is raised if the method was +called as \method{__getitem__()}. The message is represented as an instance of +the appropriate format-specific \class{Message} subclass unless a custom +message factory was specified when the \class{Mailbox} instance was +initialized. +\end{methoddesc} + +\begin{methoddesc}{get_message}{key} +Return a representation of the message corresponding to \var{key} as an +instance of the appropriate format-specific \class{Message} subclass, or raise +a \exception{KeyError} exception if no such message exists. +\end{methoddesc} + +\begin{methoddesc}{get_string}{key} +Return a string representation of the message corresponding to \var{key}, or +raise a \exception{KeyError} exception if no such message exists. +\end{methoddesc} + +\begin{methoddesc}{get_file}{key} +Return a file-like representation of the message corresponding to \var{key}, +or raise a \exception{KeyError} exception if no such message exists. The +file-like object behaves as if open in binary mode. This file should be closed +once it is no longer needed. + +\note{Unlike other representations of messages, file-like representations are +not necessarily independent of the \class{Mailbox} instance that created them +or of the underlying mailbox. More specific documentation is provided by each +subclass.} +\end{methoddesc} + +\begin{methoddesc}{has_key}{key} +\methodline{__contains__}{key} +Return \code{True} if \var{key} corresponds to a message, \code{False} +otherwise. +\end{methoddesc} + +\begin{methoddesc}{__len__}{} +Return a count of messages in the mailbox. +\end{methoddesc} + +\begin{methoddesc}{clear}{} +Delete all messages from the mailbox. +\end{methoddesc} + +\begin{methoddesc}{pop}{key\optional{, default}} +Return a representation of the message corresponding to \var{key} and delete +the message. If no such message exists, return \var{default} if it was supplied +or else raise a \exception{KeyError} exception. The message is represented as +an instance of the appropriate format-specific \class{Message} subclass unless +a custom message factory was specified when the \class{Mailbox} instance was +initialized. +\end{methoddesc} + +\begin{methoddesc}{popitem}{} +Return an arbitrary (\var{key}, \var{message}) pair, where \var{key} is a key +and \var{message} is a message representation, and delete the corresponding +message. If the mailbox is empty, raise a \exception{KeyError} exception. The +message is represented as an instance of the appropriate format-specific +\class{Message} subclass unless a custom message factory was specified when the +\class{Mailbox} instance was initialized. +\end{methoddesc} + +\begin{methoddesc}{update}{arg} +Parameter \var{arg} should be a \var{key}-to-\var{message} mapping or an +iterable of (\var{key}, \var{message}) pairs. Updates the mailbox so that, for +each given \var{key} and \var{message}, the message corresponding to \var{key} +is set to \var{message} as if by using \method{__setitem__()}. As with +\method{__setitem__()}, each \var{key} must already correspond to a message in +the mailbox or else a \exception{KeyError} exception will be raised, so in +general it is incorrect for \var{arg} to be a \class{Mailbox} instance. +\note{Unlike with dictionaries, keyword arguments are not supported.} +\end{methoddesc} + +\begin{methoddesc}{flush}{} +Write any pending changes to the filesystem. For some \class{Mailbox} +subclasses, changes are always written immediately and this method does +nothing. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +Acquire an exclusive advisory lock on the mailbox so that other processes know +not to modify it. An \exception{ExternalClashError} is raised if the lock is +not available. The particular locking mechanisms used depend upon the mailbox +format. +\end{methoddesc} + +\begin{methoddesc}{unlock}{} +Release the lock on the mailbox, if any. +\end{methoddesc} + +\begin{methoddesc}{close}{} +Flush the mailbox, unlock it if necessary, and close any open files. For some +\class{Mailbox} subclasses, this method does nothing. +\end{methoddesc} + + +\subsubsection{\class{Maildir}} +\label{mailbox-maildir} + +\begin{classdesc}{Maildir}{dirname\optional{, factory=rfc822.Message\optional{, +create=True}}} +A subclass of \class{Mailbox} for mailboxes in Maildir format. Parameter +\var{factory} is a callable object that accepts a file-like message +representation (which behaves as if opened in binary mode) and returns a custom +representation. If \var{factory} is \code{None}, \class{MaildirMessage} is used +as the default message representation. If \var{create} is \code{True}, the +mailbox is created if it does not exist. + +It is for historical reasons that \var{factory} defaults to +\class{rfc822.Message} and that \var{dirname} is named as such rather than +\var{path}. For a \class{Maildir} instance that behaves like instances of other +\class{Mailbox} subclasses, set \var{factory} to \code{None}. +\end{classdesc} + +Maildir is a directory-based mailbox format invented for the qmail mail +transfer agent and now widely supported by other programs. Messages in a +Maildir mailbox are stored in separate files within a common directory +structure. This design allows Maildir mailboxes to be accessed and modified by +multiple unrelated programs without data corruption, so file locking is +unnecessary. + +Maildir mailboxes contain three subdirectories, namely: \file{tmp}, \file{new}, +and \file{cur}. Messages are created momentarily in the \file{tmp} subdirectory +and then moved to the \file{new} subdirectory to finalize delivery. A mail user +agent may subsequently move the message to the \file{cur} subdirectory and +store information about the state of the message in a special "info" section +appended to its file name. + +Folders of the style introduced by the Courier mail transfer agent are also +supported. Any subdirectory of the main mailbox is considered a folder if +\character{.} is the first character in its name. Folder names are represented +by \class{Maildir} without the leading \character{.}. Each folder is itself a +Maildir mailbox but should not contain other folders. Instead, a logical +nesting is indicated using \character{.} to delimit levels, e.g., +"Archived.2005.07". + +\begin{notice} +The Maildir specification requires the use of a colon (\character{:}) in +certain message file names. However, some operating systems do not permit this +character in file names, If you wish to use a Maildir-like format on such an +operating system, you should specify another character to use instead. The +exclamation point (\character{!}) is a popular choice. For example: +\begin{verbatim} +import mailbox +mailbox.Maildir.colon = '!' +\end{verbatim} +The \member{colon} attribute may also be set on a per-instance basis. +\end{notice} + +\class{Maildir} instances have all of the methods of \class{Mailbox} in +addition to the following: + +\begin{methoddesc}{list_folders}{} +Return a list of the names of all folders. +\end{methoddesc} + +\begin{methoddesc}{get_folder}{folder} +Return a \class{Maildir} instance representing the folder whose name is +\var{folder}. A \exception{NoSuchMailboxError} exception is raised if the +folder does not exist. +\end{methoddesc} + +\begin{methoddesc}{add_folder}{folder} +Create a folder whose name is \var{folder} and return a \class{Maildir} +instance representing it. +\end{methoddesc} + +\begin{methoddesc}{remove_folder}{folder} +Delete the folder whose name is \var{folder}. If the folder contains any +messages, a \exception{NotEmptyError} exception will be raised and the folder +will not be deleted. +\end{methoddesc} + +\begin{methoddesc}{clean}{} +Delete temporary files from the mailbox that have not been accessed in the +last 36 hours. The Maildir specification says that mail-reading programs +should do this occasionally. +\end{methoddesc} + +Some \class{Mailbox} methods implemented by \class{Maildir} deserve special +remarks: + +\begin{methoddesc}{add}{message} +\methodline[Maildir]{__setitem__}{key, message} +\methodline[Maildir]{update}{arg} +\warning{These methods generate unique file names based upon the current +process ID. When using multiple threads, undetected name clashes may occur and +cause corruption of the mailbox unless threads are coordinated to avoid using +these methods to manipulate the same mailbox simultaneously.} +\end{methoddesc} + +\begin{methoddesc}{flush}{} +All changes to Maildir mailboxes are immediately applied, so this method does +nothing. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Maildir mailboxes do not support (or require) locking, so these methods do +nothing. +\end{methoddesc} + +\begin{methoddesc}{close}{} +\class{Maildir} instances do not keep any open files and the underlying +mailboxes do not support locking, so this method does nothing. +\end{methoddesc} + +\begin{methoddesc}{get_file}{key} +Depending upon the host platform, it may not be possible to modify or remove +the underlying message while the returned file remains open. +\end{methoddesc} + +\begin{seealso} + \seelink{http://www.qmail.org/man/man5/maildir.html}{maildir man page from + qmail}{The original specification of the format.} + \seelink{http://cr.yp.to/proto/maildir.html}{Using maildir format}{Notes + on Maildir by its inventor. Includes an updated name-creation scheme and + details on "info" semantics.} + \seelink{http://www.courier-mta.org/?maildir.html}{maildir man page from + Courier}{Another specification of the format. Describes a common extension + for supporting folders.} +\end{seealso} + +\subsubsection{\class{mbox}} +\label{mailbox-mbox} + +\begin{classdesc}{mbox}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in mbox format. Parameter +\var{factory} is a callable object that accepts a file-like message +representation (which behaves as if opened in binary mode) and returns a custom +representation. If \var{factory} is \code{None}, \class{mboxMessage} is used as +the default message representation. If \var{create} is \code{True}, the mailbox +is created if it does not exist. +\end{classdesc} + +The mbox format is the classic format for storing mail on \UNIX{} systems. All +messages in an mbox mailbox are stored in a single file with the beginning of +each message indicated by a line whose first five characters are "From~". + +Several variations of the mbox format exist to address perceived shortcomings +in the original. In the interest of compatibility, \class{mbox} implements the +original format, which is sometimes referred to as \dfn{mboxo}. This means that +the \mailheader{Content-Length} header, if present, is ignored and that any +occurrences of "From~" at the beginning of a line in a message body are +transformed to ">From~" when storing the message, although occurences of +">From~" are not transformed to "From~" when reading the message. + +Some \class{Mailbox} methods implemented by \class{mbox} deserve special +remarks: + +\begin{methoddesc}{get_file}{key} +Using the file after calling \method{flush()} or \method{close()} on the +\class{mbox} instance may yield unpredictable results or raise an exception. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + +\begin{seealso} + \seelink{http://www.qmail.org/man/man5/mbox.html}{mbox man page from + qmail}{A specification of the format and its variations.} + \seelink{http://www.tin.org/bin/man.cgi?section=5\&topic=mbox}{mbox man + page from tin}{Another specification of the format, with details on + locking.} + \seelink{http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html} + {Configuring Netscape Mail on \UNIX{}: Why The Content-Length Format is + Bad}{An argument for using the original mbox format rather than a + variation.} + \seelink{http://homepages.tesco.net./\tilde{}J.deBoynePollard/FGA/mail-mbox-formats.html} + {"mbox" is a family of several mutually incompatible mailbox formats}{A + history of mbox variations.} +\end{seealso} + +\subsubsection{\class{MH}} +\label{mailbox-mh} + +\begin{classdesc}{MH}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in MH format. Parameter +\var{factory} is a callable object that accepts a file-like message +representation (which behaves as if opened in binary mode) and returns a custom +representation. If \var{factory} is \code{None}, \class{MHMessage} is used as +the default message representation. If \var{create} is \code{True}, the mailbox +is created if it does not exist. +\end{classdesc} + +MH is a directory-based mailbox format invented for the MH Message Handling +System, a mail user agent. Each message in an MH mailbox resides in its own +file. An MH mailbox may contain other MH mailboxes (called \dfn{folders}) in +addition to messages. Folders may be nested indefinitely. MH mailboxes also +support \dfn{sequences}, which are named lists used to logically group messages +without moving them to sub-folders. Sequences are defined in a file called +\file{.mh_sequences} in each folder. + +The \class{MH} class manipulates MH mailboxes, but it does not attempt to +emulate all of \program{mh}'s behaviors. In particular, it does not modify and +is not affected by the \file{context} or \file{.mh_profile} files that are used +by \program{mh} to store its state and configuration. + +\class{MH} instances have all of the methods of \class{Mailbox} in addition to +the following: + +\begin{methoddesc}{list_folders}{} +Return a list of the names of all folders. +\end{methoddesc} + +\begin{methoddesc}{get_folder}{folder} +Return an \class{MH} instance representing the folder whose name is +\var{folder}. A \exception{NoSuchMailboxError} exception is raised if the +folder does not exist. +\end{methoddesc} + +\begin{methoddesc}{add_folder}{folder} +Create a folder whose name is \var{folder} and return an \class{MH} instance +representing it. +\end{methoddesc} + +\begin{methoddesc}{remove_folder}{folder} +Delete the folder whose name is \var{folder}. If the folder contains any +messages, a \exception{NotEmptyError} exception will be raised and the folder +will not be deleted. +\end{methoddesc} + +\begin{methoddesc}{get_sequences}{} +Return a dictionary of sequence names mapped to key lists. If there are no +sequences, the empty dictionary is returned. +\end{methoddesc} + +\begin{methoddesc}{set_sequences}{sequences} +Re-define the sequences that exist in the mailbox based upon \var{sequences}, a +dictionary of names mapped to key lists, like returned by +\method{get_sequences()}. +\end{methoddesc} + +\begin{methoddesc}{pack}{} +Rename messages in the mailbox as necessary to eliminate gaps in numbering. +Entries in the sequences list are updated correspondingly. \note{Already-issued +keys are invalidated by this operation and should not be subsequently used.} +\end{methoddesc} + +Some \class{Mailbox} methods implemented by \class{MH} deserve special remarks: + +\begin{methoddesc}{remove}{key} +\methodline{__delitem__}{key} +\methodline{discard}{key} +These methods immediately delete the message. The MH convention of marking a +message for deletion by prepending a comma to its name is not used. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. For MH mailboxes, +locking the mailbox means locking the \file{.mh_sequences} file and, only for +the duration of any operations that affect them, locking individual message +files. +\end{methoddesc} + +\begin{methoddesc}{get_file}{key} +Depending upon the host platform, it may not be possible to remove the +underlying message while the returned file remains open. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +All changes to MH mailboxes are immediately applied, so this method does +nothing. +\end{methoddesc} + +\begin{methoddesc}{close}{} +\class{MH} instances do not keep any open files, so this method is equivelant +to \method{unlock()}. +\end{methoddesc} + +\begin{seealso} +\seelink{http://www.nongnu.org/nmh/}{nmh - Message Handling System}{Home page +of \program{nmh}, an updated version of the original \program{mh}.} +\seelink{http://www.ics.uci.edu/\tilde{}mh/book/}{MH \& nmh: Email for Users \& +Programmers}{A GPL-licensed book on \program{mh} and \program{nmh}, with some +information on the mailbox format.} +\end{seealso} + +\subsubsection{\class{Babyl}} +\label{mailbox-babyl} + +\begin{classdesc}{Babyl}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameter +\var{factory} is a callable object that accepts a file-like message +representation (which behaves as if opened in binary mode) and returns a custom +representation. If \var{factory} is \code{None}, \class{BabylMessage} is used +as the default message representation. If \var{create} is \code{True}, the +mailbox is created if it does not exist. +\end{classdesc} + +Babyl is a single-file mailbox format used by the Rmail mail user agent +included with Emacs. The beginning of a message is indicated by a line +containing the two characters Control-Underscore +(\character{\textbackslash037}) and Control-L (\character{\textbackslash014}). +The end of a message is indicated by the start of the next message or, in the +case of the last message, a line containing a Control-Underscore +(\character{\textbackslash037}) character. + +Messages in a Babyl mailbox have two sets of headers, original headers and +so-called visible headers. Visible headers are typically a subset of the +original headers that have been reformatted or abridged to be more attractive. +Each message in a Babyl mailbox also has an accompanying list of \dfn{labels}, +or short strings that record extra information about the message, and a list of +all user-defined labels found in the mailbox is kept in the Babyl options +section. + +\class{Babyl} instances have all of the methods of \class{Mailbox} in addition +to the following: + +\begin{methoddesc}{get_labels}{} +Return a list of the names of all user-defined labels used in the mailbox. +\note{The actual messages are inspected to determine which labels exist in the +mailbox rather than consulting the list of labels in the Babyl options section, +but the Babyl section is updated whenever the mailbox is modified.} +\end{methoddesc} + +Some \class{Mailbox} methods implemented by \class{Babyl} deserve special +remarks: + +\begin{methoddesc}{get_file}{key} +In Babyl mailboxes, the headers of a message are not stored contiguously with +the body of the message. To generate a file-like representation, the headers +and body are copied together into a \class{StringIO} instance (from the +\module{StringIO} module), which has an API identical to that of a file. As a +result, the file-like object is truly independent of the underlying mailbox but +does not save memory compared to a string representation. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + +\begin{seealso} +\seelink{http://quimby.gnus.org/notes/BABYL}{Format of Version 5 Babyl Files}{A +specification of the Babyl format.} +\seelink{http://www.gnu.org/software/emacs/manual/html_node/Rmail.html}{Reading +Mail with Rmail}{The Rmail manual, with some information on Babyl semantics.} +\end{seealso} + +\subsubsection{\class{MMDF}} +\label{mailbox-mmdf} + +\begin{classdesc}{MMDF}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameter +\var{factory} is a callable object that accepts a file-like message +representation (which behaves as if opened in binary mode) and returns a custom +representation. If \var{factory} is \code{None}, \class{MMDFMessage} is used as +the default message representation. If \var{create} is \code{True}, the mailbox +is created if it does not exist. +\end{classdesc} + +MMDF is a single-file mailbox format invented for the Multichannel Memorandum +Distribution Facility, a mail transfer agent. Each message is in the same form +as an mbox message but is bracketed before and after by lines containing four +Control-A (\character{\textbackslash001}) characters. As with the mbox format, +the beginning of each message is indicated by a line whose first five +characters are "From~", but additional occurrences of "From~" are not +transformed to ">From~" when storing messages because the extra message +separator lines prevent mistaking such occurrences for the starts of subsequent +messages. + +Some \class{Mailbox} methods implemented by \class{MMDF} deserve special +remarks: + +\begin{methoddesc}{get_file}{key} +Using the file after calling \method{flush()} or \method{close()} on the +\class{MMDF} instance may yield unpredictable results or raise an exception. +\end{methoddesc} + +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + +\begin{seealso} +\seelink{http://www.tin.org/bin/man.cgi?section=5\&topic=mmdf}{mmdf man page +from tin}{A specification of MMDF format from the documentation of tin, a +newsreader.} +\seelink{http://en.wikipedia.org/wiki/MMDF}{MMDF}{A Wikipedia article +describing the Multichannel Memorandum Distribution Facility.} +\end{seealso} + +\subsection{\class{Message} objects} +\label{mailbox-message-objects} + +\begin{classdesc}{Message}{\optional{message}} +A subclass of the \module{email.Message} module's \class{Message}. Subclasses +of \class{mailbox.Message} add mailbox-format-specific state and behavior. + +If \var{message} is omitted, the new instance is created in a default, empty +state. If \var{message} is an \class{email.Message.Message} instance, its +contents are copied; furthermore, any format-specific information is converted +insofar as possible if \var{message} is a \class{Message} instance. If +\var{message} is a string or a file, it should contain an \rfc{2822}-compliant +message, which is read and parsed. +\end{classdesc} + +The format-specific state and behaviors offered by subclasses vary, but in +general it is only the properties that are not specific to a particular mailbox +that are supported (although presumably the properties are specific to a +particular mailbox format). For example, file offsets for single-file mailbox +formats and file names for directory-based mailbox formats are not retained, +because they are only applicable to the original mailbox. But state such as +whether a message has been read by the user or marked as important is retained, +because it applies to the message itself. + +There is no requirement that \class{Message} instances be used to represent +messages retrieved using \class{Mailbox} instances. In some situations, the +time and memory required to generate \class{Message} representations might not +not acceptable. For such situations, \class{Mailbox} instances also offer +string and file-like representations, and a custom message factory may be +specified when a \class{Mailbox} instance is initialized. + +\subsubsection{\class{MaildirMessage}} +\label{mailbox-maildirmessage} + +\begin{classdesc}{MaildirMessage}{\optional{message}} +A message with Maildir-specific behaviors. Parameter \var{message} +has the same meaning as with the \class{Message} constructor. +\end{classdesc} + +Typically, a mail user agent application moves all of the messages in the +\file{new} subdirectory to the \file{cur} subdirectory after the first time the +user opens and closes the mailbox, recording that the messages are old whether +or not they've actually been read. Each message in \file{cur} has an "info" +section added to its file name to store information about its state. (Some mail +readers may also add an "info" section to messages in \file{new}.) The "info" +section may take one of two forms: it may contain "2," followed by a list of +standardized flags (e.g., "2,FR") or it may contain "1," followed by so-called +experimental information. Standard flags for Maildir messages are as follows: + +\begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation} +\lineiii{D}{Draft}{Under composition} +\lineiii{F}{Flagged}{Marked as important} +\lineiii{P}{Passed}{Forwarded, resent, or bounced} +\lineiii{R}{Replied}{Replied to} +\lineiii{S}{Seen}{Read} +\lineiii{T}{Trashed}{Marked for subsequent deletion} +\end{tableiii} + +\class{MaildirMessage} instances offer the following methods: + +\begin{methoddesc}{get_subdir}{} +Return either "new" (if the message should be stored in the \file{new} +subdirectory) or "cur" (if the message should be stored in the \file{cur} +subdirectory). \note{A message is typically moved from \file{new} to \file{cur} +after its mailbox has been accessed, whether or not the message is has been +read. A message \code{msg} has been read if \code{"S" not in msg.get_flags()} +is \code{True}.} +\end{methoddesc} + +\begin{methoddesc}{set_subdir}{subdir} +Set the subdirectory the message should be stored in. Parameter \var{subdir} +must be either "new" or "cur". +\end{methoddesc} + +\begin{methoddesc}{get_flags}{} +Return a string specifying the flags that are currently set. If the message +complies with the standard Maildir format, the result is the concatenation in +alphabetical order of zero or one occurrence of each of \character{D}, +\character{F}, \character{P}, \character{R}, \character{S}, and \character{T}. +The empty string is returned if no flags are set or if "info" contains +experimental semantics. +\end{methoddesc} + +\begin{methoddesc}{set_flags}{flags} +Set the flags specified by \var{flags} and unset all others. +\end{methoddesc} + +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. The current "info" is overwritten whether or not it contains +experimental information rather than +flags. +\end{methoddesc} + +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. If "info" contains experimental information rather than flags, the +current "info" is not modified. +\end{methoddesc} + +\begin{methoddesc}{get_date}{} +Return the delivery date of the message as a floating-point number representing +seconds since the epoch. +\end{methoddesc} + +\begin{methoddesc}{set_date}{date} +Set the delivery date of the message to \var{date}, a floating-point number +representing seconds since the epoch. +\end{methoddesc} + +\begin{methoddesc}{get_info}{} +Return a string containing the "info" for a message. This is useful for +accessing and modifying "info" that is experimental (i.e., not a list of +flags). +\end{methoddesc} + +\begin{methoddesc}{set_info}{info} +Set "info" to \var{info}, which should be a string. +\end{methoddesc} + +When a \class{MaildirMessage} instance is created based upon an +\class{mboxMessage} or \class{MMDFMessage} instance, the \mailheader{Status} +and \mailheader{X-Status} headers are omitted and the following conversions +take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{mboxMessage} or \class{MMDFMessage} state} +\lineii{"cur" subdirectory}{O flag} +\lineii{F flag}{F flag} +\lineii{R flag}{A flag} +\lineii{S flag}{R flag} +\lineii{T flag}{D flag} +\end{tableii} + +When a \class{MaildirMessage} instance is created based upon an +\class{MHMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MHMessage} state} +\lineii{"cur" subdirectory}{"unseen" sequence} +\lineii{"cur" subdirectory and S flag}{no "unseen" sequence} +\lineii{F flag}{"flagged" sequence} +\lineii{R flag}{"replied" sequence} +\end{tableii} + +When a \class{MaildirMessage} instance is created based upon a +\class{BabylMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{BabylMessage} state} +\lineii{"cur" subdirectory}{"unseen" label} +\lineii{"cur" subdirectory and S flag}{no "unseen" label} +\lineii{P flag}{"forwarded" or "resent" label} +\lineii{R flag}{"answered" label} +\lineii{T flag}{"deleted" label} +\end{tableii} + +\subsubsection{\class{mboxMessage}} +\label{mailbox-mboxmessage} + +\begin{classdesc}{mboxMessage}{\optional{message}} +A message with mbox-specific behaviors. Parameter \var{message} has the same +meaning as with the \class{Message} constructor. +\end{classdesc} + +Messages in an mbox mailbox are stored together in a single file. The sender's +envelope address and the time of delivery are typically stored in a line +beginning with "From~" that is used to indicate the start of a message, though +there is considerable variation in the exact format of this data among mbox +implementations. Flags that indicate the state of the message, such as whether +it has been read or marked as important, are typically stored in +\mailheader{Status} and \mailheader{X-Status} headers. + +Conventional flags for mbox messages are as follows: + +\begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation} +\lineiii{R}{Read}{Read} +\lineiii{O}{Old}{Previously detected by MUA} +\lineiii{D}{Deleted}{Marked for subsequent deletion} +\lineiii{F}{Flagged}{Marked as important} +\lineiii{A}{Answered}{Replied to} +\end{tableiii} + +The "R" and "O" flags are stored in the \mailheader{Status} header, and the +"D", "F", and "A" flags are stored in the \mailheader{X-Status} header. The +flags and headers typically appear in the order mentioned. + +\class{mboxMessage} instances offer the following methods: + +\begin{methoddesc}{get_from}{} +Return a string representing the "From~" line that marks the start of the +message in an mbox mailbox. The leading "From~" and the trailing newline are +excluded. +\end{methoddesc} + +\begin{methoddesc}{set_from}{from_\optional{, time_=None}} +Set the "From~" line to \var{from_}, which should be specified without a +leading "From~" or trailing newline. For convenience, \var{time_} may be +specified and will be formatted appropriately and appended to \var{from_}. If +\var{time_} is specified, it should be a \class{struct_time} instance, a tuple +suitable for passing to \method{time.strftime()}, or \code{True} (to use +\method{time.gmtime()}). +\end{methoddesc} + +\begin{methoddesc}{get_flags}{} +Return a string specifying the flags that are currently set. If the message +complies with the conventional format, the result is the concatenation in the +following order of zero or one occurrence of each of \character{R}, +\character{O}, \character{D}, \character{F}, and \character{A}. +\end{methoddesc} + +\begin{methoddesc}{set_flags}{flags} +Set the flags specified by \var{flags} and unset all others. Parameter +\var{flags} should be the concatenation in any order of zero or more +occurrences of each of \character{R}, \character{O}, \character{D}, +\character{F}, and \character{A}. +\end{methoddesc} + +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. +\end{methoddesc} + +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. +\end{methoddesc} + +When an \class{mboxMessage} instance is created based upon a +\class{MaildirMessage} instance, a "From~" line is generated based upon the +\class{MaildirMessage} instance's delivery date, and the following conversions +take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MaildirMessage} state} +\lineii{R flag}{S flag} +\lineii{O flag}{"cur" subdirectory} +\lineii{D flag}{T flag} +\lineii{F flag}{F flag} +\lineii{A flag}{R flag} +\end{tableii} + +When an \class{mboxMessage} instance is created based upon an \class{MHMessage} +instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MHMessage} state} +\lineii{R flag and O flag}{no "unseen" sequence} +\lineii{O flag}{"unseen" sequence} +\lineii{F flag}{"flagged" sequence} +\lineii{A flag}{"replied" sequence} +\end{tableii} + +When an \class{mboxMessage} instance is created based upon a +\class{BabylMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{BabylMessage} state} +\lineii{R flag and O flag}{no "unseen" label} +\lineii{O flag}{"unseen" label} +\lineii{D flag}{"deleted" label} +\lineii{A flag}{"answered" label} +\end{tableii} + +When a \class{Message} instance is created based upon an \class{MMDFMessage} +instance, the "From~" line is copied and all flags directly correspond: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MMDFMessage} state} +\lineii{R flag}{R flag} +\lineii{O flag}{O flag} +\lineii{D flag}{D flag} +\lineii{F flag}{F flag} +\lineii{A flag}{A flag} +\end{tableii} + +\subsubsection{\class{MHMessage}} +\label{mailbox-mhmessage} + +\begin{classdesc}{MHMessage}{\optional{message}} +A message with MH-specific behaviors. Parameter \var{message} has the same +meaning as with the \class{Message} constructor. +\end{classdesc} + +MH messages do not support marks or flags in the traditional sense, but they do +support sequences, which are logical groupings of arbitrary messages. Some mail +reading programs (although not the standard \program{mh} and \program{nmh}) use +sequences in much the same way flags are used with other formats, as follows: + +\begin{tableii}{l|l}{textrm}{Sequence}{Explanation} +\lineii{unseen}{Not read, but previously detected by MUA} +\lineii{replied}{Replied to} +\lineii{flagged}{Marked as important} +\end{tableii} + +\class{MHMessage} instances offer the following methods: + +\begin{methoddesc}{get_sequences}{} +Return a list of the names of sequences that include this message. +\end{methoddesc} + +\begin{methoddesc}{set_sequences}{sequences} +Set the list of sequences that include this message. +\end{methoddesc} + +\begin{methoddesc}{add_sequence}{sequence} +Add \var{sequence} to the list of sequences that include this message. +\end{methoddesc} + +\begin{methoddesc}{remove_sequence}{sequence} +Remove \var{sequence} from the list of sequences that include this message. +\end{methoddesc} + +When an \class{MHMessage} instance is created based upon a +\class{MaildirMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MaildirMessage} state} +\lineii{"unseen" sequence}{no S flag} +\lineii{"replied" sequence}{R flag} +\lineii{"flagged" sequence}{F flag} +\end{tableii} + +When an \class{MHMessage} instance is created based upon an \class{mboxMessage} +or \class{MMDFMessage} instance, the \mailheader{Status} and +\mailheader{X-Status} headers are omitted and the following conversions take +place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{mboxMessage} or \class{MMDFMessage} state} +\lineii{"unseen" sequence}{no R flag} +\lineii{"replied" sequence}{A flag} +\lineii{"flagged" sequence}{F flag} +\end{tableii} + +When an \class{MHMessage} instance is created based upon a \class{BabylMessage} +instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{BabylMessage} state} +\lineii{"unseen" sequence}{"unseen" label} +\lineii{"replied" sequence}{"answered" label} +\end{tableii} + +\subsubsection{\class{BabylMessage}} +\label{mailbox-babylmessage} + +\begin{classdesc}{BabylMessage}{\optional{message}} +A message with Babyl-specific behaviors. Parameter \var{message} has the same +meaning as with the \class{Message} constructor. +\end{classdesc} + +Certain message labels, called \dfn{attributes}, are defined by convention to +have special meanings. The attributes are as follows: + +\begin{tableii}{l|l}{textrm}{Label}{Explanation} +\lineii{unseen}{Not read, but previously detected by MUA} +\lineii{deleted}{Marked for subsequent deletion} +\lineii{filed}{Copied to another file or mailbox} +\lineii{answered}{Replied to} +\lineii{forwarded}{Forwarded} +\lineii{edited}{Modified by the user} +\lineii{resent}{Resent} +\end{tableii} + +By default, Rmail displays only +visible headers. The \class{BabylMessage} class, though, uses the original +headers because they are more complete. Visible headers may be accessed +explicitly if desired. + +\class{BabylMessage} instances offer the following methods: + +\begin{methoddesc}{get_labels}{} +Return a list of labels on the message. +\end{methoddesc} + +\begin{methoddesc}{set_labels}{labels} +Set the list of labels on the message to \var{labels}. +\end{methoddesc} + +\begin{methoddesc}{add_label}{label} +Add \var{label} to the list of labels on the message. +\end{methoddesc} + +\begin{methoddesc}{remove_label}{label} +Remove \var{label} from the list of labels on the message. +\end{methoddesc} + +\begin{methoddesc}{get_visible}{} +Return an \class{Message} instance whose headers are the message's visible +headers and whose body is empty. +\end{methoddesc} + +\begin{methoddesc}{set_visible}{visible} +Set the message's visible headers to be the same as the headers in +\var{message}. Parameter \var{visible} should be a \class{Message} instance, an +\class{email.Message.Message} instance, a string, or a file-like object (which +should be open in text mode). +\end{methoddesc} + +\begin{methoddesc}{update_visible}{} +When a \class{BabylMessage} instance's original headers are modified, the +visible headers are not automatically modified to correspond. This method +updates the visible headers as follows: each visible header with a +corresponding original header is set to the value of the original header, each +visible header without a corresponding original header is removed, and any of +\mailheader{Date}, \mailheader{From}, \mailheader{Reply-To}, \mailheader{To}, +\mailheader{CC}, and \mailheader{Subject} that are present in the original +headers but not the visible headers are added to the visible headers. +\end{methoddesc} + +When a \class{BabylMessage} instance is created based upon a +\class{MaildirMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MaildirMessage} state} +\lineii{"unseen" label}{no S flag} +\lineii{"deleted" label}{T flag} +\lineii{"answered" label}{R flag} +\lineii{"forwarded" label}{P flag} +\end{tableii} + +When a \class{BabylMessage} instance is created based upon an +\class{mboxMessage} or \class{MMDFMessage} instance, the \mailheader{Status} +and \mailheader{X-Status} headers are omitted and the following conversions +take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{mboxMessage} or \class{MMDFMessage} state} +\lineii{"unseen" label}{no R flag} +\lineii{"deleted" label}{D flag} +\lineii{"answered" label}{A flag} +\end{tableii} + +When a \class{BabylMessage} instance is created based upon an \class{MHMessage} +instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MHMessage} state} +\lineii{"unseen" label}{"unseen" sequence} +\lineii{"answered" label}{"replied" sequence} +\end{tableii} + +\subsubsection{\class{MMDFMessage}} +\label{mailbox-mmdfmessage} + +\begin{classdesc}{MMDFMessage}{\optional{message}} +A message with MMDF-specific behaviors. Parameter \var{message} has the same +meaning as with the \class{Message} constructor. +\end{classdesc} + +As with message in an mbox mailbox, MMDF messages are stored with the sender's +address and the delivery date in an initial line beginning with "From ". +Likewise, flags that indicate the state of the message are typically stored in +\mailheader{Status} and \mailheader{X-Status} headers. + +Conventional flags for MMDF messages are identical to those of mbox message and +are as follows: + +\begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation} +\lineiii{R}{Read}{Read} +\lineiii{O}{Old}{Previously detected by MUA} +\lineiii{D}{Deleted}{Marked for subsequent deletion} +\lineiii{F}{Flagged}{Marked as important} +\lineiii{A}{Answered}{Replied to} +\end{tableiii} + +The "R" and "O" flags are stored in the \mailheader{Status} header, and the +"D", "F", and "A" flags are stored in the \mailheader{X-Status} header. The +flags and headers typically appear in the order mentioned. + +\class{MMDFMessage} instances offer the following methods, which are identical +to those offered by \class{mboxMessage}: + +\begin{methoddesc}{get_from}{} +Return a string representing the "From~" line that marks the start of the +message in an mbox mailbox. The leading "From~" and the trailing newline are +excluded. +\end{methoddesc} + +\begin{methoddesc}{set_from}{from_\optional{, time_=None}} +Set the "From~" line to \var{from_}, which should be specified without a +leading "From~" or trailing newline. For convenience, \var{time_} may be +specified and will be formatted appropriately and appended to \var{from_}. If +\var{time_} is specified, it should be a \class{struct_time} instance, a tuple +suitable for passing to \method{time.strftime()}, or \code{True} (to use +\method{time.gmtime()}). +\end{methoddesc} + +\begin{methoddesc}{get_flags}{} +Return a string specifying the flags that are currently set. If the message +complies with the conventional format, the result is the concatenation in the +following order of zero or one occurrence of each of \character{R}, +\character{O}, \character{D}, \character{F}, and \character{A}. +\end{methoddesc} + +\begin{methoddesc}{set_flags}{flags} +Set the flags specified by \var{flags} and unset all others. Parameter +\var{flags} should be the concatenation in any order of zero or more +occurrences of each of \character{R}, \character{O}, \character{D}, +\character{F}, and \character{A}. +\end{methoddesc} + +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. +\end{methoddesc} + +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. +\end{methoddesc} + +When an \class{MMDFMessage} instance is created based upon a +\class{MaildirMessage} instance, a "From~" line is generated based upon the +\class{MaildirMessage} instance's delivery date, and the following conversions +take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MaildirMessage} state} +\lineii{R flag}{S flag} +\lineii{O flag}{"cur" subdirectory} +\lineii{D flag}{T flag} +\lineii{F flag}{F flag} +\lineii{A flag}{R flag} +\end{tableii} + +When an \class{MMDFMessage} instance is created based upon an \class{MHMessage} +instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{MHMessage} state} +\lineii{R flag and O flag}{no "unseen" sequence} +\lineii{O flag}{"unseen" sequence} +\lineii{F flag}{"flagged" sequence} +\lineii{A flag}{"replied" sequence} +\end{tableii} + +When an \class{MMDFMessage} instance is created based upon a +\class{BabylMessage} instance, the following conversions take place: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{BabylMessage} state} +\lineii{R flag and O flag}{no "unseen" label} +\lineii{O flag}{"unseen" label} +\lineii{D flag}{"deleted" label} +\lineii{A flag}{"answered" label} +\end{tableii} + +When an \class{MMDFMessage} instance is created based upon an +\class{mboxMessage} instance, the "From~" line is copied and all flags directly +correspond: + +\begin{tableii}{l|l}{textrm} + {Resulting state}{\class{mboxMessage} state} +\lineii{R flag}{R flag} +\lineii{O flag}{O flag} +\lineii{D flag}{D flag} +\lineii{F flag}{F flag} +\lineii{A flag}{A flag} +\end{tableii} + +\subsection{Exceptions} +\label{mailbox-deprecated} + +The following exception classes are defined in the \module{mailbox} module: + +\begin{classdesc}{Error}{} +The based class for all other module-specific exceptions. +\end{classdesc} + +\begin{classdesc}{NoSuchMailboxError}{} +Raised when a mailbox is expected but is not found, such as when instantiating +a \class{Mailbox} subclass with a path that does not exist (and with the +\var{create} parameter set to \code{False}), or when opening a folder that does +not exist. +\end{classdesc} + +\begin{classdesc}{NotEmptyErrorError}{} +Raised when a mailbox is not empty but is expected to be, such as when deleting +a folder that contains messages. +\end{classdesc} + +\begin{classdesc}{ExternalClashError}{} +Raised when some mailbox-related condition beyond the control of the program +causes it to be unable to proceed, such as when failing to acquire a lock that +another program already holds a lock, or when a uniquely-generated file name +already exists. +\end{classdesc} + +\begin{classdesc}{FormatError}{} +Raised when the data in a file cannot be parsed, such as when an \class{MH} +instance attempts to read a corrupted \file{.mh_sequences} file. +\end{classdesc} + +\subsection{Deprecated classes and methods} +\label{mailbox-deprecated} + +Older versions of the \module{mailbox} module do not support modification of +mailboxes, such as adding or removing message, and do not provide classes to +represent format-specific message properties. For backward compatibility, the +older mailbox classes are still available, but the newer classes should be used +in preference to them. + +Older mailbox objects support only iteration and provide a single public +method: + +\begin{methoddesc}{next}{} +Return the next message in the mailbox, created with the optional \var{factory} +argument passed into the mailbox object's constructor. By default this is an +\class{rfc822.Message} object (see the \refmodule{rfc822} module). Depending +on the mailbox implementation the \var{fp} attribute of this object may be a +true file object or a class instance simulating a file object, taking care of +things like message boundaries if multiple mail messages are contained in a +single file, etc. If no more messages are available, this method returns +\code{None}. +\end{methoddesc} + +Most of the older mailbox classes have names that differ from the current +mailbox class names, except for \class{Maildir}. For this reason, the new +\class{Maildir} class defines a \method{next()} method and its constructor +differs slightly from those of the other new mailbox classes. + +The older mailbox classes whose names are not the same as their newer +counterparts are as follows: \begin{classdesc}{UnixMailbox}{fp\optional{, factory}} Access to a classic \UNIX-style mailbox, where all messages are @@ -68,12 +1309,6 @@ The name of the mailbox directory is passed in \var{dirname}. \var{factory} is as with the \class{UnixMailbox} class. \end{classdesc} -\begin{classdesc}{Maildir}{dirname\optional{, factory}} -Access a Qmail mail directory. All new and current mail for the -mailbox specified by \var{dirname} is made available. -\var{factory} is as with the \class{UnixMailbox} class. -\end{classdesc} - \begin{classdesc}{BabylMailbox}{fp\optional{, factory}} Access a Babyl mailbox, which is similar to an MMDF mailbox. In Babyl format, each message has two sets of headers, the @@ -89,11 +1324,8 @@ messages start with the EOOH line and end with a line containing only \class{UnixMailbox} class. \end{classdesc} -Note that because the \refmodule{rfc822} module is deprecated, it is -recommended that you use the \refmodule{email} package to create -message objects from a mailbox. (The default can't be changed for -backwards compatibility reasons.) The safest way to do this is with -bit of code: +If you wish to use the older mailbox classes with the \module{email} module +rather than the deprecated \module{rfc822} module, you can do so as follows: \begin{verbatim} import email @@ -105,17 +1337,14 @@ def msgfactory(fp): return email.message_from_file(fp) except email.Errors.MessageParseError: # Don't return None since that will - # stop the mailbox iterator - return '' + # stop the mailbox iterator + return '' mbox = mailbox.UnixMailbox(fp, msgfactory) \end{verbatim} -The above wrapper is defensive against ill-formed MIME messages in the -mailbox, but you have to be prepared to receive the empty string from -the mailbox's \function{next()} method. On the other hand, if you -know your mailbox contains only well-formed MIME messages, you can -simplify this to: +Alternatively, if you know your mailbox contains only well-formed MIME +messages, you can simplify this to: \begin{verbatim} import email @@ -124,35 +1353,57 @@ import mailbox mbox = mailbox.UnixMailbox(fp, email.message_from_file) \end{verbatim} -\begin{seealso} - \seetitle[http://www.qmail.org/man/man5/mbox.html]{mbox - - file containing mail messages}{Description of the - traditional ``mbox'' mailbox format.} - \seetitle[http://www.qmail.org/man/man5/maildir.html]{maildir - - directory for incoming mail messages}{Description of the - ``maildir'' mailbox format.} - \seetitle[http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html]{Configuring - Netscape Mail on \UNIX: Why the Content-Length Format is - Bad}{A description of problems with relying on the - \mailheader{Content-Length} header for messages stored in - mailbox files.} -\end{seealso} +\subsection{Examples} +\label{mailbox-examples} +A simple example of printing the subjects of all messages in a mailbox that +seem interesting: -\subsection{Mailbox Objects \label{mailbox-objects}} +\begin{verbatim} +import mailbox +for message in mailbox.mbox('~/mbox'): + subject = message['subject'] # Could possibly be None. + if subject and 'python' in subject.lower(): + print subject +\end{verbatim} -All implementations of mailbox objects are iterable objects, and -have one externally visible method. This method is used by iterators -created from mailbox objects and may also be used directly. +A (surprisingly) simple example of copying all mail from a Babyl mailbox to an +MH mailbox, converting all of the format-specific information that can be +converted: -\begin{methoddesc}[mailbox]{next}{} -Return the next message in the mailbox, created with the optional -\var{factory} argument passed into the mailbox object's constructor. -By default this is an \class{rfc822.Message} -object (see the \refmodule{rfc822} module). Depending on the mailbox -implementation the \var{fp} attribute of this object may be a true -file object or a class instance simulating a file object, taking care -of things like message boundaries if multiple mail messages are -contained in a single file, etc. If no more messages are available, -this method returns \code{None}. -\end{methoddesc} +\begin{verbatim} +import mailbox +destination = mailbox.MH('~/Mail') +for message in mailbox.Babyl('~/RMAIL'): + destination.add(MHMessage(message)) +\end{verbatim} + +An example of sorting mail from numerous mailing lists, being careful to avoid +mail corruption due to concurrent modification by other programs, mail loss due +to interruption of the program, or premature termination due to malformed +messages in the mailbox: + +\begin{verbatim} +import mailbox +import email.Errors +list_names = ('python-list', 'python-dev', 'python-bugs') +boxes = dict((name, mailbox.mbox('~/email/%s' % name)) for name in list_names) +inbox = mailbox.Maildir('~/Maildir', None) +for key in inbox.iterkeys(): + try: + message = inbox[key] + except email.Errors.MessageParseError: + continue # The message is malformed. Just leave it. + for name in list_names: + list_id = message['list-id'] + if list_id and name in list_id: + box = boxes[name] + box.lock() + box.add(message) + box.flush() # Write copy to disk before removing original. + box.unlock() + inbox.discard(key) + break # Found destination, so stop looking. +for box in boxes.itervalues(): + box.close() +\end{verbatim} diff --git a/Doc/lib/libmsilib.tex b/Doc/lib/libmsilib.tex new file mode 100644 index 0000000..1e044f4 --- /dev/null +++ b/Doc/lib/libmsilib.tex @@ -0,0 +1,485 @@ +\section{\module{msilib} --- + Read and write Microsoft Installer files} + +\declaremodule{standard}{msilib} + \platform{Windows} +\modulesynopsis{Creation of Microsoft Installer files, and CAB files.} +\moduleauthor{Martin v. L\"owis}{martin@v.loewis.de} +\sectionauthor{Martin v. L\"owis}{martin@v.loewis.de} + +\index{msi} + +\versionadded{2.5} + +The \module{msilib} supports the creation of Microsoft Installer +(\code{.msi}) files. Because these files often contain an embedded +``cabinet'' file (\code{.cab}), it also exposes an API to create +CAB files. Support for reading \code{.cab} files is currently not +implemented; read support for the \code{.msi} database is possible. + +This package aims to provide complete access to all tables in an +\code{.msi} file, therefore, it is a fairly low-level API. Two +primary applications of this package are the \module{distutils} +command \code{bdist_msi}, and the creation of Python installer +package itself (although that currently uses a different version +of \code{msilib}). + +The package contents can be roughly split into four parts: +low-level CAB routines, low-level MSI routines, higher-level +MSI routines, and standard table structures. + +\begin{funcdesc}{FCICreate}{cabname, files} + Create a new CAB file named \var{cabname}. \var{files} must + be a list of tuples, each containing the name of the file on + disk, and the name of the file inside the CAB file. + + The files are added to the CAB file in the order they appear + in the list. All files are added into a single CAB file, + using the MSZIP compression algorithm. + + Callbacks to Python for the various steps of MSI creation + are currently not exposed. +\end{funcdesc} + +\begin{funcdesc}{UUIDCreate}{} + Return the string representation of a new unique identifier. + This wraps the Windows API functions \cfunction{UuidCreate} and + \cfunction{UuidToString}. +\end{funcdesc} + +\begin{funcdesc}{OpenDatabase}{path, persist} + Return a new database object by calling MsiOpenDatabase. + \var{path} is the file name of the + MSI file; \var{persist} can be one of the constants + \code{MSIDBOPEN_CREATEDIRECT}, \code{MSIDBOPEN_CREATE}, + \code{MSIDBOPEN_DIRECT}, \code{MSIDBOPEN_READONLY}, or + \code{MSIDBOPEN_TRANSACT}, and may include the flag + \code{MSIDBOPEN_PATCHFILE}. See the Microsoft documentation for + the meaning of these flags; depending on the flags, + an existing database is opened, or a new one created. +\end{funcdesc} + +\begin{funcdesc}{CreateRecord}{count} + Return a new record object by calling \cfunction{MSICreateRecord}. + \var{count} is the number of fields of the record. +\end{funcdesc} + +\begin{funcdesc}{init_database}{name, schema, ProductName, ProductCode, ProductVersion, Manufacturer} + Create and return a new database \var{name}, initialize it + with \var{schema}, and set the properties \var{ProductName}, + \var{ProductCode}, \var{ProductVersion}, and \var{Manufacturer}. + + \var{schema} must be a module object containing \code{tables} and + \code{_Validation_records} attributes; typically, + \module{msilib.schema} should be used. + + The database will contain just the schema and the validation + records when this function returns. +\end{funcdesc} + +\begin{funcdesc}{add_data}{database, records} + Add all \var{records} to \var{database}. \var{records} should + be a list of tuples, each one containing all fields of a record + according to the schema of the table. For optional fields, + \code{None} can be passed. + + Field values can be int or long numbers, strings, or instances + of the Binary class. +\end{funcdesc} + +\begin{classdesc}{Binary}{filename} + Represents entries in the Binary table; inserting such + an object using \function{add_data} reads the file named + \var{filename} into the table. +\end{classdesc} + +\begin{funcdesc}{add_tables}{database, module} + Add all table content from \var{module} to \var{database}. + \var{module} must contain an attribute \var{tables} + listing all tables for which content should be added, + and one attribute per table that has the actual content. + + This is typically used to install the sequence tables. +\end{funcdesc} + +\begin{funcdesc}{add_stream}{database, name, path} + Add the file \var{path} into the \code{_Stream} table + of \var{database}, with the stream name \var{name}. +\end{funcdesc} + +\begin{funcdesc}{gen_uuid}{} + Return a new UUID, in the format that MSI typically + requires (i.e. in curly braces, and with all hexdigits + in upper-case). +\end{funcdesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devnotes/winprog/fcicreate.asp]{FCICreateFile}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/uuidcreate.asp]{UuidCreate}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/uuidtostring.asp]{UuidToString}{} +\end{seealso} + +\subsection{Database Objects\label{database-objects}} + +\begin{methoddesc}{OpenView}{sql} + Return a view object, by calling \cfunction{MSIDatabaseOpenView}. + \var{sql} is the SQL statement to execute. +\end{methoddesc} + +\begin{methoddesc}{Commit}{} + Commit the changes pending in the current transaction, + by calling \cfunction{MSIDatabaseCommit}. +\end{methoddesc} + +\begin{methoddesc}{GetSummaryInformation}{count} + Return a new summary information object, by calling + \cfunction{MsiGetSummaryInformation}. \var{count} is the maximum number of + updated values. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiopenview.asp]{MSIOpenView}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msidatabasecommit.asp]{MSIDatabaseCommit}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msigetsummaryinformation.asp]{MSIGetSummaryInformation}{} +\end{seealso} + +\subsection{View Objects\label{view-objects}} + +\begin{methoddesc}{Execute}{\optional{params=None}} + Execute the SQL query of the view, through \cfunction{MSIViewExecute}. + \var{params} is an optional record describing actual values + of the parameter tokens in the query. +\end{methoddesc} + +\begin{methoddesc}{GetColumnInfo}{kind} + Return a record describing the columns of the view, through + calling \cfunction{MsiViewGetColumnInfo}. \var{kind} can be either + \code{MSICOLINFO_NAMES} or \code{MSICOLINFO_TYPES}. +\end{methoddesc} + +\begin{methoddesc}{Fetch}{} + Return a result record of the query, through calling + \cfunction{MsiViewFetch}. +\end{methoddesc} + +\begin{methoddesc}{Modify}{kind, data} + Modify the view, by calling \cfunction{MsiViewModify}. \var{kind} + can be one of \code{MSIMODIFY_SEEK}, \code{MSIMODIFY_REFRESH}, + \code{MSIMODIFY_INSERT}, \code{MSIMODIFY_UPDATE}, \code{MSIMODIFY_ASSIGN}, + \code{MSIMODIFY_REPLACE}, \code{MSIMODIFY_MERGE}, \code{MSIMODIFY_DELETE}, + \code{MSIMODIFY_INSERT_TEMPORARY}, \code{MSIMODIFY_VALIDATE}, + \code{MSIMODIFY_VALIDATE_NEW}, \code{MSIMODIFY_VALIDATE_FIELD}, or + \code{MSIMODIFY_VALIDATE_DELETE}. + + \var{data} must be a record describing the new data. +\end{methoddesc} + +\begin{methoddesc}{Close}{} + Close the view, through \cfunction{MsiViewClose}. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiviewexecute.asp]{MsiViewExecute}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiviewgetcolumninfo.asp]{MSIViewGetColumnInfo}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiviewfetch.asp]{MsiViewFetch}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiviewmodify.asp]{MsiViewModify}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiviewclose.asp]{MsiViewClose}{} +\end{seealso} + +\subsection{Summary Information Objects\label{summary-objects}} + +\begin{methoddesc}{GetProperty}{field} + Return a property of the summary, through \cfunction{MsiSummaryInfoGetProperty}. + \var{field} is the name of the property, and can be one of the + constants + \code{PID_CODEPAGE}, \code{PID_TITLE}, \code{PID_SUBJECT}, + \code{PID_AUTHOR}, \code{PID_KEYWORDS}, \code{PID_COMMENTS}, + \code{PID_TEMPLATE}, \code{PID_LASTAUTHOR}, \code{PID_REVNUMBER}, + \code{PID_LASTPRINTED}, \code{PID_CREATE_DTM}, \code{PID_LASTSAVE_DTM}, + \code{PID_PAGECOUNT}, \code{PID_WORDCOUNT}, \code{PID_CHARCOUNT}, + \code{PID_APPNAME}, or \code{PID_SECURITY}. +\end{methoddesc} + +\begin{methoddesc}{GetPropertyCount}{} + Return the number of summary properties, through + \cfunction{MsiSummaryInfoGetPropertyCount}. +\end{methoddesc} + +\begin{methoddesc}{SetProperty}{field, value} + Set a property through \cfunction{MsiSummaryInfoSetProperty}. \var{field} + can have the same values as in \method{GetProperty}, \var{value} + is the new value of the property. Possible value types are integer + and string. +\end{methoddesc} + +\begin{methoddesc}{Persist}{} + Write the modified properties to the summary information stream, + using \cfunction{MsiSummaryInfoPersist}. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msisummaryinfogetproperty.asp]{MsiSummaryInfoGetProperty}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msisummaryinfogetpropertycount.asp]{MsiSummaryInfoGetPropertyCount}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msisummaryinfosetproperty.asp]{MsiSummaryInfoSetProperty}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msisummaryinfopersist.asp]{MsiSummaryInfoPersist}{} +\end{seealso} + +\subsection{Record Objects\label{record-objects}} + +\begin{methoddesc}{GetFieldCount}{} + Return the number of fields of the record, through \cfunction{MsiRecordGetFieldCount}. +\end{methoddesc} + +\begin{methoddesc}{SetString}{field, value} + Set \var{field} to \var{value} through \cfunction{MsiRecordSetString}. + \var{field} must be an integer; \var{value} a string. +\end{methoddesc} + +\begin{methoddesc}{SetStream}{field, value} + Set \var{field} to the contents of the file named \var{value}, + through \cfunction{MsiRecordSetStream}. + \var{field} must be an integer; \var{value} a string. +\end{methoddesc} + +\begin{methoddesc}{SetInteger}{field, value} + Set \var{field} to \var{value} through \cfunction{MsiRecordSetInteger}. + Both \var{field} and \var{value} must be an integer. +\end{methoddesc} + +\begin{methoddesc}{ClearData}{} + Set all fields of the record to 0, through \cfunction{MsiRecordClearData}. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msirecordgetfieldcount.asp]{MsiRecordGetFieldCount}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msirecordsetstring.asp]{MsiRecordSetString}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msirecordsetstream.asp]{MsiRecordSetStream}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msirecordsetinteger.asp]{MsiRecordSetInteger}{} + \seetitle[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msirecordclear.asp]{MsiRecordClear}{} +\end{seealso} + +\subsection{Errors\label{msi-errors}} + +All wrappers around MSI functions raise \exception{MsiError}; +the string inside the exception will contain more detail. + +\subsection{CAB Objects\label{cab}} + +\begin{classdesc}{CAB}{name} + The class \class{CAB} represents a CAB file. During MSI construction, + files will be added simultaneously to the \code{Files} table, and + to a CAB file. Then, when all files have been added, the CAB file + can be written, then added to the MSI file. + + \var{name} is the name of the CAB file in the MSI file. +\end{classdesc} + +\begin{methoddesc}[CAB]{append}{full, logical} + Add the file with the pathname \var{full} to the CAB file, + under the name \var{logical}. If there is already a file + named \var{logical}, a new file name is created. + + Return the index of the file in the CAB file, and the + new name of the file inside the CAB file. +\end{methoddesc} + +\begin{methoddesc}[CAB]{append}{database} + Generate a CAB file, add it as a stream to the MSI file, + put it into the \code{Media} table, and remove the generated + file from the disk. +\end{methoddesc} + +\subsection{Directory Objects\label{msi-directory}} + +\begin{classdesc}{Directory}{database, cab, basedir, physical, + logical, default, component, \optional{componentflags}} + Create a new directory in the Directory table. There is a current + component at each point in time for the directory, which is either + explicitly created through \method{start_component}, or implicitly when files + are added for the first time. Files are added into the current + component, and into the cab file. To create a directory, a base + directory object needs to be specified (can be \code{None}), the path to + the physical directory, and a logical directory name. \var{default} + specifies the DefaultDir slot in the directory table. \var{componentflags} + specifies the default flags that new components get. +\end{classdesc} + +\begin{methoddesc}[Directory]{start_component}{\optional{component\optional{, + feature\optional{, flags\optional{, keyfile\optional{, uuid}}}}}} + Add an entry to the Component table, and make this component the + current component for this directory. If no component name is given, the + directory name is used. If no \var{feature} is given, the current feature + is used. If no \var{flags} are given, the directory's default flags are + used. If no \var{keyfile} is given, the KeyPath is left null in the + Component table. +\end{methoddesc} + +\begin{methoddesc}[Directory]{add_file}{file\optional{, src\optional{, + version\optional{, language}}}} + Add a file to the current component of the directory, starting a new + one if there is no current component. By default, the file name + in the source and the file table will be identical. If the \var{src} file + is specified, it is interpreted relative to the current + directory. Optionally, a \var{version} and a \var{language} can be specified for + the entry in the File table. +\end{methoddesc} + +\begin{methoddesc}[Directory]{glob}{pattern\optional{, exclude}} + Add a list of files to the current component as specified in the glob + pattern. Individual files can be excluded in the \var{exclude} list. +\end{methoddesc} + +\begin{methoddesc}[Directory]{remove_pyc}{} + Remove \code{.pyc}/\code{.pyo} files on uninstall. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/directory_table.asp]{Directory Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/file_table.asp]{File Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/component_table.asp]{Component Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/featurecomponents_table.asp]{FeatureComponents Table}{} +\end{seealso} + + +\subsection{Features\label{features}} + +\begin{classdesc}{Feature}{database, id, title, desc, display\optional{, + level=1\optional{, parent\optional\{, directory\optional{, + attributes=0}}}} + + Add a new record to the \code{Feature} table, using the values + \var{id}, \var{parent.id}, \var{title}, \var{desc}, \var{display}, + \var{level}, \var{directory}, and \var{attributes}. The resulting + feature object can be passed to the \method{start_component} method + of \class{Directory}. +\end{classdesc} + +\begin{methoddesc}[Feature]{set_current}{} + Make this feature the current feature of \module{msilib}. + New components are automatically added to the default feature, + unless a feature is explicitly specified. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/feature_table.asp]{Feature Table}{} +\end{seealso} + +\subsection{GUI classes\label{msi-gui}} + +\module{msilib} provides several classes that wrap the GUI tables in +an MSI database. However, no standard user interface is provided; use +\module{bdist_msi} to create MSI files with a user-interface for +installing Python packages. + +\begin{classdesc}{Control}{dlg, name} + Base class of the dialog controls. \var{dlg} is the dialog object + the control belongs to, and \var{name} is the control's name. +\end{classdesc} + +\begin{methoddesc}[Control]{event}{event, argument\optional{, + condition = ``1''\optional{, ordering}}} + + Make an entry into the \code{ControlEvent} table for this control. +\end{methoddesc} + +\begin{methoddesc}[Control]{mapping}{event, attribute} + Make an entry into the \code{EventMapping} table for this control. +\end{methoddesc} + +\begin{methoddesc}[Control]{condition}{action, condition} + Make an entry into the \code{ControlCondition} table for this control. +\end{methoddesc} + + +\begin{classdesc}{RadioButtonGroup}{dlg, name, property} + Create a radio button control named \var{name}. \var{property} + is the installer property that gets set when a radio button + is selected. +\end{classdesc} + +\begin{methoddesc}[RadioButtonGroup]{add}{name, x, y, width, height, text + \optional{, value}} + Add a radio button named \var{name} to the group, at the + coordinates \var{x}, \var{y}, \var{width}, \var{height}, and + with the label \var{text}. If \var{value} is omitted, it + defaults to \var{name}. +\end{methoddesc} + +\begin{classdesc}{Dialog}{db, name, x, y, w, h, attr, title, first, + default, cancel} + Return a new \class{Dialog} object. An entry in the \code{Dialog} table + is made, with the specified coordinates, dialog attributes, title, + name of the first, default, and cancel controls. +\end{classdesc} + +\begin{methoddesc}[Dialog]{control}{name, type, x, y, width, height, + attributes, property, text, control_next, help} + Return a new \class{Control} object. An entry in the \code{Control} table + is made with the specified parameters. + + This is a generic method; for specific types, specialized methods + are provided. +\end{methoddesc} + + +\begin{methoddesc}[Dialog]{text}{name, x, y, width, height, attributes, text} + Add and return a \code{Text} control. +\end{methoddesc} + +\begin{methoddesc}[Dialog]{bitmap}{name, x, y, width, height, text} + Add and return a \code{Bitmap} control. +\end{methoddesc} + +\begin{methoddesc}[Dialog]{line}{name, x, y, width, height} + Add and return a \code{Line} control. +\end{methoddesc} + +\begin{methoddesc}[Dialog]{pushbutton}{name, x, y, width, height, attributes, + text, next_control} + Add and return a \code{PushButton} control. +\end{methoddesc} + +\begin{methoddesc}[Dialog]{radiogroup}{name, x, y, width, height, + attributes, property, text, next_control} + Add and return a \code{RadioButtonGroup} control. +\end{methoddesc} + +\begin{methoddesc}[Dialog]{checkbox}{name, x, y, width, height, + attributes, property, text, next_control} + Add and return a \code{CheckBox} control. +\end{methoddesc} + +\begin{seealso} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/dialog_table.asp]{Dialog Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/control_table.asp]{Control Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/controls.asp]{Control Types}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/controlcondition_table.asp]{ControlCondition Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/controlevent_table.asp]{ControlEvent Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/eventmapping_table.asp]{EventMapping Table}{} + \seetitle[http://msdn.microsoft.com/library/en-us/msi/setup/radiobutton_table.asp]{RadioButton Table}{} +\end{seealso} + +\subsection{Precomputed tables\label{msi-tables}} + +\module{msilib} provides a few subpackages that contain +only schema and table definitions. Currently, these definitions +are based on MSI version 2.0. + +\begin{datadesc}{schema} + This is the standard MSI schema for MSI 2.0, with the + \var{tables} variable providing a list of table definitions, + and \var{_Validation_records} providing the data for + MSI validation. +\end{datadesc} + +\begin{datadesc}{sequence} + This module contains table contents for the standard sequence + tables: \var{AdminExecuteSequence}, \var{AdminUISequence}, + \var{AdvtExecuteSequence}, \var{InstallExecuteSequence}, and + \var{InstallUISequence}. +\end{datadesc} + +\begin{datadesc}{text} + This module contains definitions for the UIText and ActionText + tables, for the standard installer actions. +\end{datadesc} diff --git a/Doc/lib/liboperator.tex b/Doc/lib/liboperator.tex index 41da9b7..5ba3209 100644 --- a/Doc/lib/liboperator.tex +++ b/Doc/lib/liboperator.tex @@ -320,7 +320,7 @@ and \var{b} sequences. \begin{funcdesc}{irshift}{a, b} \funcline{__irshift__}{a, b} -\code{a = irshift(a, b)} is equivalent to \code{a >}\code{>= b}. +\code{a = irshift(a, b)} is equivalent to \code{a >>= b}. \versionadded{2.5} \end{funcdesc} @@ -499,7 +499,7 @@ symbols in the Python syntax and the functions in the {\code{neg(\var{a})}} \lineiii{Negation (Logical)}{\code{not \var{a}}} {\code{not_(\var{a})}} - \lineiii{Right Shift}{\code{\var{a} >\code{>} \var{b}}} + \lineiii{Right Shift}{\code{\var{a} >> \var{b}}} {\code{rshift(\var{a}, \var{b})}} \lineiii{Sequence Repitition}{\code{\var{seq} * \var{i}}} {\code{repeat(\var{seq}, \var{i})}} diff --git a/Doc/lib/liboptparse.tex b/Doc/lib/liboptparse.tex index 8aca501..ec43e3d 100644 --- a/Doc/lib/liboptparse.tex +++ b/Doc/lib/liboptparse.tex @@ -35,9 +35,9 @@ With these few lines of code, users of your script can now do the \end{verbatim} As it parses the command line, \code{optparse} sets attributes of the -\var{options} object returned by \method{parse{\_}args()} based on user-supplied +\code{options} object returned by \method{parse{\_}args()} based on user-supplied command-line values. When \method{parse{\_}args()} returns from parsing this -command line, \var{options.filename} will be \code{"outfile"} and +command line, \code{options.filename} will be \code{"outfile"} and \code{options.verbose} will be \code{False}. \code{optparse} supports both long and short options, allows short options to be merged together, and allows options to be associated with their arguments in a variety of @@ -100,8 +100,8 @@ options; the traditional \UNIX{} syntax is a hyphen (``-'') followed by a single letter, e.g. \code{"-x"} or \code{"-F"}. Also, traditional \UNIX{} syntax allows multiple options to be merged into a single argument, e.g. \code{"-x -F"} is equivalent to \code{"-xF"}. The GNU project -introduced \code{"{--}"} followed by a series of hyphen-separated words, -e.g. \code{"{--}file"} or \code{"{--}dry-run"}. These are the only two option +introduced \code{"-{}-"} followed by a series of hyphen-separated words, +e.g. \code{"-{}-file"} or \code{"-{}-dry-run"}. These are the only two option syntaxes provided by \module{optparse}. Some other option syntaxes that the world has seen include: @@ -170,7 +170,7 @@ For example, consider this hypothetical command-line: prog -v --report /tmp/report.txt foo bar \end{verbatim} -\code{"-v"} and \code{"{--}report"} are both options. Assuming that +\code{"-v"} and \code{"-{}-report"} are both options. Assuming that \longprogramopt{report} takes one argument, \code{"/tmp/report.txt"} is an option argument. \code{"foo"} and \code{"bar"} are positional arguments. @@ -287,12 +287,12 @@ but that's rarely necessary: by default it uses \code{sys.argv{[}1:]}.) \method{parse{\_}args()} returns two values: \begin{itemize} \item {} -\var{options}, an object containing values for all of your options{---}e.g. if \code{"-{}-file"} takes a single string argument, then -\var{options.file} will be the filename supplied by the user, or +\code{options}, an object containing values for all of your options{---}e.g. if \code{"-{}-file"} takes a single string argument, then +\code{options.file} will be the filename supplied by the user, or \code{None} if the user did not supply that option \item {} -\var{args}, the list of positional arguments leftover after parsing +\code{args}, the list of positional arguments leftover after parsing options \end{itemize} @@ -309,7 +309,7 @@ command line. There is a fixed set of actions hard-coded into \module{optparse} adding new actions is an advanced topic covered in section~\ref{optparse-extending}, Extending \module{optparse}. Most actions tell \module{optparse} to store a value in some variable{---}for example, take a string from the command line and store it in an -attribute of \var{options}. +attribute of \code{options}. If you don't specify an option action, \module{optparse} defaults to \code{store}. @@ -333,8 +333,8 @@ args = ["-f", "foo.txt"] \end{verbatim} When \module{optparse} sees the option string \code{"-f"}, it consumes the next -argument, \code{"foo.txt"}, and stores it in \var{options.filename}. So, -after this call to \method{parse{\_}args()}, \var{options.filename} is +argument, \code{"foo.txt"}, and stores it in \code{options.filename}. So, +after this call to \method{parse{\_}args()}, \code{options.filename} is \code{"foo.txt"}. Some other option types supported by \module{optparse} are \code{int} and \code{float}. @@ -379,7 +379,7 @@ types is covered in section~\ref{optparse-extending}, Extending \module{optparse Flag options{---}set a variable to true or false when a particular option is seen{---}are quite common. \module{optparse} supports them with two separate actions, \code{store{\_}true} and \code{store{\_}false}. For example, you might have a -\var{verbose} flag that is turned on with \code{"-v"} and off with \code{"-q"}: +\code{verbose} flag that is turned on with \code{"-v"} and off with \code{"-q"}: \begin{verbatim} parser.add_option("-v", action="store_true", dest="verbose") parser.add_option("-q", action="store_false", dest="verbose") @@ -421,7 +421,7 @@ want more control. \module{optparse} lets you supply a default value for each destination, which is assigned before the command line is parsed. First, consider the verbose/quiet example. If we want \module{optparse} to set -\var{verbose} to \code{True} unless \code{"-q"} is seen, then we can do this: +\code{verbose} to \code{True} unless \code{"-q"} is seen, then we can do this: \begin{verbatim} parser.add_option("-v", action="store_true", dest="verbose", default=True) parser.add_option("-q", action="store_false", dest="verbose") @@ -441,7 +441,7 @@ parser.add_option("-v", action="store_true", dest="verbose", default=False) parser.add_option("-q", action="store_false", dest="verbose", default=True) \end{verbatim} -Again, the default value for \var{verbose} will be \code{True}: the last +Again, the default value for \code{verbose} will be \code{True}: the last default value supplied for any particular destination is the one that counts. @@ -566,7 +566,7 @@ argument to OptionParser: parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0") \end{verbatim} -Note that \code{"{\%}prog"} is expanded just like it is in \var{usage}. Apart +Note that \code{"{\%}prog"} is expanded just like it is in \code{usage}. Apart from that, \code{version} can contain anything you like. When you supply it, \module{optparse} automatically adds a \code{"-{}-version"} option to your parser. If it encounters this option on the command line, it expands your @@ -580,14 +580,14 @@ foo 1.0 \end{verbatim} -\subsubsection{How \module{optparse} handles errors\label{optparse-how-optik-handles-errors}} +\subsubsection{How \module{optparse} handles errors\label{optparse-how-optparse-handles-errors}} There are two broad classes of errors that \module{optparse} has to worry about: programmer errors and user errors. Programmer errors are usually -erroneous calls to \code{parse.add{\_}option()}, e.g. invalid option strings, +erroneous calls to \code{parser.add{\_}option()}, e.g. invalid option strings, unknown option attributes, missing option attributes, etc. These are dealt with in the usual way: raise an exception (either -\exception{optparse.OptionError} or \exception{TypeError}) and let the program crash. +\code{optparse.OptionError} or \code{TypeError}) and let the program crash. Handling user errors is much more important, since they are guaranteed to happen no matter how stable your code is. \module{optparse} can automatically @@ -659,12 +659,66 @@ def main(): if __name__ == "__main__": main() \end{verbatim} -% $Id: tutorial.txt 415 2004-09-30 02:26:17Z greg $ +% $Id: tutorial.txt 505 2005-07-22 01:52:40Z gward $ \subsection{Reference Guide\label{optparse-reference-guide}} +\subsubsection{Creating the parser\label{optparse-creating-parser}} + +The first step in using \module{optparse} is to create an OptionParser instance: +\begin{verbatim} +parser = OptionParser(...) +\end{verbatim} + +The OptionParser constructor has no required arguments, but a number of +optional keyword arguments. You should always pass them as keyword +arguments, i.e. do not rely on the order in which the arguments are +declared. +\begin{quote} +\begin{description} +\item[\code{usage} (default: \code{"{\%}prog {[}options]"})] +The usage summary to print when your program is run incorrectly or +with a help option. When \module{optparse} prints the usage string, it expands +\code{{\%}prog} to \code{os.path.basename(sys.argv{[}0])} (or to \code{prog} if +you passed that keyword argument). To suppress a usage message, +pass the special value \code{optparse.SUPPRESS{\_}USAGE}. +\item[\code{option{\_}list} (default: \code{{[}]})] +A list of Option objects to populate the parser with. The options +in \code{option{\_}list} are added after any options in +\code{standard{\_}option{\_}list} (a class attribute that may be set by +OptionParser subclasses), but before any version or help options. +Deprecated; use \method{add{\_}option()} after creating the parser instead. +\item[\code{option{\_}class} (default: optparse.Option)] +Class to use when adding options to the parser in \method{add{\_}option()}. +\item[\code{version} (default: \code{None})] +A version string to print when the user supplies a version option. +If you supply a true value for \code{version}, \module{optparse} automatically adds +a version option with the single option string \code{"-{}-version"}. The +substring \code{"{\%}prog"} is expanded the same as for \code{usage}. +\item[\code{conflict{\_}handler} (default: \code{"error"})] +Specifies what to do when options with conflicting option strings +are added to the parser; see section~\ref{optparse-conflicts-between-options}, Conflicts between options. +\item[\code{description} (default: \code{None})] +A paragraph of text giving a brief overview of your program. \module{optparse} +reformats this paragraph to fit the current terminal width and +prints it when the user requests help (after \code{usage}, but before +the list of options). +\item[\code{formatter} (default: a new IndentedHelpFormatter)] +An instance of optparse.HelpFormatter that will be used for +printing help text. \module{optparse} provides two concrete classes for this +purpose: IndentedHelpFormatter and TitledHelpFormatter. +\item[\code{add{\_}help{\_}option} (default: \code{True})] +If true, \module{optparse} will add a help option (with option strings \code{"-h"} +and \code{"-{}-help"}) to the parser. +\item[\code{prog}] +The string to use when expanding \code{"{\%}prog"} in \code{usage} and +\code{version} instead of \code{os.path.basename(sys.argv{[}0])}. +\end{description} +\end{quote} + + \subsubsection{Populating the parser\label{optparse-populating-parser}} There are several ways to populate the parser with options. The @@ -708,38 +762,34 @@ strings, e.g. \programopt{-f} and \longprogramopt{file}. You can specify any number of short or long option strings, but you must specify at least one overall option string. -The canonical way to create an Option instance is by calling -\function{make{\_}option()}, so that is what will be shown here. However, the -most common and convenient way is to use \code{parser.add{\_}option()}. Note -that \function{make{\_}option()} and \code{parser.add{\_}option()} have identical call -signatures: +The canonical way to create an Option instance is with the +\method{add{\_}option()} method of \class{OptionParser}: \begin{verbatim} -make_option(opt_str, ..., attr=value, ...) -parser.add_option(opt_str, ..., attr=value, ...) +parser.add_option(opt_str[, ...], attr=value, ...) \end{verbatim} To define an option with only a short option string: \begin{verbatim} -make_option("-f", attr=value, ...) +parser.add_option("-f", attr=value, ...) \end{verbatim} And to define an option with only a long option string: \begin{verbatim} -make_option("--foo", attr=value, ...) +parser.add_option("--foo", attr=value, ...) \end{verbatim} -The \code{attr=value} keyword arguments define option attributes, -i.e. attributes of the Option object. The most important option -attribute is \member{action}, and it largely determines what other attributes -are relevant or required. If you pass irrelevant option attributes, or -fail to pass required ones, \module{optparse} raises an OptionError exception -explaining your mistake. +The keyword arguments define attributes of the new Option object. The +most important option attribute is \member{action}, and it largely determines +which other attributes are relevant or required. If you pass irrelevant +option attributes, or fail to pass required ones, \module{optparse} raises an +OptionError exception explaining your mistake. -An options's \emph{action} determines what \module{optparse} does when it encounters -this option on the command-line. The actions hard-coded into \module{optparse} are: +An options's \emph{action} determines what \module{optparse} does when it encounters this +option on the command-line. The standard option actions hard-coded into +\module{optparse} are: \begin{description} \item[\code{store}] -store this option's argument {[}default] +store this option's argument (default) \item[\code{store{\_}const}] store a constant value \item[\code{store{\_}true}] @@ -748,6 +798,8 @@ store a true value store a false value \item[\code{append}] append this option's argument to a list +\item[\code{append{\_}const}] +append a constant value to a list \item[\code{count}] increment a counter by one \item[\code{callback}] @@ -762,24 +814,25 @@ action, you may also supply \member{type} and \member{dest} option attributes; s below.) As you can see, most actions involve storing or updating a value -somewhere. \module{optparse} always creates an instance of \code{optparse.Values} -specifically for this purpose; we refer to this instance as \var{options}. -Option arguments (and various other values) are stored as attributes of -this object, according to the \member{dest} (destination) option attribute. +somewhere. \module{optparse} always creates a special object for this, +conventionally called \code{options} (it happens to be an instance of +\code{optparse.Values}). Option arguments (and various other values) are +stored as attributes of this object, according to the \member{dest} +(destination) option attribute. For example, when you call \begin{verbatim} parser.parse_args() \end{verbatim} -one of the first things \module{optparse} does is create the \var{options} object: +one of the first things \module{optparse} does is create the \code{options} object: \begin{verbatim} options = Values() \end{verbatim} If one of the options in this parser is defined with \begin{verbatim} -make_option("-f", "--file", action="store", type="string", dest="filename") +parser.add_option("-f", "--file", action="store", type="string", dest="filename") \end{verbatim} and the command-line being parsed includes any of the following: @@ -790,8 +843,7 @@ and the command-line being parsed includes any of the following: --file foo \end{verbatim} -then \module{optparse}, on seeing the \programopt{-f} or \longprogramopt{file} option, will do the -equivalent of +then \module{optparse}, on seeing this option, will do the equivalent of \begin{verbatim} options.filename = "foo" \end{verbatim} @@ -912,6 +964,13 @@ options.tracks.append(int("4")) \end{verbatim} \item {} +\code{append{\_}const} {[}required: \code{const}; relevant: \member{dest}] + +Like \code{store{\_}const}, but the value \code{const} is appended to \member{dest}; +as with \code{append}, \member{dest} defaults to \code{None}, and an an empty list is +automatically created the first time the option is encountered. + +\item {} \code{count} {[}relevant: \member{dest}] Increment the integer stored at \member{dest}. If no default value is @@ -939,14 +998,9 @@ options.verbosity += 1 \code{callback} {[}required: \code{callback}; relevant: \member{type}, \code{nargs}, \code{callback{\_}args}, \code{callback{\_}kwargs}] -Call the function specified by \code{callback}. The signature of -this function should be +Call the function specified by \code{callback}, which is called as \begin{verbatim} -func(option : Option, - opt : string, - value : any, - parser : OptionParser, - *args, **kwargs) +func(option, opt_str, value, parser, *args, **kwargs) \end{verbatim} See section~\ref{optparse-option-callbacks}, Option Callbacks for more detail. @@ -956,7 +1010,7 @@ See section~\ref{optparse-option-callbacks}, Option Callbacks for more detail. Prints a complete help message for all the options in the current option parser. The help message is constructed from -the \var{usage} string passed to OptionParser's constructor and +the \code{usage} string passed to OptionParser's constructor and the \member{help} string passed to every option. If no \member{help} string is supplied for an option, it will still be @@ -1007,6 +1061,87 @@ constructor. As with \member{help} options, you will rarely create \end{itemize} +\subsubsection{Option attributes\label{optparse-option-attributes}} + +The following option attributes may be passed as keyword arguments +to \code{parser.add{\_}option()}. If you pass an option attribute +that is not relevant to a particular option, or fail to pass a required +option attribute, \module{optparse} raises OptionError. +\begin{itemize} +\item {} +\member{action} (default: \code{"store"}) + +Determines \module{optparse}'s behaviour when this option is seen on the command +line; the available options are documented above. + +\item {} +\member{type} (default: \code{"string"}) + +The argument type expected by this option (e.g., \code{"string"} or +\code{"int"}); the available option types are documented below. + +\item {} +\member{dest} (default: derived from option strings) + +If the option's action implies writing or modifying a value somewhere, +this tells \module{optparse} where to write it: \member{dest} names an attribute of the +\code{options} object that \module{optparse} builds as it parses the command line. + +\item {} +\code{default} (deprecated) + +The value to use for this option's destination if the option is not +seen on the command line. Deprecated; use \code{parser.set{\_}defaults()} +instead. + +\item {} +\code{nargs} (default: 1) + +How many arguments of type \member{type} should be consumed when this +option is seen. If {\textgreater} 1, \module{optparse} will store a tuple of values to +\member{dest}. + +\item {} +\code{const} + +For actions that store a constant value, the constant value to store. + +\item {} +\code{choices} + +For options of type \code{"choice"}, the list of strings the user +may choose from. + +\item {} +\code{callback} + +For options with action \code{"callback"}, the callable to call when this +option is seen. See section~\ref{optparse-option-callbacks}, Option Callbacks for detail on the arguments +passed to \code{callable}. + +\item {} +\code{callback{\_}args}, \code{callback{\_}kwargs} + +Additional positional and keyword arguments to pass to \code{callback} +after the four standard callback arguments. + +\item {} +\member{help} + +Help text to print for this option when listing all available options +after the user supplies a \member{help} option (such as \code{"-{}-help"}). +If no help text is supplied, the option will be listed without help +text. To hide this option, use the special value \code{SUPPRESS{\_}HELP}. + +\item {} +\code{metavar} (default: derived from option strings) + +Stand-in for the option argument(s) to use when printing help text. +See section~\ref{optparse-tutorial}, the tutorial for an example. + +\end{itemize} + + \subsubsection{Standard option types\label{optparse-standard-option-types}} \module{optparse} has six built-in option types: \code{string}, \code{int}, \code{long}, @@ -1017,22 +1152,74 @@ Arguments to string options are not checked or converted in any way: the text on the command line is stored in the destination (or passed to the callback) as-is. -Integer arguments are passed to \code{int()} to convert them to Python -integers. If \code{int()} fails, so will \module{optparse}, although with a more -useful error message. (Internally, \module{optparse} raises -\exception{OptionValueError}; OptionParser catches this exception higher -up and terminates your program with a useful error message.) +Integer arguments (type \code{int} or \code{long}) are parsed as follows: +\begin{quote} +\begin{itemize} +\item {} +if the number starts with \code{0x}, it is parsed as a hexadecimal number + +\item {} +if the number starts with \code{0}, it is parsed as an octal number + +\item {} +if the number starts with \code{0b}, is is parsed as a binary number + +\item {} +otherwise, the number is parsed as a decimal number + +\end{itemize} +\end{quote} + +The conversion is done by calling either \code{int()} or \code{long()} with +the appropriate base (2, 8, 10, or 16). If this fails, so will \module{optparse}, +although with a more useful error message. -Likewise, \code{float} arguments are passed to \code{float()} for conversion, -\code{long} arguments to \code{long()}, and \code{complex} arguments to -\code{complex()}. Apart from that, they are handled identically to integer -arguments. +\code{float} and \code{complex} option arguments are converted directly with +\code{float()} and \code{complex()}, with similar error-handling. \code{choice} options are a subtype of \code{string} options. The \code{choices} option attribute (a sequence of strings) defines the set of allowed -option arguments. \code{optparse.option.check{\_}choice()} compares +option arguments. \code{optparse.check{\_}choice()} compares user-supplied option arguments against this master list and raises -\exception{OptionValueError} if an invalid string is given. +OptionValueError if an invalid string is given. + + +\subsubsection{Parsing arguments\label{optparse-parsing-arguments}} + +The whole point of creating and populating an OptionParser is to call +its \method{parse{\_}args()} method: +\begin{verbatim} +(options, args) = parser.parse_args(args=None, options=None) +\end{verbatim} + +where the input parameters are +\begin{description} +\item[\code{args}] +the list of arguments to process (\code{sys.argv{[}1:]} by default) +\item[\code{options}] +object to store option arguments in (a new instance of +optparse.Values by default) +\end{description} + +and the return values are +\begin{description} +\item[\code{options}] +the same object as was passed in as \code{options}, or the new +optparse.Values instance created by \module{optparse} +\item[\code{args}] +the leftover positional arguments after all options have been +processed +\end{description} + +The most common usage is to supply neither keyword argument. If you +supply a \code{values} object, it will be repeatedly modified with a +\code{setattr()} call for every option argument written to an option +destination, and finally returned by \method{parse{\_}args()}. + +If \method{parse{\_}args()} encounters any errors in the argument list, it calls +the OptionParser's \method{error()} method with an appropriate end-user error +message. This ultimately terminates your process with an exit status of +2 (the traditional \UNIX{} exit status for command-line errors). \subsubsection{Querying and manipulating your option parser\label{optparse-querying-manipulating-option-parser}} @@ -1050,9 +1237,8 @@ Returns the Option instance with the option string \code{opt{\_}str}, or If the OptionParser has an option corresponding to \code{opt{\_}str}, that option is removed. If that option provided any other option strings, all of those option strings become invalid. - If \code{opt{\_}str} does not occur in any option belonging to this -OptionParser, raises \exception{ValueError}. +OptionParser, raises ValueError. \end{description} @@ -1074,20 +1260,20 @@ options. If it finds any, it invokes the current conflict-handling mechanism. You can set the conflict-handling mechanism either in the constructor: \begin{verbatim} -parser = OptionParser(..., conflict_handler="...") +parser = OptionParser(..., conflict_handler=handler) \end{verbatim} or with a separate call: \begin{verbatim} -parser.set_conflict_handler("...") +parser.set_conflict_handler(handler) \end{verbatim} -The available conflict-handling mechanisms are: +The available conflict handlers are: \begin{quote} \begin{description} \item[\code{error} (default)] assume option conflicts are a programming error and raise -\exception{OptionConflictError} +OptionConflictError \item[\code{resolve}] resolve option conflicts intelligently (see below) \end{description} @@ -1131,7 +1317,78 @@ options: -n, --noisy be noisy --dry-run new dry-run option \end{verbatim} -% $Id: reference.txt 415 2004-09-30 02:26:17Z greg $ + + +\subsubsection{Cleanup\label{optparse-cleanup}} + +OptionParser instances have several cyclic references. This should not +be a problem for Python's garbage collector, but you may wish to break +the cyclic references explicitly by calling \code{destroy()} on your +OptionParser once you are done with it. This is particularly useful in +long-running applications where large object graphs are reachable from +your OptionParser. + + +\subsubsection{Other methods\label{optparse-other-methods}} + +OptionParser supports several other public methods: +\begin{itemize} +\item {} +\code{set{\_}usage(usage)} + +Set the usage string according to the rules described above for the +\code{usage} constructor keyword argument. Passing \code{None} sets the +default usage string; use \code{SUPPRESS{\_}USAGE} to suppress a usage +message. + +\item {} +\code{enable{\_}interspersed{\_}args()}, \code{disable{\_}interspersed{\_}args()} + +Enable/disable positional arguments interspersed with options, similar +to GNU getopt (enabled by default). For example, if \code{"-a"} and +\code{"-b"} are both simple options that take no arguments, \module{optparse} +normally accepts this syntax: +\begin{verbatim} +prog -a arg1 -b arg2 +\end{verbatim} + +and treats it as equivalent to +\begin{verbatim} +prog -a -b arg1 arg2 +\end{verbatim} + +To disable this feature, call \code{disable{\_}interspersed{\_}args()}. This +restores traditional \UNIX{} syntax, where option parsing stops with the +first non-option argument. + +\item {} +\code{set{\_}defaults(dest=value, ...)} + +Set default values for several option destinations at once. Using +\method{set{\_}defaults()} is the preferred way to set default values for +options, since multiple options can share the same destination. For +example, if several ``mode'' options all set the same destination, any +one of them can set the default, and the last one wins: +\begin{verbatim} +parser.add_option("--advanced", action="store_const", + dest="mode", const="advanced", + default="novice") # overridden below +parser.add_option("--novice", action="store_const", + dest="mode", const="novice", + default="advanced") # overrides above setting +\end{verbatim} + +To avoid this confusion, use \method{set{\_}defaults()}: +\begin{verbatim} +parser.set_defaults(mode="advanced") +parser.add_option("--advanced", action="store_const", + dest="mode", const="advanced") +parser.add_option("--novice", action="store_const", + dest="mode", const="novice") +\end{verbatim} + +\end{itemize} +% $Id: reference.txt 505 2005-07-22 01:52:40Z gward $ \subsection{Option Callbacks\label{optparse-option-callbacks}} @@ -1234,7 +1491,7 @@ its instance attributes: the current list of leftover arguments, ie. arguments that have been consumed but are neither options nor option arguments. Feel free to modify \code{parser.largs}, e.g. by adding more -arguments to it. (This list will become \var{args}, the second +arguments to it. (This list will become \code{args}, the second return value of \method{parse{\_}args()}.) \item[\code{parser.rargs}] the current list of remaining arguments, ie. with \code{opt{\_}str} and @@ -1260,7 +1517,7 @@ is a dictionary of arbitrary keyword arguments supplied via \subsubsection{Raising errors in a callback\label{optparse-raising-errors-in-callback}} -The callback function should raise \exception{OptionValueError} if there are any +The callback function should raise OptionValueError if there are any problems with the option or its argument(s). \module{optparse} catches this and terminates the program, printing the error message you supply to stderr. Your message should be clear, concise, accurate, and mention diff --git a/Doc/lib/libpdb.tex b/Doc/lib/libpdb.tex index a5b36a6..b252aeb 100644 --- a/Doc/lib/libpdb.tex +++ b/Doc/lib/libpdb.tex @@ -178,12 +178,12 @@ most commands. \item[d(own)] Move the current frame one level down in the stack trace -(to an newer frame). +(to a newer frame). \item[u(p)] Move the current frame one level up in the stack trace -(to a older frame). +(to an older frame). \item[b(reak) \optional{\optional{\var{filename}:}\var{lineno}\code{\Large{|}}\var{function}\optional{, \var{condition}}}] diff --git a/Doc/lib/libposixpath.tex b/Doc/lib/libposixpath.tex index cea963e..9f0de1f 100644 --- a/Doc/lib/libposixpath.tex +++ b/Doc/lib/libposixpath.tex @@ -146,8 +146,9 @@ should detect mount points for all \UNIX{} and \POSIX{} variants. \end{funcdesc} \begin{funcdesc}{join}{path1\optional{, path2\optional{, ...}}} -Joins one or more path components intelligently. If any component is -an absolute path, all previous components are thrown away, and joining +Join one or more path components intelligently. If any component is +an absolute path, all previous components (on Windows, including the +previous drive letter, if there was one) are thrown away, and joining continues. The return value is the concatenation of \var{path1}, and optionally \var{path2}, etc., with exactly one directory separator (\code{os.sep}) inserted between components, unless \var{path2} is diff --git a/Doc/lib/libprofile.tex b/Doc/lib/libprofile.tex index 9ff5ba0..0108b21 100644 --- a/Doc/lib/libprofile.tex +++ b/Doc/lib/libprofile.tex @@ -384,14 +384,15 @@ arguments to supply the globals and locals dictionaries for the \var{command} string. \end{funcdesc} -Analysis of the profiler data is done using this class from the -\module{pstats} module: +Analysis of the profiler data is done using the \class{Stats} class. + +\note{The \class{Stats} class is defined in the \module{pstats} module.} % now switch modules.... % (This \stmodindex use may be hard to change ;-( ) \stmodindex{pstats} -\begin{classdesc}{Stats}{filename\optional{, \moreargs\optional{, stream=sys.stdout}}} +\begin{classdesc}{Stats}{filename\optional{, stream=sys.stdout\optional{, \moreargs}}} This class constructor creates an instance of a ``statistics object'' from a \var{filename} (or set of filenames). \class{Stats} objects are manipulated by methods, in order to print useful reports. You may specify @@ -409,6 +410,8 @@ functions will be coalesced, so that an overall view of several processes can be considered in a single report. If additional files need to be combined with data in an existing \class{Stats} object, the \method{add()} method can be used. + +\versionchanged[The \var{stream} parameter was added]{2.5} \end{classdesc} diff --git a/Doc/lib/librlcompleter.tex b/Doc/lib/librlcompleter.tex index b2a1eba7..cb2ac59 100644 --- a/Doc/lib/librlcompleter.tex +++ b/Doc/lib/librlcompleter.tex @@ -2,18 +2,17 @@ Completion function for GNU readline} \declaremodule{standard}{rlcompleter} - \platform{Unix} \sectionauthor{Moshe Zadka}{moshez@zadka.site.co.il} -\modulesynopsis{Python identifier completion for the GNU readline library.} +\modulesynopsis{Python identifier completion, suitable for the GNU readline library.} -The \module{rlcompleter} module defines a completion function for +The \module{rlcompleter} module defines a completion function suitable for the \refmodule{readline} module by completing valid Python identifiers and keywords. -This module is \UNIX-specific due to its dependence on the -\refmodule{readline} module. - -The \module{rlcompleter} module defines the \class{Completer} class. +When this module is imported on a \UNIX\ platform with the \module{readline} +module available, an instance of the \class{Completer} class is automatically +created and its \method{complete} method is set as the \module{readline} +completer. Example: @@ -44,6 +43,9 @@ else: \end{verbatim} +On platforms without \module{readline}, the \class{Completer} class defined +by this module can still be used for custom purposes. + \subsection{Completer Objects \label{completer-objects}} Completer objects have the following method: diff --git a/Doc/lib/librunpy.tex b/Doc/lib/librunpy.tex index 4be9901..c7a7e51 100644 --- a/Doc/lib/librunpy.tex +++ b/Doc/lib/librunpy.tex @@ -10,7 +10,7 @@ \versionadded{2.5} The \module{runpy} module is used to locate and run Python modules -without importing them first. It's main use is to implement the +without importing them first. Its main use is to implement the \programopt{-m} command line switch that allows scripts to be located using the Python module namespace rather than the filesystem. diff --git a/Doc/lib/libsqlite3.tex b/Doc/lib/libsqlite3.tex new file mode 100644 index 0000000..8c80eb6 --- /dev/null +++ b/Doc/lib/libsqlite3.tex @@ -0,0 +1,503 @@ +\section{\module{sqlite3} --- + DB-API 2.0 interface for SQLite databases} + +\declaremodule{builtin}{sqlite3} +\modulesynopsis{A DB-API 2.0 implementation using SQLite 3.x.} +\sectionauthor{Gerhard Häring}{gh@ghaering.de} +\versionadded{2.5} + +\subsection{Module functions and constants\label{sqlite3-Module-Contents}} + +\begin{datadesc}{PARSE_DECLTYPES} +This constant is meant to be used with the \var{detect_types} parameter of the +\function{connect} function. + +Setting it makes the \module{sqlite3} module parse the declared type for each column it +returns. It will parse out the first word of the declared type, i. e. for +"integer primary key", it will parse out "integer". Then for that column, it +will look into the converters dictionary and use the converter function +registered for that type there. Converter names are case-sensitive! +\end{datadesc} + + +\begin{datadesc}{PARSE_COLNAMES} +This constant is meant to be used with the \var{detect_types} parameter of the +\function{connect} function. + +Setting this makes the SQLite interface parse the column name for each column +it returns. It will look for a string formed [mytype] in there, and then +decide that 'mytype' is the type of the column. It will try to find an entry of +'mytype' in the converters dictionary and then use the converter function found +there to return the value. The column name found in \member{cursor.description} is only +the first word of the column name, i. e. if you use something like +\code{'as "x [datetime]"'} in your SQL, then we will parse out everything until the +first blank for the column name: the column name would simply be "x". +\end{datadesc} + +\begin{funcdesc}{connect}{database\optional{, timeout, isolation_level, detect_types, factory}} +Opens a connection to the SQLite database file \var{database}. You can use +\code{":memory:"} to open a database connection to a database that resides in +RAM instead of on disk. + +When a database is accessed by multiple connections, and one of the processes +modifies the database, the SQLite database is locked until that transaction is +committed. The \var{timeout} parameter specifies how long the connection should +wait for the lock to go away until raising an exception. The default for the +timeout parameter is 5.0 (five seconds). + +For the \var{isolation_level} parameter, please see \member{isolation_level} +\ref{sqlite3-Connection-IsolationLevel} property of \class{Connection} objects. + +SQLite natively supports only the types TEXT, INTEGER, FLOAT, BLOB and NULL. If +you want to use other types, like you have to add support for them yourself. +The \var{detect_types} parameter and the using custom \strong{converters} registered with +the module-level \function{register_converter} function allow you to easily do that. + +\var{detect_types} defaults to 0 (i. e. off, no type detection), you can set it +to any combination of \constant{PARSE_DECLTYPES} and \constant{PARSE_COLNAMES} to turn type +detection on. + +By default, the \module{sqlite3} module uses its \class{Connection} class for the +connect call. You can, however, subclass the \class{Connection} class and make +\function{connect} use your class instead by providing your class for the +\var{factory} parameter. + +Consult the section \ref{sqlite3-Types} of this manual for details. + +The \module{sqlite3} module internally uses a statement cache to avoid SQL parsing +overhead. If you want to explicitly set the number of statements that are +cached for the connection, you can set the \var{cached_statements} parameter. +The currently implemented default is to cache 100 statements. +\end{funcdesc} + +\begin{funcdesc}{register_converter}{typename, callable} +Registers a callable to convert a bytestring from the database into a custom +Python type. The callable will be invoked for all database values that are of +the type \var{typename}. Confer the parameter \var{detect_types} of the +\function{connect} function for how the type detection works. Note that the case of +\var{typename} and the name of the type in your query must match! +\end{funcdesc} + +\begin{funcdesc}{register_adapter}{type, callable} +Registers a callable to convert the custom Python type \var{type} into one of +SQLite's supported types. The callable \var{callable} accepts as single +parameter the Python value, and must return a value of the following types: +int, long, float, str (UTF-8 encoded), unicode or buffer. +\end{funcdesc} + +\begin{funcdesc}{complete_statement}{sql} +Returns \constant{True} if the string \var{sql} one or more complete SQL +statements terminated by semicolons. It does not verify if the SQL is +syntactically correct, only if there are no unclosed string literals and if the +statement is terminated by a semicolon. + +This can be used to build a shell for SQLite, like in the following example: + + \verbatiminput{sqlite3/complete_statement.py} +\end{funcdesc} + +\subsection{Connection Objects \label{sqlite3-Connection-Objects}} + +A \class{Connection} instance has the following attributes and methods: + +\label{sqlite3-Connection-IsolationLevel} +\begin{memberdesc}{isolation_level} + Get or set the current isolation level. None for autocommit mode or one of + "DEFERRED", "IMMEDIATE" or "EXLUSIVE". See Controlling Transactions + \ref{sqlite3-Controlling-Transactions} for a more detailed explanation. +\end{memberdesc} + +\begin{methoddesc}{cursor}{\optional{cursorClass}} + The cursor method accepts a single optional parameter \var{cursorClass}. + This is a custom cursor class which must extend \class{sqlite3.Cursor}. +\end{methoddesc} + +\begin{methoddesc}{execute}{sql, \optional{parameters}} +This is a nonstandard shortcut that creates an intermediate cursor object by +calling the cursor method, then calls the cursor's \method{execute} method with the +parameters given. +\end{methoddesc} + +\begin{methoddesc}{executemany}{sql, \optional{parameters}} +This is a nonstandard shortcut that creates an intermediate cursor object by +calling the cursor method, then calls the cursor's \method{executemany} method with the +parameters given. +\end{methoddesc} + +\begin{methoddesc}{executescript}{sql_script} +This is a nonstandard shortcut that creates an intermediate cursor object by +calling the cursor method, then calls the cursor's \method{executescript} method with the +parameters given. +\end{methoddesc} + +\begin{methoddesc}{create_function}{name, num_params, func} + +Creates a user-defined function that you can later use from within SQL +statements under the function name \var{name}. \var{num_params} is the number +of parameters the function accepts, and \var{func} is a Python callable that is +called as SQL function. + +The function can return any of the types supported by SQLite: unicode, str, +int, long, float, buffer and None. Exceptions in the function are ignored and +they are handled as if the function returned None. + +Example: + + \verbatiminput{sqlite3/md5func.py} +\end{methoddesc} + +\begin{methoddesc}{create_aggregate}{name, num_params, aggregate_class} + +Creates a user-defined aggregate function. + +The aggregate class must implement a \code{step} method, which accepts the +number of parameters \var{num_params}, and a \code{finalize} method which +will return the final result of the aggregate. + +The \code{finalize} method can return any of the types supported by SQLite: +unicode, str, int, long, float, buffer and None. Any exceptions are ignored. + +Example: + + \verbatiminput{sqlite3/mysumaggr.py} +\end{methoddesc} + +\begin{methoddesc}{create_collation}{name, callable} + +Creates a collation with the specified \var{name} and \var{callable}. The +callable will be passed two string arguments. It should return -1 if the first +is ordered lower than the second, 0 if they are ordered equal and 1 and if the +first is ordered higher than the second. Note that this controls sorting +(ORDER BY in SQL) so your comparisons don't affect other SQL operations. + +Note that the callable will get its parameters as Python bytestrings, which +will normally be encoded in UTF-8. + +The following example shows a custom collation that sorts "the wrong way": + + \verbatiminput{sqlite3/collation_reverse.py} + +To remove a collation, call \code{create_collation} with None as callable: + +\begin{verbatim} + con.create_collation("reverse", None) +\end{verbatim} +\end{methoddesc} + + +\begin{memberdesc}{row_factory} + You can change this attribute to a callable that accepts the cursor and + the original row as tuple and will return the real result row. This + way, you can implement more advanced ways of returning results, like + ones that can also access columns by name. + + Example: + + \verbatiminput{sqlite3/row_factory.py} + + If the standard tuple types don't suffice for you, and you want name-based + access to columns, you should consider setting \member{row_factory} to the + highly-optimized sqlite3.Row type. It provides both + index-based and case-insensitive name-based access to columns with almost + no memory overhead. Much better than your own custom dictionary-based + approach or even a db_row based solution. +\end{memberdesc} + +\begin{memberdesc}{text_factory} + Using this attribute you can control what objects are returned for the + TEXT data type. By default, this attribute is set to \class{unicode} and + the \module{sqlite3} module will return Unicode objects for TEXT. If you want to return + bytestrings instead, you can set it to \class{str}. + + For efficiency reasons, there's also a way to return Unicode objects only + for non-ASCII data, and bytestrings otherwise. To activate it, set this + attribute to \constant{sqlite3.OptimizedUnicode}. + + You can also set it to any other callable that accepts a single bytestring + parameter and returns the result object. + + See the following example code for illustration: + + \verbatiminput{sqlite3/text_factory.py} +\end{memberdesc} + +\begin{memberdesc}{total_changes} + Returns the total number of database rows that have be modified, inserted, + or deleted since the database connection was opened. +\end{memberdesc} + + + + + +\subsection{Cursor Objects \label{sqlite3-Cursor-Objects}} + +A \class{Cursor} instance has the following attributes and methods: + +\begin{methoddesc}{execute}{sql, \optional{parameters}} + +Executes a SQL statement. The SQL statement may be parametrized (i. e. +placeholders instead of SQL literals). The \module{sqlite3} module supports two kinds of +placeholders: question marks (qmark style) and named placeholders (named +style). + +This example shows how to use parameters with qmark style: + + \verbatiminput{sqlite3/execute_1.py} + +This example shows how to use the named style: + + \verbatiminput{sqlite3/execute_2.py} + + \method{execute} will only execute a single SQL statement. If you try to + execute more than one statement with it, it will raise a Warning. Use + \method{executescript} if want to execute multiple SQL statements with one + call. +\end{methoddesc} + + +\begin{methoddesc}{executemany}{sql, seq_of_parameters} +Executes a SQL command against all parameter sequences or mappings found in the +sequence \var{sql}. The \module{sqlite3} module also allows +to use an iterator yielding parameters instead of a sequence. + +\verbatiminput{sqlite3/executemany_1.py} + +Here's a shorter example using a generator: + +\verbatiminput{sqlite3/executemany_2.py} +\end{methoddesc} + +\begin{methoddesc}{executescript}{sql_script} + +This is a nonstandard convenience method for executing multiple SQL statements +at once. It issues a COMMIT statement before, then executes the SQL script it +gets as a parameter. + +\var{sql_script} can be a bytestring or a Unicode string. + +Example: + +\verbatiminput{sqlite3/executescript.py} +\end{methoddesc} + +\begin{memberdesc}{rowcount} + Although the \class{Cursor} class of the \module{sqlite3} module implements this + attribute, the database engine's own support for the determination of "rows + affected"/"rows selected" is quirky. + + For \code{SELECT} statements, \member{rowcount} is always None because we cannot + determine the number of rows a query produced until all rows were fetched. + + For \code{DELETE} statements, SQLite reports \member{rowcount} as 0 if you make a + \code{DELETE FROM table} without any condition. + + For \method{executemany} statements, the number of modifications are summed + up into \member{rowcount}. + + As required by the Python DB API Spec, the \member{rowcount} attribute "is -1 + in case no executeXX() has been performed on the cursor or the rowcount + of the last operation is not determinable by the interface". +\end{memberdesc} + +\subsection{SQLite and Python types\label{sqlite3-Types}} + +\subsubsection{Introduction} + +SQLite natively supports the following types: NULL, INTEGER, REAL, TEXT, BLOB. + +The following Python types can thus be sent to SQLite without any problem: + +\begin{tableii} {c|l}{code}{Python type}{SQLite type} +\lineii{None}{NULL} +\lineii{int}{INTEGER} +\lineii{long}{INTEGER} +\lineii{float}{REAL} +\lineii{str (UTF8-encoded)}{TEXT} +\lineii{unicode}{TEXT} +\lineii{buffer}{BLOB} +\end{tableii} + +This is how SQLite types are converted to Python types by default: + +\begin{tableii} {c|l}{code}{SQLite type}{Python type} +\lineii{NULL}{None} +\lineii{INTEGER}{int or long, depending on size} +\lineii{REAL}{float} +\lineii{TEXT}{depends on text_factory, unicode by default} +\lineii{BLOB}{buffer} +\end{tableii} + +The type system of the \module{sqlite3} module is extensible in both ways: you can store +additional Python types in a SQLite database via object adaptation, and you can +let the \module{sqlite3} module convert SQLite types to different Python types via +converters. + +\subsubsection{Using adapters to store additional Python types in SQLite databases} + +Like described before, SQLite supports only a limited set of types natively. To +use other Python types with SQLite, you must \strong{adapt} them to one of the sqlite3 +module's supported types for SQLite. So, one of NoneType, int, long, float, +str, unicode, buffer. + +The \module{sqlite3} module uses the Python object adaptation, like described in PEP 246 +for this. The protocol to use is \class{PrepareProtocol}. + +There are two ways to enable the \module{sqlite3} module to adapt a custom Python type +to one of the supported ones. + +\paragraph{Letting your object adapt itself} + +This is a good approach if you write the class yourself. Let's suppose you have +a class like this: + +\begin{verbatim} +class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y +\end{verbatim} + +Now you want to store the point in a single SQLite column. You'll have to +choose one of the supported types first that you use to represent the point in. +Let's just use str and separate the coordinates using a semicolon. Then you +need to give your class a method \code{__conform__(self, protocol)} which must +return the converted value. The parameter \var{protocol} will be +\class{PrepareProtocol}. + +\verbatiminput{sqlite3/adapter_point_1.py} + +\paragraph{Registering an adapter callable} + +The other possibility is to create a function that converts the type to the +string representation and register the function with \method{register_adapter}. + + \verbatiminput{sqlite3/adapter_point_2.py} + +\begin{notice} +The type/class to adapt must be a new-style class, i. e. it must have +\class{object} as one of its bases. +\end{notice} + +The \module{sqlite3} module has two default adapters for Python's builtin +\class{datetime.date} and \class{datetime.datetime} types. Now let's suppose we +want to store \class{datetime.datetime} objects not in ISO representation, but +as Unix timestamp. + + \verbatiminput{sqlite3/adapter_datetime.py} + +\subsubsection{Converting SQLite values to custom Python types} + +Now that's all nice and dandy that you can send custom Python types to SQLite. +But to make it really useful we need to make the Python to SQLite to Python +roundtrip work. + +Enter converters. + +Let's go back to the Point class. We stored the x and y coordinates separated +via semicolons as strings in SQLite. + +Let's first define a converter function that accepts the string as a parameter and constructs a Point object from it. + +\begin{notice} +Converter functions \strong{always} get called with a string, no matter +under which data type you sent the value to SQLite. +\end{notice} + +\begin{notice} +Converter names are looked up in a case-sensitive manner. +\end{notice} + + +\begin{verbatim} + def convert_point(s): + x, y = map(float, s.split(";")) + return Point(x, y) +\end{verbatim} + +Now you need to make the \module{sqlite3} module know that what you select from the +database is actually a point. There are two ways of doing this: + +\begin{itemize} + \item Implicitly via the declared type + \item Explicitly via the column name +\end{itemize} + +Both ways are described at \ref{sqlite3-Module-Contents} in the text explaining +the constants \constant{PARSE_DECLTYPES} and \constant{PARSE_COlNAMES}. + + +The following example illustrates both ways. + + \verbatiminput{sqlite3/converter_point.py} + +\subsubsection{Default adapters and converters} + +There are default adapters for the date and datetime types in the datetime +module. They will be sent as ISO dates/ISO timestamps to SQLite. + +The default converters are registered under the name "date" for datetime.date +and under the name "timestamp" for datetime.datetime. + +This way, you can use date/timestamps from Python without any additional +fiddling in most cases. The format of the adapters is also compatible with the +experimental SQLite date/time functions. + +The following example demonstrates this. + + \verbatiminput{sqlite3/pysqlite_datetime.py} + +\subsection{Controlling Transactions \label{sqlite3-Controlling-Transactions}} + +By default, the \module{sqlite3} module opens transactions implicitly before a DML +statement (INSERT/UPDATE/DELETE/REPLACE), and commits transactions implicitly +before a non-DML, non-DQL statement (i. e. anything other than +SELECT/INSERT/UPDATE/DELETE/REPLACE). + +So if you are within a transaction, and issue a command like \code{CREATE TABLE +...}, \code{VACUUM}, \code{PRAGMA}, the \module{sqlite3} module will commit implicitly +before executing that command. There are two reasons for doing that. The first +is that some of these commands don't work within transactions. The other reason +is that pysqlite needs to keep track of the transaction state (if a transaction +is active or not). + +You can control which kind of "BEGIN" statements pysqlite implicitly executes +(or none at all) via the \var{isolation_level} parameter to the +\function{connect} call, or via the \member{isolation_level} property of +connections. + +If you want \strong{autocommit mode}, then set \member{isolation_level} to None. + +Otherwise leave it at it's default, which will result in a plain "BEGIN" +statement, or set it to one of SQLite's supported isolation levels: DEFERRED, +IMMEDIATE or EXCLUSIVE. + +As the \module{sqlite3} module needs to keep track of the transaction state, you should +not use \code{OR ROLLBACK} or \code{ON CONFLICT ROLLBACK} in your SQL. Instead, +catch the \exception{IntegrityError} and call the \method{rollback} method of +the connection yourself. + +\subsection{Using pysqlite efficiently} + +\subsubsection{Using shortcut methods} + +Using the nonstandard \method{execute}, \method{executemany} and +\method{executescript} methods of the \class{Connection} object, your code can +be written more concisely, because you don't have to create the - often +superfluous \class{Cursor} objects explicitly. Instead, the \class{Cursor} +objects are created implicitly and these shortcut methods return the cursor +objects. This way, you can for example execute a SELECT statement and iterate +over it directly using only a single call on the \class{Connection} object. + + \verbatiminput{sqlite3/shortcut_methods.py} + +\subsubsection{Accessing columns by name instead of by index} + +One cool feature of the \module{sqlite3} module is the builtin \class{sqlite3.Row} class +designed to be used as a row factory. + +Rows wrapped with this class can be accessed both by index (like tuples) and +case-insensitively by name: + + \verbatiminput{sqlite3/rowclass.py} + + diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index 8d011fd..f44360b 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -1,12 +1,11 @@ \section{Built-in Types \label{types}} The following sections describe the standard types that are built into -the interpreter. Historically, Python's built-in types have differed -from user-defined types because it was not possible to use the built-in -types as the basis for object-oriented inheritance. With the 2.2 -release this situation has started to change, although the intended -unification of user-defined and built-in types is as yet far from -complete. +the interpreter. +\note{Historically (until release 2.2), Python's built-in types have +differed from user-defined types because it was not possible to use +the built-in types as the basis for object-oriented inheritance. +This limitation does not exist any longer.} The principal built-in types are numerics, sequences, mappings, files classes, instances and exceptions. @@ -19,7 +18,7 @@ the equivalent \function{repr()} function, or the slightly different \function{str()} function). The latter function is implicitly used when an object is written by the \keyword{print}\stindex{print} statement. -(Information on \ulink{\keyword{print} statement}{../ref/print.html} +(Information on the \ulink{\keyword{print} statement}{../ref/print.html} and other language statements can be found in the \citetitle[../ref/ref.html]{Python Reference Manual} and the \citetitle[../tut/tut.html]{Python Tutorial}.) @@ -728,6 +727,15 @@ a prefix; rather, all combinations of its values are stripped: \versionchanged[Support for the \var{chars} argument]{2.2.2} \end{methoddesc} +\begin{methoddesc}[string]{partition}{sep} +Split the string at the first occurrence of \var{sep}, and return +a 3-tuple containing the part before the separator, the separator +itself, and the part after the separator. If the separator is not +found, return a 3-tuple containing the string itself, followed by +two empty strings. +\versionadded{2.5} +\end{methoddesc} + \begin{methoddesc}[string]{replace}{old, new\optional{, count}} Return a copy of the string with all occurrences of substring \var{old} replaced by \var{new}. If the optional argument @@ -755,6 +763,15 @@ The original string is returned if \versionchanged[Support for the \var{fillchar} argument]{2.4} \end{methoddesc} +\begin{methoddesc}[string]{rpartition}{sep} +Split the string at the last occurrence of \var{sep}, and return +a 3-tuple containing the part before the separator, the separator +itself, and the part after the separator. If the separator is not +found, return a 3-tuple containing the string itself, followed by +two empty strings. +\versionadded{2.5} +\end{methoddesc} + \begin{methoddesc}[string]{rsplit}{\optional{sep \optional{,maxsplit}}} Return a list of the words in the string, using \var{sep} as the delimiter string. If \var{maxsplit} is given, at most \var{maxsplit} @@ -971,20 +988,22 @@ The conversion types are: \lineiii{u}{Unsigned decimal.}{} \lineiii{x}{Unsigned hexadecimal (lowercase).}{(2)} \lineiii{X}{Unsigned hexadecimal (uppercase).}{(2)} - \lineiii{e}{Floating point exponential format (lowercase).}{} - \lineiii{E}{Floating point exponential format (uppercase).}{} - \lineiii{f}{Floating point decimal format.}{} - \lineiii{F}{Floating point decimal format.}{} - \lineiii{g}{Same as \character{e} if exponent is greater than -4 or - less than precision, \character{f} otherwise.}{} - \lineiii{G}{Same as \character{E} if exponent is greater than -4 or - less than precision, \character{F} otherwise.}{} + \lineiii{e}{Floating point exponential format (lowercase).}{(3)} + \lineiii{E}{Floating point exponential format (uppercase).}{(3)} + \lineiii{f}{Floating point decimal format.}{(3)} + \lineiii{F}{Floating point decimal format.}{(3)} + \lineiii{g}{Floating point format. Uses exponential format + if exponent is greater than -4 or less than precision, + decimal format otherwise.}{(4)} + \lineiii{G}{Floating point format. Uses exponential format + if exponent is greater than -4 or less than precision, + decimal format otherwise.}{(4)} \lineiii{c}{Single character (accepts integer or single character string).}{} \lineiii{r}{String (converts any python object using - \function{repr()}).}{(3)} + \function{repr()}).}{(5)} \lineiii{s}{String (converts any python object using - \function{str()}).}{(4)} + \function{str()}).}{(6)} \lineiii{\%}{No argument is converted, results in a \character{\%} character in the result.}{} \end{tableiii} @@ -1004,10 +1023,27 @@ Notes: formatting of the number if the leading character of the result is not already a zero. \item[(3)] - The \code{\%r} conversion was added in Python 2.0. + The alternate form causes the result to always contain a decimal + point, even if no digits follow it. + + The precision determines the number of digits after the decimal + point and defaults to 6. \item[(4)] + The alternate form causes the result to always contain a decimal + point, and trailing zeroes are not removed as they would + otherwise be. + + The precision determines the number of significant digits before + and after the decimal point and defaults to 6. + \item[(5)] + The \code{\%r} conversion was added in Python 2.0. + + The precision determines the maximal number of characters used. + \item[(6)] If the object or format provided is a \class{unicode} string, the resulting string will also be \class{unicode}. + + The precision determines the maximal number of characters used. \end{description} % XXX Examples? @@ -1747,6 +1783,87 @@ implemented in C will have to provide a writable \end{memberdesc} +\subsection{Context Manager Types \label{typecontextmanager}} + +\versionadded{2.5} +\index{context manager} +\index{context management protocol} +\index{protocol!context management} + +Python's \keyword{with} statement supports the concept of a runtime +context defined by a context manager. This is implemented using +two separate methods that allow user-defined classes to define +a runtime context that is entered before the statement body is +executed and exited when the statement ends. + +The \dfn{context management protocol} consists of a pair of +methods that need to be provided for a context manager object to +define a runtime context: + +\begin{methoddesc}[context manager]{__enter__}{} + Enter the runtime context and return either this object or another + object related to the runtime context. The value returned by this + method is bound to 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 + \function{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 \code{decimal.Context.get_manager()}. + These managers set the active decimal context to a copy of the + original decimal context and then return the copy. This allows + changes to be made to the current decimal context in the body of + the \keyword{with} statement without affecting code outside + the \keyword{with} statement. +\end{methoddesc} + +\begin{methoddesc}[context manager]{__exit__}{exc_type, exc_val, exc_tb} + Exit the runtime context and return a Boolean flag indicating if any + expection that occurred should be suppressed. If an exception + occurred while executing the body of the \keyword{with} statement, the + arguments contain the exception type, value and traceback information. + Otherwise, all three arguments are \var{None}. + + Returning a true value from this method will cause the \keyword{with} + statement to suppress the exception and continue execution with the + statement immediately following the \keyword{with} statement. Otherwise + the exception continues propagating after this method has finished + executing. Exceptions that occur during execution of this method will + replace any exception that occurred in the body of the \keyword{with} + statement. + + The exception passed in should never be reraised explicitly - instead, + this method should return a false value to indicate that the method + completed successfully and does not want to suppress the raised + exception. This allows context management code (such as + \code{contextlib.nested}) to easily detect whether or not an + \method{__exit__()} method has actually failed. +\end{methoddesc} + +Python defines several context managers to support easy thread +synchronisation, prompt closure of files or other objects, and +simpler manipulation of the active decimal arithmetic +context. The specific types are not treated specially beyond +their implementation of the context management protocol. + +Python's generators and the \code{contextlib.contextfactory} decorator +provide a convenient way to implement these protocols. If a generator +function is decorated with the \code{contextlib.contextfactory} +decorator, it will return a context manager implementing the necessary +\method{__enter__()} and \method{__exit__()} methods, rather than the +iterator produced by an undecorated generator function. + +Note that there is no specific slot for any of these methods in the +type structure for Python objects in the Python/C API. Extension +types wanting to define these methods must provide them as a normal +Python accessible method. Compared to the overhead of setting up the +runtime context, the overhead of a single class dictionary lookup +is negligible. + + \subsection{Other Built-in Types \label{typesother}} The interpreter supports several other kinds of objects. diff --git a/Doc/lib/libsubprocess.tex b/Doc/lib/libsubprocess.tex index 4417797..bde92eb 100644 --- a/Doc/lib/libsubprocess.tex +++ b/Doc/lib/libsubprocess.tex @@ -70,10 +70,10 @@ value for \var{bufsize} is \constant{0} (unbuffered). The \var{executable} argument specifies the program to execute. It is very seldom needed: Usually, the program to execute is defined by the -\var{args} argument. If \var{shell=True}, the \var{executable} +\var{args} argument. If \code{shell=True}, the \var{executable} argument specifies which shell to use. On \UNIX{}, the default shell -is /bin/sh. On Windows, the default shell is specified by the COMSPEC -environment variable. +is \file{/bin/sh}. On Windows, the default shell is specified by the +\envvar{COMSPEC} environment variable. \var{stdin}, \var{stdout} and \var{stderr} specify the executed programs' standard input, standard output and standard error file @@ -88,16 +88,19 @@ handle as for stdout. If \var{preexec_fn} is set to a callable object, this object will be called in the child process just before the child is executed. +(\UNIX{} only) If \var{close_fds} is true, all file descriptors except \constant{0}, \constant{1} and \constant{2} will be closed before the child process is -executed. +executed. (\UNIX{} only) If \var{shell} is \constant{True}, the specified command will be executed through the shell. -If \var{cwd} is not \code{None}, the current directory will be changed -to cwd before the child is executed. +If \var{cwd} is not \code{None}, the child's current directory will be +changed to \var{cwd} before it is executed. Note that this directory +is not considered when searching the executable, so you can't specify +the program's path relative to \var{cwd}. If \var{env} is not \code{None}, it defines the environment variables for the new process. diff --git a/Doc/lib/libsys.tex b/Doc/lib/libsys.tex index 1a57da4..6b5b755 100644 --- a/Doc/lib/libsys.tex +++ b/Doc/lib/libsys.tex @@ -410,7 +410,7 @@ else: Strings specifying the primary and secondary prompt of the interpreter. These are only defined if the interpreter is in interactive mode. Their initial values in this case are - \code{'>\code{>}> '} and \code{'... '}. If a non-string object is + \code{'>>>~'} and \code{'...~'}. If a non-string object is assigned to either variable, its \function{str()} is re-evaluated each time the interpreter prepares to read a new interactive command; this can be used to implement a dynamic prompt. diff --git a/Doc/lib/libtarfile.tex b/Doc/lib/libtarfile.tex index f705e8d..ca6e65a 100644 --- a/Doc/lib/libtarfile.tex +++ b/Doc/lib/libtarfile.tex @@ -128,7 +128,7 @@ Some facts and figures: \seemodule{zipfile}{Documentation of the \refmodule{zipfile} standard module.} - \seetitle[http://www.gnu.org/software/tar/manual/html_chapter/tar_8.html\#SEC134] + \seetitle[http://www.gnu.org/software/tar/manual/html_node/tar_134.html\#SEC134] {GNU tar manual, Basic Tar Format}{Documentation for tar archive files, including GNU tar extensions.} \end{seealso} @@ -334,8 +334,12 @@ the file's data itself. Create and return a \class{TarInfo} object from a string buffer. \end{methoddesc} -\begin{methoddesc}{tobuf}{} +\begin{methoddesc}{tobuf}{posix} Create a string buffer from a \class{TarInfo} object. + See \class{TarFile}'s \member{posix} attribute for information + on the \var{posix} argument. It defaults to \constant{False}. + + \versionadded[The \var{posix} parameter]{2.5} \end{methoddesc} A \code{TarInfo} object has the following public data attributes: diff --git a/Doc/lib/libthread.tex b/Doc/lib/libthread.tex index 9e0c202..9573ab3 100644 --- a/Doc/lib/libthread.tex +++ b/Doc/lib/libthread.tex @@ -44,8 +44,8 @@ then the thread exits (but other threads continue to run). \end{funcdesc} \begin{funcdesc}{interrupt_main}{} -Raise a KeyboardInterrupt in the main thread. A subthread can use this -function to interrupt the main thread. +Raise a \exception{KeyboardInterrupt} exception in the main thread. A subthread +can use this function to interrupt the main thread. \versionadded{2.3} \end{funcdesc} diff --git a/Doc/lib/libtokenize.tex b/Doc/lib/libtokenize.tex index cdbb4b8..8c9ad3e 100644 --- a/Doc/lib/libtokenize.tex +++ b/Doc/lib/libtokenize.tex @@ -47,7 +47,7 @@ An older entry point is retained for backward compatibility: call to the function should return one line of input as a string. Alternately, \var{readline} may be a callable object that signals completion by raising \exception{StopIteration}. - \versionchanged[Added StopIteration support]{2.5} + \versionchanged[Added \exception{StopIteration} support]{2.5} The second parameter, \var{tokeneater}, must also be a callable object. It is called once for each token, with five arguments, diff --git a/Doc/lib/libtrace.tex b/Doc/lib/libtrace.tex new file mode 100644 index 0000000..2465aac --- /dev/null +++ b/Doc/lib/libtrace.tex @@ -0,0 +1,125 @@ +\section{\module{trace} --- + Trace or track Python statement execution} + +\declaremodule{standard}{trace} +\modulesynopsis{Trace or track Python statement execution.} + +The \module{trace} module allows you to trace program execution, generate +annotated statement coverage listings, print caller/callee relationships and +list functions executed during a program run. It can be used in another +program or from the command line. + +\subsection{Command Line Usage\label{trace-cli}} + +The \module{trace} module can be invoked from the command line. It can be +as simple as + +\begin{verbatim} +python -m trace --count somefile.py ... +\end{verbatim} + +The above will generate annotated listings of all Python modules imported +during the execution of \file{somefile.py}. + +The following command-line arguments are supported: + +\begin{description} +\item[\longprogramopt{trace}, \programopt{-t}] +Display lines as they are executed. + +\item[\longprogramopt{count}, \programopt{-c}] +Produce a set of annotated listing files upon program +completion that shows how many times each statement was executed. + +\item[\longprogramopt{report}, \programopt{-r}] +Produce an annotated list from an earlier program run that +used the \longprogramopt{count} and \longprogramopt{file} arguments. + +\item[\longprogramopt{no-report}, \programopt{-R}] +Do not generate annotated listings. This is useful if you intend to make +several runs with \longprogramopt{count} then produce a single set +of annotated listings at the end. + +\item[\longprogramopt{listfuncs}, \programopt{-l}] +List the functions executed by running the program. + +\item[\longprogramopt{trackcalls}, \programopt{-T}] +Generate calling relationships exposed by running the program. + +\item[\longprogramopt{file}, \programopt{-f}] +Name a file containing (or to contain) counts. + +\item[\longprogramopt{coverdir}, \programopt{-C}] +Name a directory in which to save annotated listing files. + +\item[\longprogramopt{missing}, \programopt{-m}] +When generating annotated listings, mark lines which +were not executed with `\code{>>>>>>}'. + +\item[\longprogramopt{summary}, \programopt{-s}] +When using \longprogramopt{count} or \longprogramopt{report}, write a +brief summary to stdout for each file processed. + +\item[\longprogramopt{ignore-module}] +Ignore the named module and its submodules (if it is +a package). May be given multiple times. + +\item[\longprogramopt{ignore-dir}] +Ignore all modules and packages in the named directory +and subdirectories. May be given multiple times. +\end{description} + +\subsection{Programming Interface\label{trace-api}} + +\begin{classdesc}{Trace}{\optional{count=1\optional{, trace=1\optional{, + countfuncs=0\optional{, countcallers=0\optional{, + ignoremods=()\optional{, ignoredirs=()\optional{, + infile=None\optional{, outfile=None}}}}}}}}} +Create an object to trace execution of a single statement or expression. +All parameters are optional. \var{count} enables counting of line numbers. +\var{trace} enables line execution tracing. \var{countfuncs} enables +listing of the functions called during the run. \var{countcallers} enables +call relationship tracking. \var{ignoremods} is a list of modules or +packages to ignore. \var{ignoredirs} is a list of directories whose modules +or packages should be ignored. \var{infile} is the file from which to read +stored count information. \var{outfile} is a file in which to write updated +count information. +\end{classdesc} + +\begin{methoddesc}[Trace]{run}{cmd} +Run \var{cmd} under control of the Trace object with the current tracing +parameters. +\end{methoddesc} + +\begin{methoddesc}[Trace]{runctx}{cmd\optional{, globals=None\optional{, + locals=None}}} +Run \var{cmd} under control of the Trace object with the current tracing +parameters in the defined global and local environments. If not defined, +\var{globals} and \var{locals} default to empty dictionaries. +\end{methoddesc} + +\begin{methoddesc}[Trace]{runfunc}{func, *args, **kwds} +Call \var{func} with the given arguments under control of the +\class{Trace} object with the current tracing parameters. +\end{methoddesc} + +This is a simple example showing the use of this module: + +\begin{verbatim} +import sys +import trace + +# create a Trace object, telling it what to ignore, and whether to +# do tracing or line-counting or both. +tracer = trace.Trace( + ignoredirs=[sys.prefix, sys.exec_prefix], + trace=0, + count=1) + +# run the new command using the given tracer +tracer.run('main()') + +# make a report, placing output in /tmp +r = tracer.results() +r.write_results(show_missing=True, coverdir="/tmp") +\end{verbatim} diff --git a/Doc/lib/libunittest.tex b/Doc/lib/libunittest.tex index 6c8769d..51b321e 100644 --- a/Doc/lib/libunittest.tex +++ b/Doc/lib/libunittest.tex @@ -226,7 +226,7 @@ runs, an exception will be raised, and the testing framework will identify the test case as a \dfn{failure}. Other exceptions that do not arise from checks made through the \method{assert*()} and \method{fail*()} methods are identified by the testing framework as -dfn{errors}. +\dfn{errors}. The way to run a test case will be described later. For now, note that to construct an instance of such a test case, we call its diff --git a/Doc/lib/liburllib2.tex b/Doc/lib/liburllib2.tex index e0c4568..7c8ad5d 100644 --- a/Doc/lib/liburllib2.tex +++ b/Doc/lib/liburllib2.tex @@ -621,14 +621,20 @@ user/password. \subsection{AbstractBasicAuthHandler Objects \label{abstract-basic-auth-handler}} -\begin{methoddesc}[AbstractBasicAuthHandler]{handle_authentication_request} +\begin{methoddesc}[AbstractBasicAuthHandler]{http_error_auth_reqed} {authreq, host, req, headers} Handle an authentication request by getting a user/password pair, and re-trying the request. \var{authreq} should be the name of the header where the information about the realm is included in the request, -\var{host} is the host to authenticate to, \var{req} should be the -(failed) \class{Request} object, and \var{headers} should be the error -headers. +\var{host} specifies the URL and path to authenticate for, \var{req} +should be the (failed) \class{Request} object, and \var{headers} +should be the error headers. + +\var{host} is either an authority (e.g. \code{"python.org"}) or a URL +containing an authority component (e.g. \code{"http://python.org/"}). +In either case, the authority must not contain a userinfo component +(so, \code{"python.org"} and \code{"python.org:80"} are fine, +\code{"joe:password@python.org"} is not). \end{methoddesc} @@ -653,7 +659,7 @@ Retry the request with authentication information, if available. \subsection{AbstractDigestAuthHandler Objects \label{abstract-digest-auth-handler}} -\begin{methoddesc}[AbstractDigestAuthHandler]{handle_authentication_request} +\begin{methoddesc}[AbstractDigestAuthHandler]{http_error_auth_reqed} {authreq, host, req, headers} \var{authreq} should be the name of the header where the information about the realm is included in the request, \var{host} should be the host to diff --git a/Doc/lib/libweakref.tex b/Doc/lib/libweakref.tex index 840b674..fc949e6 100644 --- a/Doc/lib/libweakref.tex +++ b/Doc/lib/libweakref.tex @@ -147,6 +147,24 @@ information. to vanish "by magic" (as a side effect of garbage collection).} \end{classdesc} +\class{WeakKeyDictionary} objects have the following additional +methods. These expose the internal references directly. The +references are not guaranteed to be ``live'' at the time they are +used, so the result of calling the references needs to be checked +before being used. This can be used to avoid creating references that +will cause the garbage collector to keep the keys around longer than +needed. + +\begin{methoddesc}{iterkeyrefs}{} + Return an iterator that yields the weak references to the keys. + \versionadded{2.5} +\end{methoddesc} + +\begin{methoddesc}{keyrefs}{} + Return a list of weak references to the keys. + \versionadded{2.5} +\end{methoddesc} + \begin{classdesc}{WeakValueDictionary}{\optional{dict}} Mapping class that references values weakly. Entries in the dictionary will be discarded when no strong reference to the value @@ -160,6 +178,21 @@ information. to vanish "by magic" (as a side effect of garbage collection).} \end{classdesc} +\class{WeakValueDictionary} objects have the following additional +methods. These method have the same issues as the +\method{iterkeyrefs()} and \method{keyrefs()} methods of +\class{WeakKeyDictionary} objects. + +\begin{methoddesc}{itervaluerefs}{} + Return an iterator that yields the weak references to the values. + \versionadded{2.5} +\end{methoddesc} + +\begin{methoddesc}{valuerefs}{} + Return a list of weak references to the values. + \versionadded{2.5} +\end{methoddesc} + \begin{datadesc}{ReferenceType} The type object for weak references objects. \end{datadesc} diff --git a/Doc/lib/libxmlrpclib.tex b/Doc/lib/libxmlrpclib.tex index 1c36f99..3645b82 100644 --- a/Doc/lib/libxmlrpclib.tex +++ b/Doc/lib/libxmlrpclib.tex @@ -81,9 +81,11 @@ Python type): This is the full set of data types supported by XML-RPC. Method calls may also raise a special \exception{Fault} instance, used to signal XML-RPC server errors, or \exception{ProtocolError} used to signal an -error in the HTTP/HTTPS transport layer. Note that even though starting -with Python 2.2 you can subclass builtin types, the xmlrpclib module -currently does not marshal instances of such subclasses. +error in the HTTP/HTTPS transport layer. Both \exception{Fault} and +\exception{ProtocolError} derive from a base class called +\exception{Error}. Note that even though starting with Python 2.2 you +can subclass builtin types, the xmlrpclib module currently does not +marshal instances of such subclasses. When passing strings, characters special to XML such as \samp{<}, \samp{>}, and \samp{\&} will be automatically escaped. However, it's @@ -340,6 +342,7 @@ objects, they are converted to \class{DateTime} objects internally, so only \begin{verbatim} # simple test program (from the XML-RPC specification) +from xmlrpclib import ServerProxy, Error # server = ServerProxy("http://localhost:8000") # local server server = ServerProxy("http://betty.userland.com") diff --git a/Doc/lib/libzlib.tex b/Doc/lib/libzlib.tex index dfbb43d..876f8c0 100644 --- a/Doc/lib/libzlib.tex +++ b/Doc/lib/libzlib.tex @@ -123,6 +123,12 @@ prevents compressing any more data. After calling action is to delete the object. \end{methoddesc} +\begin{methoddesc}[Compress]{copy}{} +Returns a copy of the compression object. This can be used to efficiently +compress a set of data that share a common initial prefix. +\versionadded{2.5} +\end{methoddesc} + Decompression objects support the following methods, and two attributes: \begin{memberdesc}{unused_data} @@ -176,6 +182,13 @@ The optional parameter \var{length} sets the initial size of the output buffer. \end{methoddesc} +\begin{methoddesc}[Decompress]{copy}{} +Returns a copy of the decompression object. This can be used to save the +state of the decompressor midway through the data stream in order to speed up +random seeks into the stream at a future point. +\versionadded{2.5} +\end{methoddesc} + \begin{seealso} \seemodule{gzip}{Reading and writing \program{gzip}-format files.} \seeurl{http://www.zlib.net}{The zlib library home page.} diff --git a/Doc/lib/sqlite3/adapter_datetime.py b/Doc/lib/sqlite3/adapter_datetime.py new file mode 100644 index 0000000..3460498 --- /dev/null +++ b/Doc/lib/sqlite3/adapter_datetime.py @@ -0,0 +1,14 @@ +import sqlite3 +import datetime, time + +def adapt_datetime(ts): + return time.mktime(ts.timetuple()) + +sqlite3.register_adapter(datetime.datetime, adapt_datetime) + +con = sqlite3.connect(":memory:") +cur = con.cursor() + +now = datetime.datetime.now() +cur.execute("select ?", (now,)) +print cur.fetchone()[0] diff --git a/Doc/lib/sqlite3/adapter_point_1.py b/Doc/lib/sqlite3/adapter_point_1.py new file mode 100644 index 0000000..a741f6c --- /dev/null +++ b/Doc/lib/sqlite3/adapter_point_1.py @@ -0,0 +1,16 @@ +import sqlite3 + +class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y + + def __conform__(self, protocol): + if protocol is sqlite3.PrepareProtocol: + return "%f;%f" % (self.x, self.y) + +con = sqlite3.connect(":memory:") +cur = con.cursor() + +p = Point(4.0, -3.2) +cur.execute("select ?", (p,)) +print cur.fetchone()[0] diff --git a/Doc/lib/sqlite3/adapter_point_2.py b/Doc/lib/sqlite3/adapter_point_2.py new file mode 100644 index 0000000..200a064 --- /dev/null +++ b/Doc/lib/sqlite3/adapter_point_2.py @@ -0,0 +1,17 @@ +import sqlite3 + +class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y + +def adapt_point(point): + return "%f;%f" % (point.x, point.y) + +sqlite3.register_adapter(Point, adapt_point) + +con = sqlite3.connect(":memory:") +cur = con.cursor() + +p = Point(4.0, -3.2) +cur.execute("select ?", (p,)) +print cur.fetchone()[0] diff --git a/Doc/lib/sqlite3/collation_reverse.py b/Doc/lib/sqlite3/collation_reverse.py new file mode 100644 index 0000000..e956402 --- /dev/null +++ b/Doc/lib/sqlite3/collation_reverse.py @@ -0,0 +1,15 @@ +import sqlite3 + +def collate_reverse(string1, string2): + return -cmp(string1, string2) + +con = sqlite3.connect(":memory:") +con.create_collation("reverse", collate_reverse) + +cur = con.cursor() +cur.execute("create table test(x)") +cur.executemany("insert into test(x) values (?)", [("a",), ("b",)]) +cur.execute("select x from test order by x collate reverse") +for row in cur: + print row +con.close() diff --git a/Doc/lib/sqlite3/complete_statement.py b/Doc/lib/sqlite3/complete_statement.py new file mode 100644 index 0000000..89fc250 --- /dev/null +++ b/Doc/lib/sqlite3/complete_statement.py @@ -0,0 +1,30 @@ +# A minimal SQLite shell for experiments + +import sqlite3 + +con = sqlite3.connect(":memory:") +con.isolation_level = None +cur = con.cursor() + +buffer = "" + +print "Enter your SQL commands to execute in sqlite3." +print "Enter a blank line to exit." + +while True: + line = raw_input() + if line == "": + break + buffer += line + if sqlite3.complete_statement(buffer): + try: + buffer = buffer.strip() + cur.execute(buffer) + + if buffer.lstrip().upper().startswith("SELECT"): + print cur.fetchall() + except sqlite3.Error, e: + print "An error occured:", e.args[0] + buffer = "" + +con.close() diff --git a/Doc/lib/sqlite3/connect_db_1.py b/Doc/lib/sqlite3/connect_db_1.py new file mode 100644 index 0000000..1b97523 --- /dev/null +++ b/Doc/lib/sqlite3/connect_db_1.py @@ -0,0 +1,3 @@ +import sqlite3 + +con = sqlite3.connect("mydb") diff --git a/Doc/lib/sqlite3/connect_db_2.py b/Doc/lib/sqlite3/connect_db_2.py new file mode 100644 index 0000000..f9728b36 --- /dev/null +++ b/Doc/lib/sqlite3/connect_db_2.py @@ -0,0 +1,3 @@ +import sqlite3 + +con = sqlite3.connect(":memory:") diff --git a/Doc/lib/sqlite3/converter_point.py b/Doc/lib/sqlite3/converter_point.py new file mode 100644 index 0000000..e220e9b --- /dev/null +++ b/Doc/lib/sqlite3/converter_point.py @@ -0,0 +1,47 @@ +import sqlite3 + +class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y + + def __repr__(self): + return "(%f;%f)" % (self.x, self.y) + +def adapt_point(point): + return "%f;%f" % (point.x, point.y) + +def convert_point(s): + x, y = map(float, s.split(";")) + return Point(x, y) + +# Register the adapter +sqlite3.register_adapter(Point, adapt_point) + +# Register the converter +sqlite3.register_converter("point", convert_point) + +p = Point(4.0, -3.2) + +######################### +# 1) Using declared types +con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) +cur = con.cursor() +cur.execute("create table test(p point)") + +cur.execute("insert into test(p) values (?)", (p,)) +cur.execute("select p from test") +print "with declared types:", cur.fetchone()[0] +cur.close() +con.close() + +####################### +# 1) Using column names +con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES) +cur = con.cursor() +cur.execute("create table test(p)") + +cur.execute("insert into test(p) values (?)", (p,)) +cur.execute('select p as "p [point]" from test') +print "with column names:", cur.fetchone()[0] +cur.close() +con.close() diff --git a/Doc/lib/sqlite3/countcursors.py b/Doc/lib/sqlite3/countcursors.py new file mode 100644 index 0000000..df04cad --- /dev/null +++ b/Doc/lib/sqlite3/countcursors.py @@ -0,0 +1,15 @@ +import sqlite3 + +class CountCursorsConnection(sqlite3.Connection): + def __init__(self, *args, **kwargs): + sqlite3.Connection.__init__(self, *args, **kwargs) + self.numcursors = 0 + + def cursor(self, *args, **kwargs): + self.numcursors += 1 + return sqlite3.Connection.cursor(self, *args, **kwargs) + +con = sqlite3.connect(":memory:", factory=CountCursorsConnection) +cur1 = con.cursor() +cur2 = con.cursor() +print con.numcursors diff --git a/Doc/lib/sqlite3/createdb.py b/Doc/lib/sqlite3/createdb.py new file mode 100644 index 0000000..ee2950b --- /dev/null +++ b/Doc/lib/sqlite3/createdb.py @@ -0,0 +1,28 @@ +# Not referenced from the documentation, but builds the database file the other +# code snippets expect. + +import sqlite3 +import os + +DB_FILE = "mydb" + +if os.path.exists(DB_FILE): + os.remove(DB_FILE) + +con = sqlite3.connect(DB_FILE) +cur = con.cursor() +cur.execute(""" + create table people + ( + name_last varchar(20), + age integer + ) + """) + +cur.execute("insert into people (name_last, age) values ('Yeltsin', 72)") +cur.execute("insert into people (name_last, age) values ('Putin', 51)") + +con.commit() + +cur.close() +con.close() diff --git a/Doc/lib/sqlite3/execsql_fetchonerow.py b/Doc/lib/sqlite3/execsql_fetchonerow.py new file mode 100644 index 0000000..8044ecf --- /dev/null +++ b/Doc/lib/sqlite3/execsql_fetchonerow.py @@ -0,0 +1,17 @@ +import sqlite3 + +con = sqlite3.connect("mydb") + +cur = con.cursor() +SELECT = "select name_last, age from people order by age, name_last" + +# 1. Iterate over the rows available from the cursor, unpacking the +# resulting sequences to yield their elements (name_last, age): +cur.execute(SELECT) +for (name_last, age) in cur: + print '%s is %d years old.' % (name_last, age) + +# 2. Equivalently: +cur.execute(SELECT) +for row in cur: + print '%s is %d years old.' % (row[0], row[1]) diff --git a/Doc/lib/sqlite3/execsql_printall_1.py b/Doc/lib/sqlite3/execsql_printall_1.py new file mode 100644 index 0000000..d27d735 --- /dev/null +++ b/Doc/lib/sqlite3/execsql_printall_1.py @@ -0,0 +1,13 @@ +import sqlite3 + +# Create a connection to the database file "mydb": +con = sqlite3.connect("mydb") + +# Get a Cursor object that operates in the context of Connection con: +cur = con.cursor() + +# Execute the SELECT statement: +cur.execute("select * from people order by age") + +# Retrieve all rows as a sequence and print that sequence: +print cur.fetchall() diff --git a/Doc/lib/sqlite3/execute_1.py b/Doc/lib/sqlite3/execute_1.py new file mode 100644 index 0000000..fb3784f --- /dev/null +++ b/Doc/lib/sqlite3/execute_1.py @@ -0,0 +1,11 @@ +import sqlite3 + +con = sqlite3.connect("mydb") + +cur = con.cursor() + +who = "Yeltsin" +age = 72 + +cur.execute("select name_last, age from people where name_last=? and age=?", (who, age)) +print cur.fetchone() diff --git a/Doc/lib/sqlite3/execute_2.py b/Doc/lib/sqlite3/execute_2.py new file mode 100644 index 0000000..df6c894 --- /dev/null +++ b/Doc/lib/sqlite3/execute_2.py @@ -0,0 +1,12 @@ +import sqlite3 + +con = sqlite3.connect("mydb") + +cur = con.cursor() + +who = "Yeltsin" +age = 72 + +cur.execute("select name_last, age from people where name_last=:who and age=:age", + {"who": who, "age": age}) +print cur.fetchone() diff --git a/Doc/lib/sqlite3/execute_3.py b/Doc/lib/sqlite3/execute_3.py new file mode 100644 index 0000000..b64621f --- /dev/null +++ b/Doc/lib/sqlite3/execute_3.py @@ -0,0 +1,12 @@ +import sqlite3 + +con = sqlite3.connect("mydb") + +cur = con.cursor() + +who = "Yeltsin" +age = 72 + +cur.execute("select name_last, age from people where name_last=:who and age=:age", + locals()) +print cur.fetchone() diff --git a/Doc/lib/sqlite3/executemany_1.py b/Doc/lib/sqlite3/executemany_1.py new file mode 100644 index 0000000..24357c5 --- /dev/null +++ b/Doc/lib/sqlite3/executemany_1.py @@ -0,0 +1,24 @@ +import sqlite3 + +class IterChars: + def __init__(self): + self.count = ord('a') + + def __iter__(self): + return self + + def next(self): + if self.count > ord('z'): + raise StopIteration + self.count += 1 + return (chr(self.count - 1),) # this is a 1-tuple + +con = sqlite3.connect(":memory:") +cur = con.cursor() +cur.execute("create table characters(c)") + +theIter = IterChars() +cur.executemany("insert into characters(c) values (?)", theIter) + +cur.execute("select c from characters") +print cur.fetchall() diff --git a/Doc/lib/sqlite3/executemany_2.py b/Doc/lib/sqlite3/executemany_2.py new file mode 100644 index 0000000..05857c0 --- /dev/null +++ b/Doc/lib/sqlite3/executemany_2.py @@ -0,0 +1,15 @@ +import sqlite3 + +def char_generator(): + import string + for c in string.letters[:26]: + yield (c,) + +con = sqlite3.connect(":memory:") +cur = con.cursor() +cur.execute("create table characters(c)") + +cur.executemany("insert into characters(c) values (?)", char_generator()) + +cur.execute("select c from characters") +print cur.fetchall() diff --git a/Doc/lib/sqlite3/executescript.py b/Doc/lib/sqlite3/executescript.py new file mode 100644 index 0000000..0795b47 --- /dev/null +++ b/Doc/lib/sqlite3/executescript.py @@ -0,0 +1,24 @@ +import sqlite3 + +con = sqlite3.connect(":memory:") +cur = con.cursor() +cur.executescript(""" + create table person( + firstname, + lastname, + age + ); + + create table book( + title, + author, + published + ); + + insert into book(title, author, published) + values ( + 'Dirk Gently''s Holistic Detective Agency + 'Douglas Adams', + 1987 + ); + """) diff --git a/Doc/lib/sqlite3/insert_more_people.py b/Doc/lib/sqlite3/insert_more_people.py new file mode 100644 index 0000000..edbc79e --- /dev/null +++ b/Doc/lib/sqlite3/insert_more_people.py @@ -0,0 +1,16 @@ +import sqlite3 + +con = sqlite3.connect("mydb") + +cur = con.cursor() + +newPeople = ( + ('Lebed' , 53), + ('Zhirinovsky' , 57), + ) + +for person in newPeople: + cur.execute("insert into people (name_last, age) values (?, ?)", person) + +# The changes will not be saved unless the transaction is committed explicitly: +con.commit() diff --git a/Doc/lib/sqlite3/md5func.py b/Doc/lib/sqlite3/md5func.py new file mode 100644 index 0000000..5769687 --- /dev/null +++ b/Doc/lib/sqlite3/md5func.py @@ -0,0 +1,11 @@ +import sqlite3 +import md5 + +def md5sum(t): + return md5.md5(t).hexdigest() + +con = sqlite3.connect(":memory:") +con.create_function("md5", 1, md5sum) +cur = con.cursor() +cur.execute("select md5(?)", ("foo",)) +print cur.fetchone()[0] diff --git a/Doc/lib/sqlite3/mysumaggr.py b/Doc/lib/sqlite3/mysumaggr.py new file mode 100644 index 0000000..6d0cd55 --- /dev/null +++ b/Doc/lib/sqlite3/mysumaggr.py @@ -0,0 +1,20 @@ +import sqlite3 + +class MySum: + def __init__(self): + self.count = 0 + + def step(self, value): + self.count += value + + def finalize(self): + return self.count + +con = sqlite3.connect(":memory:") +con.create_aggregate("mysum", 1, MySum) +cur = con.cursor() +cur.execute("create table test(i)") +cur.execute("insert into test(i) values (1)") +cur.execute("insert into test(i) values (2)") +cur.execute("select mysum(i) from test") +print cur.fetchone()[0] diff --git a/Doc/lib/sqlite3/parse_colnames.py b/Doc/lib/sqlite3/parse_colnames.py new file mode 100644 index 0000000..fcded00 --- /dev/null +++ b/Doc/lib/sqlite3/parse_colnames.py @@ -0,0 +1,8 @@ +import sqlite3 +import datetime + +con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES) +cur = con.cursor() +cur.execute('select ? as "x [timestamp]"', (datetime.datetime.now(),)) +dt = cur.fetchone()[0] +print dt, type(dt) diff --git a/Doc/lib/sqlite3/pysqlite_datetime.py b/Doc/lib/sqlite3/pysqlite_datetime.py new file mode 100644 index 0000000..efa4b06 --- /dev/null +++ b/Doc/lib/sqlite3/pysqlite_datetime.py @@ -0,0 +1,20 @@ +import sqlite3 +import datetime + +con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES) +cur = con.cursor() +cur.execute("create table test(d date, ts timestamp)") + +today = datetime.date.today() +now = datetime.datetime.now() + +cur.execute("insert into test(d, ts) values (?, ?)", (today, now)) +cur.execute("select d, ts from test") +row = cur.fetchone() +print today, "=>", row[0], type(row[0]) +print now, "=>", row[1], type(row[1]) + +cur.execute('select current_date as "d [date]", current_timestamp as "ts [timestamp]"') +row = cur.fetchone() +print "current_date", row[0], type(row[0]) +print "current_timestamp", row[1], type(row[1]) diff --git a/Doc/lib/sqlite3/row_factory.py b/Doc/lib/sqlite3/row_factory.py new file mode 100644 index 0000000..64676c8 --- /dev/null +++ b/Doc/lib/sqlite3/row_factory.py @@ -0,0 +1,13 @@ +import sqlite3 + +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + +con = sqlite3.connect(":memory:") +con.row_factory = dict_factory +cur = con.cursor() +cur.execute("select 1 as a") +print cur.fetchone()["a"] diff --git a/Doc/lib/sqlite3/rowclass.py b/Doc/lib/sqlite3/rowclass.py new file mode 100644 index 0000000..3fa0b87 --- /dev/null +++ b/Doc/lib/sqlite3/rowclass.py @@ -0,0 +1,12 @@ +import sqlite3 + +con = sqlite3.connect("mydb") +con.row_factory = sqlite3.Row + +cur = con.cursor() +cur.execute("select name_last, age from people") +for row in cur: + assert row[0] == row["name_last"] + assert row["name_last"] == row["nAmE_lAsT"] + assert row[1] == row["age"] + assert row[1] == row["AgE"] diff --git a/Doc/lib/sqlite3/shared_cache.py b/Doc/lib/sqlite3/shared_cache.py new file mode 100644 index 0000000..bf1d7b4 --- /dev/null +++ b/Doc/lib/sqlite3/shared_cache.py @@ -0,0 +1,6 @@ +import sqlite3 + +# The shared cache is only available in SQLite versions 3.3.3 or later +# See the SQLite documentaton for details. + +sqlite3.enable_shared_cache(True) diff --git a/Doc/lib/sqlite3/shortcut_methods.py b/Doc/lib/sqlite3/shortcut_methods.py new file mode 100644 index 0000000..72ed4b3 --- /dev/null +++ b/Doc/lib/sqlite3/shortcut_methods.py @@ -0,0 +1,21 @@ +import sqlite3 + +persons = [ + ("Hugo", "Boss"), + ("Calvin", "Klein") + ] + +con = sqlite3.connect(":memory:") + +# Create the table +con.execute("create table person(firstname, lastname)") + +# Fill the table +con.executemany("insert into person(firstname, lastname) values (?, ?)", persons) + +# Print the table contents +for row in con.execute("select firstname, lastname from person"): + print row + +# Using a dummy WHERE clause to not let SQLite take the shortcut table deletes. +print "I just deleted", con.execute("delete from person where 1=1").rowcount, "rows" diff --git a/Doc/lib/sqlite3/simple_tableprinter.py b/Doc/lib/sqlite3/simple_tableprinter.py new file mode 100644 index 0000000..67ea6a2 --- /dev/null +++ b/Doc/lib/sqlite3/simple_tableprinter.py @@ -0,0 +1,26 @@ +import sqlite3 + +FIELD_MAX_WIDTH = 20 +TABLE_NAME = 'people' +SELECT = 'select * from %s order by age, name_last' % TABLE_NAME + +con = sqlite3.connect("mydb") + +cur = con.cursor() +cur.execute(SELECT) + +# Print a header. +for fieldDesc in cur.description: + print fieldDesc[0].ljust(FIELD_MAX_WIDTH) , +print # Finish the header with a newline. +print '-' * 78 + +# For each row, print the value of each field left-justified within +# the maximum possible width of that field. +fieldIndices = range(len(cur.description)) +for row in cur: + for fieldIndex in fieldIndices: + fieldValue = str(row[fieldIndex]) + print fieldValue.ljust(FIELD_MAX_WIDTH) , + + print # Finish the row with a newline. diff --git a/Doc/lib/sqlite3/text_factory.py b/Doc/lib/sqlite3/text_factory.py new file mode 100644 index 0000000..3e157a8 --- /dev/null +++ b/Doc/lib/sqlite3/text_factory.py @@ -0,0 +1,42 @@ +import sqlite3 + +con = sqlite3.connect(":memory:") +cur = con.cursor() + +# Create the table +con.execute("create table person(lastname, firstname)") + +AUSTRIA = u"\xd6sterreich" + +# by default, rows are returned as Unicode +cur.execute("select ?", (AUSTRIA,)) +row = cur.fetchone() +assert row[0] == AUSTRIA + +# but we can make pysqlite always return bytestrings ... +con.text_factory = str +cur.execute("select ?", (AUSTRIA,)) +row = cur.fetchone() +assert type(row[0]) == str +# the bytestrings will be encoded in UTF-8, unless you stored garbage in the +# database ... +assert row[0] == AUSTRIA.encode("utf-8") + +# we can also implement a custom text_factory ... +# here we implement one that will ignore Unicode characters that cannot be +# decoded from UTF-8 +con.text_factory = lambda x: unicode(x, "utf-8", "ignore") +cur.execute("select ?", ("this is latin1 and would normally create errors" + u"\xe4\xf6\xfc".encode("latin1"),)) +row = cur.fetchone() +assert type(row[0]) == unicode + +# pysqlite offers a builtin optimized text_factory that will return bytestring +# objects, if the data is in ASCII only, and otherwise return unicode objects +con.text_factory = sqlite3.OptimizedUnicode +cur.execute("select ?", (AUSTRIA,)) +row = cur.fetchone() +assert type(row[0]) == unicode + +cur.execute("select ?", ("Germany",)) +row = cur.fetchone() +assert type(row[0]) == str diff --git a/Doc/mac/scripting.tex b/Doc/mac/scripting.tex index a6d5df7..5ec4978 100644 --- a/Doc/mac/scripting.tex +++ b/Doc/mac/scripting.tex @@ -12,7 +12,7 @@ read Apple's documentation. The "Applescript Language Guide" explains the conceptual model and the terminology, and documents the standard suite. The "Open Scripting Architecture" document explains how to use OSA from an application programmers point of view. In the Apple Help -Viewer these book sare located in the Developer Documentation, Core +Viewer these books are located in the Developer Documentation, Core Technologies section. @@ -49,7 +49,7 @@ line. The generated output is a package with a number of modules, one for every suite used in the program plus an \module{__init__} module to glue it all together. The Python inheritance graph follows the AppleScript -inheritance graph, so if a programs dictionary specifies that it +inheritance graph, so if a program's dictionary specifies that it includes support for the Standard Suite, but extends one or two verbs with extra arguments then the output suite will contain a module \module{Standard_Suite} that imports and re-exports everything from diff --git a/Doc/perl/l2hinit.perl b/Doc/perl/l2hinit.perl index 89deed0..7c5d123 100644 --- a/Doc/perl/l2hinit.perl +++ b/Doc/perl/l2hinit.perl @@ -4,7 +4,17 @@ package main; use L2hos; -$HTML_VERSION = 4.0; +$HTML_VERSION = 4.01; +$LOWER_CASE_TAGS = 1; +$NO_FRENCH_QUOTES = 1; + +# '' in \code{...} is still converted, so we can't use this yet. +#$USE_CURLY_QUOTES = 1; + +# Force Unicode support to be loaded; request UTF-8 output. +do_require_extension('unicode'); +do_require_extension('utf8'); +$HTML_OPTIONS = 'utf8'; $MAX_LINK_DEPTH = 2; $ADDRESS = ''; @@ -106,6 +116,13 @@ sub custom_driver_hook { $ENV{'TEXINPUTS'} = undef; } print "\nSetting \$TEXINPUTS to $TEXINPUTS\n"; + + # Not sure why we need to deal with this both here and at the top, + # but this is needed to actually make it work. + do_require_extension('utf8'); + $charset = $utf8_str; + $CHARSET = $utf8_str; + $USE_UTF = 1; } diff --git a/Doc/perl/python.perl b/Doc/perl/python.perl index 437c5cb..ab93c7c 100644 --- a/Doc/perl/python.perl +++ b/Doc/perl/python.perl @@ -530,7 +530,6 @@ sub add_index_entry($$){ sub new_link_name_info(){ my $name = "l2h-" . ++$globals{'max_id'}; - my $aname = "
"; my $ahref = gen_link($CURRENT_FILE, $name); return ($name, $ahref); } diff --git a/Doc/ref/ref2.tex b/Doc/ref/ref2.tex index 34e8a9e..2ed8a5d 100644 --- a/Doc/ref/ref2.tex +++ b/Doc/ref/ref2.tex @@ -308,22 +308,28 @@ identifiers. They must be spelled exactly as written here:% \index{reserved word} \begin{verbatim} -and del for is raise -assert elif from lambda return -break else global not try -class except if or while -continue exec import pass yield -def finally in print +and del from not while +as elif global or with +assert else if pass yield +break except import print +class exec in raise +continue finally is return +def for lambda try \end{verbatim} % When adding keywords, use reswords.py for reformatting -Note that although the identifier \code{as} can be used as part of the -syntax of \keyword{import} statements, it is not currently a reserved -word. +\versionchanged[\constant{None} became a constant and is now +recognized by the compiler as a name for the built-in object +\constant{None}. Although it is not a keyword, you cannot assign +a different object to it]{2.4} -In some future version of Python, the identifiers \code{as} and -\code{None} will both become keywords. +\versionchanged[Both \keyword{as} and \keyword{with} are only recognized +when the \code{with_statement} future feature has been enabled. +It will always be enabled in Python 2.6. See section~\ref{with} for +details. Note that using \keyword{as} and \keyword{with} as identifiers +will always issue a warning, even when the \code{with_statement} future +directive is not in effect]{2.5} \subsection{Reserved classes of identifiers\label{id-classes}} @@ -652,7 +658,7 @@ Some examples of floating point literals: \end{verbatim} Note that numeric literals do not include a sign; a phrase like -\code{-1} is actually an expression composed of the operator +\code{-1} is actually an expression composed of the unary operator \code{-} and the literal \code{1}. diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index 964013f..d0c8ccf 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -1875,8 +1875,8 @@ These methods are called to implement the binary arithmetic operations (\code{+}, \code{-}, \code{*}, \code{//}, \code{\%}, \function{divmod()}\bifuncindex{divmod}, -\function{pow()}\bifuncindex{pow}, \code{**}, \code{<}\code{<}, -\code{>}\code{>}, \code{\&}, \code{\^}, \code{|}). For instance, to +\function{pow()}\bifuncindex{pow}, \code{**}, \code{<<}, +\code{>>}, \code{\&}, \code{\^}, \code{|}). For instance, to evaluate the expression \var{x}\code{+}\var{y}, where \var{x} is an instance of a class that has an \method{__add__()} method, \code{\var{x}.__add__(\var{y})} is called. The \method{__divmod__()} @@ -1915,8 +1915,8 @@ These methods are called to implement the binary arithmetic operations (\code{+}, \code{-}, \code{*}, \code{/}, \code{\%}, \function{divmod()}\bifuncindex{divmod}, -\function{pow()}\bifuncindex{pow}, \code{**}, \code{<}\code{<}, -\code{>}\code{>}, \code{\&}, \code{\^}, \code{|}) with reflected +\function{pow()}\bifuncindex{pow}, \code{**}, \code{<<}, +\code{>>}, \code{\&}, \code{\^}, \code{|}) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation. For instance, to evaluate the expression \var{x}\code{-}\var{y}, where \var{y} is an @@ -1942,7 +1942,7 @@ complicated). \methodline[numeric object]{__ior__}{self, other} These methods are called to implement the augmented arithmetic operations (\code{+=}, \code{-=}, \code{*=}, \code{/=}, \code{\%=}, -\code{**=}, \code{<}\code{<=}, \code{>}\code{>=}, \code{\&=}, +\code{**=}, \code{<<=}, \code{>>=}, \code{\&=}, \code{\textasciicircum=}, \code{|=}). These methods should attempt to do the operation in-place (modifying \var{self}) and return the result (which could be, but does not have to be, \var{self}). If a specific method @@ -1983,9 +1983,9 @@ Called to implement the built-in functions \end{methoddesc} \begin{methoddesc}[numeric object]{__index__}{self} -Called to implement operator.index(). Also called whenever Python -needs an integer object (such as in slicing). Must return an integer -(int or long). +Called to implement \function{operator.index()}. Also called whenever +Python needs an integer object (such as in slicing). Must return an +integer (int or long). \versionadded{2.5} \end{methoddesc} @@ -2112,49 +2112,41 @@ implement a \method{__coerce__()} method, for use by the built-in \end{itemize} -\subsection{Context Managers and Contexts\label{context-managers}} +\subsection{With Statement Context Managers\label{context-managers}} \versionadded{2.5} -A \dfn{context manager} is an object that manages the entry to, and exit -from, a \dfn{context} surrounding a block of code. Context managers are -normally invoked using the \keyword{with} statement (described in -section~\ref{with}), but can also be used by directly invoking their -methods. +A \dfn{context manager} is an object that defines the runtime +context to be established when executing a \keyword{with} +statement. The context manager handles the entry into, +and the exit from, the desired runtime context for the execution +of the block of code. Context managers are normally invoked using +the \keyword{with} statement (described in section~\ref{with}), but +can also be used by directly invoking their methods. + \stindex{with} \index{context manager} -\index{context} - -Typical uses of context managers include saving and restoring various -kinds of global state, locking and unlocking resources, closing opened -files, etc. - -\begin{methoddesc}[context manager]{__context__}{self} -Invoked when the object is used as the context expression of a -\keyword{with} statement. The return value must implement -\method{__enter__()} and \method{__exit__()} methods. Simple context -managers that wish to directly -implement \method{__enter__()} and \method{__exit__()} should just -return \var{self}. - -Context managers written in Python can also implement this method using -a generator function decorated with the -\function{contextlib.contextmanager} decorator, as this can be simpler -than writing individual \method{__enter__()} and \method{__exit__()} -methods when the state to be managed is complex. -\end{methoddesc} -\begin{methoddesc}[context]{__enter__}{self} -Enter the context defined by this object. The \keyword{with} statement -will bind this method's return value to the target(s) specified in the -\keyword{as} clause of the statement, if any. +Typical uses of context managers include saving and +restoring various kinds of global state, locking and unlocking +resources, closing opened files, etc. + +For more information on context managers, see +``\ulink{Context Types}{../lib/typecontextmanager.html}'' in the +\citetitle[../lib/lib.html]{Python Library Reference}. + +\begin{methoddesc}[context manager]{__enter__}{self} +Enter the runtime context related to this object. The \keyword{with} +statement will bind this method's return value to the target(s) +specified in the \keyword{as} clause of the statement, if any. \end{methoddesc} -\begin{methoddesc}[context]{__exit__}{exc_type, exc_value, traceback} -Exit the context defined by this object. The parameters describe the -exception that caused the context to be exited. If the context was -exited without an exception, all three arguments will be -\constant{None}. +\begin{methoddesc}[context manager]{__exit__} +{self, exc_type, exc_value, traceback} +Exit the runtime context related to this object. The parameters +describe the exception that caused the context to be exited. If +the context was exited without an exception, all three arguments +will be \constant{None}. If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a diff --git a/Doc/ref/ref4.tex b/Doc/ref/ref4.tex index 6a3a4ef..dcdc823 100644 --- a/Doc/ref/ref4.tex +++ b/Doc/ref/ref4.tex @@ -127,7 +127,7 @@ to delete the name. An error will be reported at compile time. If the wild card form of import --- \samp{import *} --- is used in a function and the function contains or is a nested block with free -variables, the compiler will raise a SyntaxError. +variables, the compiler will raise a \exception{SyntaxError}. If \keyword{exec} is used in a function and the function contains or is a nested block with free variables, the compiler will raise a diff --git a/Doc/ref/ref5.tex b/Doc/ref/ref5.tex index 1f2dc5e..89f9977 100644 --- a/Doc/ref/ref5.tex +++ b/Doc/ref/ref5.tex @@ -22,9 +22,9 @@ are the same as for \code{othername}. When a description of an arithmetic operator below uses the phrase ``the numeric arguments are converted to a common type,'' the -arguments are coerced using the coercion rules listed at the end of -chapter \ref{datamodel}. If both arguments are standard numeric -types, the following coercions are applied: +arguments are coerced using the coercion rules listed at +~\ref{coercion-rules}. If both arguments are standard numeric types, +the following coercions are applied: \begin{itemize} \item If either argument is a complex number, the other is converted @@ -391,7 +391,8 @@ type but a string of exactly one character. A slicing selects a range of items in a sequence object (e.g., a string, tuple or list). Slicings may be used as expressions or as -targets in assignment or del statements. The syntax for a slicing: +targets in assignment or \keyword{del} statements. The syntax for a +slicing: \obindex{sequence} \obindex{string} \obindex{tuple} @@ -1158,7 +1159,7 @@ have the same precedence and chain from left to right --- see section \hline \lineii{\code{\&}} {Bitwise AND} \hline - \lineii{\code{<}\code{<}, \code{>}\code{>}} {Shifts} + \lineii{\code{<<}, \code{>>}} {Shifts} \hline \lineii{\code{+}, \code{-}}{Addition and subtraction} \hline diff --git a/Doc/ref/ref6.tex b/Doc/ref/ref6.tex index 1eb1258..04db013 100644 --- a/Doc/ref/ref6.tex +++ b/Doc/ref/ref6.tex @@ -377,7 +377,7 @@ right type (but even this is determined by the sliced object). \begin{productionlist} \production{print_stmt} {"print" ( \optional{\token{expression} ("," \token{expression})* \optional{","}}} - \productioncont{| ">\code{>}" \token{expression} + \productioncont{| ">>" \token{expression} \optional{("," \token{expression})+ \optional{","}} )} \end{productionlist} @@ -417,7 +417,7 @@ exception is raised. \keyword{print} also has an extended\index{extended print statement} form, defined by the second portion of the syntax described above. This form is sometimes referred to as ``\keyword{print} chevron.'' -In this form, the first expression after the \code{>}\code{>} must +In this form, the first expression after the \code{>>} must evaluate to a ``file-like'' object, specifically an object that has a \method{write()} method as described above. With this extended form, the subsequent expressions are printed to this file object. If the @@ -809,13 +809,14 @@ import __future__ [as name] That is not a future statement; it's an ordinary import statement with no special semantics or syntax restrictions. -Code compiled by an exec statement or calls to the builtin functions +Code compiled by an \keyword{exec} statement or calls to the builtin functions \function{compile()} and \function{execfile()} that occur in a module \module{M} containing a future statement will, by default, use the new syntax or semantics associated with the future statement. This can, starting with Python 2.2 be controlled by optional arguments to -\function{compile()} --- see the documentation of that function in the -library reference for details. +\function{compile()} --- see the documentation of that function in the +\citetitle[../lib/built-in-funcs.html]{Python Library Reference} for +details. A future statement typed at an interactive interpreter prompt will take effect for the rest of the interpreter session. If an diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index a2d46a8..6bc0b08 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -281,11 +281,8 @@ and is not handled, the exception is temporarily saved. The it is re-raised at the end of the \keyword{finally} clause. If the \keyword{finally} clause raises another exception or executes a \keyword{return} or \keyword{break} statement, the saved -exception is lost. A \keyword{continue} statement is illegal in the -\keyword{finally} clause. (The reason is a problem with the current -implementation -- this restriction may be lifted in the future). The -exception information is not available to the program during execution of -the \keyword{finally} clause. +exception is lost. The exception information is not available to the +program during execution of the \keyword{finally} clause. \kwindex{finally} When a \keyword{return}, \keyword{break} or \keyword{continue} statement is @@ -312,38 +309,34 @@ The \keyword{with} statement is used to wrap the execution of a block with methods defined by a context manager (see section~\ref{context-managers}). This allows common \keyword{try}...\keyword{except}...\keyword{finally} usage patterns to -be encapsulated as context managers for convenient reuse. +be encapsulated for convenient reuse. \begin{productionlist} \production{with_stmt} - {"with" \token{expression} ["as" target_list] ":" \token{suite}} + {"with" \token{expression} ["as" target] ":" \token{suite}} \end{productionlist} The execution of the \keyword{with} statement proceeds as follows: \begin{enumerate} -\item The expression is evaluated, to obtain a context manager -object. +\item The context expression is evaluated to obtain a context manager. -\item The context manager's \method{__context__()} method is invoked to -obtain a context object. +\item The context manager's \method{__enter__()} method is invoked. -\item The context object's \method{__enter__()} method is invoked. - -\item If a target list was included in the \keyword{with} +\item If a target was included in the \keyword{with} statement, the return value from \method{__enter__()} is assigned to it. \note{The \keyword{with} statement guarantees that if the \method{__enter__()} method returns without an error, then \method{__exit__()} will always be called. Thus, if an error occurs during the assignment to the target list, it will be treated the same as -an error occurring within the suite would be. See step 6 below.} +an error occurring within the suite would be. See step 5 below.} \item The suite is executed. -\item The context object's \method{__exit__()} method is invoked. If an -exception caused the suite to be exited, its type, value, and +\item The context manager's \method{__exit__()} method is invoked. If +an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to \method{__exit__()}. Otherwise, three \constant{None} arguments are supplied. diff --git a/Doc/texinputs/python.sty b/Doc/texinputs/python.sty index 4cb02de..3ce62f4 100644 --- a/Doc/texinputs/python.sty +++ b/Doc/texinputs/python.sty @@ -848,8 +848,17 @@ % but only if we actually used hyperref: \ifpdf \newcommand{\url}[1]{{% - \py@pdfstartlink attr{/Border [0 0 0]} user{/S /URI /URI (#1)}% - \py@LinkColor% color of the link text + \py@pdfstartlink% + attr{ /Border [0 0 0] }% + user{% + /Subtype/Link% + /A<<% + /Type/Action% + /S/URI% + /URI(#1)% + >>% + }% + \py@LinkColor% color of the link text \py@smallsize\sf #1% \py@NormalColor% Turn it back off; these are declarative \pdfendlink}% and don't appear bound to the current @@ -925,7 +934,16 @@ \ifpdf \newcommand{\ulink}[2]{{% % For PDF, we *should* only generate a link when the URL is absolute. - \py@pdfstartlink attr{/Border [0 0 0]} user{/S /URI /URI (#2)}% + \py@pdfstartlink% + attr{ /Border [0 0 0] }% + user{% + /Subtype/Link% + /A<<% + /Type/Action% + /S/URI% + /URI(#2)% + >>% + }% \py@LinkColor% color of the link text #1% \py@NormalColor% Turn it back off; these are declarative diff --git a/Doc/tut/glossary.tex b/Doc/tut/glossary.tex index c8082d5..17cc767 100644 --- a/Doc/tut/glossary.tex +++ b/Doc/tut/glossary.tex @@ -7,7 +7,7 @@ \index{>>>} -\item[\code{>\code{>}>}] +\item[\code{>>>}] The typical Python prompt of the interactive shell. Often seen for code examples that can be tried right away in the interpreter. diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex index 7f9a7ee..4e0a26b 100644 --- a/Doc/tut/tut.tex +++ b/Doc/tut/tut.tex @@ -264,7 +264,7 @@ the command or module to handle. When commands are read from a tty, the interpreter is said to be in \emph{interactive mode}. In this mode it prompts for the next command with the \emph{primary prompt}, usually three greater-than signs -(\samp{>\code{>}>~}); for continuation lines it prompts with the +(\samp{>>>~}); for continuation lines it prompts with the \emph{secondary prompt}, by default three dots (\samp{...~}). The interpreter prints a welcome message stating its version number and a copyright notice before printing the first prompt: @@ -423,7 +423,7 @@ if filename and os.path.isfile(filename): \chapter{An Informal Introduction to Python \label{informal}} In the following examples, input and output are distinguished by the -presence or absence of prompts (\samp{>\code{>}>~} and \samp{...~}): to repeat +presence or absence of prompts (\samp{>>>~} and \samp{...~}): to repeat the example, you must type everything after the prompt, when the prompt appears; lines that do not begin with a prompt are output from the interpreter. % @@ -455,7 +455,7 @@ STRING = "# This is not a comment." \section{Using Python as a Calculator \label{calculator}} Let's try some simple Python commands. Start the interpreter and wait -for the primary prompt, \samp{>\code{>}>~}. (It shouldn't take long.) +for the primary prompt, \samp{>>>~}. (It shouldn't take long.) \subsection{Numbers \label{numbers}} @@ -2723,7 +2723,7 @@ standard module \module{__builtin__}\refbimodindex{__builtin__}: 'FloatingPointError', 'FutureWarning', 'IOError', 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', - 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning', + 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', @@ -3763,6 +3763,38 @@ for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful. +\section{Predefined Clean-up Actions \label{cleanup-with}} + +Some objects define standard clean-up actions to be undertaken when +the object is no longer needed, regardless of whether or not the +operation using the object succeeded or failed. +Look at the following example, which tries to open a file and print +its contents to the screen. + +\begin{verbatim} +for line in open("myfile.txt"): + print line +\end{verbatim} + +The problem with this code is that it leaves the file open for an +indeterminate amount of time after the code has finished executing. +This is not an issue in simple scripts, but can be a problem for +larger applications. The \keyword{with} statement allows +objects like files to be used in a way that ensures they are +always cleaned up promptly and correctly. + +\begin{verbatim} +with open("myfile.txt") as f: + for line in f: + print line +\end{verbatim} + +After the statement is executed, the file \var{f} is always closed, +even if a problem was encountered while processing the lines. Other +objects which provide predefined clean-up actions will indicate +this in their documentation. + + \chapter{Classes \label{classes}} Python's class mechanism adds classes to the language with a minimum @@ -4757,7 +4789,7 @@ for sending mail: \begin{verbatim} >>> import urllib2 >>> for line in urllib2.urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl'): -... if 'EST' in line: # look for Eastern Standard Time +... if 'EST' in line or 'EDT' in line: # look for Eastern Time ... print line
Nov. 25, 09:43:32 PM EST diff --git a/Doc/whatsnew/whatsnew20.tex b/Doc/whatsnew/whatsnew20.tex index bf458fa..56d15b8 100644 --- a/Doc/whatsnew/whatsnew20.tex +++ b/Doc/whatsnew/whatsnew20.tex @@ -330,7 +330,7 @@ List comprehensions have the form: [ expression for expr in sequence1 for expr2 in sequence2 ... for exprN in sequenceN - if condition + if condition ] \end{verbatim} The \keyword{for}...\keyword{in} clauses contain the sequences to be @@ -356,7 +356,7 @@ for expr1 in sequence1: # resulting list. \end{verbatim} -This means that when there are \keyword{for}...\keyword{in} clauses, +This means that when there are multiple \keyword{for}...\keyword{in} clauses, the resulting list will be equal to the product of the lengths of all the sequences. If you have two lists of length 3, the output list is 9 elements long: @@ -400,7 +400,7 @@ statement \code{a += 2} increments the value of the variable % The empty groups below prevent conversion to guillemets. The full list of supported assignment operators is \code{+=}, \code{-=}, \code{*=}, \code{/=}, \code{\%=}, \code{**=}, \code{\&=}, -\code{|=}, \verb|^=|, \code{>{}>=}, and \code{<{}<=}. Python classes can +\code{|=}, \verb|^=|, \code{>>=}, and \code{<<=}. Python classes can override the augmented assignment operators by defining methods named \method{__iadd__}, \method{__isub__}, etc. For example, the following \class{Number} class stores a number and supports using += to create a diff --git a/Doc/whatsnew/whatsnew23.tex b/Doc/whatsnew/whatsnew23.tex index e29ecdd..a122083 100644 --- a/Doc/whatsnew/whatsnew23.tex +++ b/Doc/whatsnew/whatsnew23.tex @@ -318,7 +318,7 @@ Hisao and Martin von~L\"owis.} %====================================================================== -\section{PEP 273: Importing Modules from Zip Archives} +\section{PEP 273: Importing Modules from ZIP Archives} The new \module{zipimport} module adds support for importing modules from a ZIP-format archive. You don't need to import the diff --git a/Doc/whatsnew/whatsnew25.tex b/Doc/whatsnew/whatsnew25.tex index 750162f..3006624 100644 --- a/Doc/whatsnew/whatsnew25.tex +++ b/Doc/whatsnew/whatsnew25.tex @@ -2,13 +2,11 @@ \usepackage{distutils} % $Id$ -% The easy_install stuff -% Describe the pkgutil module % Fix XXX comments % Count up the patches and bugs \title{What's New in Python 2.5} -\release{0.1} +\release{0.2} \author{A.M. Kuchling} \authoraddress{\email{amk@amk.ca}} @@ -34,32 +32,6 @@ rationale, refer to the PEP for a particular new feature. %====================================================================== -\section{PEP 243: Uploading Modules to PyPI\label{pep-243}} - -PEP 243 describes an HTTP-based protocol for submitting software -packages to a central archive. The Python package index at -\url{http://cheeseshop.python.org} now supports package uploads, and -the new \command{upload} Distutils command will upload a package to the -repository. - -Before a package can be uploaded, you must be able to build a -distribution using the \command{sdist} Distutils command. Once that -works, you can run \code{python setup.py upload} to add your package -to the PyPI archive. Optionally you can GPG-sign the package by -supplying the \longprogramopt{sign} and -\longprogramopt{identity} options. - -\begin{seealso} - -\seepep{243}{Module Repository Upload Mechanism}{PEP written by -Sean Reifschneider; implemented by Martin von~L\"owis -and Richard Jones. Note that the PEP doesn't exactly -describe what's implemented in PyPI.} - -\end{seealso} - - -%====================================================================== \section{PEP 308: Conditional Expressions\label{pep-308}} For a long time, people have been requesting a way to write @@ -236,6 +208,20 @@ setup(name='PyPackage', % VERSION), ) \end{verbatim} + +Another new enhancement to the Python package index at +\url{http://cheeseshop.python.org} is storing source and binary +archives for a package. The new \command{upload} Distutils command +will upload a package to the repository. + +Before a package can be uploaded, you must be able to build a +distribution using the \command{sdist} Distutils command. Once that +works, you can run \code{python setup.py upload} to add your package +to the PyPI archive. Optionally you can GPG-sign the package by +supplying the \longprogramopt{sign} and +\longprogramopt{identity} options. + +Package uploading was implemented by Martin von~L\"owis and Richard Jones. \begin{seealso} @@ -394,13 +380,17 @@ finally: \end{verbatim} The code in \var{block-1} is executed. If the code raises an -exception, the handlers are tried in order: \var{handler-1}, -\var{handler-2}, ... If no exception is raised, the \var{else-block} -is executed. No matter what happened previously, the -\var{final-block} is executed once the code block is complete and any -raised exceptions handled. Even if there's an error in an exception -handler or the \var{else-block} and a new exception is raised, the -\var{final-block} is still executed. +exception, the various \keyword{except} blocks are tested: if the +exception is of class \class{Exception1}, \var{handler-1} is executed; +otherwise if it's of class \class{Exception2}, \var{handler-2} is +executed, and so forth. If no exception is raised, the +\var{else-block} is executed. + +No matter what happened previously, the \var{final-block} is executed +once the code block is complete and any raised exceptions handled. +Even if there's an error in an exception handler or the +\var{else-block} and a new exception is raised, the +code in the \var{final-block} is still run. \begin{seealso} @@ -415,7 +405,7 @@ implementation by Thomas Lee.} Python 2.5 adds a simple way to pass values \emph{into} a generator. As introduced in Python 2.3, generators only produce output; once a -generator's code is invoked to create an iterator, there's no way to +generator's code was invoked to create an iterator, there was no way to pass any new information into the function when its execution is resumed. Sometimes the ability to pass in some information would be useful. Hackish solutions to this include making the generator's code @@ -522,9 +512,9 @@ generators: \exception{GeneratorExit} or \exception{StopIteration}; catching the exception and doing anything else is illegal and will trigger a \exception{RuntimeError}. \method{close()} will also be called by - Python's garbage collection when the generator is garbage-collected. + Python's garbage collector when the generator is garbage-collected. - If you need to run cleanup code in case of a \exception{GeneratorExit}, + If you need to run cleanup code when a \exception{GeneratorExit} occurs, I suggest using a \code{try: ... finally:} suite instead of catching \exception{GeneratorExit}. @@ -535,8 +525,8 @@ one-way producers of information into both producers and consumers. Generators also become \emph{coroutines}, a more generalized form of subroutines. Subroutines are entered at one point and exited at -another point (the top of the function, and a \keyword{return -statement}), but coroutines can be entered, exited, and resumed at +another point (the top of the function, and a \keyword{return} +statement), but coroutines can be entered, exited, and resumed at many different points (the \keyword{yield} statements). We'll have to figure out patterns for using coroutines effectively in Python. @@ -579,14 +569,12 @@ Sugalski.} %====================================================================== \section{PEP 343: The 'with' statement\label{pep-343}} -The '\keyword{with}' statement allows a clearer version of code that -uses \code{try...finally} blocks to ensure that clean-up code is -executed. - -In this section, I'll discuss the statement as it will commonly be -used. In the next section, I'll examine the implementation details -and show how to write objects called ``context managers'' and -``contexts'' for use with this statement. +The '\keyword{with}' statement clarifies code that previously would +use \code{try...finally} blocks to ensure that clean-up code is +executed. In this section, I'll discuss the statement as it will +commonly be used. In the next section, I'll examine the +implementation details and show how to write objects for use with this +statement. The '\keyword{with}' statement is a new control-flow structure whose basic structure is: @@ -596,13 +584,13 @@ with expression [as variable]: with-block \end{verbatim} -The expression is evaluated, and it should result in a type of object -that's called a context manager. The context manager can return a +The expression is evaluated, and it should result in an object that +supports the context management protocol. This object may return a value that can optionally be bound to the name \var{variable}. (Note -carefully: \var{variable} is \emph{not} assigned the result of -\var{expression}.) One method of the context manager is run before -\var{with-block} is executed, and another method is run after the -block is done, even if the block raised an exception. +carefully that \var{variable} is \emph{not} assigned the result of +\var{expression}.) The object can then run set-up code +before \var{with-block} is executed and some clean-up code +is executed after the block is done, even if the block raised an exception. To enable the statement in Python 2.5, you need to add the following directive to your module: @@ -613,7 +601,8 @@ from __future__ import with_statement The statement will always be enabled in Python 2.6. -Some standard Python objects can now behave as context managers. File +Some standard Python objects now support the context management +protocol and can be used with the '\keyword{with}' statement. File objects are one example: \begin{verbatim} @@ -637,12 +626,12 @@ with lock: ... \end{verbatim} -The lock is acquired before the block is executed, and always released once +The lock is acquired before the block is executed and always released once the block is complete. The \module{decimal} module's contexts, which encapsulate the desired -precision and rounding characteristics for computations, can also be -used as context managers. +precision and rounding characteristics for computations, provide a +\method{context_manager()} method for getting a context manager: \begin{verbatim} import decimal @@ -651,7 +640,8 @@ import decimal v1 = decimal.Decimal('578') print v1.sqrt() -with decimal.Context(prec=16): +ctx = decimal.Context(prec=16) +with ctx.context_manager(): # All code in this block uses a precision of 16 digits. # The original context is restored on exiting the block. print v1.sqrt() @@ -660,47 +650,45 @@ with decimal.Context(prec=16): \subsection{Writing Context Managers\label{context-managers}} Under the hood, the '\keyword{with}' statement is fairly complicated. -Most people will only use '\keyword{with}' in company with -existing objects that are documented to work as context managers, and -don't need to know these details, so you can skip the following section if -you like. Authors of new context managers will need to understand the -details of the underlying implementation. +Most people will only use '\keyword{with}' in company with existing +objects and don't need to know these details, so you can skip the rest +of this section if you like. Authors of new objects will need to +understand the details of the underlying implementation and should +keep reading. A high-level explanation of the context management protocol is: \begin{itemize} -\item The expression is evaluated and should result in an object -that's a context manager, meaning that it has a -\method{__context__()} method. -\item This object's \method{__context__()} method is called, and must -return a context object. +\item The expression is evaluated and should result in an object +called a ``context manager''. The context manager must have +\method{__enter__()} and \method{__exit__()} methods. -\item The context's \method{__enter__()} method is called. -The value returned is assigned to \var{VAR}. If no \code{'as \var{VAR}'} -clause is present, the value is simply discarded. +\item The context manager's \method{__enter__()} method is called. The value +returned is assigned to \var{VAR}. If no \code{'as \var{VAR}'} clause +is present, the value is simply discarded. \item The code in \var{BLOCK} is executed. -\item If \var{BLOCK} raises an exception, the context object's +\item If \var{BLOCK} raises an exception, the \method{__exit__(\var{type}, \var{value}, \var{traceback})} is called -with the exception's information, the same values returned by -\function{sys.exc_info()}. The method's return value -controls whether the exception is re-raised: any false value -re-raises the exception, and \code{True} will result in suppressing it. -You'll only rarely want to suppress the exception; the -author of the code containing the '\keyword{with}' statement will -never realize anything went wrong. +with the exception details, the same values returned by +\function{sys.exc_info()}. The method's return value controls whether +the exception is re-raised: any false value re-raises the exception, +and \code{True} will result in suppressing it. You'll only rarely +want to suppress the exception, because if you do +the author of the code containing the +'\keyword{with}' statement will never realize anything went wrong. \item If \var{BLOCK} didn't raise an exception, -the context object's \method{__exit__()} is still called, +the \method{__exit__()} method is still called, but \var{type}, \var{value}, and \var{traceback} are all \code{None}. \end{itemize} Let's think through an example. I won't present detailed code but -will only sketch the necessary code. The example will be writing a -context manager for a database that supports transactions. +will only sketch the methods necessary for a database that supports +transactions. (For people unfamiliar with database terminology: a set of changes to the database are grouped into a transaction. Transactions can be @@ -721,22 +709,13 @@ with db_connection as cursor: # ... more operations ... \end{verbatim} -The transaction should either be committed if the code in the block -runs flawlessly, or rolled back if there's an exception. - -First, the \class{DatabaseConnection} needs a \method{__context__()} -method. Sometimes an object can be its own context manager and can -simply return \code{self}; the \module{threading} module's lock objects -can do this. For our database example, though, we need to -create a new object; I'll call this class \class{DatabaseContext}. -Our \method{__context__()} must therefore look like this: +The transaction should be committed if the code in the block +runs flawlessly or rolled back if there's an exception. +Here's the basic interface +for \class{DatabaseConnection} that I'll assume: \begin{verbatim} class DatabaseConnection: - ... - def __context__ (self): - return DatabaseContext(self) - # Database interface def cursor (self): "Returns a cursor object and starts a new transaction" @@ -746,29 +725,18 @@ class DatabaseConnection: "Rolls back current transaction" \end{verbatim} -The context needs the connection object so that the connection -object's \method{commit()} or \method{rollback()} methods can be -called: +The \method {__enter__()} method is pretty easy, having only to start +a new transaction. For this application the resulting cursor object +would be a useful result, so the method will return it. The user can +then add \code{as cursor} to their '\keyword{with}' statement to bind +the cursor to a variable name. \begin{verbatim} -class DatabaseContext: - def __init__ (self, connection): - self.connection = connection -\end{verbatim} - -The \method {__enter__()} method is pretty easy, having only -to start a new transaction. In this example, -the resulting cursor object would be a useful result, -so the method will return it. The user can -then add \code{as cursor} to their '\keyword{with}' statement -to bind the cursor to a variable name. - -\begin{verbatim} -class DatabaseContext: +class DatabaseConnection: ... def __enter__ (self): # Code to start a new transaction - cursor = self.connection.cursor() + cursor = self.cursor() return cursor \end{verbatim} @@ -776,21 +744,23 @@ The \method{__exit__()} method is the most complicated because it's where most of the work has to be done. The method has to check if an exception occurred. If there was no exception, the transaction is committed. The transaction is rolled back if there was an exception. -Here the code will just fall off the end of the function, returning -the default value of \code{None}. \code{None} is false, so the exception -will be re-raised automatically. If you wished, you could be more explicit -and add a \keyword{return} at the marked location. + +In the code below, execution will just fall off the end of the +function, returning the default value of \code{None}. \code{None} is +false, so the exception will be re-raised automatically. If you +wished, you could be more explicit and add a \keyword{return} +statement at the marked location. \begin{verbatim} -class DatabaseContext: +class DatabaseConnection: ... def __exit__ (self, type, value, tb): if tb is None: # No exception, so commit - self.connection.commit() + self.commit() else: # Exception occurred, so rollback. - self.connection.rollback() + self.rollback() # return False \end{verbatim} @@ -798,25 +768,26 @@ class DatabaseContext: \subsection{The contextlib module\label{module-contextlib}} The new \module{contextlib} module provides some functions and a -decorator that are useful for writing context managers. - -The decorator is called \function{contextmanager}, and lets you write -a simple context manager as a generator. The generator should yield -exactly one value. The code up to the \keyword{yield} will be -executed as the \method{__enter__()} method, and the value yielded -will be the method's return value that will get bound to the variable -in the '\keyword{with}' statement's \keyword{as} clause, if any. The -code after the \keyword{yield} will be executed in the -\method{__exit__()} method. Any exception raised in the block -will be raised by the \keyword{yield} statement. +decorator that are useful for writing objects for use with the +'\keyword{with}' statement. + +The decorator is called \function{contextfactory}, and lets you write +a single generator function instead of defining a new class. The generator +should yield exactly one value. The code up to the \keyword{yield} +will be executed as the \method{__enter__()} method, and the value +yielded will be the method's return value that will get bound to the +variable in the '\keyword{with}' statement's \keyword{as} clause, if +any. The code after the \keyword{yield} will be executed in the +\method{__exit__()} method. Any exception raised in the block will be +raised by the \keyword{yield} statement. Our database example from the previous section could be written using this decorator as: \begin{verbatim} -from contextlib import contextmanager +from contextlib import contextfactory -@contextmanager +@contextfactory def db_transaction (connection): cursor = connection.cursor() try: @@ -832,29 +803,11 @@ with db_transaction(db) as cursor: ... \end{verbatim} -You can also use this decorator to write the \method{__context__()} method -for a class without creating a new class for the context: - -\begin{verbatim} -class DatabaseConnection: - - @contextmanager - def __context__ (self): - cursor = self.cursor() - try: - yield cursor - except: - self.rollback() - raise - else: - self.commit() -\end{verbatim} - - -There's a \function{nested(\var{mgr1}, \var{mgr2}, ...)} manager that -combines a number of context managers so you don't need to write -nested '\keyword{with}' statements. This example statement does two -things, starting a database transaction and acquiring a thread lock: +The \module{contextlib} module also has a \function{nested(\var{mgr1}, +\var{mgr2}, ...)} function that combines a number of context managers so you +don't need to write nested '\keyword{with}' statements. In this +example, the single '\keyword{with}' statement both starts a database +transaction and acquires a thread lock: \begin{verbatim} lock = threading.Lock() @@ -862,7 +815,7 @@ with nested (db_transaction(db), lock) as (cursor, locked): ... \end{verbatim} -Finally, the \function{closing(\var{object})} context manager +Finally, the \function{closing(\var{object})} function returns \var{object} so that it can be bound to a variable, and calls \code{\var{object}.close()} at the end of the block. @@ -880,8 +833,7 @@ with closing(urllib.urlopen('http://www.yahoo.com')) as f: \seepep{343}{The ``with'' statement}{PEP written by Guido van~Rossum and Nick Coghlan; implemented by Mike Bland, Guido van~Rossum, and Neal Norwitz. The PEP shows the code generated for a '\keyword{with}' -statement, which can be helpful in learning how context managers -work.} +statement, which can be helpful in learning how the statement works.} \seeurl{../lib/module-contextlib.html}{The documentation for the \module{contextlib} module.} @@ -1064,7 +1016,7 @@ and implemented by Travis Oliphant.} %====================================================================== -\section{Other Language Changes} +\section{Other Language Changes\label{other-lang}} Here are all of the changes that Python 2.5 makes to the core Python language. @@ -1090,6 +1042,36 @@ print d[1], d[2] # Prints 1, 2 print d[3], d[4] # Prints 0, 0 \end{verbatim} +\item Both 8-bit and Unicode strings have new \method{partition(sep)} +and \method{rpartition(sep)} methods that simplify a common use case. +The \method{find(S)} method is often used to get an index which is +then used to slice the string and obtain the pieces that are before +and after the separator. + +\method{partition(sep)} condenses this +pattern into a single method call that returns a 3-tuple containing +the substring before the separator, the separator itself, and the +substring after the separator. If the separator isn't found, the +first element of the tuple is the entire string and the other two +elements are empty. \method{rpartition(sep)} also returns a 3-tuple +but starts searching from the end of the string; the \samp{r} stands +for 'reverse'. + +Some examples: + +\begin{verbatim} +>>> ('http://www.python.org').partition('://') +('http', '://', 'www.python.org') +>>> (u'Subject: a quick question').partition(':') +(u'Subject', u':', u' a quick question') +>>> ('file:/usr/share/doc/index.html').partition('://') +('file:/usr/share/doc/index.html', '', '') +>>> 'www.python.org'.rpartition('.') +('www.python', '.', 'org') +\end{verbatim} + +(Implemented by Fredrik Lundh following a suggestion by Raymond Hettinger.) + \item The \function{min()} and \function{max()} built-in functions gained a \code{key} keyword parameter analogous to the \code{key} argument for \method{sort()}. This parameter supplies a function that @@ -1127,6 +1109,14 @@ a line like this near the top of the source file: # -*- coding: latin1 -*- \end{verbatim} +\item One error that Python programmers sometimes make is forgetting +to include an \file{__init__.py} module in a package directory. +Debugging this mistake can be confusing, and usually requires running +Python with the \programopt{-v} switch to log all the paths searched. +In Python 2.5, a new \exception{ImportWarning} warning is raised when +an import would have picked up a directory as a package but no +\file{__init__.py} was found. (Implemented by Thomas Wouters.) + \item The list of base classes in a class definition can now be empty. As an example, this is now legal: @@ -1140,7 +1130,7 @@ class C(): %====================================================================== -\subsection{Interactive Interpreter Changes} +\subsection{Interactive Interpreter Changes\label{interactive}} In the interactive interpreter, \code{quit} and \code{exit} have long been strings so that new users get a somewhat helpful message @@ -1158,7 +1148,14 @@ interpreter as they expect. (Implemented by Georg Brandl.) %====================================================================== -\subsection{Optimizations} +\subsection{Optimizations\label{opts}} + +Several of the optimizations were developed at the NeedForSpeed +sprint, an event held in Reykjavik, Iceland, from May 21--28 2006. +The sprint focused on speed enhancements to the CPython implementation +and was funded by EWT LLC with local support from CCP Games. Those +optimizations added at this sprint are specially marked in the +following list. \begin{itemize} @@ -1169,15 +1166,53 @@ In 2.5 the internal data structure has been customized for implementing sets, and as a result sets will use a third less memory and are somewhat faster. (Implemented by Raymond Hettinger.) -\item The performance of some Unicode operations, such as -character map decoding, has been improved. +\item The speed of some Unicode operations, such as +finding substrings, string splitting, and character map decoding, has +been improved. (Substring search and splitting improvements were +added by Fredrik Lundh and Andrew Dalke at the NeedForSpeed +sprint. Character map decoding was improved by Walter D\"orwald.) % Patch 1313939 +\item The \function{long(\var{str}, \var{base})} function is now +faster on long digit strings because fewer intermediate results are +calculated. The peak is for strings of around 800--1000 digits where +the function is 6 times faster. +(Contributed by Alan McIntyre and committed at the NeedForSpeed sprint.) +% Patch 1442927 + +\item The \module{struct} module now compiles structure format +strings into an internal representation and caches this +representation, yielding a 20\% speedup. (Contributed by Bob Ippolito +at the NeedForSpeed sprint.) + \item The code generator's peephole optimizer now performs simple constant folding in expressions. If you write something like \code{a = 2+3}, the code generator will do the arithmetic and produce code corresponding to \code{a = 5}. +\item Function calls are now faster because code objects now keep +the most recently finished frame (a ``zombie frame'') in an internal +field of the code object, reusing it the next time the code object is +invoked. (Original patch by Michael Hudson, modified by Armin Rigo +and Richard Jones; committed at the NeedForSpeed sprint.) +% Patch 876206 + +Frame objects are also slightly smaller, which may improve cache locality +and reduce memory usage a bit. (Contributed by Neal Norwitz.) +% Patch 1337051 + +\item Python's built-in exceptions are now new-style classes, a change +that speeds up instantiation considerably. Exception handling in +Python 2.5 is therefore about 30\% faster than in 2.4. +(Contributed by Richard Jones, Georg Brandl and Sean Reifschneider at +the NeedForSpeed sprint.) + +\item Importing now caches the paths tried, recording whether +they exist or not so that the interpreter makes fewer +\cfunction{open()} and \cfunction{stat()} calls on startup. +(Contributed by Martin von~L\"owis and Georg Brandl.) +% Patch 921466 + \end{itemize} The net result of the 2.5 optimizations is that Python 2.5 runs the @@ -1185,7 +1220,7 @@ pystone benchmark around XXX\% faster than Python 2.4. %====================================================================== -\section{New, Improved, and Removed Modules} +\section{New, Improved, and Removed Modules\label{modules}} The standard library received many enhancements and bug fixes in Python 2.5. Here's a partial list of the most notable changes, sorted @@ -1255,7 +1290,6 @@ raising \exception{ValueError} if the value isn't found. \item New module: The \module{contextlib} module contains helper functions for use with the new '\keyword{with}' statement. See section~\ref{module-contextlib} for more about this module. -(Contributed by Phillip J. Eby.) \item New module: The \module{cProfile} module is a C implementation of the existing \module{profile} module that has much lower overhead. @@ -1266,8 +1300,8 @@ which is also written in C but doesn't match the \module{profile} module's interface, will continue to be maintained in future versions of Python. (Contributed by Armin Rigo.) -Also, the \module{pstats} module used to analyze the data measured by -the profiler now supports directing the output to any file stream +Also, the \module{pstats} module for analyzing the data measured by +the profiler now supports directing the output to any file object by supplying a \var{stream} argument to the \class{Stats} constructor. (Contributed by Skip Montanaro.) @@ -1295,6 +1329,11 @@ ts = datetime.strptime('10:13:15 2006-03-07', '%H:%M:%S %Y-%m-%d') \end{verbatim} +\item The \module{doctest} module gained a \code{SKIP} option that +keeps an example from being executed at all. This is intended for +code snippets that are usage examples intended for the reader and +aren't actually test cases. + \item The \module{fileinput} module was made more flexible. Unicode filenames are now supported, and a \var{mode} parameter that defaults to \code{"r"} was added to the @@ -1344,6 +1383,35 @@ itertools.islice(iterable, s.start, s.stop, s.step) (Contributed by Raymond Hettinger.) +\item The \module{mailbox} module underwent a massive rewrite to add +the capability to modify mailboxes in addition to reading them. A new +set of classes that include \class{mbox}, \class{MH}, and +\class{Maildir} are used to read mailboxes, and have an +\method{add(\var{message})} method to add messages, +\method{remove(\var{key})} to remove messages, and +\method{lock()}/\method{unlock()} to lock/unlock the mailbox. The +following example converts a maildir-format mailbox into an mbox-format one: + +\begin{verbatim} +import mailbox + +# 'factory=None' uses email.Message.Message as the class representing +# individual messages. +src = mailbox.Maildir('maildir', factory=None) +dest = mailbox.mbox('/tmp/mbox') + +for msg in src: + dest.add(msg) +\end{verbatim} + +(Contributed by Gregory K. Johnson. Funding was provided by Google's +2005 Summer of Code.) + +\item New module: the \module{msilib} module allows creating +Microsoft Installer \file{.msi} files and CAB files. Some support +for reading the \file{.msi} database is also included. +(Contributed by Martin von~L\"owis.) + \item The \module{nis} module now supports accessing domains other than the system default domain by supplying a \var{domain} argument to the \function{nis.match()} and \function{nis.maps()} functions. @@ -1358,6 +1426,11 @@ this new feature with the \method{sort()} method's \code{key} parameter lets you easily sort lists using multiple fields. (Contributed by Raymond Hettinger.) +\item The \module{optparse} module was updated to version 1.5.1 of the +Optik library. The \class{OptionParser} class gained an +\member{epilog} attribute, a string that will be printed after the +help message, and a \method{destroy()} method to break reference +cycles created by the object. (Contributed by Greg Ward.) \item The \module{os} module underwent several changes. The \member{stat_float_times} variable now defaults to true, meaning that @@ -1389,12 +1462,35 @@ The \member{st_flags} member is also available, if the platform supports it. (Contributed by Antti Louko and Diego Petten\`o.) % (Patch 1180695, 1212117) +\item The Python debugger provided by the \module{pdb} module +can now store lists of commands to execute when a breakpoint is +reached and execution stops. Once breakpoint \#1 has been created, +enter \samp{commands 1} and enter a series of commands to be executed, +finishing the list with \samp{end}. The command list can include +commands that resume execution, such as \samp{continue} or +\samp{next}. (Contributed by Gr\'egoire Dooms.) +% Patch 790710 + \item The \module{pickle} and \module{cPickle} modules no longer accept a return value of \code{None} from the \method{__reduce__()} method; the method must return a tuple of arguments instead. The ability to return \code{None} was deprecated in Python 2.4, so this completes the removal of the feature. +\item The \module{pkgutil} module, containing various utility +functions for finding packages, was enhanced to support PEP 302's +import hooks and now also works for packages stored in ZIP-format archives. +(Contributed by Phillip J. Eby.) + +\item The pybench benchmark suite by Marc-Andr\'e~Lemburg is now +included in the \file{Tools/pybench} directory. The pybench suite is +an improvement on the commonly used \file{pystone.py} program because +pybench provides a more detailed measurement of the interpreter's +speed. It times particular operations such as function calls, +tuple slicing, method lookups, and numeric operations, instead of +performing many different operations and reducing the result to a +single number as \file{pystone.py} does. + \item The old \module{regex} and \module{regsub} modules, which have been deprecated ever since Python 2.0, have finally been deleted. Other deleted modules: \module{statcache}, \module{tzparse}, @@ -1406,6 +1502,12 @@ which includes ancient modules such as \module{dircmp} and \code{sys.path}, so unless your programs explicitly added the directory to \code{sys.path}, this removal shouldn't affect your code. +\item The \module{rlcompleter} module is no longer +dependent on importing the \module{readline} module and +therefore now works on non-{\UNIX} platforms. +(Patch from Robert Kiendl.) +% Patch #1472854 + \item The \module{socket} module now supports \constant{AF_NETLINK} sockets on Linux, thanks to a patch from Philippe Biondi. Netlink sockets are a Linux-specific mechanism for communications @@ -1414,20 +1516,52 @@ article about them is at \url{http://www.linuxjournal.com/article/7356}. In Python code, netlink addresses are represented as a tuple of 2 integers, \code{(\var{pid}, \var{group_mask})}. -Socket objects also gained accessor methods \method{getfamily()}, -\method{gettype()}, and \method{getproto()} methods to retrieve the -family, type, and protocol values for the socket. +Two new methods on socket objects, \method{recv_buf(\var{buffer})} and +\method{recvfrom_buf(\var{buffer})}, store the received data in an object +that supports the buffer protocol instead of returning the data as a +string. This means you can put the data directly into an array or a +memory-mapped file. + +Socket objects also gained \method{getfamily()}, \method{gettype()}, +and \method{getproto()} accessor methods to retrieve the family, type, +and protocol values for the socket. \item New module: the \module{spwd} module provides functions for accessing the shadow password database on systems that support shadow passwords. +\item The \module{struct} is now faster because it +compiles format strings into \class{Struct} objects +with \method{pack()} and \method{unpack()} methods. This is similar +to how the \module{re} module lets you create compiled regular +expression objects. You can still use the module-level +\function{pack()} and \function{unpack()} functions; they'll create +\class{Struct} objects and cache them. Or you can use +\class{Struct} instances directly: + +\begin{verbatim} +s = struct.Struct('ih3s') + +data = s.pack(1972, 187, 'abc') +year, number, name = s.unpack(data) +\end{verbatim} + +You can also pack and unpack data to and from buffer objects directly +using the \method{pack_to(\var{buffer}, \var{offset}, \var{v1}, +\var{v2}, ...)} and \method{unpack_from(\var{buffer}, \var{offset})} +methods. This lets you store data directly into an array or a +memory-mapped file. + +(\class{Struct} objects were implemented by Bob Ippolito at the +NeedForSpeed sprint. Support for buffer objects was added by Martin +Blais, also at the NeedForSpeed sprint.) + \item The Python developers switched from CVS to Subversion during the 2.5 -development process. Information about the exact build version is -available as the \code{sys.subversion} variable, a 3-tuple -of \code{(\var{interpreter-name}, \var{branch-name}, \var{revision-range})}. -For example, at the time of writing -my copy of 2.5 was reporting \code{('CPython', 'trunk', '45313:45315')}. +development process. Information about the exact build version is +available as the \code{sys.subversion} variable, a 3-tuple of +\code{(\var{interpreter-name}, \var{branch-name}, +\var{revision-range})}. For example, at the time of writing my copy +of 2.5 was reporting \code{('CPython', 'trunk', '45313:45315')}. This information is also available to C extensions via the \cfunction{Py_GetBuildInfo()} function that returns a @@ -1449,7 +1583,7 @@ using the mode \code{'r|*'}. \item The \module{unicodedata} module has been updated to use version 4.1.0 of the Unicode character database. Version 3.2.0 is required by some specifications, so it's still available as -\member{unicodedata.db_3_2_0}. +\member{unicodedata.ucd_3_2_0}. \item The \module{webbrowser} module received a number of enhancements. @@ -1474,13 +1608,19 @@ Brandl.) (Contributed by Skip Montanaro.) % Patch 1120353 +\item The \module{zlib} module's \class{Compress} and \class{Decompress} +objects now support a \method{copy()} method that makes a copy of the +object's internal state and returns a new +\class{Compress} or \class{Decompress} object. +(Contributed by Chris AtLee.) +% Patch 1435422 \end{itemize} %====================================================================== -\subsection{The ctypes package} +\subsection{The ctypes package\label{module-ctypes}} The \module{ctypes} package, written by Thomas Heller, has been added to the standard library. \module{ctypes} lets you call arbitrary functions @@ -1562,10 +1702,10 @@ of extension modules, now that \module{ctypes} is included with core Python. %====================================================================== -\subsection{The ElementTree package} +\subsection{The ElementTree package\label{module-etree}} A subset of Fredrik Lundh's ElementTree library for processing XML has -been added to the standard library as \module{xmlcore.etree}. The +been added to the standard library as \module{xml.etree}. The available modules are \module{ElementTree}, \module{ElementPath}, and \module{ElementInclude} from ElementTree 1.2.6. @@ -1587,7 +1727,7 @@ takes either a string (assumed to contain a filename) or a file-like object and returns an \class{ElementTree} instance: \begin{verbatim} -from xmlcore.etree import ElementTree as ET +from xml.etree import ElementTree as ET tree = ET.parse('ex-1.xml') @@ -1605,7 +1745,7 @@ This function provides a tidy way to incorporate XML fragments, approaching the convenience of an XML literal: \begin{verbatim} -svg = et.XML(""" +svg = ET.XML(""" """) svg.set('height', '320px') svg.append(elem1) @@ -1619,7 +1759,7 @@ values, and list-like operations are used to access child nodes. \lineii{elem[n]}{Returns n'th child element.} \lineii{elem[m:n]}{Returns list of m'th through n'th child elements.} \lineii{len(elem)}{Returns number of child elements.} - \lineii{elem.getchildren()}{Returns list of child elements.} + \lineii{list(elem)}{Returns list of child elements.} \lineii{elem.append(elem2)}{Adds \var{elem2} as a child.} \lineii{elem.insert(index, elem2)}{Inserts \var{elem2} at the specified location.} \lineii{del elem[n]}{Deletes n'th child element.} @@ -1651,14 +1791,15 @@ tree.write('output.xml') # Encoding is UTF-8 f = open('output.xml', 'w') -tree.write(f, 'utf-8') +tree.write(f, encoding='utf-8') \end{verbatim} -(Caution: the default encoding used for output is ASCII, which isn't -very useful for general XML work, raising an exception if there are -any characters with values greater than 127. You should always -specify a different encoding such as UTF-8 that can handle any Unicode -character.) +(Caution: the default encoding used for output is ASCII. For general +XML work, where an element's name may contain arbitrary Unicode +characters, ASCII isn't a very useful encoding because it will raise +an exception if an element's name contains any characters with values +greater than 127. Therefore, it's best to specify a different +encoding such as UTF-8 that can handle any Unicode character.) This section is only a partial description of the ElementTree interfaces. Please read the package's official documentation for more details. @@ -1673,7 +1814,7 @@ Please read the package's official documentation for more details. %====================================================================== -\subsection{The hashlib package} +\subsection{The hashlib package\label{module-hashlib}} A new \module{hashlib} module, written by Gregory P. Smith, has been added to replace the @@ -1721,7 +1862,7 @@ and \method{copy()} returns a new hashing object with the same digest state. %====================================================================== -\subsection{The sqlite3 package} +\subsection{The sqlite3 package\label{module-sqlite}} The pysqlite module (\url{http://www.pysqlite.org}), a wrapper for the SQLite embedded database, has been added to the standard library under @@ -1786,7 +1927,7 @@ c.execute("... where symbol = '%s'" % symbol) # Do this instead t = (symbol,) -c.execute('select * from stocks where symbol=?', ('IBM',)) +c.execute('select * from stocks where symbol=?', t) # Larger example for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00), @@ -1835,7 +1976,7 @@ Marc-Andr\'e Lemburg.} % ====================================================================== -\section{Build and C API Changes} +\section{Build and C API Changes\label{build-api}} Changes to Python's build process and to the C API include: @@ -1901,6 +2042,22 @@ string of build information like this: \code{"trunk:45355:45356M, Apr 13 2006, 07:42:19"}. (Contributed by Barry Warsaw.) +\item Two new macros can be used to indicate C functions that are +local to the current file so that a faster calling convention can be +used. \cfunction{Py_LOCAL(\var{type})} declares the function as +returning a value of the specified \var{type} and uses a fast-calling +qualifier. \cfunction{Py_LOCAL_INLINE(\var{type})} does the same thing +and also requests the function be inlined. If +\cfunction{PY_LOCAL_AGGRESSIVE} is defined before \file{python.h} is +included, a set of more aggressive optimizations are enabled for the +module; you should benchmark the results to find out if these +optimizations actually make the code faster. (Contributed by Fredrik +Lundh at the NeedForSpeed sprint.) + +\item \cfunction{PyErr_NewException(\var{name}, \var{base}, +\var{dict})} can now accept a tuple of base classes as its \var{base} +argument. (Contributed by Georg Brandl.) + \item The CPython interpreter is still written in C, but the code can now be compiled with a {\Cpp} compiler without errors. (Implemented by Anthony Baxter, Martin von~L\"owis, Skip Montanaro.) @@ -1913,7 +2070,7 @@ error checking. %====================================================================== -\subsection{Port-Specific Changes} +\subsection{Port-Specific Changes\label{ports}} \begin{itemize} @@ -1921,6 +2078,11 @@ error checking. now uses the \cfunction{dlopen()} function instead of MacOS-specific functions. +\item MacOS X: a \longprogramopt{enable-universalsdk} switch was added +to the \program{configure} script that compiles the interpreter as a +universal binary able to run on both PowerPC and Intel processors. +(Contributed by Ronald Oussoren.) + \item Windows: \file{.dll} is no longer supported as a filename extension for extension modules. \file{.pyd} is now the only filename extension that will be searched for. @@ -1977,7 +2139,7 @@ carefully test your C extension modules with Python 2.5. %====================================================================== -\section{Porting to Python 2.5} +\section{Porting to Python 2.5\label{porting}} This section lists previously described changes that may require changes to your code: @@ -2023,7 +2185,7 @@ freed with the corresponding family's \cfunction{*_Free()} function. The author would like to thank the following people for offering suggestions, corrections and assistance with various drafts of this -article: Phillip J. Eby, Kent Johnson, Martin von~L\"owis, Gustavo -Niemeyer, Mike Rovner, Thomas Wouters. +article: Phillip J. Eby, Kent Johnson, Martin von~L\"owis, Fredrik Lundh, +Gustavo Niemeyer, James Pryor, Mike Rovner, Scott Weikart, Thomas Wouters. \end{document} diff --git a/Grammar/Grammar b/Grammar/Grammar index a613de6..7a7f6bc 100644 --- a/Grammar/Grammar +++ b/Grammar/Grammar @@ -50,7 +50,7 @@ yield_stmt: yield_expr raise_stmt: 'raise' [test [',' test [',' test]]] import_stmt: import_name | import_from import_name: 'import' dotted_as_names -import_from: ('from' ('.'* dotted_name | '.') +import_from: ('from' ('.'* dotted_name | '.'+) 'import' ('*' | '(' import_as_names ')' | import_as_names)) import_as_name: NAME ['as' NAME] dotted_as_name: dotted_name ['as' NAME] diff --git a/Include/Python.h b/Include/Python.h index fffc688..ca16c64 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -35,7 +35,9 @@ #endif #include +#ifndef DONT_HAVE_ERRNO_H #include +#endif #include #ifdef HAVE_UNISTD_H #include diff --git a/Include/code.h b/Include/code.h index 23d9e17..432ec8e 100644 --- a/Include/code.h +++ b/Include/code.h @@ -24,6 +24,7 @@ typedef struct { PyObject *co_name; /* string (name, for reference) */ int co_firstlineno; /* first source line number */ PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ + void *co_zombieframe; /* for optimization only (see frameobject.c) */ } PyCodeObject; /* Masks for co_flags above */ diff --git a/Include/frameobject.h b/Include/frameobject.h index 7dc14e3..cce598b 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -26,7 +26,16 @@ typedef struct _frame { to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ + + /* If an exception is raised in this frame, the next three are used to + * record the exception info (if any) originally in the thread state. See + * comments before set_exc_info() -- it's not obvious. + * Invariant: if _type is NULL, then so are _value and _traceback. + * Desired invariant: all three are NULL, or all three are non-NULL. That + * one isn't currently true, but "should be". + */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; + PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ /* As of 2.3 f_lineno is only valid when tracing is active (i.e. when @@ -36,10 +45,6 @@ typedef struct _frame { in this scope */ int f_iblock; /* index in f_blockstack */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ - int f_nlocals; /* number of locals */ - int f_ncells; - int f_nfreevars; - int f_stacksize; /* size of value stack */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject; diff --git a/Include/longobject.h b/Include/longobject.h index 77544ef..eef4e9b 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -25,6 +25,7 @@ PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); PyAPI_FUNC(Py_ssize_t) _PyLong_AsSsize_t(PyObject *); PyAPI_FUNC(PyObject *) _PyLong_FromSize_t(size_t); PyAPI_FUNC(PyObject *) _PyLong_FromSsize_t(Py_ssize_t); +PyAPI_DATA(int) _PyLong_DigitValue[256]; /* _PyLong_AsScaledDouble returns a double x and an exponent e such that the true value is approximately equal to x * 2**(SHIFT*e). e is >= 0. diff --git a/Include/osdefs.h b/Include/osdefs.h index 8190a75..6937659 100644 --- a/Include/osdefs.h +++ b/Include/osdefs.h @@ -37,8 +37,12 @@ extern "C" { /* Max pathname length */ #ifndef MAXPATHLEN +#if defined(PATH_MAX) && PATH_MAX > 1024 +#define MAXPATHLEN PATH_MAX +#else #define MAXPATHLEN 1024 #endif +#endif /* Search path entry delimiter */ #ifndef DELIM diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 0e7718c..6006ac7 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -4,6 +4,72 @@ extern "C" { #endif +/* Error objects */ + +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; +} PyBaseExceptionObject; + +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; + PyObject *msg; + PyObject *filename; + PyObject *lineno; + PyObject *offset; + PyObject *text; + PyObject *print_file_and_line; +} PySyntaxErrorObject; + +#ifdef Py_USING_UNICODE +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; + PyObject *encoding; + PyObject *object; + PyObject *start; + PyObject *end; + PyObject *reason; +} PyUnicodeErrorObject; +#endif + +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; + PyObject *code; +} PySystemExitObject; + +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; + PyObject *myerrno; + PyObject *strerror; + PyObject *filename; +} PyEnvironmentErrorObject; + +#ifdef MS_WINDOWS +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *args; + PyObject *message; + PyObject *myerrno; + PyObject *strerror; + PyObject *filename; + PyObject *winerror; +} PyWindowsErrorObject; +#endif /* Error handling definitions */ @@ -97,10 +163,9 @@ PyAPI_DATA(PyObject *) PyExc_UserWarning; PyAPI_DATA(PyObject *) PyExc_DeprecationWarning; PyAPI_DATA(PyObject *) PyExc_PendingDeprecationWarning; PyAPI_DATA(PyObject *) PyExc_SyntaxWarning; -/* PyExc_OverflowWarning will go away for Python 2.5 */ -PyAPI_DATA(PyObject *) PyExc_OverflowWarning; PyAPI_DATA(PyObject *) PyExc_RuntimeWarning; PyAPI_DATA(PyObject *) PyExc_FutureWarning; +PyAPI_DATA(PyObject *) PyExc_ImportWarning; /* Convenience functions */ diff --git a/Include/pyport.h b/Include/pyport.h index 2bce415..74ce993 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -137,6 +137,43 @@ typedef Py_intptr_t Py_ssize_t; # endif #endif +/* Py_LOCAL can be used instead of static to get the fastest possible calling + * convention for functions that are local to a given module. + * + * Py_LOCAL_INLINE does the same thing, and also explicitly requests inlining, + * for platforms that support that. + * + * If PY_LOCAL_AGGRESSIVE is defined before python.h is included, more + * "aggressive" inlining/optimizaion is enabled for the entire module. This + * may lead to code bloat, and may slow things down for those reasons. It may + * also lead to errors, if the code relies on pointer aliasing. Use with + * care. + * + * NOTE: You can only use this for functions that are entirely local to a + * module; functions that are exported via method tables, callbacks, etc, + * should keep using static. + */ + +#undef USE_INLINE /* XXX - set via configure? */ + +#if defined(_MSC_VER) +#if defined(PY_LOCAL_AGGRESSIVE) +/* enable more aggressive optimization for visual studio */ +#pragma optimize("agtw", on) +#endif +/* ignore warnings if the compiler decides not to inline a function */ +#pragma warning(disable: 4710) +/* fastest possible local call under MSVC */ +#define Py_LOCAL(type) static type __fastcall +#define Py_LOCAL_INLINE(type) static __inline type __fastcall +#elif defined(USE_INLINE) +#define Py_LOCAL(type) static type +#define Py_LOCAL_INLINE(type) static inline type +#else +#define Py_LOCAL(type) static type +#define Py_LOCAL_INLINE(type) static type +#endif + #include #include /* Moved here from the math section, before extern "C" */ @@ -295,6 +332,15 @@ extern "C" { #define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X)) #endif +/* Py_IS_FINITE(X) + * Return 1 if float or double arg is neither infinite nor NAN, else 0. + * Some compilers (e.g. VisualStudio) have intrisics for this, so a special + * macro for this particular test is useful + */ +#ifndef Py_IS_FINITE +#define Py_IS_FINITE(X) (!Py_IS_INFINITY(X) && !Py_IS_NAN(X)) +#endif + /* HUGE_VAL is supposed to expand to a positive double infinity. Python * uses Py_HUGE_VAL instead because some platforms are broken in this * respect. We used to embed code in pyport.h to try to worm around that, @@ -685,4 +731,16 @@ typedef struct fd_set { #pragma error_messages (off,E_END_OF_LOOP_CODE_NOT_REACHED) #endif +/* + * Older Microsoft compilers don't support the C99 long long literal suffixes, + * so these will be defined in PC/pyconfig.h for those compilers. + */ +#ifndef Py_LL +#define Py_LL(x) x##LL +#endif + +#ifndef Py_ULL +#define Py_ULL(x) Py_LL(x##U) +#endif + #endif /* Py_PYPORT_H */ diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h index 9012257..0531aed 100644 --- a/Include/unicodeobject.h +++ b/Include/unicodeobject.h @@ -184,11 +184,13 @@ typedef PY_UNICODE_TYPE Py_UNICODE; # define PyUnicode_GetMax PyUnicodeUCS2_GetMax # define PyUnicode_GetSize PyUnicodeUCS2_GetSize # define PyUnicode_Join PyUnicodeUCS2_Join +# define PyUnicode_Partition PyUnicodeUCS2_Partition +# define PyUnicode_RPartition PyUnicodeUCS2_RPartition +# define PyUnicode_RSplit PyUnicodeUCS2_RSplit # define PyUnicode_Replace PyUnicodeUCS2_Replace # define PyUnicode_Resize PyUnicodeUCS2_Resize # define PyUnicode_SetDefaultEncoding PyUnicodeUCS2_SetDefaultEncoding # define PyUnicode_Split PyUnicodeUCS2_Split -# define PyUnicode_RSplit PyUnicodeUCS2_RSplit # define PyUnicode_Splitlines PyUnicodeUCS2_Splitlines # define PyUnicode_Tailmatch PyUnicodeUCS2_Tailmatch # define PyUnicode_Translate PyUnicodeUCS2_Translate @@ -259,6 +261,9 @@ typedef PY_UNICODE_TYPE Py_UNICODE; # define PyUnicode_GetMax PyUnicodeUCS4_GetMax # define PyUnicode_GetSize PyUnicodeUCS4_GetSize # define PyUnicode_Join PyUnicodeUCS4_Join +# define PyUnicode_Partition PyUnicodeUCS4_Partition +# define PyUnicode_RPartition PyUnicodeUCS4_RPartition +# define PyUnicode_RSplit PyUnicodeUCS4_RSplit # define PyUnicode_Replace PyUnicodeUCS4_Replace # define PyUnicode_Resize PyUnicodeUCS4_Resize # define PyUnicode_SetDefaultEncoding PyUnicodeUCS4_SetDefaultEncoding @@ -352,17 +357,27 @@ typedef PY_UNICODE_TYPE Py_UNICODE; Py_UNICODE_ISDIGIT(ch) || \ Py_UNICODE_ISNUMERIC(ch)) -#define Py_UNICODE_COPY(target, source, length)\ - (memcpy((target), (source), (length)*sizeof(Py_UNICODE))) +/* memcpy has a considerable setup overhead on many platforms; use a + loop for short strings (the "16" below is pretty arbitary) */ +#define Py_UNICODE_COPY(target, source, length) do\ + {Py_ssize_t i_; Py_UNICODE *t_ = (target); const Py_UNICODE *s_ = (source);\ + if (length > 16)\ + memcpy(t_, s_, (length)*sizeof(Py_UNICODE));\ + else\ + for (i_ = 0; i_ < (length); i_++) t_[i_] = s_[i_];\ + } while (0) #define Py_UNICODE_FILL(target, value, length) do\ - {int i; for (i = 0; i < (length); i++) (target)[i] = (value);}\ - while (0) + {Py_ssize_t i_; Py_UNICODE *t_ = (target); Py_UNICODE v_ = (value);\ + for (i_ = 0; i_ < (length); i_++) t_[i_] = v_;\ + } while (0) -#define Py_UNICODE_MATCH(string, offset, substring)\ - ((*((string)->str + (offset)) == *((substring)->str)) &&\ - !memcmp((string)->str + (offset), (substring)->str,\ - (substring)->length*sizeof(Py_UNICODE))) +/* check if substring matches at given offset. the offset must be + valid, and the substring must not be empty */ +#define Py_UNICODE_MATCH(string, offset, substring) \ + ((*((string)->str + (offset)) == *((substring)->str)) && \ + ((*((string)->str + (offset) + (substring)->length-1) == *((substring)->str + (substring)->length-1))) && \ + !memcmp((string)->str + (offset), (substring)->str, (substring)->length*sizeof(Py_UNICODE))) #ifdef __cplusplus extern "C" { @@ -1008,6 +1023,21 @@ PyAPI_FUNC(PyObject*) PyUnicode_Splitlines( int keepends /* If true, line end markers are included */ ); +/* Partition a string using a given separator. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Partition( + PyObject *s, /* String to partition */ + PyObject *sep /* String separator */ + ); + +/* Partition a string using a given separator, searching from the end of the + string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_RPartition( + PyObject *s, /* String to partition */ + PyObject *sep /* String separator */ + ); + /* Split a string giving a list of Unicode strings. If sep is NULL, splitting will be done at all whitespace diff --git a/Lib/UserString.py b/Lib/UserString.py index 94eb66d..473ee88 100755 --- a/Lib/UserString.py +++ b/Lib/UserString.py @@ -102,6 +102,8 @@ class UserString: return self.__class__(self.data.ljust(width, *args)) def lower(self): return self.__class__(self.data.lower()) def lstrip(self, chars=None): return self.__class__(self.data.lstrip(chars)) + def partition(self, sep): + return self.data.partition(sep) def replace(self, old, new, maxsplit=-1): return self.__class__(self.data.replace(old, new, maxsplit)) def rfind(self, sub, start=0, end=sys.maxint): @@ -110,6 +112,8 @@ class UserString: return self.data.rindex(sub, start, end) def rjust(self, width, *args): return self.__class__(self.data.rjust(width, *args)) + def rpartition(self, sep): + return self.data.rpartition(sep) def rstrip(self, chars=None): return self.__class__(self.data.rstrip(chars)) def split(self, sep=None, maxsplit=-1): return self.data.split(sep, maxsplit) diff --git a/Lib/_LWPCookieJar.py b/Lib/_LWPCookieJar.py index 6d5ce18..2a4fa7b 100644 --- a/Lib/_LWPCookieJar.py +++ b/Lib/_LWPCookieJar.py @@ -11,10 +11,11 @@ libwww-perl, I hope. """ -import time, re, logging -from cookielib import (reraise_unmasked_exceptions, FileCookieJar, LoadError, - Cookie, MISSING_FILENAME_TEXT, join_header_words, split_header_words, - iso2time, time2isoz) +import time, re +from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError, + Cookie, MISSING_FILENAME_TEXT, + join_header_words, split_header_words, + iso2time, time2isoz) def lwp_cookie_str(cookie): """Return string representation of Cookie in an the LWP cookie file format. @@ -92,7 +93,8 @@ class LWPCookieJar(FileCookieJar): def _really_load(self, f, filename, ignore_discard, ignore_expires): magic = f.readline() if not re.search(self.magic_re, magic): - msg = "%s does not seem to contain cookies" % filename + msg = ("%r does not look like a Set-Cookie3 (LWP) format " + "file" % filename) raise LoadError(msg) now = time.time() @@ -159,6 +161,10 @@ class LWPCookieJar(FileCookieJar): if not ignore_expires and c.is_expired(now): continue self.set_cookie(c) - except: - reraise_unmasked_exceptions((IOError,)) - raise LoadError("invalid Set-Cookie3 format file %s" % filename) + + except IOError: + raise + except Exception: + _warn_unhandled_exception() + raise LoadError("invalid Set-Cookie3 format file %r: %r" % + (filename, line)) diff --git a/Lib/_MozillaCookieJar.py b/Lib/_MozillaCookieJar.py index 4f2f375..1776b93 100644 --- a/Lib/_MozillaCookieJar.py +++ b/Lib/_MozillaCookieJar.py @@ -1,9 +1,9 @@ """Mozilla / Netscape cookie loading / saving.""" -import re, time, logging +import re, time -from cookielib import (reraise_unmasked_exceptions, FileCookieJar, LoadError, - Cookie, MISSING_FILENAME_TEXT) +from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError, + Cookie, MISSING_FILENAME_TEXT) class MozillaCookieJar(FileCookieJar): """ @@ -51,7 +51,7 @@ class MozillaCookieJar(FileCookieJar): if not re.search(self.magic_re, magic): f.close() raise LoadError( - "%s does not look like a Netscape format cookies file" % + "%r does not look like a Netscape format cookies file" % filename) try: @@ -104,9 +104,11 @@ class MozillaCookieJar(FileCookieJar): continue self.set_cookie(c) - except: - reraise_unmasked_exceptions((IOError,)) - raise LoadError("invalid Netscape format file %s: %s" % + except IOError: + raise + except Exception: + _warn_unhandled_exception() + raise LoadError("invalid Netscape format cookies file %r: %r" % (filename, line)) def save(self, filename=None, ignore_discard=False, ignore_expires=False): diff --git a/Lib/bdb.py b/Lib/bdb.py index 08b48c3..0c56b63 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -473,7 +473,9 @@ class Breakpoint: def disable(self): self.enabled = 0 - def bpprint(self): + def bpprint(self, out=None): + if out is None: + out = sys.stdout if self.temporary: disp = 'del ' else: @@ -482,17 +484,17 @@ class Breakpoint: disp = disp + 'yes ' else: disp = disp + 'no ' - print '%-4dbreakpoint %s at %s:%d' % (self.number, disp, - self.file, self.line) + print >>out, '%-4dbreakpoint %s at %s:%d' % (self.number, disp, + self.file, self.line) if self.cond: - print '\tstop only if %s' % (self.cond,) + print >>out, '\tstop only if %s' % (self.cond,) if self.ignore: - print '\tignore next %d hits' % (self.ignore) + print >>out, '\tignore next %d hits' % (self.ignore) if (self.hits): if (self.hits > 1): ss = 's' else: ss = '' - print ('\tbreakpoint already hit %d time%s' % - (self.hits, ss)) + print >>out, ('\tbreakpoint already hit %d time%s' % + (self.hits, ss)) # -----------end of Breakpoint class---------- diff --git a/Lib/binhex.py b/Lib/binhex.py index 16985fb..4f3882a 100644 --- a/Lib/binhex.py +++ b/Lib/binhex.py @@ -217,7 +217,11 @@ class BinHex: def _writecrc(self): # XXXX Should this be here?? # self.crc = binascii.crc_hqx('\0\0', self.crc) - self.ofp.write(struct.pack('>h', self.crc)) + if self.crc < 0: + fmt = '>h' + else: + fmt = '>H' + self.ofp.write(struct.pack(fmt, self.crc)) self.crc = 0 def write(self, data): diff --git a/Lib/bsddb/test/test_thread.py b/Lib/bsddb/test/test_thread.py index 44e3e9c..31964f0 100644 --- a/Lib/bsddb/test/test_thread.py +++ b/Lib/bsddb/test/test_thread.py @@ -24,6 +24,12 @@ try: except ImportError: have_threads = False +try: + WindowsError +except NameError: + class WindowsError(Exception): + pass + import unittest from test_all import verbose diff --git a/Lib/calendar.py b/Lib/calendar.py index 7800aae..00948ef 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -484,9 +484,6 @@ class TimeEncoding: def __init__(self, locale): self.locale = locale - def __context__(self): - return self - def __enter__(self): self.oldlocale = locale.setlocale(locale.LC_TIME, self.locale) return locale.getlocale(locale.LC_TIME)[1] diff --git a/Lib/codeop.py b/Lib/codeop.py index daa7eb8..5616d92 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -95,15 +95,7 @@ def _maybe_compile(compiler, source, filename, symbol): if code: return code - try: - e1 = err1.__dict__ - except AttributeError: - e1 = err1 - try: - e2 = err2.__dict__ - except AttributeError: - e2 = err2 - if not code1 and e1 == e2: + if not code1 and repr(err1) == repr(err2): raise SyntaxError, err1 def _compile(source, filename, symbol): diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index aac2dda..c093128 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -831,8 +831,6 @@ class CodeGenerator: self.__with_count += 1 self.set_lineno(node) self.visit(node.expr) - self.emit('LOAD_ATTR', '__context__') - self.emit('CALL_FUNCTION', 0) self.emit('DUP_TOP') self.emit('LOAD_ATTR', '__exit__') self._implicitNameOp('STORE', exitvar) diff --git a/Lib/compiler/transformer.py b/Lib/compiler/transformer.py index 604c57a..96bcce3 100644 --- a/Lib/compiler/transformer.py +++ b/Lib/compiler/transformer.py @@ -841,17 +841,15 @@ class Transformer: names.append(self.com_fpdef(node)) i = i + 1 - if i >= len(nodelist): - break - - if nodelist[i][0] == token.EQUAL: + if i < len(nodelist) and nodelist[i][0] == token.EQUAL: defaults.append(self.com_node(nodelist[i + 1])) i = i + 2 elif len(defaults): - # XXX This should be a syntax error. - # Treat "(a=1, b)" as "(a=1, b=None)" - defaults.append(Const(None)) + # we have already seen an argument with default, but here + # came one without + raise SyntaxError, "non-default argument follows default argument" + # skip the comma i = i + 1 return names, defaults, flags diff --git a/Lib/contextlib.py b/Lib/contextlib.py index aa5335d..a807c42 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -10,9 +10,6 @@ class GeneratorContextManager(object): def __init__(self, gen): self.gen = gen - def __context__(self): - return self - def __enter__(self): try: return self.gen.next() @@ -88,7 +85,7 @@ def contextmanager(func): @contextmanager -def nested(*contexts): +def nested(*managers): """Support multiple context managers in a single with-statement. Code like this: @@ -109,8 +106,7 @@ def nested(*contexts): exc = (None, None, None) try: try: - for context in contexts: - mgr = context.__context__() + for mgr in managers: exit = mgr.__exit__ enter = mgr.__enter__ vars.append(enter()) @@ -127,12 +123,14 @@ def nested(*contexts): except: exc = sys.exc_info() if exc != (None, None, None): - raise + # Don't rely on sys.exc_info() still containing + # the right information. Another exception may + # have been raised and caught by an exit method + raise exc[0], exc[1], exc[2] -@contextmanager -def closing(thing): - """Context manager to automatically close something at the end of a block. +class closing(object): + """Context to automatically close something at the end of a block. Code like this: @@ -148,7 +146,9 @@ def closing(thing): f.close() """ - try: - yield thing - finally: - thing.close() + def __init__(self, thing): + self.thing = thing + def __enter__(self): + return self.thing + def __exit__(self, *exc_info): + self.thing.close() diff --git a/Lib/cookielib.py b/Lib/cookielib.py index f0a89a5..e8fee0e 100644 --- a/Lib/cookielib.py +++ b/Lib/cookielib.py @@ -7,9 +7,9 @@ Docstrings, comments and debug strings in this code refer to the attributes of the HTTP cookie system as cookie-attributes, to distinguish them clearly from Python attributes. -Class diagram (note that the classes which do not derive from -FileCookieJar are not distributed with the Python standard library, but -are available from http://wwwsearch.sf.net/): +Class diagram (note that BSDDBCookieJar and the MSIE* classes are not +distributed with the Python standard library, but are available from +http://wwwsearch.sf.net/): CookieJar____ / \ \ @@ -25,7 +25,10 @@ are available from http://wwwsearch.sf.net/): """ -import sys, re, urlparse, copy, time, urllib, logging +__all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy', + 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] + +import re, urlparse, copy, time, urllib try: import threading as _threading except ImportError: @@ -33,21 +36,27 @@ except ImportError: import httplib # only for the default HTTP port from calendar import timegm -debug = logging.getLogger("cookielib").debug +debug = False # set to True to enable debugging via the logging module +logger = None + +def _debug(*args): + if not debug: + return + global logger + if not logger: + import logging + logger = logging.getLogger("cookielib") + return logger.debug(*args) + DEFAULT_HTTP_PORT = str(httplib.HTTP_PORT) MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " "instance initialised with one)") -def reraise_unmasked_exceptions(unmasked=()): +def _warn_unhandled_exception(): # There are a few catch-all except: statements in this module, for - # catching input that's bad in unexpected ways. - # This function re-raises some exceptions we don't want to trap. - unmasked = unmasked + (KeyboardInterrupt, SystemExit, MemoryError) - etype = sys.exc_info()[0] - if issubclass(etype, unmasked): - raise - # swallowed an exception + # catching input that's bad in unexpected ways. Warn if any + # exceptions are caught there. import warnings, traceback, StringIO f = StringIO.StringIO() traceback.print_exc(None, f) @@ -613,7 +622,7 @@ def request_port(request): try: int(port) except ValueError: - debug("nonnumeric port: '%s'", port) + _debug("nonnumeric port: '%s'", port) return None else: port = DEFAULT_HTTP_PORT @@ -904,7 +913,7 @@ class DefaultCookiePolicy(CookiePolicy): strict about which cookies to accept). """ - debug(" - checking cookie %s=%s", cookie.name, cookie.value) + _debug(" - checking cookie %s=%s", cookie.name, cookie.value) assert cookie.name is not None @@ -920,25 +929,25 @@ class DefaultCookiePolicy(CookiePolicy): if cookie.version is None: # Version is always set to 0 by parse_ns_headers if it's a Netscape # cookie, so this must be an invalid RFC 2965 cookie. - debug(" Set-Cookie2 without version attribute (%s=%s)", - cookie.name, cookie.value) + _debug(" Set-Cookie2 without version attribute (%s=%s)", + cookie.name, cookie.value) return False if cookie.version > 0 and not self.rfc2965: - debug(" RFC 2965 cookies are switched off") + _debug(" RFC 2965 cookies are switched off") return False elif cookie.version == 0 and not self.netscape: - debug(" Netscape cookies are switched off") + _debug(" Netscape cookies are switched off") return False return True def set_ok_verifiability(self, cookie, request): if request.is_unverifiable() and is_third_party(request): if cookie.version > 0 and self.strict_rfc2965_unverifiable: - debug(" third-party RFC 2965 cookie during " + _debug(" third-party RFC 2965 cookie during " "unverifiable transaction") return False elif cookie.version == 0 and self.strict_ns_unverifiable: - debug(" third-party Netscape cookie during " + _debug(" third-party Netscape cookie during " "unverifiable transaction") return False return True @@ -948,7 +957,7 @@ class DefaultCookiePolicy(CookiePolicy): # servers that know both V0 and V1 protocols. if (cookie.version == 0 and self.strict_ns_set_initial_dollar and cookie.name.startswith("$")): - debug(" illegal name (starts with '$'): '%s'", cookie.name) + _debug(" illegal name (starts with '$'): '%s'", cookie.name) return False return True @@ -958,33 +967,36 @@ class DefaultCookiePolicy(CookiePolicy): if ((cookie.version > 0 or (cookie.version == 0 and self.strict_ns_set_path)) and not req_path.startswith(cookie.path)): - debug(" path attribute %s is not a prefix of request " - "path %s", cookie.path, req_path) + _debug(" path attribute %s is not a prefix of request " + "path %s", cookie.path, req_path) return False return True def set_ok_domain(self, cookie, request): if self.is_blocked(cookie.domain): - debug(" domain %s is in user block-list", cookie.domain) + _debug(" domain %s is in user block-list", cookie.domain) return False if self.is_not_allowed(cookie.domain): - debug(" domain %s is not in user allow-list", cookie.domain) + _debug(" domain %s is not in user allow-list", cookie.domain) return False if cookie.domain_specified: req_host, erhn = eff_request_host(request) domain = cookie.domain if self.strict_domain and (domain.count(".") >= 2): + # XXX This should probably be compared with the Konqueror + # (kcookiejar.cpp) and Mozilla implementations, but it's a + # losing battle. i = domain.rfind(".") j = domain.rfind(".", 0, i) if j == 0: # domain like .foo.bar tld = domain[i+1:] sld = domain[j+1:i] - if (sld.lower() in ( - "co", "ac", - "com", "edu", "org", "net", "gov", "mil", "int") and - len(tld) == 2): + if sld.lower() in ("co", "ac", "com", "edu", "org", "net", + "gov", "mil", "int", "aero", "biz", "cat", "coop", + "info", "jobs", "mobi", "museum", "name", "pro", + "travel", "eu") and len(tld) == 2: # domain like .co.uk - debug(" country-code second level domain %s", domain) + _debug(" country-code second level domain %s", domain) return False if domain.startswith("."): undotted_domain = domain[1:] @@ -992,30 +1004,30 @@ class DefaultCookiePolicy(CookiePolicy): undotted_domain = domain embedded_dots = (undotted_domain.find(".") >= 0) if not embedded_dots and domain != ".local": - debug(" non-local domain %s contains no embedded dot", - domain) + _debug(" non-local domain %s contains no embedded dot", + domain) return False if cookie.version == 0: if (not erhn.endswith(domain) and (not erhn.startswith(".") and not ("."+erhn).endswith(domain))): - debug(" effective request-host %s (even with added " - "initial dot) does not end end with %s", - erhn, domain) + _debug(" effective request-host %s (even with added " + "initial dot) does not end end with %s", + erhn, domain) return False if (cookie.version > 0 or (self.strict_ns_domain & self.DomainRFC2965Match)): if not domain_match(erhn, domain): - debug(" effective request-host %s does not domain-match " - "%s", erhn, domain) + _debug(" effective request-host %s does not domain-match " + "%s", erhn, domain) return False if (cookie.version > 0 or (self.strict_ns_domain & self.DomainStrictNoDots)): host_prefix = req_host[:-len(domain)] if (host_prefix.find(".") >= 0 and not IPV4_RE.search(req_host)): - debug(" host prefix %s for domain %s contains a dot", - host_prefix, domain) + _debug(" host prefix %s for domain %s contains a dot", + host_prefix, domain) return False return True @@ -1030,13 +1042,13 @@ class DefaultCookiePolicy(CookiePolicy): try: int(p) except ValueError: - debug(" bad port %s (not numeric)", p) + _debug(" bad port %s (not numeric)", p) return False if p == req_port: break else: - debug(" request port (%s) not found in %s", - req_port, cookie.port) + _debug(" request port (%s) not found in %s", + req_port, cookie.port) return False return True @@ -1049,7 +1061,7 @@ class DefaultCookiePolicy(CookiePolicy): """ # Path has already been checked by .path_return_ok(), and domain # blocking done by .domain_return_ok(). - debug(" - checking cookie %s=%s", cookie.name, cookie.value) + _debug(" - checking cookie %s=%s", cookie.name, cookie.value) for n in "version", "verifiability", "secure", "expires", "port", "domain": fn_name = "return_ok_"+n @@ -1060,34 +1072,34 @@ class DefaultCookiePolicy(CookiePolicy): def return_ok_version(self, cookie, request): if cookie.version > 0 and not self.rfc2965: - debug(" RFC 2965 cookies are switched off") + _debug(" RFC 2965 cookies are switched off") return False elif cookie.version == 0 and not self.netscape: - debug(" Netscape cookies are switched off") + _debug(" Netscape cookies are switched off") return False return True def return_ok_verifiability(self, cookie, request): if request.is_unverifiable() and is_third_party(request): if cookie.version > 0 and self.strict_rfc2965_unverifiable: - debug(" third-party RFC 2965 cookie during unverifiable " - "transaction") + _debug(" third-party RFC 2965 cookie during unverifiable " + "transaction") return False elif cookie.version == 0 and self.strict_ns_unverifiable: - debug(" third-party Netscape cookie during unverifiable " - "transaction") + _debug(" third-party Netscape cookie during unverifiable " + "transaction") return False return True def return_ok_secure(self, cookie, request): if cookie.secure and request.get_type() != "https": - debug(" secure cookie with non-secure request") + _debug(" secure cookie with non-secure request") return False return True def return_ok_expires(self, cookie, request): if cookie.is_expired(self._now): - debug(" cookie expired") + _debug(" cookie expired") return False return True @@ -1100,8 +1112,8 @@ class DefaultCookiePolicy(CookiePolicy): if p == req_port: break else: - debug(" request port %s does not match cookie port %s", - req_port, cookie.port) + _debug(" request port %s does not match cookie port %s", + req_port, cookie.port) return False return True @@ -1113,17 +1125,17 @@ class DefaultCookiePolicy(CookiePolicy): if (cookie.version == 0 and (self.strict_ns_domain & self.DomainStrictNonDomain) and not cookie.domain_specified and domain != erhn): - debug(" cookie with unspecified domain does not string-compare " - "equal to request domain") + _debug(" cookie with unspecified domain does not string-compare " + "equal to request domain") return False if cookie.version > 0 and not domain_match(erhn, domain): - debug(" effective request-host name %s does not domain-match " - "RFC 2965 cookie domain %s", erhn, domain) + _debug(" effective request-host name %s does not domain-match " + "RFC 2965 cookie domain %s", erhn, domain) return False if cookie.version == 0 and not ("."+erhn).endswith(domain): - debug(" request-host %s does not match Netscape cookie domain " - "%s", req_host, domain) + _debug(" request-host %s does not match Netscape cookie domain " + "%s", req_host, domain) return False return True @@ -1136,24 +1148,24 @@ class DefaultCookiePolicy(CookiePolicy): if not erhn.startswith("."): erhn = "."+erhn if not (req_host.endswith(domain) or erhn.endswith(domain)): - #debug(" request domain %s does not match cookie domain %s", - # req_host, domain) + #_debug(" request domain %s does not match cookie domain %s", + # req_host, domain) return False if self.is_blocked(domain): - debug(" domain %s is in user block-list", domain) + _debug(" domain %s is in user block-list", domain) return False if self.is_not_allowed(domain): - debug(" domain %s is not in user allow-list", domain) + _debug(" domain %s is not in user allow-list", domain) return False return True def path_return_ok(self, path, request): - debug("- checking cookie path=%s", path) + _debug("- checking cookie path=%s", path) req_path = request_path(request) if not req_path.startswith(path): - debug(" %s does not path-match %s", req_path, path) + _debug(" %s does not path-match %s", req_path, path) return False return True @@ -1215,7 +1227,7 @@ class CookieJar: cookies = [] if not self._policy.domain_return_ok(domain, request): return [] - debug("Checking %s for cookies to return", domain) + _debug("Checking %s for cookies to return", domain) cookies_by_path = self._cookies[domain] for path in cookies_by_path.keys(): if not self._policy.path_return_ok(path, request): @@ -1223,9 +1235,9 @@ class CookieJar: cookies_by_name = cookies_by_path[path] for cookie in cookies_by_name.values(): if not self._policy.return_ok(cookie, request): - debug(" not returning cookie") + _debug(" not returning cookie") continue - debug(" it's a match") + _debug(" it's a match") cookies.append(cookie) return cookies @@ -1302,7 +1314,7 @@ class CookieJar: The Cookie2 header is also added unless policy.hide_cookie2 is true. """ - debug("add_cookie_header") + _debug("add_cookie_header") self._cookies_lock.acquire() self._policy._now = self._now = int(time.time()) @@ -1379,7 +1391,7 @@ class CookieJar: continue if k == "domain": if v is None: - debug(" missing value for domain attribute") + _debug(" missing value for domain attribute") bad_cookie = True break # RFC 2965 section 3.3.3 @@ -1389,7 +1401,7 @@ class CookieJar: # Prefer max-age to expires (like Mozilla) continue if v is None: - debug(" missing or invalid value for expires " + _debug(" missing or invalid value for expires " "attribute: treating as session cookie") continue if k == "max-age": @@ -1397,7 +1409,7 @@ class CookieJar: try: v = int(v) except ValueError: - debug(" missing or invalid (non-numeric) value for " + _debug(" missing or invalid (non-numeric) value for " "max-age attribute") bad_cookie = True break @@ -1410,7 +1422,7 @@ class CookieJar: if (k in value_attrs) or (k in boolean_attrs): if (v is None and k not in ("port", "comment", "commenturl")): - debug(" missing value for %s attribute" % k) + _debug(" missing value for %s attribute" % k) bad_cookie = True break standard[k] = v @@ -1496,8 +1508,8 @@ class CookieJar: self.clear(domain, path, name) except KeyError: pass - debug("Expiring cookie, domain='%s', path='%s', name='%s'", - domain, path, name) + _debug("Expiring cookie, domain='%s', path='%s', name='%s'", + domain, path, name) return None return Cookie(version, @@ -1552,8 +1564,8 @@ class CookieJar: try: cookies = self._cookies_from_attrs_set( split_header_words(rfc2965_hdrs), request) - except: - reraise_unmasked_exceptions() + except Exception: + _warn_unhandled_exception() cookies = [] if ns_hdrs and netscape: @@ -1561,8 +1573,8 @@ class CookieJar: # RFC 2109 and Netscape cookies ns_cookies = self._cookies_from_attrs_set( parse_ns_headers(ns_hdrs), request) - except: - reraise_unmasked_exceptions() + except Exception: + _warn_unhandled_exception() ns_cookies = [] self._process_rfc2109_cookies(ns_cookies) @@ -1612,13 +1624,13 @@ class CookieJar: def extract_cookies(self, response, request): """Extract cookies from response, where allowable given the request.""" - debug("extract_cookies: %s", response.info()) + _debug("extract_cookies: %s", response.info()) self._cookies_lock.acquire() self._policy._now = self._now = int(time.time()) for cookie in self.make_cookies(response, request): if self._policy.set_ok(cookie, request): - debug(" setting cookie: %s", cookie) + _debug(" setting cookie: %s", cookie) self.set_cookie(cookie) self._cookies_lock.release() diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 28ac180..f2ddbaa 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -3,7 +3,7 @@ import os as _os, sys as _sys from itertools import chain as _chain -__version__ = "0.9.9.4" +__version__ = "0.9.9.6" from _ctypes import Union, Structure, Array from _ctypes import _Pointer @@ -23,8 +23,6 @@ if _os.name in ("nt", "ce"): from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI -from ctypes._loader import LibraryLoader - """ WINOLEAPI -> HRESULT WINOLEAPI_(type) @@ -72,9 +70,11 @@ def CFUNCTYPE(restype, *argtypes): The function prototype can be called in three ways to create a callable object: - prototype(funct) - returns a C callable function calling funct - prototype(vtbl_index, method_name[, paramflags]) - a Python callable that calls a COM method - prototype(funct_name, dll[, paramflags]) - a Python callable that calls an exported function in a dll + prototype(integer address) -> foreign function + prototype(callable) -> create and return a C callable function from callable + prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method + prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal + prototype((function name, dll object)[, paramflags]) -> foreign function exported by name """ try: return _c_functype_cache[(restype, argtypes)] @@ -352,6 +352,23 @@ if _os.name in ("nt", "ce"): _flags_ = _FUNCFLAG_STDCALL _restype_ = HRESULT +class LibraryLoader(object): + def __init__(self, dlltype): + self._dlltype = dlltype + + def __getattr__(self, name): + if name[0] == '_': + raise AttributeError(name) + dll = self._dlltype(name) + setattr(self, name, dll) + return dll + + def __getitem__(self, name): + return getattr(self, name) + + def LoadLibrary(self, name): + return self._dlltype(name) + cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) @@ -402,7 +419,12 @@ def PYFUNCTYPE(restype, *argtypes): _restype_ = restype _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI return CFunctionType -cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr) +_cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr) + +def cast(obj, typ): + result = _cast(obj, typ) + result.__keepref = obj + return result _string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=0): diff --git a/Lib/ctypes/_loader.py b/Lib/ctypes/_loader.py deleted file mode 100644 index 7a48c1c..0000000 --- a/Lib/ctypes/_loader.py +++ /dev/null @@ -1,262 +0,0 @@ -import sys, os -import ctypes - -if os.name in ("nt", "ce"): - from _ctypes import LoadLibrary as dlopen -else: - from _ctypes import dlopen -from _ctypes import RTLD_LOCAL, RTLD_GLOBAL - -# _findLib(name) returns an iterable of possible names for a library. -if os.name in ("nt", "ce"): - def _findLib(name): - return [name] - -if os.name == "posix" and sys.platform == "darwin": - from ctypes.macholib.dyld import dyld_find as _dyld_find - def _findLib(name): - possible = ['lib%s.dylib' % name, - '%s.dylib' % name, - '%s.framework/%s' % (name, name)] - for name in possible: - try: - return [_dyld_find(name)] - except ValueError: - continue - return [] - -elif os.name == "posix": - # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump - import re, tempfile - - def _findLib_gcc(name): - expr = '[^\(\)\s]*lib%s\.[^\(\)\s]*' % name - cmd = 'if type gcc &>/dev/null; then CC=gcc; else CC=cc; fi;' \ - '$CC -Wl,-t -o /dev/null 2>&1 -l' + name - try: - fdout, outfile = tempfile.mkstemp() - fd = os.popen(cmd) - trace = fd.read() - err = fd.close() - finally: - try: - os.unlink(outfile) - except OSError, e: - if e.errno != errno.ENOENT: - raise - res = re.search(expr, trace) - if not res: - return None - return res.group(0) - - def _findLib_ld(name): - expr = '/[^\(\)\s]*lib%s\.[^\(\)\s]*' % name - res = re.search(expr, os.popen('/sbin/ldconfig -p 2>/dev/null').read()) - if not res: - cmd = 'ldd %s 2>/dev/null' % sys.executable - res = re.search(expr, os.popen(cmd).read()) - if not res: - return None - return res.group(0) - - def _get_soname(f): - cmd = "objdump -p -j .dynamic 2>/dev/null " + f - res = re.search(r'\sSONAME\s+([^\s]+)', os.popen(cmd).read()) - if not res: - return f - return res.group(1) - - def _findLib(name): - lib = _findLib_ld(name) - if not lib: - lib = _findLib_gcc(name) - if not lib: - return [name] - return [_get_soname(lib)] - -class LibraryLoader(object): - """Loader for shared libraries. - - Shared libraries are accessed when compiling/linking a program, - and when the program is run. The purpose of the 'find' method is - to locate a library similar to what the compiler does (on machines - with several versions of a shared library the most recent should - be loaded), while 'load' acts like when the program is run, and - uses the runtime loader directly. 'load_version' works like - 'load' but tries to be platform independend (for cases where this - makes sense). Loading via attribute access is a shorthand - notation especially useful for interactive use.""" - - - def __init__(self, dlltype, mode=RTLD_LOCAL): - """Create a library loader instance which loads libraries by - creating an instance of 'dlltype'. 'mode' can be RTLD_LOCAL - or RTLD_GLOBAL, it is ignored on Windows. - """ - self._dlltype = dlltype - self._mode = mode - - def load(self, libname, mode=None): - """Load and return the library with the given libname. On - most systems 'libname' is the filename of the shared library; - when it's not a pathname it will be searched in a system - dependend list of locations (on many systems additional search - paths can be specified by an environment variable). Sometimes - the extension (like '.dll' on Windows) can be omitted. - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - if mode is None: - mode = self._mode - return self._load(libname, mode) - - def load_library(self, libname, mode=None): - """Load and return the library with the given libname. This - method passes the specified 'libname' directly to the - platform's library loading function (dlopen, or LoadLibrary). - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - if mode is None: - mode = self._mode - return self._dlltype(libname, mode) - - # alias name for backwards compatiblity - LoadLibrary = load_library - - # Helpers for load and load_version - assembles a filename from name and filename - if os.name in ("nt", "ce"): - # Windows (XXX what about cygwin?) - def _plat_load_version(self, name, version, mode): - # not sure if this makes sense - if version is not None: - return self.load(name + version, mode) - return self.load(name, mode) - - _load = load_library - - elif os.name == "posix" and sys.platform == "darwin": - # Mac OS X - def _plat_load_version(self, name, version, mode): - if version: - return self.load("lib%s.%s.dylib" % (name, version), mode) - return self.load("lib%s.dylib" % name, mode) - - def _load(self, libname, mode): - # _dyld_find raises ValueError, convert this into OSError - try: - pathname = _dyld_find(libname) - except ValueError: - raise OSError("Library %s could not be found" % libname) - return self.load_library(pathname, mode) - - elif os.name == "posix": - # Posix - def _plat_load_version(self, name, version, mode): - if version: - return self.load("lib%s.so.%s" % (name, version), mode) - return self.load("lib%s.so" % name, mode) - - _load = load_library - - else: - # Others, TBD - def _plat_load_version(self, name, version, mode=None): - return self.load(name, mode) - - _load = load_library - - def load_version(self, name, version=None, mode=None): - """Build a (system dependend) filename from 'name' and - 'version', then load and return it. 'name' is the library - name without any prefix like 'lib' and suffix like '.so' or - '.dylib'. This method should be used if a library is - available on different platforms, using the particular naming - convention of each platform. - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - return self._plat_load_version(name, version, mode) - - def find(self, name, mode=None): - """Try to find a library, load and return it. 'name' is the - library name without any prefix like 'lib', suffix like '.so', - '.dylib' or version number (this is the form used for the - posix linker option '-l'). - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - - On windows, this method does the same as the 'load' method. - - On other platforms, this function might call other programs - like the compiler to find the library. When using ctypes to - write a shared library wrapping, consider using .load() or - .load_version() instead. - """ - for libname in _findLib(name): - try: - return self.load(libname, mode) - except OSError: - continue - raise OSError("Library %r not found" % name) - - def __getattr__(self, name): - """Load a library via attribute access. Calls - .load_version(). The result is cached.""" - if name.startswith("_"): - raise AttributeError(name) - dll = self.load_version(name) - setattr(self, name, dll) - return dll - -################################################################ -# test code - -class CDLL(object): - def __init__(self, name, mode): - self._handle = dlopen(name, mode) - self._name = name - - def __repr__(self): - return "<%s '%s', handle %x at %x>" % \ - (self.__class__.__name__, self._name, - (self._handle & (sys.maxint*2 + 1)), - id(self)) - -cdll = LibraryLoader(CDLL) - -def test(): - if os.name == "nt": - print cdll.msvcrt - print cdll.load("msvcrt") - # load_version looks more like an artefact: - print cdll.load_version("msvcr", "t") - print cdll.find("msvcrt") - - if os.name == "posix": - # find and load_version - print cdll.find("m") - print cdll.find("c") - print cdll.load_version("crypto", "0.9.7") - - # getattr - print cdll.m - print cdll.bz2 - - # load - if sys.platform == "darwin": - print cdll.load("libm.dylib") - print cdll.load("libcrypto.dylib") - print cdll.load("libSystem.dylib") - print cdll.load("System.framework/System") - else: - print cdll.load("libm.so") - print cdll.load("libcrypt.so") - print cdll.find("crypt") - -if __name__ == "__main__": - test() diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py index 54ea839..92c4669 100644 --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -24,7 +24,7 @@ class BITS(Structure): ("R", c_short, 6), ("S", c_short, 7)] -func = cdll.load(_ctypes_test.__file__).unpack_bitfields +func = CDLL(_ctypes_test.__file__).unpack_bitfields func.argtypes = POINTER(BITS), c_char ##for n in "ABCDEFGHIMNOPQRS": diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py index d0ada40..1f68992 100644 --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -15,7 +15,7 @@ def bin(s): class Test(unittest.TestCase): def X_test(self): - print sys.byteorder + print >> sys.stderr, sys.byteorder for i in range(32): bits = BITS() setattr(bits, "i%s" % i, 1) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index a6ee150..9d96a54 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -115,7 +115,7 @@ class SampleCallbacksTestCase(unittest.TestCase): def test_integrate(self): # Derived from some then non-working code, posted by David Foster - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) # The function prototype called by 'integrate': double func(double); CALLBACK = CFUNCTYPE(c_double, c_double) diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py index 6f25feb..821ce3f 100644 --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -23,33 +23,24 @@ class Test(unittest.TestCase): def test_address2pointer(self): array = (c_int * 3)(42, 17, 2) - # on AMD64, sizeof(int) == 4 and sizeof(void *) == 8. - # By default, cast would convert a Python int (or long) into - # a C int, which would be too short to represent a pointer - # on this platform. - - # So we have to wrap the address into a c_void_p for this to work. - # - # XXX Better would be to hide the differences in the cast function. address = addressof(array) ptr = cast(c_void_p(address), POINTER(c_int)) self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + ptr = cast(address, POINTER(c_int)) + self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + def test_ptr2array(self): array = (c_int * 3)(42, 17, 2) -## # Hm, already tested above. -## ptr = cast(array, POINTER(c_int)) -## self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + from sys import getrefcount -# print cast(addressof(array), c_int * 3)[:] -## ptr = cast(addressof(ptr) - -## print ptr[0], ptr[1], ptr[2] -## ptr = POINTER(c_int).from_address(addressof(array)) -## # XXX this crashes: -## print ptr[0], ptr[1], ptr[2] + before = getrefcount(array) + ptr = cast(array, POINTER(c_int)) + self.failUnlessEqual(getrefcount(array), before + 1) + del ptr + self.failUnlessEqual(getrefcount(array), before) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py index 6e0798d..9d8db1f 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -7,7 +7,7 @@ from ctypes import * import _ctypes_test class CFunctions(unittest.TestCase): - _dll = cdll.load(_ctypes_test.__file__) + _dll = CDLL(_ctypes_test.__file__) def S(self): return c_longlong.in_dll(self._dll, "last_tf_arg_s").value diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py index 344d0bc..e055c49 100644 --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -14,7 +14,7 @@ class Test(unittest.TestCase): def test_checkretval(self): import _ctypes_test - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) self.failUnlessEqual(42, dll._testfunc_p_p(42)) dll._testfunc_p_p.restype = CHECKED diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py new file mode 100644 index 0000000..810467f --- /dev/null +++ b/Lib/ctypes/test/test_find.py @@ -0,0 +1,104 @@ +import unittest +import os, sys +from ctypes import * +from ctypes.util import find_library +from ctypes.test import is_resource_enabled + +if sys.platform == "win32": + lib_gl = find_library("OpenGL32") + lib_glu = find_library("Glu32") + lib_glut = find_library("glut32") + lib_gle = None +elif sys.platform == "darwin": + lib_gl = lib_glu = find_library("OpenGL") + lib_glut = find_library("GLUT") + lib_gle = None +else: + lib_gl = find_library("GL") + lib_glu = find_library("GLU") + lib_glut = find_library("glut") + lib_gle = find_library("gle") + +## print, for debugging +if is_resource_enabled("printing"): + if lib_gl or lib_glu or lib_glut or lib_gle: + print "OpenGL libraries:" + for item in (("GL", lib_gl), + ("GLU", lib_glu), + ("glut", lib_glut), + ("gle", lib_gle)): + print "\t", item + + +# On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode. +class Test_OpenGL_libs(unittest.TestCase): + def setUp(self): + self.gl = self.glu = self.gle = self.glut = None + if lib_gl: + self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) + if lib_glu: + self.glu = CDLL(lib_glu, RTLD_GLOBAL) + if lib_glut: + # On some systems, additional libraries seem to be + # required, loading glut fails with + # "OSError: /usr/lib/libglut.so.3: undefined symbol: XGetExtensionVersion" + # I cannot figure out how to repair the test on these + # systems (red hat), so we ignore it when the glut or gle + # libraries cannot be loaded. See also: + # https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1478253&group_id=5470 + # http://mail.python.org/pipermail/python-dev/2006-May/064789.html + try: + self.glut = CDLL(lib_glut) + except OSError: + pass + if lib_gle: + try: + self.gle = CDLL(lib_gle) + except OSError: + pass + + if lib_gl: + def test_gl(self): + if self.gl: + self.gl.glClearIndex + + if lib_glu: + def test_glu(self): + if self.glu: + self.glu.gluBeginCurve + + if lib_glut: + def test_glut(self): + if self.glut: + self.glut.glutWireTetrahedron + + if lib_gle: + def test_gle(self): + if self.gle: + self.gle.gleGetJoinStyle + +##if os.name == "posix" and sys.platform != "darwin": + +## # On platforms where the default shared library suffix is '.so', +## # at least some libraries can be loaded as attributes of the cdll +## # object, since ctypes now tries loading the lib again +## # with '.so' appended of the first try fails. +## # +## # Won't work for libc, unfortunately. OTOH, it isn't +## # needed for libc since this is already mapped into the current +## # process (?) +## # +## # On MAC OSX, it won't work either, because dlopen() needs a full path, +## # and the default suffix is either none or '.dylib'. + +## class LoadLibs(unittest.TestCase): +## def test_libm(self): +## import math +## libm = cdll.libm +## sqrt = libm.sqrt +## sqrt.argtypes = (c_double,) +## sqrt.restype = c_double +## self.failUnlessEqual(sqrt(2), math.sqrt(2)) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index 89b93c4..7ea873f 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -8,7 +8,7 @@ except NameError: WINFUNCTYPE = CFUNCTYPE import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class CFuncPtrTestCase(unittest.TestCase): def test_basic(self): diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index ada9def..bfa0cad 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -15,9 +15,9 @@ except NameError: WINFUNCTYPE = CFUNCTYPE import _ctypes_test -dll = cdll.load(_ctypes_test.__file__) +dll = CDLL(_ctypes_test.__file__) if sys.platform == "win32": - windll = windll.load(_ctypes_test.__file__) + windll = WinDLL(_ctypes_test.__file__) class POINT(Structure): _fields_ = [("x", c_int), ("y", c_int)] diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py index 8fd2789..c39f350 100644 --- a/Lib/ctypes/test/test_libc.py +++ b/Lib/ctypes/test/test_libc.py @@ -4,7 +4,7 @@ import unittest from ctypes import * import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class LibTest(unittest.TestCase): def test_sqrt(self): diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index 4558417..45585ae 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -1,6 +1,8 @@ from ctypes import * import sys, unittest import os, StringIO +from ctypes.util import find_library +from ctypes.test import is_resource_enabled libc_name = None if os.name == "nt": @@ -18,39 +20,49 @@ else: libc_name = line.split()[4] else: libc_name = line.split()[2] -## print "libc_name is", libc_name break +if is_resource_enabled("printing"): + print "libc_name is", libc_name + class LoaderTest(unittest.TestCase): unknowndll = "xxrandomnamexx" if libc_name is not None: def test_load(self): - cdll.load(libc_name) - cdll.load(os.path.basename(libc_name)) - self.assertRaises(OSError, cdll.load, self.unknowndll) + CDLL(libc_name) + CDLL(os.path.basename(libc_name)) + self.assertRaises(OSError, CDLL, self.unknowndll) if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": def test_load_version(self): - cdll.load_version("c", "6") + cdll.LoadLibrary("libc.so.6") # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.load_version, "c", "9") - self.assertRaises(OSError, cdll.load_version, self.unknowndll, "") + self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") + self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) - def test_find(self): - name = "c" - cdll.find(name) - self.assertRaises(OSError, cdll.find, self.unknowndll) + def test_find(self): + for name in ("c", "m"): + lib = find_library(name) + if lib: + cdll.LoadLibrary(lib) + CDLL(lib) if os.name in ("nt", "ce"): def test_load_library(self): + if is_resource_enabled("printing"): + print find_library("kernel32") + print find_library("user32") + if os.name == "nt": - windll.load_library("kernel32").GetModuleHandleW + windll.kernel32.GetModuleHandleW + windll["kernel32"].GetModuleHandleW windll.LoadLibrary("kernel32").GetModuleHandleW WinDLL("kernel32").GetModuleHandleW elif os.name == "ce": - windll.load_library("coredll").GetModuleHandleW + windll.coredll.GetModuleHandleW + windll["coredll"].GetModuleHandleW windll.LoadLibrary("coredll").GetModuleHandleW WinDLL("coredll").GetModuleHandleW diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index 3a324a6..600bb75 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -20,7 +20,7 @@ class PointersTestCase(unittest.TestCase): self.failUnlessRaises(TypeError, A, c_ulong(33)) def test_pass_pointers(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_p_p func.restype = c_long @@ -35,7 +35,7 @@ class PointersTestCase(unittest.TestCase): self.failUnlessEqual(res[0], 12345678) def test_change_pointers(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_p_p i = c_int(87654) @@ -70,7 +70,7 @@ class PointersTestCase(unittest.TestCase): return 0 callback = PROTOTYPE(func) - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) # This function expects a function pointer, # and calls this with an integer pointer as parameter. # The int pointer points to a table containing the numbers 1..10 @@ -133,30 +133,9 @@ class PointersTestCase(unittest.TestCase): self.failUnlessEqual(p[0], 42) self.failUnlessEqual(p.contents.value, 42) - def test_incomplete(self): - lpcell = POINTER("cell") - class cell(Structure): - _fields_ = [("value", c_int), - ("next", lpcell)] - SetPointerType(lpcell, cell) - - # Make a structure containing a pointer to itself: - c = cell() - c.value = 42 - c.next = pointer(c) - - result = [] - for i in range(8): - result.append(c.value) - c = c.next[0] - self.failUnlessEqual(result, [42] * 8) - - from ctypes import _pointer_type_cache - del _pointer_type_cache[cell] - def test_charpp( self ): """Test that a character pointer-to-pointer is correctly passed""" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_c_p_p func.restype = c_char_p argv = (c_char_p * 2)() diff --git a/Lib/ctypes/test/test_posix.py b/Lib/ctypes/test/test_posix.py deleted file mode 100644 index fe0a40a..0000000 --- a/Lib/ctypes/test/test_posix.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest, os, sys -from ctypes import * - -if os.name == "posix" and sys.platform == "linux2": - # I don't really know on which platforms this works, - # later it should use the find_library stuff to avoid - # hardcoding the names. - - class TestRTLD_GLOBAL(unittest.TestCase): - def test_GL(self): - if os.path.exists('/usr/lib/libGL.so'): - cdll.load('libGL.so', mode=RTLD_GLOBAL) - if os.path.exists('/usr/lib/libGLU.so'): - cdll.load('libGLU.so') - -##if os.name == "posix" and sys.platform != "darwin": - -## # On platforms where the default shared library suffix is '.so', -## # at least some libraries can be loaded as attributes of the cdll -## # object, since ctypes now tries loading the lib again -## # with '.so' appended of the first try fails. -## # -## # Won't work for libc, unfortunately. OTOH, it isn't -## # needed for libc since this is already mapped into the current -## # process (?) -## # -## # On MAC OSX, it won't work either, because dlopen() needs a full path, -## # and the default suffix is either none or '.dylib'. - -## class LoadLibs(unittest.TestCase): -## def test_libm(self): -## import math -## libm = cdll.libm -## sqrt = libm.sqrt -## sqrt.argtypes = (c_double,) -## sqrt.restype = c_double -## self.failUnlessEqual(sqrt(2), math.sqrt(2)) - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py index 47f5da1..aaaa47a 100644 --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -22,7 +22,7 @@ import unittest # In this case, there would have to be an additional reference to the argument... import _ctypes_test -testdll = cdll.load(_ctypes_test.__file__) +testdll = CDLL(_ctypes_test.__file__) # Return machine address `a` as a (possibly long) non-negative integer. # Starting with Python 2.5, id(anything) is always non-negative, and diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py index c29b721..78e0231 100644 --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -1,5 +1,6 @@ from ctypes import * import unittest, sys +from ctypes.test import is_resource_enabled ################################################################ # This section should be moved into ctypes\__init__.py, when it's ready. @@ -33,20 +34,24 @@ class PythonAPITestCase(unittest.TestCase): del pyob self.failUnlessEqual(grc(s), refcnt) - def test_PyInt_Long(self): - ref42 = grc(42) - pythonapi.PyInt_FromLong.restype = py_object - self.failUnlessEqual(pythonapi.PyInt_FromLong(42), 42) + if is_resource_enabled("refcount"): + # This test is unreliable, because it is possible that code in + # unittest changes the refcount of the '42' integer. So, it + # is disabled by default. + def test_PyInt_Long(self): + ref42 = grc(42) + pythonapi.PyInt_FromLong.restype = py_object + self.failUnlessEqual(pythonapi.PyInt_FromLong(42), 42) - self.failUnlessEqual(grc(42), ref42) + self.failUnlessEqual(grc(42), ref42) - pythonapi.PyInt_AsLong.argtypes = (py_object,) - pythonapi.PyInt_AsLong.restype = c_long + pythonapi.PyInt_AsLong.argtypes = (py_object,) + pythonapi.PyInt_AsLong.restype = c_long - res = pythonapi.PyInt_AsLong(42) - self.failUnlessEqual(grc(res), ref42 + 1) - del res - self.failUnlessEqual(grc(42), ref42) + res = pythonapi.PyInt_AsLong(42) + self.failUnlessEqual(grc(res), ref42 + 1) + del res + self.failUnlessEqual(grc(42), ref42) def test_PyObj_FromPtr(self): s = "abc def ghi jkl" diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index 0c62bf2..448f292 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -6,7 +6,7 @@ MyCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int) OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) import _ctypes_test -dll = ctypes.cdll.load(_ctypes_test.__file__) +dll = ctypes.CDLL(_ctypes_test.__file__) class RefcountTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py index ef1f6fd..88dccf2 100644 --- a/Lib/ctypes/test/test_returnfuncptrs.py +++ b/Lib/ctypes/test/test_returnfuncptrs.py @@ -8,7 +8,7 @@ class ReturnFuncPtrTestCase(unittest.TestCase): def test_with_prototype(self): # The _ctypes_test shared lib/dll exports quite some functions for testing. # The get_strchr function returns a *pointer* to the C strchr function. - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) get_strchr = dll.get_strchr get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) strchr = get_strchr() @@ -18,7 +18,7 @@ class ReturnFuncPtrTestCase(unittest.TestCase): self.assertRaises(TypeError, strchr, "abcdef") def test_without_prototype(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) get_strchr = dll.get_strchr # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) get_strchr.restype = c_void_p diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py index 306c585..08c811e 100644 --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -37,18 +37,21 @@ class SlicesTestCase(unittest.TestCase): def test_char_ptr(self): s = "abcdefghijklmnopqrstuvwxyz\0" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) dll.my_strdup.restype = POINTER(c_char) + dll.my_free.restype = None res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)], s) import operator self.assertRaises(TypeError, operator.setslice, res, 0, 5, u"abcde") + dll.my_free(res) dll.my_strdup.restype = POINTER(c_byte) res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)-1], range(ord("a"), ord("z")+1)) + dll.my_free(res) def test_char_array(self): s = "abcdefghijklmnopqrstuvwxyz\0" @@ -65,15 +68,17 @@ class SlicesTestCase(unittest.TestCase): def test_wchar_ptr(self): s = u"abcdefghijklmnopqrstuvwxyz\0" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) dll.my_wcsdup.restype = POINTER(c_wchar) dll.my_wcsdup.argtypes = POINTER(c_wchar), + dll.my_free.restype = None res = dll.my_wcsdup(s) self.failUnlessEqual(res[:len(s)], s) import operator self.assertRaises(TypeError, operator.setslice, res, 0, 5, u"abcde") + dll.my_free(res) if sizeof(c_wchar) == sizeof(c_short): dll.my_wcsdup.restype = POINTER(c_short) @@ -81,8 +86,11 @@ class SlicesTestCase(unittest.TestCase): dll.my_wcsdup.restype = POINTER(c_int) elif sizeof(c_wchar) == sizeof(c_long): dll.my_wcsdup.restype = POINTER(c_long) + else: + return res = dll.my_wcsdup(s) self.failUnlessEqual(res[:len(s)-1], range(ord("a"), ord("z")+1)) + dll.my_free(res) ################################################################ diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py index 183a60c..6ee6ae0 100644 --- a/Lib/ctypes/test/test_stringptr.py +++ b/Lib/ctypes/test/test_stringptr.py @@ -3,7 +3,7 @@ from ctypes import * import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class StringPtrTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index b6eaac4..5340f79 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -294,20 +294,20 @@ class StructureTestCase(unittest.TestCase): # In Python 2.5, Exception is a new-style class, and the repr changed if issubclass(Exception, object): self.failUnlessEqual(msg, - "(Phone) : " + "(Phone) : " "expected string or Unicode object, int found") else: self.failUnlessEqual(msg, - "(Phone) exceptions.TypeError: " + "(Phone) TypeError: " "expected string or Unicode object, int found") cls, msg = self.get_except(Person, "Someone", ("a", "b", "c")) self.failUnlessEqual(cls, RuntimeError) if issubclass(Exception, object): self.failUnlessEqual(msg, - "(Phone) : too many initializers") + "(Phone) : too many initializers") else: - self.failUnlessEqual(msg, "(Phone) exceptions.ValueError: too many initializers") + self.failUnlessEqual(msg, "(Phone) ValueError: too many initializers") def get_except(self, func, *args): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py index bb39746..78c5cf8 100644 --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -8,7 +8,7 @@ except AttributeError: pass else: import _ctypes_test - dll = ctypes.cdll.load(_ctypes_test.__file__) + dll = ctypes.CDLL(_ctypes_test.__file__) wcslen = dll.my_wcslen wcslen.argtypes = [ctypes.c_wchar_p] @@ -66,7 +66,7 @@ else: self.failUnlessEqual(buf[:], u"ab\0\0\0\0") import _ctypes_test - func = ctypes.cdll.load(_ctypes_test.__file__)._testfunc_p_p + func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p class StringTestCase(UnicodeTestCase): def setUp(self): diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py index 1f25f9b..7ba3e21 100644 --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -10,7 +10,7 @@ import _ctypes_test class ValuesTestCase(unittest.TestCase): def test_an_integer(self): - ctdll = cdll.load(_ctypes_test.__file__) + ctdll = CDLL(_ctypes_test.__file__) an_integer = c_int.in_dll(ctdll, "an_integer") x = an_integer.value self.failUnlessEqual(x, ctdll.get_an_integer()) @@ -18,7 +18,7 @@ class ValuesTestCase(unittest.TestCase): self.failUnlessEqual(x*2, ctdll.get_an_integer()) def test_undefined(self): - ctdll = cdll.load(_ctypes_test.__file__) + ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") class Win_ValuesTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py index 3d0b825..8247d37 100644 --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -54,7 +54,7 @@ class Structures(unittest.TestCase): ("right", c_long), ("bottom", c_long)] - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) pt = POINT(10, 10) rect = RECT(0, 0, 20, 20) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py new file mode 100644 index 0000000..d756c1c --- /dev/null +++ b/Lib/ctypes/util.py @@ -0,0 +1,122 @@ +import sys, os +import ctypes + +# find_library(name) returns the pathname of a library, or None. +if os.name == "nt": + def find_library(name): + # See MSDN for the REAL search order. + for directory in os.environ['PATH'].split(os.pathsep): + fname = os.path.join(directory, name) + if os.path.exists(fname): + return fname + if fname.lower().endswith(".dll"): + continue + fname = fname + ".dll" + if os.path.exists(fname): + return fname + return None + +if os.name == "ce": + # search path according to MSDN: + # - absolute path specified by filename + # - The .exe launch directory + # - the Windows directory + # - ROM dll files (where are they?) + # - OEM specified search path: HKLM\Loader\SystemPath + def find_library(name): + return name + +if os.name == "posix" and sys.platform == "darwin": + from ctypes.macholib.dyld import dyld_find as _dyld_find + def find_library(name): + possible = ['lib%s.dylib' % name, + '%s.dylib' % name, + '%s.framework/%s' % (name, name)] + for name in possible: + try: + return _dyld_find(name) + except ValueError: + continue + return None + +elif os.name == "posix": + # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump + import re, tempfile + + def _findLib_gcc(name): + expr = '[^\(\)\s]*lib%s\.[^\(\)\s]*' % name + cmd = 'if type gcc &>/dev/null; then CC=gcc; else CC=cc; fi;' \ + '$CC -Wl,-t -o /dev/null 2>&1 -l' + name + try: + fdout, outfile = tempfile.mkstemp() + fd = os.popen(cmd) + trace = fd.read() + err = fd.close() + finally: + try: + os.unlink(outfile) + except OSError, e: + if e.errno != errno.ENOENT: + raise + res = re.search(expr, trace) + if not res: + return None + return res.group(0) + + def _findLib_ld(name): + expr = '/[^\(\)\s]*lib%s\.[^\(\)\s]*' % name + res = re.search(expr, os.popen('/sbin/ldconfig -p 2>/dev/null').read()) + if not res: + # Hm, this works only for libs needed by the python executable. + cmd = 'ldd %s 2>/dev/null' % sys.executable + res = re.search(expr, os.popen(cmd).read()) + if not res: + return None + return res.group(0) + + def _get_soname(f): + cmd = "objdump -p -j .dynamic 2>/dev/null " + f + res = re.search(r'\sSONAME\s+([^\s]+)', os.popen(cmd).read()) + if not res: + return None + return res.group(1) + + def find_library(name): + lib = _findLib_ld(name) or _findLib_gcc(name) + if not lib: + return None + return _get_soname(lib) + +################################################################ +# test code + +def test(): + from ctypes import cdll + if os.name == "nt": + print cdll.msvcrt + print cdll.load("msvcrt") + print find_library("msvcrt") + + if os.name == "posix": + # find and load_version + print find_library("m") + print find_library("c") + print find_library("bz2") + + # getattr +## print cdll.m +## print cdll.bz2 + + # load + if sys.platform == "darwin": + print cdll.LoadLibrary("libm.dylib") + print cdll.LoadLibrary("libcrypto.dylib") + print cdll.LoadLibrary("libSystem.dylib") + print cdll.LoadLibrary("System.framework/System") + else: + print cdll.LoadLibrary("libm.so") + print cdll.LoadLibrary("libcrypt.so") + print find_library("crypt") + +if __name__ == "__main__": + test() diff --git a/Lib/decimal.py b/Lib/decimal.py index 9815ab3..210db52 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -731,7 +731,7 @@ class Decimal(object): """x.__hash__() <==> hash(x)""" # Decimal integers must hash the same as the ints # Non-integer decimals are normalized and hashed as strings - # Normalization assures that hast(100E-1) == hash(10) + # Normalization assures that hash(100E-1) == hash(10) if self._is_special: if self._isnan(): raise TypeError('Cannot hash a NaN value.') @@ -2246,7 +2246,7 @@ class Context(object): s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']') return ', '.join(s) + ')' - def __context__(self): + def get_manager(self): return ContextManager(self.copy()) def clear_flags(self): diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py index 6dad757..1349abe 100644 --- a/Lib/distutils/ccompiler.py +++ b/Lib/distutils/ccompiler.py @@ -15,7 +15,6 @@ from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group -from distutils.sysconfig import python_build from distutils.util import split_quoted, execute from distutils import log @@ -368,7 +367,7 @@ class CCompiler: # Get the list of expected output (object) files objects = self.object_filenames(sources, - strip_dir=python_build, + strip_dir=0, output_dir=outdir) assert len(objects) == len(sources) @@ -475,8 +474,7 @@ class CCompiler: which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=python_build, - output_dir=output_dir) + objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) if self.force: diff --git a/Lib/distutils/command/bdist_msi.py b/Lib/distutils/command/bdist_msi.py index f05d66c..75db877 100644 --- a/Lib/distutils/command/bdist_msi.py +++ b/Lib/distutils/command/bdist_msi.py @@ -1,5 +1,5 @@ # -*- coding: iso-8859-1 -*- -# Copyright (C) 2005 Martin v. Löwis +# Copyright (C) 2005, 2006 Martin v. Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst @@ -16,7 +16,7 @@ from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError from distutils import log import msilib -from msilib import schema, sequence, uisample +from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data class PyDialog(Dialog): @@ -374,8 +374,8 @@ class bdist_msi (Command): ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), ("ProgressDlg", None, 1280)]) - add_data(db, 'ActionText', uisample.ActionText) - add_data(db, 'UIText', uisample.UIText) + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) ##################################################################### # Standard dialogs: FatalError, UserExit, ExitDialog fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, @@ -502,9 +502,9 @@ class bdist_msi (Command): seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") - c.event("SetTargetPath", "TARGETDIR", order=1) - c.event("SpawnWaitDialog", "WaitForCostingDlg", order=2) - c.event("EndDialog", "Return", order=3) + c.event("SetTargetPath", "TARGETDIR", ordering=1) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=2) + c.event("EndDialog", "Return", ordering=3) c = seldlg.cancel("Cancel", "DirectoryCombo") c.event("SpawnDialog", "CancelDlg") @@ -561,7 +561,7 @@ class bdist_msi (Command): c = whichusers.next("Next >", "Cancel") c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", order = 2) + c.event("EndDialog", "Return", ordering = 2) c = whichusers.cancel("Cancel", "AdminInstall") c.event("SpawnDialog", "CancelDlg") diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index 5771252..9626710 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py @@ -689,6 +689,11 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra + + elif sys.platform == 'darwin': + # Don't use the default code below + return ext.libraries + else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py index 6f4ce81..4a9ed39 100644 --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -6,7 +6,7 @@ from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn from distutils import log -from md5 import md5 +from hashlib import md5 import os import socket import platform diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py index f88f365..d24d0ac 100644 --- a/Lib/distutils/msvccompiler.py +++ b/Lib/distutils/msvccompiler.py @@ -618,7 +618,7 @@ class MSVCCompiler (CCompiler) : "but the expected registry settings are not present.\n" "You must at least run the Visual Studio GUI once " "so that these entries are created.") - break + break return [] def set_path_env_var(self, name): diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 49536f0..e1397a1 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -366,8 +366,8 @@ def _init_posix(): # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and g.has_key('CONFIGURE_MACOSX_DEPLOYMENT_TARGET'): - cfg_target = g['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] + if sys.platform == 'darwin' and g.has_key('MACOSX_DEPLOYMENT_TARGET'): + cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': cur_target = cfg_target @@ -500,6 +500,21 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS'): + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]* ', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py index 56998c3..324819d 100644 --- a/Lib/distutils/unixccompiler.py +++ b/Lib/distutils/unixccompiler.py @@ -42,6 +42,48 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. +def _darwin_compiler_fixup(compiler_so, cc_args): + """ + This function will strip '-isysroot PATH' and '-arch ARCH' from the + compile flags if the user has specified one them in extra_compile_flags. + + This is needed because '-arch ARCH' adds another architecture to the + build, without a way to remove an architecture. Furthermore GCC will + barf if multiple '-isysroot' arguments are present. + """ + stripArch = stripSysroot = 0 + + compiler_so = list(compiler_so) + kernel_version = os.uname()[2] # 8.4.3 + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # OSX before 10.4.0, these don't support -arch and -isysroot at + # all. + stripArch = stripSysroot = True + else: + stripArch = '-arch' in cc_args + stripSysroot = '-isysroot' in cc_args + + if stripArch: + while 1: + try: + index = compiler_so.index('-arch') + # Strip this argument and the next one: + del compiler_so[index:index+2] + except ValueError: + break + + if stripSysroot: + try: + index = compiler_so.index('-isysroot') + # Strip this argument and the next one: + del compiler_so[index:index+1] + except ValueError: + pass + + return compiler_so + class UnixCCompiler(CCompiler): compiler_type = 'unix' @@ -108,8 +150,11 @@ class UnixCCompiler(CCompiler): raise CompileError, msg def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = self.compiler_so + if sys.platform == 'darwin': + compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError, msg: raise CompileError, msg @@ -172,7 +217,22 @@ class UnixCCompiler(CCompiler): else: linker = self.linker_so[:] if target_lang == "c++" and self.compiler_cxx: - linker[0] = self.compiler_cxx[0] + # skip over environment variable settings if /usr/bin/env + # is used to set up the linker's environment. + # This is needed on OSX. Note: this assumes that the + # normal and C++ compiler have the same environment + # settings. + i = 0 + if os.path.basename(linker[0]) == "env": + i = 1 + while '=' in linker[i]: + i = i + 1 + + linker[i] = self.compiler_cxx[i] + + if sys.platform == 'darwin': + linker = _darwin_compiler_fixup(linker, ld_args) + self.spawn(linker + ld_args) except DistutilsExecError, msg: raise LinkError, msg diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 889bf13..1265f4c 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -45,6 +45,7 @@ def get_platform (): osname = string.lower(osname) osname = string.replace(osname, '/', '') machine = string.replace(machine, ' ', '_') + machine = string.replace(machine, '/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- @@ -66,6 +67,54 @@ def get_platform (): m = rel_re.match(release) if m: release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + from distutils.sysconfig import get_config_vars + cfgvars = get_config_vars() + + macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') + if not macver: + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if not macver: + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + f.close() + if m is not None: + macver = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if macver: + from distutils.sysconfig import get_config_vars + release = macver + osname = "macosx" + + + if (release + '.') < '10.4.' and \ + get_config_vars().get('UNIVERSALSDK', '').strip(): + # The universal build will build fat binaries, but not on + # systems before 10.4 + machine = 'fat' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + machine = 'ppc' return "%s-%s-%s" % (osname, release, machine) diff --git a/Lib/doctest.py b/Lib/doctest.py index 70c355a..857bc1a 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -54,6 +54,7 @@ __all__ = [ 'DONT_ACCEPT_BLANKLINE', 'NORMALIZE_WHITESPACE', 'ELLIPSIS', + 'SKIP', 'IGNORE_EXCEPTION_DETAIL', 'COMPARISON_FLAGS', 'REPORT_UDIFF', @@ -128,20 +129,21 @@ warnings.filterwarnings("ignore", "is_private", DeprecationWarning, OPTIONFLAGS_BY_NAME = {} def register_optionflag(name): - flag = 1 << len(OPTIONFLAGS_BY_NAME) - OPTIONFLAGS_BY_NAME[name] = flag - return flag + # Create a new flag unless `name` is already known. + return OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(OPTIONFLAGS_BY_NAME)) DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1') DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE') NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE') ELLIPSIS = register_optionflag('ELLIPSIS') +SKIP = register_optionflag('SKIP') IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL') COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 | DONT_ACCEPT_BLANKLINE | NORMALIZE_WHITESPACE | ELLIPSIS | + SKIP | IGNORE_EXCEPTION_DETAIL) REPORT_UDIFF = register_optionflag('REPORT_UDIFF') @@ -350,7 +352,7 @@ class _OutputRedirectingPdb(pdb.Pdb): """ def __init__(self, out): self.__out = out - pdb.Pdb.__init__(self) + pdb.Pdb.__init__(self, stdout=out) def trace_dispatch(self, *args): # Redirect stdout to the given stream. @@ -1233,6 +1235,10 @@ class DocTestRunner: else: self.optionflags &= ~optionflag + # If 'SKIP' is set, then skip this example. + if self.optionflags & SKIP: + continue + # Record that we started this example. tries += 1 if not quiet: @@ -1792,6 +1798,7 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE ELLIPSIS + SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF @@ -1914,6 +1921,7 @@ def testfile(filename, module_relative=True, name=None, package=None, DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE ELLIPSIS + SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF diff --git a/Lib/dummy_thread.py b/Lib/dummy_thread.py index d69d840..21fd03f 100644 --- a/Lib/dummy_thread.py +++ b/Lib/dummy_thread.py @@ -118,9 +118,6 @@ class LockType(object): def __exit__(self, typ, val, tb): self.release() - def __context__(self): - return self - def release(self): """Release the dummy lock.""" # XXX Perhaps shouldn't actually bother to test? Could lead diff --git a/Lib/easy_install.py b/Lib/easy_install.py deleted file mode 100644 index d87e984..0000000 --- a/Lib/easy_install.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Run the EasyInstall command""" - -if __name__ == '__main__': - from setuptools.command.easy_install import main - main() diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 5821ddf..a08c43e 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -367,6 +367,7 @@ class AddrlistClass: break elif allowcomments and self.field[self.pos] == '(': slist.append(self.getcomment()) + continue # have already advanced pos from getcomment elif self.field[self.pos] == '\\': quote = True else: diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py index d977693..a197a36 100644 --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -2215,6 +2215,12 @@ class TestMiscellaneous(TestEmailBase): ['foo: ;', '"Jason R. Mastaler" ']), [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" + eq = self.assertEqual + addrs = Utils.getaddresses(['User ((nested comment)) ']) + eq(addrs[0][1], 'foo@bar.com') + def test_utils_quote_unquote(self): eq = self.assertEqual msg = Message() diff --git a/Lib/email/test/test_email_renamed.py b/Lib/email/test/test_email_renamed.py index 4ac2ee9..95d06cb 100644 --- a/Lib/email/test/test_email_renamed.py +++ b/Lib/email/test/test_email_renamed.py @@ -2221,6 +2221,12 @@ class TestMiscellaneous(TestEmailBase): ['foo: ;', '"Jason R. Mastaler" ']), [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" + eq = self.assertEqual + addrs = utils.getaddresses(['User ((nested comment)) ']) + eq(addrs[0][1], 'foo@bar.com') + def test_utils_quote_unquote(self): eq = self.assertEqual msg = Message() diff --git a/Lib/gzip.py b/Lib/gzip.py index 3c1ebf2..860accc 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -107,6 +107,8 @@ class GzipFile: self.extrabuf = "" self.extrasize = 0 self.filename = filename + # Starts small, scales exponentially + self.min_readsize = 100 elif mode[0:1] == 'w' or mode[0:1] == 'a': self.mode = WRITE @@ -381,32 +383,35 @@ class GzipFile: self.read(count % 1024) def readline(self, size=-1): - if size < 0: size = sys.maxint + if size < 0: + size = sys.maxint + readsize = self.min_readsize + else: + readsize = size bufs = [] - readsize = min(100, size) # Read from the file in small chunks - while True: - if size == 0: - return "".join(bufs) # Return resulting line - + while size != 0: c = self.read(readsize) i = c.find('\n') - if size is not None: - # We set i=size to break out of the loop under two - # conditions: 1) there's no newline, and the chunk is - # larger than size, or 2) there is a newline, but the - # resulting line would be longer than 'size'. - if i==-1 and len(c) > size: i=size-1 - elif size <= i: i = size -1 + + # We set i=size to break out of the loop under two + # conditions: 1) there's no newline, and the chunk is + # larger than size, or 2) there is a newline, but the + # resulting line would be longer than 'size'. + if (size <= i) or (i == -1 and len(c) > size): + i = size - 1 if i >= 0 or c == '': - bufs.append(c[:i+1]) # Add portion of last chunk - self._unread(c[i+1:]) # Push back rest of chunk - return ''.join(bufs) # Return resulting line + bufs.append(c[:i + 1]) # Add portion of last chunk + self._unread(c[i + 1:]) # Push back rest of chunk + break # Append chunk to list, decrease 'size', bufs.append(c) size = size - len(c) readsize = min(size, readsize * 2) + if readsize > self.min_readsize: + self.min_readsize = min(readsize, self.min_readsize * 2, 512) + return ''.join(bufs) # Return resulting line def readlines(self, sizehint=0): # Negative numbers result in reading all the lines diff --git a/Lib/httplib.py b/Lib/httplib.py index b4bd536..36381de 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -796,11 +796,20 @@ class HTTPConnection: nil, netloc, nil, nil, nil = urlsplit(url) if netloc: - self.putheader('Host', netloc.encode("idna")) - elif self.port == HTTP_PORT: - self.putheader('Host', self.host.encode("idna")) + try: + netloc_enc = netloc.encode("ascii") + except UnicodeEncodeError: + netloc_enc = netloc.encode("idna") + self.putheader('Host', netloc_enc) else: - self.putheader('Host', "%s:%s" % (self.host.encode("idna"), self.port)) + try: + host_enc = self.host.encode("ascii") + except UnicodeEncodeError: + host_enc = self.host.encode("idna") + if self.port == HTTP_PORT: + self.putheader('Host', host_enc) + else: + self.putheader('Host', "%s:%s" % (host_enc, self.port)) # note: we are assuming that clients will not attempt to set these # headers since *this* library must deal with the diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 8163330..25e5d40 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,3 +1,8 @@ +What's New in IDLE 1.2a2? +========================= + +*Release date: 27-APR-2006* + What's New in IDLE 1.2a1? ========================= diff --git a/Lib/idlelib/configHelpSourceEdit.py b/Lib/idlelib/configHelpSourceEdit.py index 3db1e0a..8924f79 100644 --- a/Lib/idlelib/configHelpSourceEdit.py +++ b/Lib/idlelib/configHelpSourceEdit.py @@ -151,6 +151,7 @@ class GetHelpSourceDialog(Toplevel): pass else: # Mac Safari insists on using the URI form for local files + self.result = list(self.result) self.result[1] = "file://" + path self.destroy() diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index fbde56c..b7deb3f 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "1.2a1" +IDLE_VERSION = "1.2a2" diff --git a/Lib/imputil.py b/Lib/imputil.py index e6ad7ec..8a49bb1 100644 --- a/Lib/imputil.py +++ b/Lib/imputil.py @@ -131,9 +131,12 @@ class ImportManager: if importer: return importer._finish_import(top_module, parts[1:], fromlist) - # Grrr, some people "import os.path" + # Grrr, some people "import os.path" or do "from os.path import ..." if len(parts) == 2 and hasattr(top_module, parts[1]): - return top_module + if fromlist: + return getattr(top_module, parts[1]) + else: + return top_module # If the importer does not exist, then we have to bail. A missing # importer means that something else imported the module, and we have diff --git a/Lib/inspect.py b/Lib/inspect.py index 2e4d987..bf7f006 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -353,7 +353,13 @@ def getsourcefile(object): if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix: # Looks like a binary file. We want to only return a text file. return None - if os.path.exists(filename) or hasattr(getmodule(object), '__loader__'): + if os.path.exists(filename): + return filename + # Ugly but necessary - '' and '' mean that getmodule() + # would infinitely recurse, because they're not real files nor loadable + # Note that this means that writing a PEP 302 loader that uses '<' + # at the start of a filename is now not a good idea. :( + if filename[:1]!='<' and hasattr(getmodule(object), '__loader__'): return filename def getabsfile(object): @@ -406,7 +412,11 @@ def findsource(object): in the file and the line number indexes a line in that list. An IOError is raised if the source code cannot be retrieved.""" file = getsourcefile(object) or getfile(object) - lines = linecache.getlines(file, getmodule(object).__dict__) + module = getmodule(object) + if module: + lines = linecache.getlines(file, module.__dict__) + else: + lines = linecache.getlines(file) if not lines: raise IOError('could not get source code') diff --git a/Lib/lib-tk/turtle.py b/Lib/lib-tk/turtle.py index a395613..d68e405 100644 --- a/Lib/lib-tk/turtle.py +++ b/Lib/lib-tk/turtle.py @@ -1,8 +1,24 @@ # LogoMation-like turtle graphics +""" +Turtle graphics is a popular way for introducing programming to +kids. It was part of the original Logo programming language developed +by Wally Feurzeig and Seymour Papert in 1966. + +Imagine a robotic turtle starting at (0, 0) in the x-y plane. Give it +the command turtle.forward(15), and it moves (on-screen!) 15 pixels in +the direction it is facing, drawing a line as it moves. Give it the +command turtle.left(25), and it rotates in-place 25 degrees clockwise. + +By combining together these and similar commands, intricate shapes and +pictures can easily be drawn. +""" + from math import * # Also for export import Tkinter +speeds = ['fastest', 'fast', 'normal', 'slow', 'slowest'] + class Error(Exception): pass @@ -13,17 +29,42 @@ class RawPen: self._items = [] self._tracing = 1 self._arrow = 0 + self._delay = 10 # default delay for drawing self.degrees() self.reset() def degrees(self, fullcircle=360.0): + """ Set angle measurement units to degrees. + + Example: + >>> turtle.degrees() + """ self._fullcircle = fullcircle self._invradian = pi / (fullcircle * 0.5) def radians(self): + """ Set the angle measurement units to radians. + + Example: + >>> turtle.radians() + """ self.degrees(2.0*pi) def reset(self): + """ Clear the screen, re-center the pen, and set variables to + the default values. + + Example: + >>> turtle.position() + [0.0, -22.0] + >>> turtle.heading() + 100.0 + >>> turtle.reset() + >>> turtle.position() + [0.0, 0.0] + >>> turtle.heading() + 0.0 + """ canvas = self._canvas self._canvas.update() width = canvas.winfo_width() @@ -45,6 +86,11 @@ class RawPen: canvas._root().tkraise() def clear(self): + """ Clear the screen. The turtle does not move. + + Example: + >>> turtle.clear() + """ self.fill(0) canvas = self._canvas items = self._items @@ -55,37 +101,130 @@ class RawPen: self._draw_turtle() def tracer(self, flag): + """ Set tracing on if flag is True, and off if it is False. + Tracing means line are drawn more slowly, with an + animation of an arrow along the line. + + Example: + >>> turtle.tracer(False) # turns off Tracer + """ self._tracing = flag if not self._tracing: self._delete_turtle() self._draw_turtle() def forward(self, distance): + """ Go forward distance steps. + + Example: + >>> turtle.position() + [0.0, 0.0] + >>> turtle.forward(25) + >>> turtle.position() + [25.0, 0.0] + >>> turtle.forward(-75) + >>> turtle.position() + [-50.0, 0.0] + """ x0, y0 = start = self._position x1 = x0 + distance * cos(self._angle*self._invradian) y1 = y0 - distance * sin(self._angle*self._invradian) self._goto(x1, y1) def backward(self, distance): + """ Go backwards distance steps. + + The turtle's heading does not change. + + Example: + >>> turtle.position() + [0.0, 0.0] + >>> turtle.backward(30) + >>> turtle.position() + [-30.0, 0.0] + """ self.forward(-distance) def left(self, angle): + """ Turn left angle units (units are by default degrees, + but can be set via the degrees() and radians() functions.) + + When viewed from above, the turning happens in-place around + its front tip. + + Example: + >>> turtle.heading() + 22 + >>> turtle.left(45) + >>> turtle.heading() + 67.0 + """ self._angle = (self._angle + angle) % self._fullcircle self._draw_turtle() def right(self, angle): + """ Turn right angle units (units are by default degrees, + but can be set via the degrees() and radians() functions.) + + When viewed from above, the turning happens in-place around + its front tip. + + Example: + >>> turtle.heading() + 22 + >>> turtle.right(45) + >>> turtle.heading() + 337.0 + """ self.left(-angle) def up(self): + """ Pull the pen up -- no drawing when moving. + + Example: + >>> turtle.up() + """ self._drawing = 0 def down(self): + """ Put the pen down -- draw when moving. + + Example: + >>> turtle.down() + """ self._drawing = 1 def width(self, width): + """ Set the line to thickness to width. + + Example: + >>> turtle.width(10) + """ self._width = float(width) def color(self, *args): + """ Set the pen color. + + Three input formats are allowed: + + color(s) + s is a Tk specification string, such as "red" or "yellow" + + color((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range [0..1] + + color(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range [0..1] + + Example: + + >>> turtle.color('brown') + >>> tup = (0.2, 0.8, 0.55) + >>> turtle.color(tup) + >>> turtle.color(0, .5, 0) + """ if not args: raise Error, "no color arguments" if len(args) == 1: @@ -118,11 +257,20 @@ class RawPen: self._color = color self._draw_turtle() - def write(self, arg, move=0): - x, y = start = self._position + def write(self, text, move=False): + """ Write text at the current pen position. + + If move is true, the pen is moved to the bottom-right corner + of the text. By default, move is False. + + Example: + >>> turtle.write('The race is on!') + >>> turtle.write('Home = (0, 0)', True) + """ + x, y = self._position x = x-1 # correction -- calibrated for Windows item = self._canvas.create_text(x, y, - text=str(arg), anchor="sw", + text=str(text), anchor="sw", fill=self._color) self._items.append(item) if move: @@ -131,6 +279,20 @@ class RawPen: self._draw_turtle() def fill(self, flag): + """ Call fill(1) before drawing the shape you + want to fill, and fill(0) when done. + + Example: + >>> turtle.fill(1) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.fill(0) + """ if self._filling: path = tuple(self._path) smooth = self._filling < 0 @@ -139,7 +301,6 @@ class RawPen: {'fill': self._color, 'smooth': smooth}) self._items.append(item) - self._canvas.lower(item) if self._tofill: for item in self._tofill: self._canvas.itemconfigure(item, fill=self._color) @@ -151,16 +312,62 @@ class RawPen: self._path.append(self._position) self.forward(0) + def begin_fill(self): + """ Called just before drawing a shape to be filled. + + Example: + >>> turtle.begin_fill() + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.end_fill() + """ + self.fill(1) + + def end_fill(self): + """ Called after drawing a shape to be filled. + + Example: + >>> turtle.begin_fill() + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.left(90) + >>> turtle.forward(100) + >>> turtle.end_fill() + """ + self.fill(0) + def circle(self, radius, extent=None): + """ Draw a circle with given radius. + The center is radius units left of the turtle; extent + determines which part of the circle is drawn. If not given, + the entire circle is drawn. + + If extent is not a full circle, one endpoint of the arc is the + current pen position. The arc is drawn in a counter clockwise + direction if radius is positive, otherwise in a clockwise + direction. In the process, the direction of the turtle is + changed by the amount of the extent. + + >>> turtle.circle(50) + >>> turtle.circle(120, 180) # half a circle + """ if extent is None: extent = self._fullcircle x0, y0 = self._position xc = x0 - radius * sin(self._angle * self._invradian) yc = y0 - radius * cos(self._angle * self._invradian) if radius >= 0.0: - start = self._angle - 90.0 + start = self._angle - (self._fullcircle / 4.0) else: - start = self._angle + 90.0 + start = self._angle + (self._fullcircle / 4.0) extent = -extent if self._filling: if abs(extent) >= self._fullcircle: @@ -202,40 +409,145 @@ class RawPen: self._draw_turtle() def heading(self): + """ Return the turtle's current heading. + + Example: + >>> turtle.heading() + 67.0 + """ return self._angle def setheading(self, angle): + """ Set the turtle facing the given angle. + + Here are some common directions in degrees: + + 0 - east + 90 - north + 180 - west + 270 - south + + Example: + >>> turtle.setheading(90) + >>> turtle.heading() + 90 + >>> turtle.setheading(128) + >>> turtle.heading() + 128 + """ self._angle = angle self._draw_turtle() def window_width(self): + """ Returns the width of the turtle window. + + Example: + >>> turtle.window_width() + 640 + """ width = self._canvas.winfo_width() if width <= 1: # the window isn't managed by a geometry manager width = self._canvas['width'] return width def window_height(self): + """ Return the height of the turtle window. + + Example: + >>> turtle.window_height() + 768 + """ height = self._canvas.winfo_height() if height <= 1: # the window isn't managed by a geometry manager height = self._canvas['height'] return height def position(self): + """ Return the current (x, y) location of the turtle. + + Example: + >>> turtle.position() + [0.0, 240.0] + """ x0, y0 = self._origin x1, y1 = self._position return [x1-x0, -y1+y0] def setx(self, xpos): + """ Set the turtle's x coordinate to be xpos. + + Example: + >>> turtle.position() + [10.0, 240.0] + >>> turtle.setx(10) + >>> turtle.position() + [10.0, 240.0] + """ x0, y0 = self._origin x1, y1 = self._position self._goto(x0+xpos, y1) def sety(self, ypos): + """ Set the turtle's y coordinate to be ypos. + + Example: + >>> turtle.position() + [0.0, 0.0] + >>> turtle.sety(-22) + >>> turtle.position() + [0.0, -22.0] + """ x0, y0 = self._origin x1, y1 = self._position self._goto(x1, y0-ypos) + def towards(self, *args): + """Returs the angle, which corresponds to the line + from turtle-position to point (x,y). + + Argument can be two coordinates or one pair of coordinates + or a RawPen/Pen instance. + + Example: + >>> turtle.position() + [10.0, 10.0] + >>> turtle.towards(0,0) + 225.0 + """ + if len(args) == 2: + x, y = args + else: + arg = args[0] + if isinstance(arg, RawPen): + x, y = arg.position() + else: + x, y = arg + x0, y0 = self.position() + dx = x - x0 + dy = y - y0 + return (atan2(dy,dx) / self._invradian) % self._fullcircle + def goto(self, *args): + """ Go to the given point. + + If the pen is down, then a line will be drawn. The turtle's + orientation does not change. + + Two input formats are accepted: + + goto(x, y) + go to point (x, y) + + goto((x, y)) + go to point (x, y) + + Example: + >>> turtle.position() + [0.0, 0.0] + >>> turtle.goto(50, -45) + >>> turtle.position() + [50.0, -45.0] + """ if len(args) == 1: try: x, y = args[0] @@ -250,7 +562,7 @@ class RawPen: self._goto(x0+x, y0-y) def _goto(self, x1, y1): - x0, y0 = start = self._position + x0, y0 = self._position self._position = map(float, (x1, y1)) if self._filling: self._path.append(self._position) @@ -270,7 +582,7 @@ class RawPen: self._canvas.coords(item, x0, y0, x, y) self._draw_turtle((x,y)) self._canvas.update() - self._canvas.after(10) + self._canvas.after(self._delay) # in case nhops==0 self._canvas.coords(item, x0, y0, x1, y1) self._canvas.itemconfigure(item, arrow="none") @@ -285,7 +597,42 @@ class RawPen: self._items.append(item) self._draw_turtle() - def _draw_turtle(self,position=[]): + def speed(self, speed): + """ Set the turtle's speed. + + speed must one of these five strings: + + 'fastest' is a 0 ms delay + 'fast' is a 5 ms delay + 'normal' is a 10 ms delay + 'slow' is a 15 ms delay + 'slowest' is a 20 ms delay + + Example: + >>> turtle.speed('slow') + """ + try: + speed = speed.strip().lower() + self._delay = speeds.index(speed) * 5 + except: + raise ValueError("%r is not a valid speed. speed must be " + "one of %s" % (speed, speeds)) + + + def delay(self, delay): + """ Set the drawing delay in milliseconds. + + This is intended to allow finer control of the drawing speed + than the speed() method + + Example: + >>> turtle.delay(15) + """ + if int(delay) < 0: + raise ValueError("delay must be greater than or equal to 0") + self._delay = int(delay) + + def _draw_turtle(self, position=[]): if not self._tracing: return if position == []: @@ -305,13 +652,17 @@ class RawPen: def _delete_turtle(self): if self._arrow != 0: self._canvas.delete(self._arrow) - self._arrow = 0 - + self._arrow = 0 _root = None _canvas = None _pen = None +_width = 0.50 # 50% of window width +_height = 0.75 # 75% of window height +_startx = None +_starty = None +_title = "Turtle Graphics" # default title class Pen(RawPen): @@ -320,10 +671,15 @@ class Pen(RawPen): if _root is None: _root = Tkinter.Tk() _root.wm_protocol("WM_DELETE_WINDOW", self._destroy) + _root.title(_title) + if _canvas is None: # XXX Should have scroll bars _canvas = Tkinter.Canvas(_root, background="white") _canvas.pack(expand=1, fill="both") + + setup(width=_width, height= _height, startx=_startx, starty=_starty) + RawPen.__init__(self, _canvas) def _destroy(self): @@ -335,13 +691,18 @@ class Pen(RawPen): _canvas = None root.destroy() - def _getpen(): global _pen - pen = _pen - if not pen: - _pen = pen = Pen() - return pen + if not _pen: + _pen = Pen() + return _pen + +class Turtle(Pen): + pass + +"""For documentation of the following functions see + the RawPen methods with the same names +""" def degrees(): _getpen().degrees() def radians(): _getpen().radians() @@ -358,6 +719,8 @@ def width(width): _getpen().width(width) def color(*args): _getpen().color(*args) def write(arg, move=0): _getpen().write(arg, move) def fill(flag): _getpen().fill(flag) +def begin_fill(): _getpen().begin_fill() +def end_fill(): _getpen.end_fill() def circle(radius, extent=None): _getpen().circle(radius, extent) def goto(*args): _getpen().goto(*args) def heading(): return _getpen().heading() @@ -367,6 +730,106 @@ def window_width(): return _getpen().window_width() def window_height(): return _getpen().window_height() def setx(xpos): _getpen().setx(xpos) def sety(ypos): _getpen().sety(ypos) +def towards(*args): return _getpen().towards(*args) + +def done(): _root.mainloop() +def delay(delay): return _getpen().delay(delay) +def speed(speed): return _getpen().speed(speed) + +for methodname in dir(RawPen): + """ copies RawPen docstrings to module functions of same name """ + if not methodname.startswith("_"): + eval(methodname).__doc__ = RawPen.__dict__[methodname].__doc__ + + +def setup(**geometry): + """ Sets the size and position of the main window. + + Keywords are width, height, startx and starty + + width: either a size in pixels or a fraction of the screen. + Default is 50% of screen. + height: either the height in pixels or a fraction of the screen. + Default is 75% of screen. + + Setting either width or height to None before drawing will force + use of default geometry as in older versions of turtle.py + + startx: starting position in pixels from the left edge of the screen. + Default is to center window. Setting startx to None is the default + and centers window horizontally on screen. + + starty: starting position in pixels from the top edge of the screen. + Default is to center window. Setting starty to None is the default + and centers window vertically on screen. + + Examples: + >>> setup (width=200, height=200, startx=0, starty=0) + + sets window to 200x200 pixels, in upper left of screen + + >>> setup(width=.75, height=0.5, startx=None, starty=None) + + sets window to 75% of screen by 50% of screen and centers + + >>> setup(width=None) + + forces use of default geometry as in older versions of turtle.py + """ + + global _width, _height, _startx, _starty + + width = geometry.get('width',_width) + if width >= 0 or width == None: + _width = width + else: + raise ValueError, "width can not be less than 0" + + height = geometry.get('height',_height) + if height >= 0 or height == None: + _height = height + else: + raise ValueError, "height can not be less than 0" + + startx = geometry.get('startx', _startx) + if startx >= 0 or startx == None: + _startx = _startx + else: + raise ValueError, "startx can not be less than 0" + + starty = geometry.get('starty', _starty) + if starty >= 0 or starty == None: + _starty = starty + else: + raise ValueError, "startx can not be less than 0" + + + if _root and _width and _height: + if 0 < _width <= 1: + _width = _root.winfo_screenwidth() * +width + if 0 < _height <= 1: + _height = _root.winfo_screenheight() * _height + + # center window on screen + if _startx is None: + _startx = (_root.winfo_screenwidth() - _width) / 2 + + if _starty is None: + _starty = (_root.winfo_screenheight() - _height) / 2 + + _root.geometry("%dx%d+%d+%d" % (_width, _height, _startx, _starty)) + +def title(title): + """ set the window title. + + By default this is set to 'Turtle Graphics' + + Example: + >>> title("My Window") + """ + + global _title + _title = title def demo(): reset() @@ -417,10 +880,94 @@ def demo(): forward(20) right(90) fill(0) + tracer(1) # more text write("end") - if __name__ == '__main__': - _root.mainloop() + +def demo2(): + # exercises some new and improved features + speed('fast') + width(3) + + # draw a segmented half-circle + setheading(towards(0,0)) + x,y = position() + r = (x**2+y**2)**.5/2.0 + right(90) + pendown = True + for i in range(18): + if pendown: + up() + pendown = False + else: + down() + pendown = True + circle(r,10) + sleep(2) + + reset() + left(90) + + # draw a series of triangles + l = 10 + color("green") + width(3) + left(180) + sp = 5 + for i in range(-2,16): + if i > 0: + color(1.0-0.05*i,0,0.05*i) + fill(1) + color("green") + for j in range(3): + forward(l) + left(120) + l += 10 + left(15) + if sp > 0: + sp = sp-1 + speed(speeds[sp]) + color(0.25,0,0.75) + fill(0) + color("green") + + left(130) + up() + forward(90) + color("red") + speed('fastest') + down(); + + # create a second turtle and make the original pursue and catch it + turtle=Turtle() + turtle.reset() + turtle.left(90) + turtle.speed('normal') + turtle.up() + turtle.goto(280,40) + turtle.left(24) + turtle.down() + turtle.speed('fast') + turtle.color("blue") + turtle.width(2) + speed('fastest') + + # turn default turtle towards new turtle object + setheading(towards(turtle)) + while ( abs(position()[0]-turtle.position()[0])>4 or + abs(position()[1]-turtle.position()[1])>4): + turtle.forward(3.5) + turtle.left(0.6) + # turn default turtle towards new turtle object + setheading(towards(turtle)) + forward(4) + write("CAUGHT! ", move=True) + + if __name__ == '__main__': + from time import sleep demo() + sleep(3) + demo2() + done() diff --git a/Lib/locale.py b/Lib/locale.py index cfc69b1..fd549bb 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -88,13 +88,16 @@ except ImportError: ### Number formatting APIs # Author: Martin von Loewis +# improved by Georg Brandl #perform the grouping from right to left -def _group(s): - conv=localeconv() - grouping=conv['grouping'] - if not grouping:return (s, 0) - result="" +def _group(s, monetary=False): + conv = localeconv() + thousands_sep = conv[monetary and 'mon_thousands_sep' or 'thousands_sep'] + grouping = conv[monetary and 'mon_grouping' or 'grouping'] + if not grouping: + return (s, 0) + result = "" seps = 0 spaces = "" if s[-1] == ' ': @@ -103,63 +106,142 @@ def _group(s): s = s[:sp] while s and grouping: # if grouping is -1, we are done - if grouping[0]==CHAR_MAX: + if grouping[0] == CHAR_MAX: break # 0: re-use last group ad infinitum - elif grouping[0]!=0: + elif grouping[0] != 0: #process last group - group=grouping[0] - grouping=grouping[1:] + group = grouping[0] + grouping = grouping[1:] if result: - result=s[-group:]+conv['thousands_sep']+result + result = s[-group:] + thousands_sep + result seps += 1 else: - result=s[-group:] - s=s[:-group] + result = s[-group:] + s = s[:-group] if s and s[-1] not in "0123456789": # the leading string is only spaces and signs - return s+result+spaces,seps + return s + result + spaces, seps if not result: - return s+spaces,seps + return s + spaces, seps if s: - result=s+conv['thousands_sep']+result + result = s + thousands_sep + result seps += 1 - return result+spaces,seps - -def format(f,val,grouping=0): - """Formats a value in the same way that the % formatting would use, + return result + spaces, seps + +def format(percent, value, grouping=False, monetary=False, *additional): + """Returns the locale-aware substitution of a %? specifier + (percent). + + additional is for format strings which contain one or more + '*' modifiers.""" + # this is only for one-percent-specifier strings and this should be checked + if percent[0] != '%': + raise ValueError("format() must be given exactly one %char " + "format specifier") + if additional: + formatted = percent % ((value,) + additional) + else: + formatted = percent % value + # floats and decimal ints need special action! + if percent[-1] in 'eEfFgG': + seps = 0 + parts = formatted.split('.') + if grouping: + parts[0], seps = _group(parts[0], monetary=monetary) + decimal_point = localeconv()[monetary and 'mon_decimal_point' + or 'decimal_point'] + formatted = decimal_point.join(parts) + while seps: + sp = formatted.find(' ') + if sp == -1: break + formatted = formatted[:sp] + formatted[sp+1:] + seps -= 1 + elif percent[-1] in 'diu': + if grouping: + formatted = _group(formatted, monetary=monetary)[0] + return formatted + +import re, operator +_percent_re = re.compile(r'%(?:\((?P.*?)\))?' + r'(?P[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]') + +def format_string(f, val, grouping=False): + """Formats a string in the same way that the % formatting would use, but takes the current locale into account. Grouping is applied if the third parameter is true.""" - result = f % val - fields = result.split(".") - seps = 0 - if grouping: - fields[0],seps=_group(fields[0]) - if len(fields)==2: - result = fields[0]+localeconv()['decimal_point']+fields[1] - elif len(fields)==1: - result = fields[0] + 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 operator.isMappingType(val): + for perc in percents: + key = perc.group("key") + val[key] = format(perc.group(), val[key], grouping) else: - raise Error, "Too many decimal points in result string" + # val is a single value + val = format(percents[0].group(), val, grouping) + + return new_f % val + +def currency(val, symbol=True, grouping=False, international=False): + """Formats val according to the currency settings + in the current locale.""" + conv = localeconv() - while seps: - # If the number was formatted for a specific width, then it - # might have been filled with spaces to the left or right. If - # so, kill as much spaces as there where separators. - # Leading zeroes as fillers are not yet dealt with, as it is - # not clear how they should interact with grouping. - sp = result.find(" ") - if sp==-1:break - result = result[:sp]+result[sp+1:] - seps -= 1 + # check for illegal values + digits = conv[international and 'int_frac_digits' or 'frac_digits'] + if digits == 127: + raise ValueError("Currency formatting is not possible using " + "the 'C' locale.") + + s = format('%%.%if' % digits, abs(val), grouping, monetary=True) + # '<' and '>' are markers if the sign must be inserted between symbol and value + s = '<' + s + '>' + + if symbol: + smb = conv[international and 'int_curr_symbol' or 'currency_symbol'] + precedes = conv[val<0 and 'n_cs_precedes' or 'p_cs_precedes'] + separated = conv[val<0 and 'n_sep_by_space' or 'p_sep_by_space'] + + if precedes: + s = smb + (separated and ' ' or '') + s + else: + s = s + (separated and ' ' or '') + smb + + sign_pos = conv[val<0 and 'n_sign_posn' or 'p_sign_posn'] + sign = conv[val<0 and 'negative_sign' or 'positive_sign'] + + if sign_pos == 0: + s = '(' + s + ')' + elif sign_pos == 1: + s = sign + s + elif sign_pos == 2: + s = s + sign + elif sign_pos == 3: + s = s.replace('<', sign) + elif sign_pos == 4: + s = s.replace('>', sign) + else: + # the default if nothing specified; + # this should be the most fitting sign position + s = sign + s - return result + return s.replace('<', '').replace('>', '') def str(val): """Convert float to integer, taking the locale into account.""" - return format("%.12g",val) + return format("%.12g", val) -def atof(string,func=float): +def atof(string, func=float): "Parses a string as a float according to the locale settings." #First, get rid of the grouping ts = localeconv()['thousands_sep'] @@ -179,10 +261,10 @@ def atoi(str): def _test(): setlocale(LC_ALL, "") #do grouping - s1=format("%d", 123456789,1) + s1 = format("%d", 123456789,1) print s1, "is", atoi(s1) #standard formatting - s1=str(3.14) + s1 = str(3.14) print s1, "is", atof(s1) ### Locale name aliasing engine diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 9798931..c65d07f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -40,7 +40,7 @@ except ImportError: thread = None __author__ = "Vinay Sajip " -__status__ = "beta" +__status__ = "production" __version__ = "0.4.9.9" __date__ = "06 February 2006" diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 8e569a7..e0da254 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -44,6 +44,8 @@ DEFAULT_HTTP_LOGGING_PORT = 9022 DEFAULT_SOAP_LOGGING_PORT = 9023 SYSLOG_UDP_PORT = 514 +_MIDNIGHT = 24 * 60 * 60 # number of seconds in a day + class BaseRotatingHandler(logging.FileHandler): """ Base class for handlers that rotate log files at a certain point. @@ -217,12 +219,8 @@ class TimedRotatingFileHandler(BaseRotatingHandler): currentMinute = t[4] currentSecond = t[5] # r is the number of seconds left between now and midnight - if (currentMinute == 0) and (currentSecond == 0): - r = (24 - currentHour) * 60 * 60 # number of hours in seconds - else: - r = (23 - currentHour) * 60 * 60 - r = r + (59 - currentMinute) * 60 # plus the number of minutes (in secs) - r = r + (60 - currentSecond) # plus the number of seconds + r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 + + currentSecond) self.rolloverAt = currentTime + r # If we are rolling over on a certain day, add in the number of days until # the next rollover, but offset by 1 since we just calculated the time diff --git a/Lib/mailbox.py b/Lib/mailbox.py index c89c1a4..bb115e1 100755 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -1,93 +1,1907 @@ #! /usr/bin/env python -"""Classes to handle Unix style, MMDF style, and MH style mailboxes.""" +"""Read/write support for Maildir, mbox, MH, Babyl, and MMDF mailboxes.""" - -import rfc822 +import sys import os +import time +import calendar +import socket +import errno +import copy +import email +import email.Message +import email.Generator +import rfc822 +import StringIO +try: + import fnctl +except ImportError: + fcntl = None -__all__ = ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox", - "PortableUnixMailbox"] +__all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', + 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', + 'BabylMessage', 'MMDFMessage', 'UnixMailbox', + 'PortableUnixMailbox', 'MmdfMailbox', 'MHMailbox', 'BabylMailbox' ] -class _Mailbox: +class Mailbox: + """A group of messages in a particular place.""" - def __init__(self, fp, factory=rfc822.Message): - self.fp = fp - self.seekp = 0 - self.factory = factory + def __init__(self, path, factory=None, create=True): + """Initialize a Mailbox instance.""" + self._path = os.path.abspath(os.path.expanduser(path)) + self._factory = factory + + def add(self, message): + """Add message and return assigned key.""" + raise NotImplementedError('Method must be implemented by subclass') + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __delitem__(self, key): + self.remove(key) + + def discard(self, key): + """If the keyed message exists, remove it.""" + try: + self.remove(key) + except KeyError: + pass + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get(self, key, default=None): + """Return the keyed message, or default if it doesn't exist.""" + try: + return self.__getitem__(key) + except KeyError: + return default + + def __getitem__(self, key): + """Return the keyed message; raise KeyError if it doesn't exist.""" + if not self._factory: + return self.get_message(key) + else: + return self._factory(self.get_file(key)) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def iterkeys(self): + """Return an iterator over keys.""" + raise NotImplementedError('Method must be implemented by subclass') + + def keys(self): + """Return a list of keys.""" + return list(self.iterkeys()) + + def itervalues(self): + """Return an iterator over all messages.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield value def __iter__(self): - return iter(self.next, None) + return self.itervalues() + + def values(self): + """Return a list of messages. Memory intensive.""" + return list(self.itervalues()) + + def iteritems(self): + """Return an iterator over (key, message) tuples.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield (key, value) + + def items(self): + """Return a list of (key, message) tuples. Memory intensive.""" + return list(self.iteritems()) + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __contains__(self, key): + return self.has_key(key) + + def __len__(self): + """Return a count of messages in the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def clear(self): + """Delete all messages.""" + for key in self.iterkeys(): + self.discard(key) + + def pop(self, key, default=None): + """Delete the keyed message and return it, or default.""" + try: + result = self[key] + except KeyError: + return default + self.discard(key) + return result + + def popitem(self): + """Delete an arbitrary (key, message) pair and return it.""" + for key in self.iterkeys(): + return (key, self.pop(key)) # This is only run once. + else: + raise KeyError('No messages in mailbox') + + def update(self, arg=None): + """Change the messages that correspond to certain keys.""" + if hasattr(arg, 'iteritems'): + source = arg.iteritems() + elif hasattr(arg, 'items'): + source = arg.items() + else: + source = arg + bad_key = False + for key, message in source: + try: + self[key] = message + except KeyError: + bad_key = True + if bad_key: + raise KeyError('No message with key(s)') + + def flush(self): + """Write any pending changes to the disk.""" + raise NotImplementedError('Method must be implemented by subclass') + + def lock(self): + """Lock the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def unlock(self): + """Unlock the mailbox if it is locked.""" + raise NotImplementedError('Method must be implemented by subclass') + + def close(self): + """Flush and close the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + def _dump_message(self, message, target, mangle_from_=False): + # Most files are opened in binary mode to allow predictable seeking. + # To get native line endings on disk, the user-friendly \n line endings + # used in strings and by email.Message are translated here. + """Dump message contents to target file.""" + if isinstance(message, email.Message.Message): + buffer = StringIO.StringIO() + gen = email.Generator.Generator(buffer, mangle_from_, 0) + gen.flatten(message) + buffer.seek(0) + target.write(buffer.read().replace('\n', os.linesep)) + elif isinstance(message, str): + if mangle_from_: + message = message.replace('\nFrom ', '\n>From ') + message = message.replace('\n', os.linesep) + target.write(message) + elif hasattr(message, 'read'): + while True: + line = message.readline() + if line == '': + break + if mangle_from_ and line.startswith('From '): + line = '>From ' + line[5:] + line = line.replace('\n', os.linesep) + target.write(line) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + +class Maildir(Mailbox): + """A qmail-style Maildir mailbox.""" + + colon = ':' + + def __init__(self, dirname, factory=rfc822.Message, create=True): + """Initialize a Maildir instance.""" + Mailbox.__init__(self, dirname, factory, create) + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0700) + os.mkdir(os.path.join(self._path, 'tmp'), 0700) + os.mkdir(os.path.join(self._path, 'new'), 0700) + os.mkdir(os.path.join(self._path, 'cur'), 0700) + else: + raise NoSuchMailboxError(self._path) + self._toc = {} + + def add(self, message): + """Add message and return assigned key.""" + tmp_file = self._create_tmp() + try: + self._dump_message(message, tmp_file) + finally: + tmp_file.close() + if isinstance(message, MaildirMessage): + subdir = message.get_subdir() + suffix = self.colon + message.get_info() + if suffix == self.colon: + suffix = '' + else: + subdir = 'new' + suffix = '' + uniq = os.path.basename(tmp_file.name).split(self.colon)[0] + dest = os.path.join(self._path, subdir, uniq + suffix) + os.rename(tmp_file.name, dest) + if isinstance(message, MaildirMessage): + os.utime(dest, (os.path.getatime(dest), message.get_date())) + return uniq + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + os.remove(os.path.join(self._path, self._lookup(key))) + + def discard(self, key): + """If the keyed message exists, remove it.""" + # This overrides an inapplicable implementation in the superclass. + try: + self.remove(key) + except KeyError: + pass + except OSError, e: + if e.errno != errno.ENOENT: + raise + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + old_subpath = self._lookup(key) + temp_key = self.add(message) + temp_subpath = self._lookup(temp_key) + if isinstance(message, MaildirMessage): + # temp's subdir and suffix were specified by message. + dominant_subpath = temp_subpath + else: + # temp's subdir and suffix were defaults from add(). + dominant_subpath = old_subpath + subdir = os.path.dirname(dominant_subpath) + if self.colon in dominant_subpath: + suffix = self.colon + dominant_subpath.split(self.colon)[-1] + else: + suffix = '' + self.discard(key) + new_path = os.path.join(self._path, subdir, key + suffix) + os.rename(os.path.join(self._path, temp_subpath), new_path) + if isinstance(message, MaildirMessage): + os.utime(new_path, (os.path.getatime(new_path), + message.get_date())) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + subpath = self._lookup(key) + f = open(os.path.join(self._path, subpath), 'r') + try: + msg = MaildirMessage(f) + finally: + f.close() + subdir, name = os.path.split(subpath) + msg.set_subdir(subdir) + if self.colon in name: + msg.set_info(name.split(self.colon)[-1]) + msg.set_date(os.path.getmtime(os.path.join(self._path, subpath))) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + f = open(os.path.join(self._path, self._lookup(key)), 'r') + try: + return f.read() + finally: + f.close() + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + f = open(os.path.join(self._path, self._lookup(key)), 'rb') + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + self._refresh() + for key in self._toc: + try: + self._lookup(key) + except KeyError: + continue + yield key + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._refresh() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._refresh() + return len(self._toc) + + def flush(self): + """Write any pending changes to disk.""" + return # Maildir changes are always written immediately. + + def lock(self): + """Lock the mailbox.""" + return + + def unlock(self): + """Unlock the mailbox if it is locked.""" + return + + def close(self): + """Flush and close the mailbox.""" + return + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if len(entry) > 1 and entry[0] == '.' and \ + os.path.isdir(os.path.join(self._path, entry)): + result.append(entry[1:]) + return result + + def get_folder(self, folder): + """Return a Maildir instance for the named folder.""" + return Maildir(os.path.join(self._path, '.' + folder), create=False) + + def add_folder(self, folder): + """Create a folder and return a Maildir instance representing it.""" + path = os.path.join(self._path, '.' + folder) + result = Maildir(path) + maildirfolder_path = os.path.join(path, 'maildirfolder') + if not os.path.exists(maildirfolder_path): + os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY)) + return result + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, '.' + folder) + for entry in os.listdir(os.path.join(path, 'new')) + \ + os.listdir(os.path.join(path, 'cur')): + if len(entry) < 1 or entry[0] != '.': + raise NotEmptyError('Folder contains message(s): %s' % folder) + for entry in os.listdir(path): + if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ + os.path.isdir(os.path.join(path, entry)): + raise NotEmptyError("Folder contains subdirectory '%s': %s" % + (folder, entry)) + for root, dirs, files in os.walk(path, topdown=False): + for entry in files: + os.remove(os.path.join(root, entry)) + for entry in dirs: + os.rmdir(os.path.join(root, entry)) + os.rmdir(path) + + def clean(self): + """Delete old files in "tmp".""" + now = time.time() + for entry in os.listdir(os.path.join(self._path, 'tmp')): + path = os.path.join(self._path, 'tmp', entry) + if now - os.path.getatime(path) > 129600: # 60 * 60 * 36 + os.remove(path) + + _count = 1 # This is used to generate unique file names. + + def _create_tmp(self): + """Create a file in the tmp subdirectory and open and return it.""" + now = time.time() + hostname = socket.gethostname() + if '/' in hostname: + hostname = hostname.replace('/', r'\057') + if ':' in hostname: + hostname = hostname.replace(':', r'\072') + uniq = "%s.M%sP%sQ%s.%s" % (int(now), int(now % 1 * 1e6), os.getpid(), + Maildir._count, hostname) + path = os.path.join(self._path, 'tmp', uniq) + try: + os.stat(path) + except OSError, e: + if e.errno == errno.ENOENT: + Maildir._count += 1 + return open(path, 'wb+') + else: + raise + else: + raise ExternalClashError('Name clash prevented file creation: %s' % + path) + + def _refresh(self): + """Update table of contents mapping.""" + self._toc = {} + for subdir in ('new', 'cur'): + for entry in os.listdir(os.path.join(self._path, subdir)): + uniq = entry.split(self.colon)[0] + self._toc[uniq] = os.path.join(subdir, entry) + + def _lookup(self, key): + """Use TOC to return subpath for given key, or raise a KeyError.""" + try: + if os.path.exists(os.path.join(self._path, self._toc[key])): + return self._toc[key] + except KeyError: + pass + self._refresh() + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) + + # This method is for backward compatibility only. def next(self): - while 1: - self.fp.seek(self.seekp) + """Return the next message in a one-time iteration.""" + if not hasattr(self, '_onetime_keys'): + self._onetime_keys = self.iterkeys() + while True: try: - self._search_start() - except EOFError: - self.seekp = self.fp.tell() + return self[self._onetime_keys.next()] + except StopIteration: return None - start = self.fp.tell() - self._search_end() - self.seekp = stop = self.fp.tell() - if start != stop: + except KeyError: + continue + + +class _singlefileMailbox(Mailbox): + """A single-file mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize a single-file mailbox.""" + Mailbox.__init__(self, path, factory, create) + try: + f = open(self._path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + if create: + f = open(self._path, 'wb+') + else: + raise NoSuchMailboxError(self._path) + elif e.errno == errno.EACCES: + f = open(self._path, 'rb') + else: + raise + self._file = f + self._toc = None + self._next_key = 0 + self._pending = False # No changes require rewriting the file. + self._locked = False + + def add(self, message): + """Add message and return assigned key.""" + self._lookup() + self._toc[self._next_key] = self._append_message(message) + self._next_key += 1 + self._pending = True + return self._next_key - 1 + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + del self._toc[key] + self._pending = True + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + self._toc[key] = self._append_message(message) + self._pending = True + + def iterkeys(self): + """Return an iterator over keys.""" + self._lookup() + for key in self._toc.keys(): + yield key + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._lookup() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._lookup() + return len(self._toc) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._locked = False + + def flush(self): + """Write any pending changes to disk.""" + if not self._pending: + return + self._lookup() + new_file = _create_temporary(self._path) + try: + new_toc = {} + self._pre_mailbox_hook(new_file) + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + self._pre_message_hook(new_file) + new_start = new_file.tell() + while True: + buffer = self._file.read(min(4096, + stop - self._file.tell())) + if buffer == '': + break + new_file.write(buffer) + new_toc[key] = (new_start, new_file.tell()) + self._post_message_hook(new_file) + except: + new_file.close() + os.remove(new_file.name) + raise + new_file.close() + self._file.close() + try: + os.rename(new_file.name, self._path) + except OSError, e: + if e.errno == errno.EEXIST: + os.remove(self._path) + os.rename(new_file.name, self._path) + else: + raise + self._file = open(self._path, 'rb+') + self._toc = new_toc + self._pending = False + if self._locked: + _lock_file(new_file, dotlock=False) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + return + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + return + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + return + + def close(self): + """Flush and close the mailbox.""" + self.flush() + if self._locked: + self.unlock() + self._file.close() + + def _lookup(self, key=None): + """Return (start, stop) or raise KeyError.""" + if self._toc is None: + self._generate_toc() + if key is not None: + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) + + def _append_message(self, message): + """Append message to mailbox and return (start, stop) offsets.""" + self._file.seek(0, 2) + self._pre_message_hook(self._file) + offsets = self._install_message(message) + self._post_message_hook(self._file) + self._file.flush() + return offsets + + + +class _mboxMMDF(_singlefileMailbox): + """An mbox or MMDF mailbox.""" + + _mangle_from_ = True + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + from_line = self._file.readline().replace(os.linesep, '') + string = self._file.read(stop - self._file.tell()) + msg = self._message_factory(string.replace(os.linesep, '\n')) + msg.set_from(from_line[5:]) + return msg + + def get_string(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + string = self._file.read(stop - self._file.tell()) + return string.replace(os.linesep, '\n') + + def get_file(self, key, from_=False): + """Return a file-like representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + return _PartialFile(self._file, self._file.tell(), stop) + + def _install_message(self, message): + """Format a message and blindly write to self._file.""" + from_line = None + if isinstance(message, str) and message.startswith('From '): + newline = message.find('\n') + if newline != -1: + from_line = message[:newline] + message = message[newline + 1:] + else: + from_line = message + message = '' + elif isinstance(message, _mboxMMDFMessage): + from_line = 'From ' + message.get_from() + elif isinstance(message, email.Message.Message): + from_line = message.get_unixfrom() # May be None. + if from_line is None: + from_line = 'From MAILER-DAEMON %s' % time.asctime(time.gmtime()) + start = self._file.tell() + self._file.write(from_line + os.linesep) + self._dump_message(message, self._file, self._mangle_from_) + stop = self._file.tell() + return (start, stop) + + +class mbox(_mboxMMDF): + """A classic mbox mailbox.""" + + _mangle_from_ = True + + def __init__(self, path, factory=None, create=True): + """Initialize an mbox mailbox.""" + self._message_factory = mboxMessage + _mboxMMDF.__init__(self, path, factory, create) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + if f.tell() != 0: + f.write(os.linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + while True: + line_pos = self._file.tell() + line = self._file.readline() + if line.startswith('From '): + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(line_pos) + elif line == '': + stops.append(line_pos) break - return self.factory(_Subfile(self.fp, start, stop)) + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) -class _Subfile: +class MMDF(_mboxMMDF): + """An MMDF mailbox.""" - def __init__(self, fp, start, stop): - self.fp = fp - self.start = start - self.stop = stop - self.pos = self.start + def __init__(self, path, factory=None, create=True): + """Initialize an MMDF mailbox.""" + self._message_factory = MMDFMessage + _mboxMMDF.__init__(self, path, factory, create) + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write('\001\001\001\001' + os.linesep) - def _read(self, length, read_function): - if self.pos >= self.stop: - return '' - remaining = self.stop - self.pos - if length is None or length < 0 or length > remaining: - length = remaining - self.fp.seek(self.pos) - data = read_function(length) - self.pos = self.fp.tell() - return data - - def read(self, length = None): - return self._read(length, self.fp.read) - - def readline(self, length = None): - return self._read(length, self.fp.readline) - - def readlines(self, sizehint = -1): - lines = [] - while 1: - line = self.readline() - if not line: + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(os.linesep + '\001\001\001\001' + os.linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line.startswith('\001\001\001\001' + os.linesep): + starts.append(next_pos) + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == '\001\001\001\001' + os.linesep: + stops.append(line_pos - len(os.linesep)) + break + elif line == '': + stops.append(line_pos) + break + elif line == '': + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + +class MH(Mailbox): + """An MH mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize an MH instance.""" + Mailbox.__init__(self, path, factory, create) + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0700) + os.close(os.open(os.path.join(self._path, '.mh_sequences'), + os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0600)) + else: + raise NoSuchMailboxError(self._path) + self._locked = False + + def add(self, message): + """Add message and return assigned key.""" + keys = self.keys() + if len(keys) == 0: + new_key = 1 + else: + new_key = max(keys) + 1 + new_path = os.path.join(self._path, str(new_key)) + f = _create_carefully(new_path) + try: + if self._locked: + _lock_file(f) + try: + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, new_key) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + return new_key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + f.close() + os.remove(os.path.join(self._path, str(key))) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + os.close(os.open(path, os.O_WRONLY | os.O_TRUNC)) + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, key) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'r+') + else: + f = open(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + msg = MHMessage(f) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + for name, key_list in self.get_sequences(): + if key in key_list: + msg.add_sequence(name) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'r+') + else: + f = open(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + return f.read() + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + try: + f = open(os.path.join(self._path, str(key)), 'rb') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + return iter(sorted(int(entry) for entry in os.listdir(self._path) + if entry.isdigit())) + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + return os.path.exists(os.path.join(self._path, str(key))) + + def __len__(self): + """Return a count of messages in the mailbox.""" + return len(list(self.iterkeys())) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + self._file = open(os.path.join(self._path, '.mh_sequences'), 'rb+') + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._file.close() + del self._file + self._locked = False + + def flush(self): + """Write any pending changes to the disk.""" + return + + def close(self): + """Flush and close the mailbox.""" + if self._locked: + self.unlock() + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if os.path.isdir(os.path.join(self._path, entry)): + result.append(entry) + return result + + def get_folder(self, folder): + """Return an MH instance for the named folder.""" + return MH(os.path.join(self._path, folder), create=False) + + def add_folder(self, folder): + """Create a folder and return an MH instance representing it.""" + return MH(os.path.join(self._path, folder)) + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, folder) + entries = os.listdir(path) + if entries == ['.mh_sequences']: + os.remove(os.path.join(path, '.mh_sequences')) + elif entries == []: + pass + else: + raise NotEmptyError('Folder not empty: %s' % self._path) + os.rmdir(path) + + def get_sequences(self): + """Return a name-to-key-list dictionary to define each sequence.""" + results = {} + f = open(os.path.join(self._path, '.mh_sequences'), 'r') + try: + all_keys = set(self.keys()) + for line in f: + try: + name, contents = line.split(':') + keys = set() + for spec in contents.split(): + if spec.isdigit(): + keys.add(int(spec)) + else: + start, stop = (int(x) for x in spec.split('-')) + keys.update(range(start, stop + 1)) + results[name] = [key for key in sorted(keys) \ + if key in all_keys] + if len(results[name]) == 0: + del results[name] + except ValueError: + raise FormatError('Invalid sequence specification: %s' % + line.rstrip()) + finally: + f.close() + return results + + def set_sequences(self, sequences): + """Set sequences using the given name-to-key-list dictionary.""" + f = open(os.path.join(self._path, '.mh_sequences'), 'r+') + try: + os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC)) + for name, keys in sequences.iteritems(): + if len(keys) == 0: + continue + f.write('%s:' % name) + prev = None + completing = False + for key in sorted(set(keys)): + if key - 1 == prev: + if not completing: + completing = True + f.write('-') + elif completing: + completing = False + f.write('%s %s' % (prev, key)) + else: + f.write(' %s' % key) + prev = key + if completing: + f.write(str(prev) + '\n') + else: + f.write('\n') + finally: + f.close() + + def pack(self): + """Re-name messages to eliminate numbering gaps. Invalidates keys.""" + sequences = self.get_sequences() + prev = 0 + changes = [] + for key in self.iterkeys(): + if key - 1 != prev: + changes.append((key, prev + 1)) + f = open(os.path.join(self._path, str(key)), 'r+') + try: + if self._locked: + _lock_file(f) + try: + if hasattr(os, 'link'): + os.link(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + os.unlink(os.path.join(self._path, str(key))) + else: + f.close() + os.rename(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + prev += 1 + self._next_key = prev + 1 + if len(changes) == 0: + return + for name, key_list in sequences.items(): + for old, new in changes: + if old in key_list: + key_list[key_list.index(old)] = new + self.set_sequences(sequences) + + def _dump_sequences(self, message, key): + """Inspect a new MHMessage and update sequences appropriately.""" + pending_sequences = message.get_sequences() + all_sequences = self.get_sequences() + for name, key_list in all_sequences.iteritems(): + if name in pending_sequences: + key_list.append(key) + elif key in key_list: + del key_list[key_list.index(key)] + for sequence in pending_sequences: + if sequence not in all_sequences: + all_sequences[sequence] = [key] + self.set_sequences(all_sequences) + + +class Babyl(_singlefileMailbox): + """An Rmail-style Babyl mailbox.""" + + _special_labels = frozenset(('unseen', 'deleted', 'filed', 'answered', + 'forwarded', 'edited', 'resent')) + + def __init__(self, path, factory=None, create=True): + """Initialize a Babyl mailbox.""" + _singlefileMailbox.__init__(self, path, factory, create) + self._labels = {} + + def add(self, message): + """Add message and return assigned key.""" + key = _singlefileMailbox.add(self, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + return key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.remove(self, key) + if key in self._labels: + del self._labels[key] + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.__setitem__(self, key, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip '1,' line specifying labels. + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': + break + original_headers.write(line.replace(os.linesep, '\n')) + visible_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + visible_headers.write(line.replace(os.linesep, '\n')) + body = self._file.read(stop - self._file.tell()).replace(os.linesep, + '\n') + msg = BabylMessage(original_headers.getvalue() + body) + msg.set_visible(visible_headers.getvalue()) + if key in self._labels: + msg.set_labels(self._labels[key]) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip '1,' line specifying labels. + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': break - lines.append(line) - if sizehint >= 0: - sizehint = sizehint - len(line) + original_headers.write(line.replace(os.linesep, '\n')) + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + return original_headers.getvalue() + \ + self._file.read(stop - self._file.tell()).replace(os.linesep, + '\n') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + return StringIO.StringIO(self.get_string(key).replace('\n', + os.linesep)) + + def get_labels(self): + """Return a list of user-defined labels in the mailbox.""" + self._lookup() + labels = set() + for label_list in self._labels.values(): + labels.update(label_list) + labels.difference_update(self._special_labels) + return list(labels) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + label_lists = [] + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == '\037\014' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(next_pos) + labels = [label.strip() for label + in self._file.readline()[1:].split(',') + if label.strip() != ''] + label_lists.append(labels) + elif line == '\037' or line == '\037' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + elif line == '': + stops.append(line_pos - len(os.linesep)) + break + self._toc = dict(enumerate(zip(starts, stops))) + self._labels = dict(enumerate(label_lists)) + self._next_key = len(self._toc) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + f.write('BABYL OPTIONS:%sVersion: 5%sLabels:%s%s\037' % + (os.linesep, os.linesep, ','.join(self.get_labels()), + os.linesep)) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write('\014' + os.linesep) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(os.linesep + '\037') + + def _install_message(self, message): + """Write message contents and return (start, stop).""" + start = self._file.tell() + if isinstance(message, BabylMessage): + special_labels = [] + labels = [] + for label in message.get_labels(): + if label in self._special_labels: + special_labels.append(label) + else: + labels.append(label) + self._file.write('1') + for label in special_labels: + self._file.write(', ' + label) + self._file.write(',,') + for label in labels: + self._file.write(' ' + label + ',') + self._file.write(os.linesep) + else: + self._file.write('1,,' + os.linesep) + if isinstance(message, email.Message.Message): + orig_buffer = StringIO.StringIO() + orig_generator = email.Generator.Generator(orig_buffer, False, 0) + orig_generator.flatten(message) + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + self._file.write('*** EOOH ***' + os.linesep) + if isinstance(message, BabylMessage): + vis_buffer = StringIO.StringIO() + vis_generator = email.Generator.Generator(vis_buffer, False, 0) + vis_generator.flatten(message.get_visible()) + while True: + line = vis_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + else: + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + while True: + buffer = orig_buffer.read(4096) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer.replace('\n', os.linesep)) + elif isinstance(message, str): + body_start = message.find('\n\n') + 2 + if body_start - 2 != -1: + self._file.write(message[:body_start].replace('\n', + os.linesep)) + self._file.write('*** EOOH ***' + os.linesep) + self._file.write(message[:body_start].replace('\n', + os.linesep)) + self._file.write(message[body_start:].replace('\n', + os.linesep)) + else: + self._file.write('*** EOOH ***' + os.linesep + os.linesep) + self._file.write(message.replace('\n', os.linesep)) + elif hasattr(message, 'readline'): + original_pos = message.tell() + first_pass = True + while True: + line = message.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + self._file.write('*** EOOH ***' + os.linesep) + if first_pass: + first_pass = False + message.seek(original_pos) + else: + break + while True: + buffer = message.read(4096) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer.replace('\n', os.linesep)) + else: + raise TypeError('Invalid message type: %s' % type(message)) + stop = self._file.tell() + return (start, stop) + + +class Message(email.Message.Message): + """Message with mailbox-format-specific properties.""" + + def __init__(self, message=None): + """Initialize a Message instance.""" + if isinstance(message, email.Message.Message): + self._become_message(copy.deepcopy(message)) + if isinstance(message, Message): + message._explain_to(self) + elif isinstance(message, str): + self._become_message(email.message_from_string(message)) + elif hasattr(message, "read"): + self._become_message(email.message_from_file(message)) + elif message is None: + email.Message.Message.__init__(self) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + def _become_message(self, message): + """Assume the non-format-specific state of message.""" + for name in ('_headers', '_unixfrom', '_payload', '_charset', + 'preamble', 'epilogue', 'defects', '_default_type'): + self.__dict__[name] = message.__dict__[name] + + def _explain_to(self, message): + """Copy format-specific state to message insofar as possible.""" + if isinstance(message, Message): + return # There's nothing format-specific to explain. + else: + raise TypeError('Cannot convert to specified type') + + +class MaildirMessage(Message): + """Message with Maildir-specific properties.""" + + def __init__(self, message=None): + """Initialize a MaildirMessage instance.""" + self._subdir = 'new' + self._info = '' + self._date = time.time() + Message.__init__(self, message) + + def get_subdir(self): + """Return 'new' or 'cur'.""" + return self._subdir + + def set_subdir(self, subdir): + """Set subdir to 'new' or 'cur'.""" + if subdir == 'new' or subdir == 'cur': + self._subdir = subdir + else: + raise ValueError("subdir must be 'new' or 'cur': %s" % subdir) + + def get_flags(self): + """Return as a string the flags that are set.""" + if self._info.startswith('2,'): + return self._info[2:] + else: + return '' + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + self._info = '2,' + ''.join(sorted(flags)) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if self.get_flags() != '': + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def get_date(self): + """Return delivery date of message, in seconds since the epoch.""" + return self._date + + def set_date(self, date): + """Set delivery date of message, in seconds since the epoch.""" + try: + self._date = float(date) + except ValueError: + raise TypeError("can't convert to float: %s" % date) + + def get_info(self): + """Get the message's "info" as a string.""" + return self._info + + def set_info(self, info): + """Set the message's "info" string.""" + if isinstance(info, str): + self._info = info + else: + raise TypeError('info must be a string: %s' % type(info)) + + def _explain_to(self, message): + """Copy Maildir-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + message.set_flags(self.get_flags()) + message.set_subdir(self.get_subdir()) + message.set_date(self.get_date()) + elif isinstance(message, _mboxMMDFMessage): + flags = set(self.get_flags()) + if 'S' in flags: + message.add_flag('R') + if self.get_subdir() == 'cur': + message.add_flag('O') + if 'T' in flags: + message.add_flag('D') + if 'F' in flags: + message.add_flag('F') + if 'R' in flags: + message.add_flag('A') + message.set_from('MAILER-DAEMON', time.gmtime(self.get_date())) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_sequence('unseen') + if 'R' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_label('unseen') + if 'T' in flags: + message.add_label('deleted') + if 'R' in flags: + message.add_label('answered') + if 'P' in flags: + message.add_label('forwarded') + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class _mboxMMDFMessage(Message): + """Message with mbox- or MMDF-specific properties.""" + + def __init__(self, message=None): + """Initialize an mboxMMDFMessage instance.""" + self.set_from('MAILER-DAEMON', True) + if isinstance(message, email.Message.Message): + unixfrom = message.get_unixfrom() + if unixfrom is not None and unixfrom.startswith('From '): + self.set_from(unixfrom[5:]) + Message.__init__(self, message) + + def get_from(self): + """Return contents of "From " line.""" + return self._from + + def set_from(self, from_, time_=None): + """Set "From " line, formatting and appending time_ if specified.""" + if time_ is not None: + if time_ is True: + time_ = time.gmtime() + from_ += ' ' + time.asctime(time_) + self._from = from_ + + def get_flags(self): + """Return as a string the flags that are set.""" + return self.get('Status', '') + self.get('X-Status', '') + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + flags = set(flags) + status_flags, xstatus_flags = '', '' + for flag in ('R', 'O'): + if flag in flags: + status_flags += flag + flags.remove(flag) + for flag in ('D', 'F', 'A'): + if flag in flags: + xstatus_flags += flag + flags.remove(flag) + xstatus_flags += ''.join(sorted(flags)) + try: + self.replace_header('Status', status_flags) + except KeyError: + self.add_header('Status', status_flags) + try: + self.replace_header('X-Status', xstatus_flags) + except KeyError: + self.add_header('X-Status', xstatus_flags) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if 'Status' in self or 'X-Status' in self: + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def _explain_to(self, message): + """Copy mbox- or MMDF-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + flags = set(self.get_flags()) + if 'O' in flags: + message.set_subdir('cur') + if 'F' in flags: + message.add_flag('F') + if 'A' in flags: + message.add_flag('R') + if 'R' in flags: + message.add_flag('S') + if 'D' in flags: + message.add_flag('T') + del message['status'] + del message['x-status'] + maybe_date = ' '.join(self.get_from().split()[-5:]) + try: + message.set_date(calendar.timegm(time.strptime(maybe_date, + '%a %b %d %H:%M:%S %Y'))) + except (ValueError, OverflowError): + pass + elif isinstance(message, _mboxMMDFMessage): + message.set_flags(self.get_flags()) + message.set_from(self.get_from()) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_sequence('unseen') + if 'A' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + del message['status'] + del message['x-status'] + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_label('unseen') + if 'D' in flags: + message.add_label('deleted') + if 'A' in flags: + message.add_label('answered') + del message['status'] + del message['x-status'] + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class mboxMessage(_mboxMMDFMessage): + """Message with mbox-specific properties.""" + + +class MHMessage(Message): + """Message with MH-specific properties.""" + + def __init__(self, message=None): + """Initialize an MHMessage instance.""" + self._sequences = [] + Message.__init__(self, message) + + def get_sequences(self): + """Return a list of sequences that include the message.""" + return self._sequences[:] + + def set_sequences(self, sequences): + """Set the list of sequences that include the message.""" + self._sequences = list(sequences) + + def add_sequence(self, sequence): + """Add sequence to list of sequences including the message.""" + if isinstance(sequence, str): + if not sequence in self._sequences: + self._sequences.append(sequence) + else: + raise TypeError('sequence must be a string: %s' % type(sequence)) + + def remove_sequence(self, sequence): + """Remove sequence from the list of sequences including the message.""" + try: + self._sequences.remove(sequence) + except ValueError: + pass + + def _explain_to(self, message): + """Copy MH-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + sequences = set(self.get_sequences()) + if 'unseen' in sequences: + message.set_subdir('cur') + else: + message.set_subdir('cur') + message.add_flag('S') + if 'flagged' in sequences: + message.add_flag('F') + if 'replied' in sequences: + message.add_flag('R') + elif isinstance(message, _mboxMMDFMessage): + sequences = set(self.get_sequences()) + if 'unseen' not in sequences: + message.add_flag('RO') + else: + message.add_flag('O') + if 'flagged' in sequences: + message.add_flag('F') + if 'replied' in sequences: + message.add_flag('A') + elif isinstance(message, MHMessage): + for sequence in self.get_sequences(): + message.add_sequence(sequence) + elif isinstance(message, BabylMessage): + sequences = set(self.get_sequences()) + if 'unseen' in sequences: + message.add_label('unseen') + if 'replied' in sequences: + message.add_label('answered') + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class BabylMessage(Message): + """Message with Babyl-specific properties.""" + + def __init__(self, message=None): + """Initialize an BabylMessage instance.""" + self._labels = [] + self._visible = Message() + Message.__init__(self, message) + + def get_labels(self): + """Return a list of labels on the message.""" + return self._labels[:] + + def set_labels(self, labels): + """Set the list of labels on the message.""" + self._labels = list(labels) + + def add_label(self, label): + """Add label to list of labels on the message.""" + if isinstance(label, str): + if label not in self._labels: + self._labels.append(label) + else: + raise TypeError('label must be a string: %s' % type(label)) + + def remove_label(self, label): + """Remove label from the list of labels on the message.""" + try: + self._labels.remove(label) + except ValueError: + pass + + def get_visible(self): + """Return a Message representation of visible headers.""" + return Message(self._visible) + + def set_visible(self, visible): + """Set the Message representation of visible headers.""" + self._visible = Message(visible) + + def update_visible(self): + """Update and/or sensibly generate a set of visible headers.""" + for header in self._visible.keys(): + if header in self: + self._visible.replace_header(header, self[header]) + else: + del self._visible[header] + for header in ('Date', 'From', 'Reply-To', 'To', 'CC', 'Subject'): + if header in self and header not in self._visible: + self._visible[header] = self[header] + + def _explain_to(self, message): + """Copy Babyl-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + labels = set(self.get_labels()) + if 'unseen' in labels: + message.set_subdir('cur') + else: + message.set_subdir('cur') + message.add_flag('S') + if 'forwarded' in labels or 'resent' in labels: + message.add_flag('P') + if 'answered' in labels: + message.add_flag('R') + if 'deleted' in labels: + message.add_flag('T') + elif isinstance(message, _mboxMMDFMessage): + labels = set(self.get_labels()) + if 'unseen' not in labels: + message.add_flag('RO') + else: + message.add_flag('O') + if 'deleted' in labels: + message.add_flag('D') + if 'answered' in labels: + message.add_flag('A') + elif isinstance(message, MHMessage): + labels = set(self.get_labels()) + if 'unseen' in labels: + message.add_sequence('unseen') + if 'answered' in labels: + message.add_sequence('replied') + elif isinstance(message, BabylMessage): + message.set_visible(self.get_visible()) + for label in self.get_labels(): + message.add_label(label) + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class MMDFMessage(_mboxMMDFMessage): + """Message with MMDF-specific properties.""" + + +class _ProxyFile: + """A read-only wrapper of a file.""" + + def __init__(self, f, pos=None): + """Initialize a _ProxyFile.""" + self._file = f + if pos is None: + self._pos = f.tell() + else: + self._pos = pos + + def read(self, size=None): + """Read bytes.""" + return self._read(size, self._file.read) + + def readline(self, size=None): + """Read a line.""" + return self._read(size, self._file.readline) + + def readlines(self, sizehint=None): + """Read multiple lines.""" + result = [] + for line in self: + result.append(line) + if sizehint is not None: + sizehint -= len(line) if sizehint <= 0: break - return lines + return result + + def __iter__(self): + """Iterate over lines.""" + return iter(self.readline, "") def tell(self): - return self.pos - self.start + """Return the position.""" + return self._pos + + def seek(self, offset, whence=0): + """Change position.""" + if whence == 1: + self._file.seek(self._pos) + self._file.seek(offset, whence) + self._pos = self._file.tell() - def seek(self, pos, whence=0): + def close(self): + """Close the file.""" + del self._file + + def _read(self, size, read_method): + """Read size bytes using read_method.""" + if size is None: + size = -1 + self._file.seek(self._pos) + result = read_method(size) + self._pos = self._file.tell() + return result + + +class _PartialFile(_ProxyFile): + """A read-only wrapper of part of a file.""" + + def __init__(self, f, start=None, stop=None): + """Initialize a _PartialFile.""" + _ProxyFile.__init__(self, f, start) + self._start = start + self._stop = stop + + def tell(self): + """Return the position with respect to start.""" + return _ProxyFile.tell(self) - self._start + + def seek(self, offset, whence=0): + """Change position, possibly with respect to start or stop.""" if whence == 0: - self.pos = self.start + pos - elif whence == 1: - self.pos = self.pos + pos + self._pos = self._start + whence = 1 elif whence == 2: - self.pos = self.stop + pos + self._pos = self._stop + whence = 1 + _ProxyFile.seek(self, offset, whence) - def close(self): - del self.fp + def _read(self, size, read_method): + """Read size bytes using read_method, honoring start and stop.""" + remaining = self._stop - self._pos + if remaining <= 0: + return '' + if size is None or size < 0 or size > remaining: + size = remaining + return _ProxyFile._read(self, size, read_method) + + +def _lock_file(f, dotlock=True): + """Lock file f using lockf, flock, and dot locking.""" + dotlock_done = False + try: + if fcntl: + try: + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError, e: + if e.errno == errno.EAGAIN: + raise ExternalClashError('lockf: lock unavailable: %s' % + f.name) + else: + raise + try: + fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError, e: + if e.errno == errno.EWOULDBLOCK: + raise ExternalClashError('flock: lock unavailable: %s' % + f.name) + else: + raise + if dotlock: + try: + pre_lock = _create_temporary(f.name + '.lock') + pre_lock.close() + except IOError, e: + if e.errno == errno.EACCES: + return # Without write access, just skip dotlocking. + else: + raise + try: + if hasattr(os, 'link'): + os.link(pre_lock.name, f.name + '.lock') + dotlock_done = True + os.unlink(pre_lock.name) + else: + os.rename(pre_lock.name, f.name + '.lock') + dotlock_done = True + except OSError, e: + if e.errno == errno.EEXIST: + os.remove(pre_lock.name) + raise ExternalClashError('dot lock unavailable: %s' % + f.name) + else: + raise + except: + if fcntl: + fcntl.lockf(f, fcntl.LOCK_UN) + fcntl.flock(f, fcntl.LOCK_UN) + if dotlock_done: + os.remove(f.name + '.lock') + raise + +def _unlock_file(f): + """Unlock file f using lockf, flock, and dot locking.""" + if fcntl: + fcntl.lockf(f, fcntl.LOCK_UN) + fcntl.flock(f, fcntl.LOCK_UN) + if os.path.exists(f.name + '.lock'): + os.remove(f.name + '.lock') + +def _create_carefully(path): + """Create a file if it doesn't exist and open for reading and writing.""" + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR) + try: + return open(path, 'rb+') + finally: + os.close(fd) + +def _create_temporary(path): + """Create a temp file based on path and open for reading and writing.""" + return _create_carefully('%s.%s.%s.%s' % (path, int(time.time()), + socket.gethostname(), + os.getpid())) + + +## Start: classes from the original module (for backward compatibility). + +# Note that the Maildir class, whose name is unchanged, itself offers a next() +# method for backward compatibility. + +class _Mailbox: + + def __init__(self, fp, factory=rfc822.Message): + self.fp = fp + self.seekp = 0 + self.factory = factory + def __iter__(self): + return iter(self.next, None) + + def next(self): + while 1: + self.fp.seek(self.seekp) + try: + self._search_start() + except EOFError: + self.seekp = self.fp.tell() + return None + start = self.fp.tell() + self._search_end() + self.seekp = stop = self.fp.tell() + if start != stop: + break + return self.factory(_PartialFile(self.fp, start, stop)) # Recommended to use PortableUnixMailbox instead! class UnixMailbox(_Mailbox): @@ -213,36 +2027,6 @@ class MHMailbox: return msg -class Maildir: - # Qmail directory mailbox - - def __init__(self, dirname, factory=rfc822.Message): - self.dirname = dirname - self.factory = factory - - # check for new mail - newdir = os.path.join(self.dirname, 'new') - boxes = [os.path.join(newdir, f) - for f in os.listdir(newdir) if f[0] != '.'] - - # Now check for current mail in this maildir - curdir = os.path.join(self.dirname, 'cur') - boxes += [os.path.join(curdir, f) - for f in os.listdir(curdir) if f[0] != '.'] - boxes.reverse() - self.boxes = boxes - - def __iter__(self): - return iter(self.next, None) - - def next(self): - if not self.boxes: - return None - fn = self.boxes.pop() - fp = open(fn) - return self.factory(fp) - - class BabylMailbox(_Mailbox): def _search_start(self): @@ -263,59 +2047,20 @@ class BabylMailbox(_Mailbox): self.fp.seek(pos) return +## End: classes from the original module (for backward compatibility). -def _test(): - import sys - args = sys.argv[1:] - if not args: - for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER': - if key in os.environ: - mbox = os.environ[key] - break - else: - print "$MAIL, $LOGNAME nor $USER set -- who are you?" - return - else: - mbox = args[0] - if mbox[:1] == '+': - mbox = os.environ['HOME'] + '/Mail/' + mbox[1:] - elif not '/' in mbox: - if os.path.isfile('/var/mail/' + mbox): - mbox = '/var/mail/' + mbox - else: - mbox = '/usr/mail/' + mbox - if os.path.isdir(mbox): - if os.path.isdir(os.path.join(mbox, 'cur')): - mb = Maildir(mbox) - else: - mb = MHMailbox(mbox) - else: - fp = open(mbox, 'r') - mb = PortableUnixMailbox(fp) - - msgs = [] - while 1: - msg = mb.next() - if msg is None: - break - msgs.append(msg) - if len(args) <= 1: - msg.fp = None - if len(args) > 1: - num = int(args[1]) - print 'Message %d body:'%num - msg = msgs[num-1] - msg.rewindbody() - sys.stdout.write(msg.fp.read()) - else: - print 'Mailbox',mbox,'has',len(msgs),'messages:' - for msg in msgs: - f = msg.getheader('from') or "" - s = msg.getheader('subject') or "" - d = msg.getheader('date') or "" - print '-%20.20s %20.20s %-30.30s'%(f, d[5:], s) - - -if __name__ == '__main__': - _test() +class Error(Exception): + """Raised for module-specific errors.""" + +class NoSuchMailboxError(Error): + """The specified mailbox does not exist and won't be created.""" + +class NotEmptyError(Error): + """The specified mailbox is not empty and deletion was requested.""" + +class ExternalClashError(Error): + """Another process caused an action to fail.""" + +class FormatError(Error): + """A file appears to have an invalid format.""" diff --git a/Lib/msilib/__init__.py b/Lib/msilib/__init__.py index d847259..0881409 100644 --- a/Lib/msilib/__init__.py +++ b/Lib/msilib/__init__.py @@ -196,11 +196,9 @@ class CAB: self.filenames.add(logical) return logical - def append(self, full, file, logical = None): + def append(self, full, logical): if os.path.isdir(full): return - if not logical: - logical = self.gen_id(dir, file) self.index += 1 self.files.append((full, logical)) return self.index, logical @@ -330,7 +328,7 @@ class Directory: logical = self.keyfiles[file] else: logical = None - sequence, logical = self.cab.append(absolute, file, logical) + sequence, logical = self.cab.append(absolute, logical) assert logical not in self.ids self.ids.add(logical) short = self.make_short(file) @@ -400,13 +398,14 @@ class Control: self.dlg = dlg self.name = name - def event(self, ev, arg, cond = "1", order = None): + def event(self, event, argument, condition = "1", ordering = None): add_data(self.dlg.db, "ControlEvent", - [(self.dlg.name, self.name, ev, arg, cond, order)]) + [(self.dlg.name, self.name, event, argument, + condition, ordering)]) - def mapping(self, ev, attr): + def mapping(self, mapping, attribute): add_data(self.dlg.db, "EventMapping", - [(self.dlg.name, self.name, ev, attr)]) + [(self.dlg.name, self.name, event, attribute)]) def condition(self, action, condition): add_data(self.dlg.db, "ControlCondition", diff --git a/Lib/msilib/text.py b/Lib/msilib/text.py new file mode 100644 index 0000000..12fd2d7 --- /dev/null +++ b/Lib/msilib/text.py @@ -0,0 +1,129 @@ +import msilib,os;dirname=os.path.dirname(__file__) + +ActionText = [ +(u'InstallValidate', u'Validating install', None), +(u'InstallFiles', u'Copying new files', u'File: [1], Directory: [9], Size: [6]'), +(u'InstallAdminPackage', u'Copying network install files', u'File: [1], Directory: [9], Size: [6]'), +(u'FileCost', u'Computing space requirements', None), +(u'CostInitialize', u'Computing space requirements', None), +(u'CostFinalize', u'Computing space requirements', None), +(u'CreateShortcuts', u'Creating shortcuts', u'Shortcut: [1]'), +(u'PublishComponents', u'Publishing Qualified Components', u'Component ID: [1], Qualifier: [2]'), +(u'PublishFeatures', u'Publishing Product Features', u'Feature: [1]'), +(u'PublishProduct', u'Publishing product information', None), +(u'RegisterClassInfo', u'Registering Class servers', u'Class Id: [1]'), +(u'RegisterExtensionInfo', u'Registering extension servers', u'Extension: [1]'), +(u'RegisterMIMEInfo', u'Registering MIME info', u'MIME Content Type: [1], Extension: [2]'), +(u'RegisterProgIdInfo', u'Registering program identifiers', u'ProgId: [1]'), +(u'AllocateRegistrySpace', u'Allocating registry space', u'Free space: [1]'), +(u'AppSearch', u'Searching for installed applications', u'Property: [1], Signature: [2]'), +(u'BindImage', u'Binding executables', u'File: [1]'), +(u'CCPSearch', u'Searching for qualifying products', None), +(u'CreateFolders', u'Creating folders', u'Folder: [1]'), +(u'DeleteServices', u'Deleting services', u'Service: [1]'), +(u'DuplicateFiles', u'Creating duplicate files', u'File: [1], Directory: [9], Size: [6]'), +(u'FindRelatedProducts', u'Searching for related applications', u'Found application: [1]'), +(u'InstallODBC', u'Installing ODBC components', None), +(u'InstallServices', u'Installing new services', u'Service: [2]'), +(u'LaunchConditions', u'Evaluating launch conditions', None), +(u'MigrateFeatureStates', u'Migrating feature states from related applications', u'Application: [1]'), +(u'MoveFiles', u'Moving files', u'File: [1], Directory: [9], Size: [6]'), +(u'PatchFiles', u'Patching files', u'File: [1], Directory: [2], Size: [3]'), +(u'ProcessComponents', u'Updating component registration', None), +(u'RegisterComPlus', u'Registering COM+ Applications and Components', u'AppId: [1]{{, AppType: [2], Users: [3], RSN: [4]}}'), +(u'RegisterFonts', u'Registering fonts', u'Font: [1]'), +(u'RegisterProduct', u'Registering product', u'[1]'), +(u'RegisterTypeLibraries', u'Registering type libraries', u'LibID: [1]'), +(u'RegisterUser', u'Registering user', u'[1]'), +(u'RemoveDuplicateFiles', u'Removing duplicated files', u'File: [1], Directory: [9]'), +(u'RemoveEnvironmentStrings', u'Updating environment strings', u'Name: [1], Value: [2], Action [3]'), +(u'RemoveExistingProducts', u'Removing applications', u'Application: [1], Command line: [2]'), +(u'RemoveFiles', u'Removing files', u'File: [1], Directory: [9]'), +(u'RemoveFolders', u'Removing folders', u'Folder: [1]'), +(u'RemoveIniValues', u'Removing INI files entries', u'File: [1], Section: [2], Key: [3], Value: [4]'), +(u'RemoveODBC', u'Removing ODBC components', None), +(u'RemoveRegistryValues', u'Removing system registry values', u'Key: [1], Name: [2]'), +(u'RemoveShortcuts', u'Removing shortcuts', u'Shortcut: [1]'), +(u'RMCCPSearch', u'Searching for qualifying products', None), +(u'SelfRegModules', u'Registering modules', u'File: [1], Folder: [2]'), +(u'SelfUnregModules', u'Unregistering modules', u'File: [1], Folder: [2]'), +(u'SetODBCFolders', u'Initializing ODBC directories', None), +(u'StartServices', u'Starting services', u'Service: [1]'), +(u'StopServices', u'Stopping services', u'Service: [1]'), +(u'UnpublishComponents', u'Unpublishing Qualified Components', u'Component ID: [1], Qualifier: [2]'), +(u'UnpublishFeatures', u'Unpublishing Product Features', u'Feature: [1]'), +(u'UnregisterClassInfo', u'Unregister Class servers', u'Class Id: [1]'), +(u'UnregisterComPlus', u'Unregistering COM+ Applications and Components', u'AppId: [1]{{, AppType: [2]}}'), +(u'UnregisterExtensionInfo', u'Unregistering extension servers', u'Extension: [1]'), +(u'UnregisterFonts', u'Unregistering fonts', u'Font: [1]'), +(u'UnregisterMIMEInfo', u'Unregistering MIME info', u'MIME Content Type: [1], Extension: [2]'), +(u'UnregisterProgIdInfo', u'Unregistering program identifiers', u'ProgId: [1]'), +(u'UnregisterTypeLibraries', u'Unregistering type libraries', u'LibID: [1]'), +(u'WriteEnvironmentStrings', u'Updating environment strings', u'Name: [1], Value: [2], Action [3]'), +(u'WriteIniValues', u'Writing INI files values', u'File: [1], Section: [2], Key: [3], Value: [4]'), +(u'WriteRegistryValues', u'Writing system registry values', u'Key: [1], Name: [2], Value: [3]'), +(u'Advertise', u'Advertising application', None), +(u'GenerateScript', u'Generating script operations for action:', u'[1]'), +(u'InstallSFPCatalogFile', u'Installing system catalog', u'File: [1], Dependencies: [2]'), +(u'MsiPublishAssemblies', u'Publishing assembly information', u'Application Context:[1], Assembly Name:[2]'), +(u'MsiUnpublishAssemblies', u'Unpublishing assembly information', u'Application Context:[1], Assembly Name:[2]'), +(u'Rollback', u'Rolling back action:', u'[1]'), +(u'RollbackCleanup', u'Removing backup files', u'File: [1]'), +(u'UnmoveFiles', u'Removing moved files', u'File: [1], Directory: [9]'), +(u'UnpublishProduct', u'Unpublishing product information', None), +] + +UIText = [ +(u'AbsentPath', None), +(u'bytes', u'bytes'), +(u'GB', u'GB'), +(u'KB', u'KB'), +(u'MB', u'MB'), +(u'MenuAbsent', u'Entire feature will be unavailable'), +(u'MenuAdvertise', u'Feature will be installed when required'), +(u'MenuAllCD', u'Entire feature will be installed to run from CD'), +(u'MenuAllLocal', u'Entire feature will be installed on local hard drive'), +(u'MenuAllNetwork', u'Entire feature will be installed to run from network'), +(u'MenuCD', u'Will be installed to run from CD'), +(u'MenuLocal', u'Will be installed on local hard drive'), +(u'MenuNetwork', u'Will be installed to run from network'), +(u'ScriptInProgress', u'Gathering required information...'), +(u'SelAbsentAbsent', u'This feature will remain uninstalled'), +(u'SelAbsentAdvertise', u'This feature will be set to be installed when required'), +(u'SelAbsentCD', u'This feature will be installed to run from CD'), +(u'SelAbsentLocal', u'This feature will be installed on the local hard drive'), +(u'SelAbsentNetwork', u'This feature will be installed to run from the network'), +(u'SelAdvertiseAbsent', u'This feature will become unavailable'), +(u'SelAdvertiseAdvertise', u'Will be installed when required'), +(u'SelAdvertiseCD', u'This feature will be available to run from CD'), +(u'SelAdvertiseLocal', u'This feature will be installed on your local hard drive'), +(u'SelAdvertiseNetwork', u'This feature will be available to run from the network'), +(u'SelCDAbsent', u"This feature will be uninstalled completely, you won't be able to run it from CD"), +(u'SelCDAdvertise', u'This feature will change from run from CD state to set to be installed when required'), +(u'SelCDCD', u'This feature will remain to be run from CD'), +(u'SelCDLocal', u'This feature will change from run from CD state to be installed on the local hard drive'), +(u'SelChildCostNeg', u'This feature frees up [1] on your hard drive.'), +(u'SelChildCostPos', u'This feature requires [1] on your hard drive.'), +(u'SelCostPending', u'Compiling cost for this feature...'), +(u'SelLocalAbsent', u'This feature will be completely removed'), +(u'SelLocalAdvertise', u'This feature will be removed from your local hard drive, but will be set to be installed when required'), +(u'SelLocalCD', u'This feature will be removed from your local hard drive, but will be still available to run from CD'), +(u'SelLocalLocal', u'This feature will remain on you local hard drive'), +(u'SelLocalNetwork', u'This feature will be removed from your local hard drive, but will be still available to run from the network'), +(u'SelNetworkAbsent', u"This feature will be uninstalled completely, you won't be able to run it from the network"), +(u'SelNetworkAdvertise', u'This feature will change from run from network state to set to be installed when required'), +(u'SelNetworkLocal', u'This feature will change from run from network state to be installed on the local hard drive'), +(u'SelNetworkNetwork', u'This feature will remain to be run from the network'), +(u'SelParentCostNegNeg', u'This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.'), +(u'SelParentCostNegPos', u'This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.'), +(u'SelParentCostPosNeg', u'This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.'), +(u'SelParentCostPosPos', u'This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.'), +(u'TimeRemaining', u'Time remaining: {[1] minutes }{[2] seconds}'), +(u'VolumeCostAvailable', u'Available'), +(u'VolumeCostDifference', u'Difference'), +(u'VolumeCostRequired', u'Required'), +(u'VolumeCostSize', u'Disk Size'), +(u'VolumeCostVolume', u'Volume'), +] + +tables=['ActionText', 'UIText'] diff --git a/Lib/msilib/uisample.py b/Lib/msilib/uisample.py deleted file mode 100644 index d66c3cd..0000000 --- a/Lib/msilib/uisample.py +++ /dev/null @@ -1,1399 +0,0 @@ -import msilib,os;dirname=os.path.dirname(__file__) -AdminExecuteSequence = [ -(u'InstallValidate', None, 1400), -(u'InstallInitialize', None, 1500), -(u'InstallFinalize', None, 6600), -(u'InstallFiles', None, 4000), -(u'InstallAdminPackage', None, 3900), -(u'FileCost', None, 900), -(u'CostInitialize', None, 800), -(u'CostFinalize', None, 1000), -] - -AdminUISequence = [ -(u'AdminWelcomeDlg', None, 1230), -(u'FileCost', None, 900), -(u'CostInitialize', None, 800), -(u'CostFinalize', None, 1000), -(u'ExecuteAction', None, 1300), -(u'ExitDialog', None, -1), -(u'FatalError', None, -3), -(u'PrepareDlg', None, 140), -(u'ProgressDlg', None, 1280), -(u'UserExit', None, -2), -] - -AdvtExecuteSequence = [ -(u'InstallValidate', None, 1400), -(u'InstallInitialize', None, 1500), -(u'InstallFinalize', None, 6600), -(u'CostInitialize', None, 800), -(u'CostFinalize', None, 1000), -(u'CreateShortcuts', None, 4500), -(u'PublishComponents', None, 6200), -(u'PublishFeatures', None, 6300), -(u'PublishProduct', None, 6400), -(u'RegisterClassInfo', None, 4600), -(u'RegisterExtensionInfo', None, 4700), -(u'RegisterMIMEInfo', None, 4900), -(u'RegisterProgIdInfo', None, 4800), -] - -BBControl = [ -] - -Billboard = [ -] - -Binary = [ -(u'bannrbmp', msilib.Binary(os.path.join(dirname,"bannrbmp.bin"))), -(u'completi', msilib.Binary(os.path.join(dirname,"completi.bin"))), -(u'custicon', msilib.Binary(os.path.join(dirname,"custicon.bin"))), -(u'dlgbmp', msilib.Binary(os.path.join(dirname,"dlgbmp.bin"))), -(u'exclamic', msilib.Binary(os.path.join(dirname,"exclamic.bin"))), -(u'info', msilib.Binary(os.path.join(dirname,"info.bin"))), -(u'insticon', msilib.Binary(os.path.join(dirname,"insticon.bin"))), -(u'New', msilib.Binary(os.path.join(dirname,"New.bin"))), -(u'removico', msilib.Binary(os.path.join(dirname,"removico.bin"))), -(u'repairic', msilib.Binary(os.path.join(dirname,"repairic.bin"))), -(u'Up', msilib.Binary(os.path.join(dirname,"Up.bin"))), -] - -CheckBox = [ -] - -Property = [ -(u'BannerBitmap', u'bannrbmp'), -(u'IAgree', u'No'), -(u'ProductID', u'none'), -(u'ARPHELPLINK', u'http://www.microsoft.com/management'), -(u'ButtonText_Back', u'< &Back'), -(u'ButtonText_Browse', u'Br&owse'), -(u'ButtonText_Cancel', u'Cancel'), -(u'ButtonText_Exit', u'&Exit'), -(u'ButtonText_Finish', u'&Finish'), -(u'ButtonText_Ignore', u'&Ignore'), -(u'ButtonText_Install', u'&Install'), -(u'ButtonText_Next', u'&Next >'), -(u'ButtonText_No', u'&No'), -(u'ButtonText_OK', u'OK'), -(u'ButtonText_Remove', u'&Remove'), -(u'ButtonText_Repair', u'&Repair'), -(u'ButtonText_Reset', u'&Reset'), -(u'ButtonText_Resume', u'&Resume'), -(u'ButtonText_Retry', u'&Retry'), -(u'ButtonText_Return', u'&Return'), -(u'ButtonText_Yes', u'&Yes'), -(u'CompleteSetupIcon', u'completi'), -(u'ComponentDownload', u'ftp://anonymous@microsoft.com/components/'), -(u'CustomSetupIcon', u'custicon'), -(u'DefaultUIFont', u'DlgFont8'), -(u'DialogBitmap', u'dlgbmp'), -(u'DlgTitleFont', u'{&DlgFontBold8}'), -(u'ErrorDialog', u'ErrorDlg'), -(u'ExclamationIcon', u'exclamic'), -(u'InfoIcon', u'info'), -(u'InstallerIcon', u'insticon'), -(u'INSTALLLEVEL', u'3'), -(u'InstallMode', u'Typical'), -(u'PIDTemplate', u'12345<###-%%%%%%%>@@@@@'), -#(u'ProductLanguage', u'1033'), -(u'Progress1', u'Installing'), -(u'Progress2', u'installs'), -(u'PROMPTROLLBACKCOST', u'P'), -(u'RemoveIcon', u'removico'), -(u'RepairIcon', u'repairic'), -(u'Setup', u'Setup'), -(u'ShowUserRegistrationDlg', u'1'), -(u'Wizard', u'Setup Wizard'), -] - -ComboBox = [ -] - -Control = [ -(u'AdminWelcomeDlg', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'AdminWelcomeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'AdminWelcomeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'AdminWelcomeDlg', u'Description', u'Text', 135, 70, 220, 30, 196611, None, u'The [Wizard] will create a server image of [ProductName], at a specified network location. Click Next to continue or Cancel to exit the [Wizard].', None, None), -(u'AdminWelcomeDlg', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Welcome to the [ProductName] [Wizard]', None, None), -(u'AdminWelcomeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Next', None), -(u'AdminWelcomeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'ExitDialog', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'ExitDialog', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'ExitDialog', u'Cancel', u'PushButton', 304, 243, 56, 17, 1, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'ExitDialog', u'Description', u'Text', 135, 70, 220, 20, 196611, None, u'Click the Finish button to exit the [Wizard].', None, None), -(u'ExitDialog', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Completing the [ProductName] [Wizard]', None, None), -(u'ExitDialog', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Finish', None), -(u'ExitDialog', u'Finish', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Finish]', u'Cancel', None), -(u'FatalError', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'FatalError', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'FatalError', u'Cancel', u'PushButton', 304, 243, 56, 17, 1, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'FatalError', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}[ProductName] [Wizard] ended prematurely', None, None), -(u'FatalError', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Finish', None), -(u'FatalError', u'Finish', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Finish]', u'Cancel', None), -(u'FatalError', u'Description1', u'Text', 135, 70, 220, 40, 196611, None, u'[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.', None, None), -(u'FatalError', u'Description2', u'Text', 135, 115, 220, 20, 196611, None, u'Click the Finish button to exit the [Wizard].', None, None), -(u'PrepareDlg', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Cancel', None), -(u'PrepareDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'PrepareDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'PrepareDlg', u'Description', u'Text', 135, 70, 220, 20, 196611, None, u'Please wait while the [Wizard] prepares to guide you through the installation.', None, None), -(u'PrepareDlg', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Welcome to the [ProductName] [Wizard]', None, None), -(u'PrepareDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', None, None), -(u'PrepareDlg', u'Next', u'PushButton', 236, 243, 56, 17, 1, None, u'[ButtonText_Next]', None, None), -(u'PrepareDlg', u'ActionData', u'Text', 135, 125, 220, 30, 196611, None, None, None, None), -(u'PrepareDlg', u'ActionText', u'Text', 135, 100, 220, 20, 196611, None, None, None, None), -(u'ProgressDlg', u'Text', u'Text', 35, 65, 300, 20, 3, None, u'Please wait while the [Wizard] [Progress2] [ProductName]. This may take several minutes.', None, None), -(u'ProgressDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Back', None), -(u'ProgressDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'ProgressDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'ProgressDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'ProgressDlg', u'Title', u'Text', 20, 15, 200, 15, 196611, None, u'[DlgTitleFont][Progress1] [ProductName]', None, None), -(u'ProgressDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Next', None), -(u'ProgressDlg', u'Next', u'PushButton', 236, 243, 56, 17, 1, None, u'[ButtonText_Next]', u'Cancel', None), -(u'ProgressDlg', u'ActionText', u'Text', 70, 100, 265, 10, 3, None, None, None, None), -(u'ProgressDlg', u'ProgressBar', u'ProgressBar', 35, 115, 300, 10, 65537, None, u'Progress done', None, None), -(u'ProgressDlg', u'StatusLabel', u'Text', 35, 100, 35, 10, 3, None, u'Status:', None, None), -(u'UserExit', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'UserExit', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'UserExit', u'Cancel', u'PushButton', 304, 243, 56, 17, 1, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'UserExit', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}[ProductName] [Wizard] was interrupted', None, None), -(u'UserExit', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Finish', None), -(u'UserExit', u'Finish', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Finish]', u'Cancel', None), -(u'UserExit', u'Description1', u'Text', 135, 70, 220, 40, 196611, None, u'[ProductName] setup was interrupted. Your system has not been modified. To install this program at a later time, please run the installation again.', None, None), -(u'UserExit', u'Description2', u'Text', 135, 115, 220, 20, 196611, None, u'Click the Finish button to exit the [Wizard].', None, None), -(u'AdminBrowseDlg', u'Up', u'PushButton', 298, 55, 19, 19, 3670019, None, u'Up', u'NewFolder', u'Up One Level|'), -(u'AdminBrowseDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'PathEdit', None), -(u'AdminBrowseDlg', u'PathEdit', u'PathEdit', 84, 202, 261, 17, 3, u'TARGETDIR', None, u'OK', None), -(u'AdminBrowseDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'AdminBrowseDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'AdminBrowseDlg', u'Cancel', u'PushButton', 240, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'ComboLabel', None), -(u'AdminBrowseDlg', u'ComboLabel', u'Text', 25, 58, 44, 10, 3, None, u'&Look in:', u'DirectoryCombo', None), -(u'AdminBrowseDlg', u'DirectoryCombo', u'DirectoryCombo', 70, 55, 220, 80, 458755, u'TARGETDIR', None, u'Up', None), -(u'AdminBrowseDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Browse to the destination folder', None, None), -(u'AdminBrowseDlg', u'DirectoryList', u'DirectoryList', 25, 83, 320, 110, 7, u'TARGETDIR', None, u'PathLabel', None), -(u'AdminBrowseDlg', u'PathLabel', u'Text', 25, 205, 59, 10, 3, None, u'&Folder name:', u'BannerBitmap', None), -(u'AdminBrowseDlg', u'NewFolder', u'PushButton', 325, 55, 19, 19, 3670019, None, u'New', u'DirectoryList', u'Create A New Folder|'), -(u'AdminBrowseDlg', u'OK', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_OK]', u'Cancel', None), -(u'AdminBrowseDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Change current destination folder', None, None), -(u'AdminInstallPointDlg', u'Text', u'Text', 25, 80, 320, 10, 3, None, u'&Enter a new network location or click Browse to browse to one.', u'PathEdit', None), -(u'AdminInstallPointDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Text', None), -(u'AdminInstallPointDlg', u'PathEdit', u'PathEdit', 25, 93, 320, 18, 3, u'TARGETDIR', None, u'Browse', None), -(u'AdminInstallPointDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'AdminInstallPointDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'AdminInstallPointDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'AdminInstallPointDlg', u'Description', u'Text', 25, 20, 280, 20, 196611, None, u'Please specify a network location for the server image of [ProductName] product', None, None), -(u'AdminInstallPointDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Network Location', None, None), -(u'AdminInstallPointDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'AdminInstallPointDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'AdminInstallPointDlg', u'Browse', u'PushButton', 289, 119, 56, 17, 3, None, u'[ButtonText_Browse]', u'Back', None), -(u'AdminRegistrationDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'OrganizationLabel', None), -(u'AdminRegistrationDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'AdminRegistrationDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'AdminRegistrationDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'AdminRegistrationDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Please enter your company information', None, None), -(u'AdminRegistrationDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Company Information', None, None), -(u'AdminRegistrationDlg', u'Back', u'PushButton', 180, 243, 56, 17, 65539, None, u'[ButtonText_Back]', u'Next', None), -(u'AdminRegistrationDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'AdminRegistrationDlg', u'OrganizationLabel', u'Text', 45, 71, 285, 30, 3, None, u'&Please enter the name of your organization in the box below. This will be used as default company name for subsequent installations of [ProductName]:', u'OrganizationEdit', None), -(u'AdminRegistrationDlg', u'CDKeyEdit', u'MaskedEdit', 45, 143, 250, 16, 3, u'PIDKEY', u'[PIDTemplate]', u'Back', None), -(u'AdminRegistrationDlg', u'CDKeyLabel', u'Text', 45, 130, 50, 10, 3, None, u'CD &Key:', u'CDKeyEdit', None), -(u'AdminRegistrationDlg', u'OrganizationEdit', u'Edit', 45, 105, 220, 18, 3, u'COMPANYNAME', u'{80}', u'CDKeyLabel', None), -(u'BrowseDlg', u'Up', u'PushButton', 298, 55, 19, 19, 3670019, None, u'Up', u'NewFolder', u'Up One Level|'), -(u'BrowseDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'PathEdit', None), -(u'BrowseDlg', u'PathEdit', u'PathEdit', 84, 202, 261, 18, 11, u'_BrowseProperty', None, u'OK', None), -(u'BrowseDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'BrowseDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'BrowseDlg', u'Cancel', u'PushButton', 240, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'ComboLabel', None), -(u'BrowseDlg', u'ComboLabel', u'Text', 25, 58, 44, 10, 3, None, u'&Look in:', u'DirectoryCombo', None), -(u'BrowseDlg', u'DirectoryCombo', u'DirectoryCombo', 70, 55, 220, 80, 393227, u'_BrowseProperty', None, u'Up', None), -(u'BrowseDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Browse to the destination folder', None, None), -(u'BrowseDlg', u'DirectoryList', u'DirectoryList', 25, 83, 320, 110, 15, u'_BrowseProperty', None, u'PathLabel', None), -(u'BrowseDlg', u'PathLabel', u'Text', 25, 205, 59, 10, 3, None, u'&Folder name:', u'BannerBitmap', None), -(u'BrowseDlg', u'NewFolder', u'PushButton', 325, 55, 19, 19, 3670019, None, u'New', u'DirectoryList', u'Create A New Folder|'), -(u'BrowseDlg', u'OK', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_OK]', u'Cancel', None), -(u'BrowseDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Change current destination folder', None, None), -(u'CancelDlg', u'Text', u'Text', 48, 15, 194, 30, 3, None, u'Are you sure you want to cancel [ProductName] installation?', None, None), -(u'CancelDlg', u'Icon', u'Icon', 15, 15, 24, 24, 5242881, None, u'[InfoIcon]', None, u'Information icon|'), -(u'CancelDlg', u'No', u'PushButton', 132, 57, 56, 17, 3, None, u'[ButtonText_No]', u'Yes', None), -(u'CancelDlg', u'Yes', u'PushButton', 72, 57, 56, 17, 3, None, u'[ButtonText_Yes]', u'No', None), -(u'CustomizeDlg', u'Text', u'Text', 25, 55, 320, 20, 3, None, u'Click on the icons in the tree below to change the way features will be installed.', None, None), -(u'CustomizeDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Tree', None), -(u'CustomizeDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'CustomizeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'CustomizeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'CustomizeDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Select the way you want features to be installed.', None, None), -(u'CustomizeDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Custom Setup', None, None), -(u'CustomizeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'CustomizeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'CustomizeDlg', u'Browse', u'PushButton', 304, 200, 56, 17, 3, None, u'[ButtonText_Browse]', u'Reset', None), -(u'CustomizeDlg', u'Tree', u'SelectionTree', 25, 85, 175, 95, 7, u'_BrowseProperty', u'Tree of selections', u'Browse', None), -(u'CustomizeDlg', u'Box', u'GroupBox', 210, 81, 140, 98, 1, None, None, None, None), -(u'CustomizeDlg', u'Reset', u'PushButton', 42, 243, 56, 17, 3, None, u'[ButtonText_Reset]', u'DiskCost', None), -(u'CustomizeDlg', u'DiskCost', u'PushButton', 111, 243, 56, 17, 3, None, u'Disk &Usage', u'Back', None), -(u'CustomizeDlg', u'ItemDescription', u'Text', 215, 90, 131, 30, 3, None, u'Multiline description of the currently selected item.', None, None), -(u'CustomizeDlg', u'ItemSize', u'Text', 215, 130, 131, 45, 3, None, u'The size of the currently selected item.', None, None), -(u'CustomizeDlg', u'Location', u'Text', 75, 200, 215, 20, 3, None, u"", None, None), -(u'CustomizeDlg', u'LocationLabel', u'Text', 25, 200, 50, 10, 3, None, u'Location:', None, None), -(u'DiskCostDlg', u'Text', u'Text', 20, 53, 330, 40, 3, None, u'The highlighted volumes (if any) do not have enough disk space available for the currently selected features. You can either remove some files from the highlighted volumes, or choose to install less features onto local drive(s), or select different destination drive(s).', None, None), -(u'DiskCostDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'OK', None), -(u'DiskCostDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'DiskCostDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'DiskCostDlg', u'Description', u'Text', 20, 20, 280, 20, 196611, None, u'The disk space required for the installation of the selected features.', None, None), -(u'DiskCostDlg', u'OK', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_OK]', u'BannerBitmap', None), -(u'DiskCostDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Disk Space Requirements', None, None), -(u'DiskCostDlg', u'VolumeList', u'VolumeCostList', 20, 100, 330, 120, 393223, None, u'{120}{70}{70}{70}{70}', None, None), -(u'ErrorDlg', u'Y', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_Yes]', None, None), -(u'ErrorDlg', u'A', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_Cancel]', None, None), -(u'ErrorDlg', u'C', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_Cancel]', None, None), -(u'ErrorDlg', u'ErrorIcon', u'Icon', 15, 15, 24, 24, 5242881, None, u'[InfoIcon]', None, u'Information icon|'), -(u'ErrorDlg', u'ErrorText', u'Text', 48, 15, 205, 60, 3, None, u'Information text', None, None), -(u'ErrorDlg', u'I', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_Ignore]', None, None), -(u'ErrorDlg', u'N', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_No]', None, None), -(u'ErrorDlg', u'O', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_OK]', None, None), -(u'ErrorDlg', u'R', u'PushButton', 100, 80, 56, 17, 3, None, u'[ButtonText_Retry]', None, None), -(u'FilesInUse', u'Text', u'Text', 20, 55, 330, 30, 3, None, u'The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.', None, None), -(u'FilesInUse', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Retry', None), -(u'FilesInUse', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'FilesInUse', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'FilesInUse', u'Description', u'Text', 20, 23, 280, 20, 196611, None, u'Some files that need to be updated are currently in use.', None, None), -(u'FilesInUse', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Files in Use', None, None), -(u'FilesInUse', u'Retry', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Retry]', u'Ignore', None), -(u'FilesInUse', u'Exit', u'PushButton', 166, 243, 56, 17, 3, None, u'[ButtonText_Exit]', u'BannerBitmap', None), -(u'FilesInUse', u'Ignore', u'PushButton', 235, 243, 56, 17, 3, None, u'[ButtonText_Ignore]', u'Exit', None), -(u'FilesInUse', u'List', u'ListBox', 20, 87, 330, 130, 7, u'FileInUseProcess', None, None, None), -(u'LicenseAgreementDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'AgreementText', None), -(u'LicenseAgreementDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'LicenseAgreementDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'LicenseAgreementDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'LicenseAgreementDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Please read the following license agreement carefully', None, None), -(u'LicenseAgreementDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]End-User License Agreement', None, None), -(u'LicenseAgreementDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'LicenseAgreementDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'LicenseAgreementDlg', u'AgreementText', u'ScrollableText', 20, 60, 330, 120, 7, None, u'{\\rtf1\\ansi\\ansicpg1252\\deff0\\deftab720{\\fonttbl{\\f0\\froman\\fprq2 Times New Roman;}}{\\colortbl\\red0\\green0\\blue0;} \\deflang1033\\horzdoc{\\*\\fchars }{\\*\\lchars }\\pard\\plain\\f0\\fs20 \\par }', u'Buttons', None), -(u'LicenseAgreementDlg', u'Buttons', u'RadioButtonGroup', 20, 187, 330, 40, 3, u'IAgree', None, u'Back', None), -(u'MaintenanceTypeDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'ChangeLabel', None), -(u'MaintenanceTypeDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'MaintenanceTypeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'MaintenanceTypeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'MaintenanceTypeDlg', u'Description', u'Text', 25, 23, 280, 20, 196611, None, u'Select the operation you wish to perform.', None, None), -(u'MaintenanceTypeDlg', u'Title', u'Text', 15, 6, 240, 15, 196611, None, u'[DlgTitleFont]Modify, Repair or Remove installation', None, None), -(u'MaintenanceTypeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'MaintenanceTypeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 1, None, u'[ButtonText_Next]', u'Cancel', None), -(u'MaintenanceTypeDlg', u'ChangeLabel', u'Text', 105, 65, 100, 10, 3, None, u'[DlgTitleFont]&Modify', u'ChangeButton', None), -(u'MaintenanceTypeDlg', u'ChangeButton', u'PushButton', 50, 65, 38, 38, 5767171, None, u'[CustomSetupIcon]', u'RepairLabel', u'Modify Installation|'), -(u'MaintenanceTypeDlg', u'RepairLabel', u'Text', 105, 114, 100, 10, 3, None, u'[DlgTitleFont]Re&pair', u'RepairButton', None), -(u'MaintenanceTypeDlg', u'ChangeText', u'Text', 105, 78, 230, 20, 3, None, u'Allows users to change the way features are installed.', None, None), -(u'MaintenanceTypeDlg', u'RemoveButton', u'PushButton', 50, 163, 38, 38, 5767171, None, u'[RemoveIcon]', u'Back', u'Remove Installation|'), -(u'MaintenanceTypeDlg', u'RemoveLabel', u'Text', 105, 163, 100, 10, 3, None, u'[DlgTitleFont]&Remove', u'RemoveButton', None), -(u'MaintenanceTypeDlg', u'RemoveText', u'Text', 105, 176, 230, 20, 3, None, u'Removes [ProductName] from your computer.', None, None), -(u'MaintenanceTypeDlg', u'RepairButton', u'PushButton', 50, 114, 38, 38, 5767171, None, u'[RepairIcon]', u'RemoveLabel', u'Repair Installation|'), -(u'MaintenanceTypeDlg', u'RepairText', u'Text', 105, 127, 230, 30, 3, None, u'Repairs errors in the most recent installation state - fixes missing or corrupt files, shortcuts and registry entries.', None, None), -(u'MaintenanceWelcomeDlg', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'MaintenanceWelcomeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'MaintenanceWelcomeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'MaintenanceWelcomeDlg', u'Description', u'Text', 135, 70, 220, 60, 196611, None, u'The [Wizard] will allow you to change the way [ProductName] features are installed on your computer or even to remove [ProductName] from your computer. Click Next to continue or Cancel to exit the [Wizard].', None, None), -(u'MaintenanceWelcomeDlg', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Welcome to the [ProductName] [Wizard]', None, None), -(u'MaintenanceWelcomeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Next', None), -(u'MaintenanceWelcomeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'OutOfDiskDlg', u'Text', u'Text', 20, 53, 330, 40, 3, None, u'The highlighted volumes do not have enough disk space available for the currently selected features. You can either remove some files from the highlighted volumes, or choose to install less features onto local drive(s), or select different destination drive(s).', None, None), -(u'OutOfDiskDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'OK', None), -(u'OutOfDiskDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'OutOfDiskDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'OutOfDiskDlg', u'Description', u'Text', 20, 20, 280, 20, 196611, None, u'Disk space required for the installation exceeds available disk space.', None, None), -(u'OutOfDiskDlg', u'OK', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_OK]', u'BannerBitmap', None), -(u'OutOfDiskDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Out of Disk Space', None, None), -(u'OutOfDiskDlg', u'VolumeList', u'VolumeCostList', 20, 100, 330, 120, 393223, None, u'{120}{70}{70}{70}{70}', None, None), -(u'OutOfRbDiskDlg', u'Text', u'Text', 20, 53, 330, 40, 3, None, u'The highlighted volumes do not have enough disk space available for the currently selected features. You can either remove some files from the highlighted volumes, or choose to install less features onto local drive(s), or select different destination drive(s).', None, None), -(u'OutOfRbDiskDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'No', None), -(u'OutOfRbDiskDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'OutOfRbDiskDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'OutOfRbDiskDlg', u'Description', u'Text', 20, 20, 280, 20, 196611, None, u'Disk space required for the installation exceeds available disk space.', None, None), -(u'OutOfRbDiskDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Out of Disk Space', None, None), -(u'OutOfRbDiskDlg', u'No', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_No]', u'Yes', None), -(u'OutOfRbDiskDlg', u'Yes', u'PushButton', 240, 243, 56, 17, 3, None, u'[ButtonText_Yes]', u'BannerBitmap', None), -(u'OutOfRbDiskDlg', u'VolumeList', u'VolumeCostList', 20, 140, 330, 80, 4587527, None, u'{120}{70}{70}{70}{70}', None, None), -(u'OutOfRbDiskDlg', u'Text2', u'Text', 20, 94, 330, 40, 3, None, u"Alternatively, you may choose to disable the installer's rollback functionality. This allows the installer to restore your computer's original state should the installation be interrupted in any way. Click Yes if you wish to take the risk to disable rollback.", None, None), -(u'ResumeDlg', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'ResumeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'ResumeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'ResumeDlg', u'Description', u'Text', 135, 70, 220, 30, 196611, None, u'The [Wizard] will complete the installation of [ProductName] on your computer. Click Install to continue or Cancel to exit the [Wizard].', None, None), -(u'ResumeDlg', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Resuming the [ProductName] [Wizard]', None, None), -(u'ResumeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Install', None), -(u'ResumeDlg', u'Install', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Install]', u'Cancel', None), -(u'SetupTypeDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'TypicalLabel', None), -(u'SetupTypeDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'SetupTypeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'SetupTypeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'SetupTypeDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Choose the setup type that best suits your needs', None, None), -(u'SetupTypeDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Choose Setup Type', None, None), -(u'SetupTypeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'SetupTypeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 1, None, u'[ButtonText_Next]', u'Cancel', None), -(u'SetupTypeDlg', u'TypicalLabel', u'Text', 105, 65, 100, 10, 3, None, u'[DlgTitleFont]&Typical', u'TypicalButton', None), -(u'SetupTypeDlg', u'CompleteButton', u'PushButton', 50, 171, 38, 38, 5767171, None, u'[CompleteSetupIcon]', u'Back', u'Complete Installation|'), -(u'SetupTypeDlg', u'CompleteLabel', u'Text', 105, 171, 100, 10, 3, None, u'[DlgTitleFont]C&omplete', u'CompleteButton', None), -(u'SetupTypeDlg', u'CompleteText', u'Text', 105, 184, 230, 20, 3, None, u'All program features will be installed. (Requires most disk space)', None, None), -(u'SetupTypeDlg', u'CustomButton', u'PushButton', 50, 118, 38, 38, 5767171, None, u'[CustomSetupIcon]', u'CompleteLabel', u'Custom Installation|'), -(u'SetupTypeDlg', u'CustomLabel', u'Text', 105, 118, 100, 10, 3, None, u'[DlgTitleFont]C&ustom', u'CustomButton', None), -(u'SetupTypeDlg', u'CustomText', u'Text', 105, 131, 230, 30, 3, None, u'Allows users to choose which program features will be installed and where they will be installed. Recommended for advanced users.', None, None), -(u'SetupTypeDlg', u'TypicalButton', u'PushButton', 50, 65, 38, 38, 5767171, None, u'[InstallerIcon]', u'CustomLabel', u'Typical Installation|'), -(u'SetupTypeDlg', u'TypicalText', u'Text', 105, 78, 230, 20, 3, None, u'Installs the most common program features. Recommended for most users.', None, None), -(u'UserRegistrationDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'NameLabel', None), -(u'UserRegistrationDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'UserRegistrationDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'UserRegistrationDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'UserRegistrationDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'Please enter your customer information', None, None), -(u'UserRegistrationDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Customer Information', None, None), -(u'UserRegistrationDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Next', None), -(u'UserRegistrationDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -(u'UserRegistrationDlg', u'OrganizationLabel', u'Text', 45, 110, 100, 15, 3, None, u'&Organization:', u'OrganizationEdit', None), -(u'UserRegistrationDlg', u'CDKeyEdit', u'MaskedEdit', 45, 159, 250, 16, 3, u'PIDKEY', u'[PIDTemplate]', u'Back', None), -(u'UserRegistrationDlg', u'CDKeyLabel', u'Text', 45, 147, 50, 10, 3, None, u'CD &Key:', u'CDKeyEdit', None), -(u'UserRegistrationDlg', u'OrganizationEdit', u'Edit', 45, 122, 220, 18, 3, u'COMPANYNAME', u'{80}', u'CDKeyLabel', None), -(u'UserRegistrationDlg', u'NameLabel', u'Text', 45, 73, 100, 15, 3, None, u'&User Name:', u'NameEdit', None), -(u'UserRegistrationDlg', u'NameEdit', u'Edit', 45, 85, 220, 18, 3, u'USERNAME', u'{80}', u'OrganizationLabel', None), -(u'VerifyReadyDlg', u'Text', u'Text', 25, 70, 320, 20, 3, None, u'Click Install to begin the installation. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard.', None, None), -(u'VerifyReadyDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Back', None), -(u'VerifyReadyDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'VerifyReadyDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'VerifyReadyDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'VerifyReadyDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'The [Wizard] is ready to begin the [InstallMode] installation', None, None), -(u'VerifyReadyDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Ready to Install', None, None), -(u'VerifyReadyDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Install', None), -(u'VerifyReadyDlg', u'Install', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Install]', u'Cancel', None), -(u'VerifyRemoveDlg', u'Text', u'Text', 25, 70, 320, 30, 3, None, u'Click Remove to remove [ProductName] from your computer. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard.', None, None), -(u'VerifyRemoveDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Back', None), -(u'VerifyRemoveDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'VerifyRemoveDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'VerifyRemoveDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'VerifyRemoveDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'You have chosen to remove the program from your computer.', None, None), -(u'VerifyRemoveDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Remove [ProductName]', None, None), -(u'VerifyRemoveDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Remove', None), -(u'VerifyRemoveDlg', u'Remove', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Remove]', u'Cancel', None), -(u'VerifyRepairDlg', u'Text', u'Text', 25, 70, 320, 30, 3, None, u'Click Repair to repair the installation of [ProductName]. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard.', None, None), -(u'VerifyRepairDlg', u'BannerBitmap', u'Bitmap', 0, 0, 374, 44, 1, None, u'[BannerBitmap]', u'Back', None), -(u'VerifyRepairDlg', u'BannerLine', u'Line', 0, 44, 374, 0, 1, None, None, None, None), -(u'VerifyRepairDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'VerifyRepairDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'BannerBitmap', None), -(u'VerifyRepairDlg', u'Description', u'Text', 25, 23, 280, 15, 196611, None, u'The [Wizard] is ready to begin the repair of [ProductName].', None, None), -(u'VerifyRepairDlg', u'Title', u'Text', 15, 6, 200, 15, 196611, None, u'[DlgTitleFont]Repair [ProductName]', None, None), -(u'VerifyRepairDlg', u'Back', u'PushButton', 180, 243, 56, 17, 3, None, u'[ButtonText_Back]', u'Repair', None), -(u'VerifyRepairDlg', u'Repair', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Repair]', u'Cancel', None), -(u'WaitForCostingDlg', u'Text', u'Text', 48, 15, 194, 30, 3, None, u'Please wait while the installer finishes determining your disk space requirements.', None, None), -(u'WaitForCostingDlg', u'Icon', u'Icon', 15, 15, 24, 24, 5242881, None, u'[ExclamationIcon]', None, u'Exclamation icon|'), -(u'WaitForCostingDlg', u'Return', u'PushButton', 102, 57, 56, 17, 3, None, u'[ButtonText_Return]', None, None), -(u'WelcomeDlg', u'Bitmap', u'Bitmap', 0, 0, 370, 234, 1, None, u'[DialogBitmap]', u'Back', None), -(u'WelcomeDlg', u'BottomLine', u'Line', 0, 234, 374, 0, 1, None, None, None, None), -(u'WelcomeDlg', u'Cancel', u'PushButton', 304, 243, 56, 17, 3, None, u'[ButtonText_Cancel]', u'Bitmap', None), -(u'WelcomeDlg', u'Description', u'Text', 135, 70, 220, 30, 196611, None, u'The [Wizard] will install [ProductName] on your computer. Click Next to continue or Cancel to exit the [Wizard].', None, None), -(u'WelcomeDlg', u'Title', u'Text', 135, 20, 220, 60, 196611, None, u'{\\VerdanaBold13}Welcome to the [ProductName] [Wizard]', None, None), -(u'WelcomeDlg', u'Back', u'PushButton', 180, 243, 56, 17, 1, None, u'[ButtonText_Back]', u'Next', None), -(u'WelcomeDlg', u'Next', u'PushButton', 236, 243, 56, 17, 3, None, u'[ButtonText_Next]', u'Cancel', None), -] - -ListBox = [ -] - -ActionText = [ -(u'InstallValidate', u'Validating install', None), -(u'InstallFiles', u'Copying new files', u'File: [1], Directory: [9], Size: [6]'), -(u'InstallAdminPackage', u'Copying network install files', u'File: [1], Directory: [9], Size: [6]'), -(u'FileCost', u'Computing space requirements', None), -(u'CostInitialize', u'Computing space requirements', None), -(u'CostFinalize', u'Computing space requirements', None), -(u'CreateShortcuts', u'Creating shortcuts', u'Shortcut: [1]'), -(u'PublishComponents', u'Publishing Qualified Components', u'Component ID: [1], Qualifier: [2]'), -(u'PublishFeatures', u'Publishing Product Features', u'Feature: [1]'), -(u'PublishProduct', u'Publishing product information', None), -(u'RegisterClassInfo', u'Registering Class servers', u'Class Id: [1]'), -(u'RegisterExtensionInfo', u'Registering extension servers', u'Extension: [1]'), -(u'RegisterMIMEInfo', u'Registering MIME info', u'MIME Content Type: [1], Extension: [2]'), -(u'RegisterProgIdInfo', u'Registering program identifiers', u'ProgId: [1]'), -(u'AllocateRegistrySpace', u'Allocating registry space', u'Free space: [1]'), -(u'AppSearch', u'Searching for installed applications', u'Property: [1], Signature: [2]'), -(u'BindImage', u'Binding executables', u'File: [1]'), -(u'CCPSearch', u'Searching for qualifying products', None), -(u'CreateFolders', u'Creating folders', u'Folder: [1]'), -(u'DeleteServices', u'Deleting services', u'Service: [1]'), -(u'DuplicateFiles', u'Creating duplicate files', u'File: [1], Directory: [9], Size: [6]'), -(u'FindRelatedProducts', u'Searching for related applications', u'Found application: [1]'), -(u'InstallODBC', u'Installing ODBC components', None), -(u'InstallServices', u'Installing new services', u'Service: [2]'), -(u'LaunchConditions', u'Evaluating launch conditions', None), -(u'MigrateFeatureStates', u'Migrating feature states from related applications', u'Application: [1]'), -(u'MoveFiles', u'Moving files', u'File: [1], Directory: [9], Size: [6]'), -(u'PatchFiles', u'Patching files', u'File: [1], Directory: [2], Size: [3]'), -(u'ProcessComponents', u'Updating component registration', None), -(u'RegisterComPlus', u'Registering COM+ Applications and Components', u'AppId: [1]{{, AppType: [2], Users: [3], RSN: [4]}}'), -(u'RegisterFonts', u'Registering fonts', u'Font: [1]'), -(u'RegisterProduct', u'Registering product', u'[1]'), -(u'RegisterTypeLibraries', u'Registering type libraries', u'LibID: [1]'), -(u'RegisterUser', u'Registering user', u'[1]'), -(u'RemoveDuplicateFiles', u'Removing duplicated files', u'File: [1], Directory: [9]'), -(u'RemoveEnvironmentStrings', u'Updating environment strings', u'Name: [1], Value: [2], Action [3]'), -(u'RemoveExistingProducts', u'Removing applications', u'Application: [1], Command line: [2]'), -(u'RemoveFiles', u'Removing files', u'File: [1], Directory: [9]'), -(u'RemoveFolders', u'Removing folders', u'Folder: [1]'), -(u'RemoveIniValues', u'Removing INI files entries', u'File: [1], Section: [2], Key: [3], Value: [4]'), -(u'RemoveODBC', u'Removing ODBC components', None), -(u'RemoveRegistryValues', u'Removing system registry values', u'Key: [1], Name: [2]'), -(u'RemoveShortcuts', u'Removing shortcuts', u'Shortcut: [1]'), -(u'RMCCPSearch', u'Searching for qualifying products', None), -(u'SelfRegModules', u'Registering modules', u'File: [1], Folder: [2]'), -(u'SelfUnregModules', u'Unregistering modules', u'File: [1], Folder: [2]'), -(u'SetODBCFolders', u'Initializing ODBC directories', None), -(u'StartServices', u'Starting services', u'Service: [1]'), -(u'StopServices', u'Stopping services', u'Service: [1]'), -(u'UnpublishComponents', u'Unpublishing Qualified Components', u'Component ID: [1], Qualifier: [2]'), -(u'UnpublishFeatures', u'Unpublishing Product Features', u'Feature: [1]'), -(u'UnregisterClassInfo', u'Unregister Class servers', u'Class Id: [1]'), -(u'UnregisterComPlus', u'Unregistering COM+ Applications and Components', u'AppId: [1]{{, AppType: [2]}}'), -(u'UnregisterExtensionInfo', u'Unregistering extension servers', u'Extension: [1]'), -(u'UnregisterFonts', u'Unregistering fonts', u'Font: [1]'), -(u'UnregisterMIMEInfo', u'Unregistering MIME info', u'MIME Content Type: [1], Extension: [2]'), -(u'UnregisterProgIdInfo', u'Unregistering program identifiers', u'ProgId: [1]'), -(u'UnregisterTypeLibraries', u'Unregistering type libraries', u'LibID: [1]'), -(u'WriteEnvironmentStrings', u'Updating environment strings', u'Name: [1], Value: [2], Action [3]'), -(u'WriteIniValues', u'Writing INI files values', u'File: [1], Section: [2], Key: [3], Value: [4]'), -(u'WriteRegistryValues', u'Writing system registry values', u'Key: [1], Name: [2], Value: [3]'), -(u'Advertise', u'Advertising application', None), -(u'GenerateScript', u'Generating script operations for action:', u'[1]'), -(u'InstallSFPCatalogFile', u'Installing system catalog', u'File: [1], Dependencies: [2]'), -(u'MsiPublishAssemblies', u'Publishing assembly information', u'Application Context:[1], Assembly Name:[2]'), -(u'MsiUnpublishAssemblies', u'Unpublishing assembly information', u'Application Context:[1], Assembly Name:[2]'), -(u'Rollback', u'Rolling back action:', u'[1]'), -(u'RollbackCleanup', u'Removing backup files', u'File: [1]'), -(u'UnmoveFiles', u'Removing moved files', u'File: [1], Directory: [9]'), -(u'UnpublishProduct', u'Unpublishing product information', None), -] - -ControlCondition = [ -(u'CustomizeDlg', u'Browse', u'Hide', u'Installed'), -(u'CustomizeDlg', u'Location', u'Hide', u'Installed'), -(u'CustomizeDlg', u'LocationLabel', u'Hide', u'Installed'), -(u'LicenseAgreementDlg', u'Next', u'Disable', u'IAgree <> "Yes"'), -(u'LicenseAgreementDlg', u'Next', u'Enable', u'IAgree = "Yes"'), -] - -ControlEvent = [ -(u'AdminWelcomeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'AdminWelcomeDlg', u'Next', u'NewDialog', u'AdminRegistrationDlg', u'1', 2), -(u'AdminWelcomeDlg', u'Next', u'[InstallMode]', u'Server Image', u'1', 1), -(u'ExitDialog', u'Finish', u'EndDialog', u'Return', u'1', None), -(u'FatalError', u'Finish', u'EndDialog', u'Exit', u'1', None), -(u'PrepareDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'ProgressDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'UserExit', u'Finish', u'EndDialog', u'Exit', u'1', None), -(u'AdminBrowseDlg', u'Up', u'DirectoryListUp', u'0', u'1', None), -(u'AdminBrowseDlg', u'Cancel', u'Reset', u'0', u'1', 1), -(u'AdminBrowseDlg', u'Cancel', u'EndDialog', u'Return', u'1', 2), -(u'AdminBrowseDlg', u'NewFolder', u'DirectoryListNew', u'0', u'1', None), -(u'AdminBrowseDlg', u'OK', u'EndDialog', u'Return', u'1', 2), -(u'AdminBrowseDlg', u'OK', u'SetTargetPath', u'TARGETDIR', u'1', 1), -(u'AdminInstallPointDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'AdminInstallPointDlg', u'Back', u'NewDialog', u'AdminRegistrationDlg', u'1', None), -(u'AdminInstallPointDlg', u'Next', u'SetTargetPath', u'TARGETDIR', u'1', 1), -(u'AdminInstallPointDlg', u'Next', u'NewDialog', u'VerifyReadyDlg', u'1', 2), -(u'AdminInstallPointDlg', u'Browse', u'SpawnDialog', u'AdminBrowseDlg', u'1', None), -(u'AdminRegistrationDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'AdminRegistrationDlg', u'Back', u'NewDialog', u'AdminWelcomeDlg', u'1', None), -(u'AdminRegistrationDlg', u'Next', u'NewDialog', u'AdminInstallPointDlg', u'ProductID', 2), -(u'AdminRegistrationDlg', u'Next', u'ValidateProductID', u'0', u'0', 1), -(u'BrowseDlg', u'Up', u'DirectoryListUp', u'0', u'1', None), -(u'BrowseDlg', u'Cancel', u'Reset', u'0', u'1', 1), -(u'BrowseDlg', u'Cancel', u'EndDialog', u'Return', u'1', 2), -(u'BrowseDlg', u'NewFolder', u'DirectoryListNew', u'0', u'1', None), -(u'BrowseDlg', u'OK', u'EndDialog', u'Return', u'1', 2), -(u'BrowseDlg', u'OK', u'SetTargetPath', u'[_BrowseProperty]', u'1', 1), -(u'CancelDlg', u'No', u'EndDialog', u'Return', u'1', None), -(u'CancelDlg', u'Yes', u'EndDialog', u'Exit', u'1', None), -(u'CustomizeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'CustomizeDlg', u'Back', u'NewDialog', u'MaintenanceTypeDlg', u'InstallMode = "Change"', None), -(u'CustomizeDlg', u'Back', u'NewDialog', u'SetupTypeDlg', u'InstallMode = "Custom"', None), -(u'CustomizeDlg', u'Next', u'NewDialog', u'VerifyReadyDlg', u'1', None), -(u'CustomizeDlg', u'Browse', u'SelectionBrowse', u'BrowseDlg', u'1', None), -(u'CustomizeDlg', u'Reset', u'Reset', u'0', u'1', None), -(u'CustomizeDlg', u'DiskCost', u'SpawnDialog', u'DiskCostDlg', u'1', 2), -(u'DiskCostDlg', u'OK', u'EndDialog', u'Return', u'1', None), -(u'ErrorDlg', u'Y', u'EndDialog', u'ErrorYes', u'1', None), -(u'ErrorDlg', u'A', u'EndDialog', u'ErrorAbort', u'1', None), -(u'ErrorDlg', u'C', u'EndDialog', u'ErrorCancel', u'1', None), -(u'ErrorDlg', u'I', u'EndDialog', u'ErrorIgnore', u'1', None), -(u'ErrorDlg', u'N', u'EndDialog', u'ErrorNo', u'1', None), -(u'ErrorDlg', u'O', u'EndDialog', u'ErrorOk', u'1', None), -(u'ErrorDlg', u'R', u'EndDialog', u'ErrorRetry', u'1', None), -(u'FilesInUse', u'Retry', u'EndDialog', u'Retry', u'1', None), -(u'FilesInUse', u'Exit', u'EndDialog', u'Exit', u'1', None), -(u'FilesInUse', u'Ignore', u'EndDialog', u'Ignore', u'1', None), -(u'LicenseAgreementDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'LicenseAgreementDlg', u'Back', u'NewDialog', u'WelcomeDlg', u'1', None), -(u'LicenseAgreementDlg', u'Next', u'NewDialog', u'SetupTypeDlg', u'IAgree = "Yes" AND ShowUserRegistrationDlg <> 1', 3), -(u'LicenseAgreementDlg', u'Next', u'NewDialog', u'UserRegistrationDlg', u'IAgree = "Yes" AND ShowUserRegistrationDlg = 1', 1), -(u'LicenseAgreementDlg', u'Next', u'SpawnWaitDialog', u'WaitForCostingDlg', u'CostingComplete = 1', 2), -(u'MaintenanceTypeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'MaintenanceTypeDlg', u'Back', u'NewDialog', u'MaintenanceWelcomeDlg', u'1', None), -(u'MaintenanceTypeDlg', u'ChangeButton', u'NewDialog', u'CustomizeDlg', u'1', 4), -(u'MaintenanceTypeDlg', u'ChangeButton', u'[InstallMode]', u'Change', u'1', 1), -(u'MaintenanceTypeDlg', u'ChangeButton', u'[Progress1]', u'Changing', u'1', 2), -(u'MaintenanceTypeDlg', u'ChangeButton', u'[Progress2]', u'changes', u'1', 3), -(u'MaintenanceTypeDlg', u'RemoveButton', u'NewDialog', u'VerifyRemoveDlg', u'1', 4), -(u'MaintenanceTypeDlg', u'RemoveButton', u'[InstallMode]', u'Remove', u'1', 1), -(u'MaintenanceTypeDlg', u'RemoveButton', u'[Progress1]', u'Removing', u'1', 2), -(u'MaintenanceTypeDlg', u'RemoveButton', u'[Progress2]', u'removes', u'1', 3), -(u'MaintenanceTypeDlg', u'RepairButton', u'NewDialog', u'VerifyRepairDlg', u'1', 4), -(u'MaintenanceTypeDlg', u'RepairButton', u'[InstallMode]', u'Repair', u'1', 1), -(u'MaintenanceTypeDlg', u'RepairButton', u'[Progress1]', u'Repairing', u'1', 2), -(u'MaintenanceTypeDlg', u'RepairButton', u'[Progress2]', u'repairs', u'1', 3), -(u'MaintenanceWelcomeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'MaintenanceWelcomeDlg', u'Next', u'NewDialog', u'MaintenanceTypeDlg', u'1', 2), -(u'MaintenanceWelcomeDlg', u'Next', u'SpawnWaitDialog', u'WaitForCostingDlg', u'CostingComplete = 1', 1), -(u'OutOfDiskDlg', u'OK', u'EndDialog', u'Return', u'1', None), -(u'OutOfRbDiskDlg', u'No', u'EndDialog', u'Return', u'1', None), -(u'OutOfRbDiskDlg', u'Yes', u'EndDialog', u'Return', u'1', 2), -(u'OutOfRbDiskDlg', u'Yes', u'EnableRollback', u'False', u'1', 1), -(u'ResumeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'ResumeDlg', u'Install', u'EndDialog', u'Return', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 4), -(u'ResumeDlg', u'Install', u'EndDialog', u'Return', u'OutOfDiskSpace <> 1', 2), -(u'ResumeDlg', u'Install', u'SpawnDialog', u'OutOfDiskDlg', u'(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")', 6), -(u'ResumeDlg', u'Install', u'SpawnDialog', u'OutOfRbDiskDlg', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)', 3), -(u'ResumeDlg', u'Install', u'SpawnWaitDialog', u'WaitForCostingDlg', u'CostingComplete = 1', 1), -(u'ResumeDlg', u'Install', u'EnableRollback', u'False', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 5), -(u'SetupTypeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'SetupTypeDlg', u'Back', u'NewDialog', u'LicenseAgreementDlg', u'ShowUserRegistrationDlg <> 1', None), -(u'SetupTypeDlg', u'Back', u'NewDialog', u'UserRegistrationDlg', u'ShowUserRegistrationDlg = 1', None), -(u'SetupTypeDlg', u'CompleteButton', u'NewDialog', u'VerifyReadyDlg', u'1', 3), -(u'SetupTypeDlg', u'CompleteButton', u'[InstallMode]', u'Complete', u'1', 1), -(u'SetupTypeDlg', u'CompleteButton', u'SetInstallLevel', u'1000', u'1', 2), -(u'SetupTypeDlg', u'CustomButton', u'NewDialog', u'CustomizeDlg', u'1', 2), -(u'SetupTypeDlg', u'CustomButton', u'[InstallMode]', u'Custom', u'1', 1), -(u'SetupTypeDlg', u'TypicalButton', u'NewDialog', u'VerifyReadyDlg', u'1', 3), -(u'SetupTypeDlg', u'TypicalButton', u'[InstallMode]', u'Typical', u'1', 1), -(u'SetupTypeDlg', u'TypicalButton', u'SetInstallLevel', u'3', u'1', 2), -(u'UserRegistrationDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'UserRegistrationDlg', u'Back', u'NewDialog', u'LicenseAgreementDlg', u'1', None), -(u'UserRegistrationDlg', u'Next', u'NewDialog', u'SetupTypeDlg', u'ProductID', 3), -(u'UserRegistrationDlg', u'Next', u'ValidateProductID', u'0', u'0', 1), -(u'UserRegistrationDlg', u'Next', u'SpawnWaitDialog', u'WaitForCostingDlg', u'CostingComplete = 1', 2), -(u'VerifyReadyDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'VerifyReadyDlg', u'Back', u'NewDialog', u'AdminInstallPointDlg', u'InstallMode = "Server Image"', None), -(u'VerifyReadyDlg', u'Back', u'NewDialog', u'CustomizeDlg', u'InstallMode = "Custom" OR InstallMode = "Change"', None), -(u'VerifyReadyDlg', u'Back', u'NewDialog', u'MaintenanceTypeDlg', u'InstallMode = "Repair"', None), -(u'VerifyReadyDlg', u'Back', u'NewDialog', u'SetupTypeDlg', u'InstallMode = "Typical" OR InstallMode = "Complete"', None), -(u'VerifyReadyDlg', u'Install', u'EndDialog', u'Return', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 3), -(u'VerifyReadyDlg', u'Install', u'EndDialog', u'Return', u'OutOfDiskSpace <> 1', 1), -(u'VerifyReadyDlg', u'Install', u'SpawnDialog', u'OutOfDiskDlg', u'(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")', 5), -(u'VerifyReadyDlg', u'Install', u'SpawnDialog', u'OutOfRbDiskDlg', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)', 2), -(u'VerifyReadyDlg', u'Install', u'EnableRollback', u'False', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 4), -(u'VerifyRemoveDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'VerifyRemoveDlg', u'Back', u'NewDialog', u'MaintenanceTypeDlg', u'1', None), -(u'VerifyRemoveDlg', u'Remove', u'Remove', u'All', u'OutOfDiskSpace <> 1', 1), -(u'VerifyRemoveDlg', u'Remove', u'EndDialog', u'Return', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 4), -(u'VerifyRemoveDlg', u'Remove', u'EndDialog', u'Return', u'OutOfDiskSpace <> 1', 2), -(u'VerifyRemoveDlg', u'Remove', u'SpawnDialog', u'OutOfDiskDlg', u'(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")', 6), -(u'VerifyRemoveDlg', u'Remove', u'SpawnDialog', u'OutOfRbDiskDlg', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)', 3), -(u'VerifyRemoveDlg', u'Remove', u'EnableRollback', u'False', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 5), -(u'VerifyRepairDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'VerifyRepairDlg', u'Back', u'NewDialog', u'MaintenanceTypeDlg', u'1', None), -(u'VerifyRepairDlg', u'Repair', u'EndDialog', u'Return', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 5), -(u'VerifyRepairDlg', u'Repair', u'EndDialog', u'Return', u'OutOfDiskSpace <> 1', 3), -(u'VerifyRepairDlg', u'Repair', u'SpawnDialog', u'OutOfDiskDlg', u'(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")', 7), -(u'VerifyRepairDlg', u'Repair', u'SpawnDialog', u'OutOfRbDiskDlg', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)', 4), -(u'VerifyRepairDlg', u'Repair', u'EnableRollback', u'False', u'OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"', 6), -(u'VerifyRepairDlg', u'Repair', u'Reinstall', u'All', u'OutOfDiskSpace <> 1', 2), -(u'VerifyRepairDlg', u'Repair', u'ReinstallMode', u'ecmus', u'OutOfDiskSpace <> 1', 1), -(u'WaitForCostingDlg', u'Return', u'EndDialog', u'Exit', u'1', None), -(u'WelcomeDlg', u'Cancel', u'SpawnDialog', u'CancelDlg', u'1', None), -(u'WelcomeDlg', u'Next', u'NewDialog', u'LicenseAgreementDlg', u'1', None), -] - -Dialog = [ -(u'AdminWelcomeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Next', u'Next', u'Cancel'), -(u'ExitDialog', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Finish', u'Finish', u'Finish'), -(u'FatalError', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Finish', u'Finish', u'Finish'), -(u'PrepareDlg', 50, 50, 370, 270, 1, u'[ProductName] [Setup]', u'Cancel', u'Cancel', u'Cancel'), -(u'ProgressDlg', 50, 50, 370, 270, 1, u'[ProductName] [Setup]', u'Cancel', u'Cancel', u'Cancel'), -(u'UserExit', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Finish', u'Finish', u'Finish'), -(u'AdminBrowseDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'PathEdit', u'OK', u'Cancel'), -(u'AdminInstallPointDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Text', u'Next', u'Cancel'), -(u'AdminRegistrationDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'OrganizationLabel', u'Next', u'Cancel'), -(u'BrowseDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'PathEdit', u'OK', u'Cancel'), -(u'CancelDlg', 50, 10, 260, 85, 3, u'[ProductName] [Setup]', u'No', u'No', u'No'), -(u'CustomizeDlg', 50, 50, 370, 270, 35, u'[ProductName] [Setup]', u'Tree', u'Next', u'Cancel'), -(u'DiskCostDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'OK', u'OK', u'OK'), -(u'ErrorDlg', 50, 10, 270, 105, 65539, u'Installer Information', u'ErrorText', None, None), -(u'FilesInUse', 50, 50, 370, 270, 19, u'[ProductName] [Setup]', u'Retry', u'Retry', u'Retry'), -(u'LicenseAgreementDlg', 50, 50, 370, 270, 3, u'[ProductName] License Agreement', u'Buttons', u'Next', u'Cancel'), -(u'MaintenanceTypeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'ChangeLabel', u'ChangeButton', u'Cancel'), -(u'MaintenanceWelcomeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Next', u'Next', u'Cancel'), -(u'OutOfDiskDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'OK', u'OK', u'OK'), -(u'OutOfRbDiskDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'No', u'No', u'No'), -(u'ResumeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Install', u'Install', u'Cancel'), -(u'SetupTypeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'TypicalLabel', u'TypicalButton', u'Cancel'), -(u'UserRegistrationDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'NameLabel', u'Next', u'Cancel'), -(u'VerifyReadyDlg', 50, 50, 370, 270, 35, u'[ProductName] [Setup]', u'Install', u'Install', u'Cancel'), -(u'VerifyRemoveDlg', 50, 50, 370, 270, 35, u'[ProductName] [Setup]', u'Back', u'Back', u'Cancel'), -(u'VerifyRepairDlg', 50, 50, 370, 270, 35, u'[ProductName] [Setup]', u'Repair', u'Repair', u'Cancel'), -(u'WaitForCostingDlg', 50, 10, 260, 85, 3, u'[ProductName] [Setup]', u'Return', u'Return', u'Return'), -(u'WelcomeDlg', 50, 50, 370, 270, 3, u'[ProductName] [Setup]', u'Next', u'Next', u'Cancel'), -] - -EventMapping = [ -(u'PrepareDlg', u'ActionData', u'ActionData', u'Text'), -(u'PrepareDlg', u'ActionText', u'ActionText', u'Text'), -(u'ProgressDlg', u'ActionText', u'ActionText', u'Text'), -(u'ProgressDlg', u'ProgressBar', u'SetProgress', u'Progress'), -(u'AdminBrowseDlg', u'DirectoryCombo', u'IgnoreChange', u'IgnoreChange'), -(u'BrowseDlg', u'DirectoryCombo', u'IgnoreChange', u'IgnoreChange'), -(u'CustomizeDlg', u'Next', u'SelectionNoItems', u'Enabled'), -(u'CustomizeDlg', u'Reset', u'SelectionNoItems', u'Enabled'), -(u'CustomizeDlg', u'DiskCost', u'SelectionNoItems', u'Enabled'), -(u'CustomizeDlg', u'ItemDescription', u'SelectionDescription', u'Text'), -(u'CustomizeDlg', u'ItemSize', u'SelectionSize', u'Text'), -(u'CustomizeDlg', u'Location', u'SelectionPath', u'Text'), -(u'CustomizeDlg', u'Location', u'SelectionPathOn', u'Visible'), -(u'CustomizeDlg', u'LocationLabel', u'SelectionPathOn', u'Visible'), -] - -InstallExecuteSequence = [ -(u'InstallValidate', None, 1400), -(u'InstallInitialize', None, 1500), -(u'InstallFinalize', None, 6600), -(u'InstallFiles', None, 4000), -(u'FileCost', None, 900), -(u'CostInitialize', None, 800), -(u'CostFinalize', None, 1000), -(u'CreateShortcuts', None, 4500), -(u'PublishComponents', None, 6200), -(u'PublishFeatures', None, 6300), -(u'PublishProduct', None, 6400), -(u'RegisterClassInfo', None, 4600), -(u'RegisterExtensionInfo', None, 4700), -(u'RegisterMIMEInfo', None, 4900), -(u'RegisterProgIdInfo', None, 4800), -(u'ValidateProductID', None, 700), -(u'AllocateRegistrySpace', u'NOT Installed', 1550), -(u'AppSearch', None, 400), -(u'BindImage', None, 4300), -(u'CCPSearch', u'NOT Installed', 500), -(u'CreateFolders', None, 3700), -(u'DeleteServices', u'VersionNT', 2000), -(u'DuplicateFiles', None, 4210), -(u'FindRelatedProducts', None, 200), -(u'InstallODBC', None, 5400), -(u'InstallServices', u'VersionNT', 5800), -(u'LaunchConditions', None, 100), -(u'MigrateFeatureStates', None, 1200), -(u'MoveFiles', None, 3800), -(u'PatchFiles', None, 4090), -(u'ProcessComponents', None, 1600), -(u'RegisterComPlus', None, 5700), -(u'RegisterFonts', None, 5300), -(u'RegisterProduct', None, 6100), -(u'RegisterTypeLibraries', None, 5500), -(u'RegisterUser', None, 6000), -(u'RemoveDuplicateFiles', None, 3400), -(u'RemoveEnvironmentStrings', None, 3300), -(u'RemoveExistingProducts', None, 6700), -(u'RemoveFiles', None, 3500), -(u'RemoveFolders', None, 3600), -(u'RemoveIniValues', None, 3100), -(u'RemoveODBC', None, 2400), -(u'RemoveRegistryValues', None, 2600), -(u'RemoveShortcuts', None, 3200), -(u'RMCCPSearch', u'NOT Installed', 600), -(u'SelfRegModules', None, 5600), -(u'SelfUnregModules', None, 2200), -(u'SetODBCFolders', None, 1100), -(u'StartServices', u'VersionNT', 5900), -(u'StopServices', u'VersionNT', 1900), -(u'UnpublishComponents', None, 1700), -(u'UnpublishFeatures', None, 1800), -(u'UnregisterClassInfo', None, 2700), -(u'UnregisterComPlus', None, 2100), -(u'UnregisterExtensionInfo', None, 2800), -(u'UnregisterFonts', None, 2500), -(u'UnregisterMIMEInfo', None, 3000), -(u'UnregisterProgIdInfo', None, 2900), -(u'UnregisterTypeLibraries', None, 2300), -(u'WriteEnvironmentStrings', None, 5200), -(u'WriteIniValues', None, 5100), -(u'WriteRegistryValues', None, 5000), -] - -InstallUISequence = [ -#(u'FileCost', None, 900), -#(u'CostInitialize', None, 800), -#(u'CostFinalize', None, 1000), -#(u'ExecuteAction', None, 1300), -#(u'ExitDialog', None, -1), -#(u'FatalError', None, -3), -(u'PrepareDlg', None, 140), -(u'ProgressDlg', None, 1280), -#(u'UserExit', None, -2), -(u'MaintenanceWelcomeDlg', u'Installed AND NOT RESUME AND NOT Preselected', 1250), -(u'ResumeDlg', u'Installed AND (RESUME OR Preselected)', 1240), -(u'WelcomeDlg', u'NOT Installed', 1230), -#(u'AppSearch', None, 400), -#(u'CCPSearch', u'NOT Installed', 500), -#(u'FindRelatedProducts', None, 200), -#(u'LaunchConditions', None, 100), -#(u'MigrateFeatureStates', None, 1200), -#(u'RMCCPSearch', u'NOT Installed', 600), -] - -ListView = [ -] - -RadioButton = [ -(u'IAgree', 1, u'Yes', 5, 0, 250, 15, u'{\\DlgFont8}I &accept the terms in the License Agreement', None), -(u'IAgree', 2, u'No', 5, 20, 250, 15, u'{\\DlgFont8}I &do not accept the terms in the License Agreement', None), -] - -TextStyle = [ -(u'DlgFont8', u'Tahoma', 8, None, 0), -(u'DlgFontBold8', u'Tahoma', 8, None, 1), -(u'VerdanaBold13', u'Verdana', 13, None, 1), -] - -UIText = [ -(u'AbsentPath', None), -(u'bytes', u'bytes'), -(u'GB', u'GB'), -(u'KB', u'KB'), -(u'MB', u'MB'), -(u'MenuAbsent', u'Entire feature will be unavailable'), -(u'MenuAdvertise', u'Feature will be installed when required'), -(u'MenuAllCD', u'Entire feature will be installed to run from CD'), -(u'MenuAllLocal', u'Entire feature will be installed on local hard drive'), -(u'MenuAllNetwork', u'Entire feature will be installed to run from network'), -(u'MenuCD', u'Will be installed to run from CD'), -(u'MenuLocal', u'Will be installed on local hard drive'), -(u'MenuNetwork', u'Will be installed to run from network'), -(u'ScriptInProgress', u'Gathering required information...'), -(u'SelAbsentAbsent', u'This feature will remain uninstalled'), -(u'SelAbsentAdvertise', u'This feature will be set to be installed when required'), -(u'SelAbsentCD', u'This feature will be installed to run from CD'), -(u'SelAbsentLocal', u'This feature will be installed on the local hard drive'), -(u'SelAbsentNetwork', u'This feature will be installed to run from the network'), -(u'SelAdvertiseAbsent', u'This feature will become unavailable'), -(u'SelAdvertiseAdvertise', u'Will be installed when required'), -(u'SelAdvertiseCD', u'This feature will be available to run from CD'), -(u'SelAdvertiseLocal', u'This feature will be installed on your local hard drive'), -(u'SelAdvertiseNetwork', u'This feature will be available to run from the network'), -(u'SelCDAbsent', u"This feature will be uninstalled completely, you won't be able to run it from CD"), -(u'SelCDAdvertise', u'This feature will change from run from CD state to set to be installed when required'), -(u'SelCDCD', u'This feature will remain to be run from CD'), -(u'SelCDLocal', u'This feature will change from run from CD state to be installed on the local hard drive'), -(u'SelChildCostNeg', u'This feature frees up [1] on your hard drive.'), -(u'SelChildCostPos', u'This feature requires [1] on your hard drive.'), -(u'SelCostPending', u'Compiling cost for this feature...'), -(u'SelLocalAbsent', u'This feature will be completely removed'), -(u'SelLocalAdvertise', u'This feature will be removed from your local hard drive, but will be set to be installed when required'), -(u'SelLocalCD', u'This feature will be removed from your local hard drive, but will be still available to run from CD'), -(u'SelLocalLocal', u'This feature will remain on you local hard drive'), -(u'SelLocalNetwork', u'This feature will be removed from your local hard drive, but will be still available to run from the network'), -(u'SelNetworkAbsent', u"This feature will be uninstalled completely, you won't be able to run it from the network"), -(u'SelNetworkAdvertise', u'This feature will change from run from network state to set to be installed when required'), -(u'SelNetworkLocal', u'This feature will change from run from network state to be installed on the local hard drive'), -(u'SelNetworkNetwork', u'This feature will remain to be run from the network'), -(u'SelParentCostNegNeg', u'This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.'), -(u'SelParentCostNegPos', u'This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.'), -(u'SelParentCostPosNeg', u'This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.'), -(u'SelParentCostPosPos', u'This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.'), -(u'TimeRemaining', u'Time remaining: {[1] minutes }{[2] seconds}'), -(u'VolumeCostAvailable', u'Available'), -(u'VolumeCostDifference', u'Difference'), -(u'VolumeCostRequired', u'Required'), -(u'VolumeCostSize', u'Disk Size'), -(u'VolumeCostVolume', u'Volume'), -] - -_Validation = [ -(u'AdminExecuteSequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'AdminExecuteSequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'AdminExecuteSequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'AdminUISequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'AdminUISequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'AdminUISequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'Condition', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Expression evaluated to determine if Level in the Feature table is to change.'), -(u'Condition', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Reference to a Feature entry in Feature table.'), -(u'Condition', u'Level', u'N', 0, 32767, None, None, None, None, u'New selection Level to set in Feature table if Condition evaluates to TRUE.'), -(u'AdvtExecuteSequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'AdvtExecuteSequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'AdvtExecuteSequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'BBControl', u'Type', u'N', None, None, None, None, u'Identifier', None, u'The type of the control.'), -(u'BBControl', u'BBControl', u'N', None, None, None, None, u'Identifier', None, u'Name of the control. This name must be unique within a billboard, but can repeat on different billboard.'), -(u'BBControl', u'Billboard_', u'N', None, None, u'Billboard', 1, u'Identifier', None, u'External key to the Billboard table, name of the billboard.'), -(u'BBControl', u'X', u'N', 0, 32767, None, None, None, None, u'Horizontal coordinate of the upper left corner of the bounding rectangle of the control.'), -(u'BBControl', u'Y', u'N', 0, 32767, None, None, None, None, u'Vertical coordinate of the upper left corner of the bounding rectangle of the control.'), -(u'BBControl', u'Width', u'N', 0, 32767, None, None, None, None, u'Width of the bounding rectangle of the control.'), -(u'BBControl', u'Height', u'N', 0, 32767, None, None, None, None, u'Height of the bounding rectangle of the control.'), -(u'BBControl', u'Attributes', u'Y', 0, 2147483647, None, None, None, None, u'A 32-bit word that specifies the attribute flags to be applied to this control.'), -(u'BBControl', u'Text', u'Y', None, None, None, None, u'Text', None, u'A string used to set the initial text contained within a control (if appropriate).'), -(u'Billboard', u'Action', u'Y', None, None, None, None, u'Identifier', None, u'The name of an action. The billboard is displayed during the progress messages received from this action.'), -(u'Billboard', u'Billboard', u'N', None, None, None, None, u'Identifier', None, u'Name of the billboard.'), -(u'Billboard', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'An external key to the Feature Table. The billboard is shown only if this feature is being installed.'), -(u'Billboard', u'Ordering', u'Y', 0, 32767, None, None, None, None, u'A positive integer. If there is more than one billboard corresponding to an action they will be shown in the order defined by this column.'), -(u'Binary', u'Name', u'N', None, None, None, None, u'Identifier', None, u'Unique key identifying the binary data.'), -(u'Binary', u'Data', u'N', None, None, None, None, u'Binary', None, u'The unformatted binary data.'), -(u'CheckBox', u'Property', u'N', None, None, None, None, u'Identifier', None, u'A named property to be tied to the item.'), -(u'CheckBox', u'Value', u'Y', None, None, None, None, u'Formatted', None, u'The value string associated with the item.'), -(u'Property', u'Property', u'N', None, None, None, None, u'Identifier', None, u'Name of property, uppercase if settable by launcher or loader.'), -(u'Property', u'Value', u'N', None, None, None, None, u'Text', None, u'String value for property. Never null or empty.'), -(u'ComboBox', u'Text', u'Y', None, None, None, None, u'Formatted', None, u'The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.'), -(u'ComboBox', u'Property', u'N', None, None, None, None, u'Identifier', None, u'A named property to be tied to this item. All the items tied to the same property become part of the same combobox.'), -(u'ComboBox', u'Value', u'N', None, None, None, None, u'Formatted', None, u'The value string associated with this item. Selecting the line will set the associated property to this value.'), -(u'ComboBox', u'Order', u'N', 1, 32767, None, None, None, None, u'A positive integer used to determine the ordering of the items within one list.\tThe integers do not have to be consecutive.'), -(u'Control', u'Type', u'N', None, None, None, None, u'Identifier', None, u'The type of the control.'), -(u'Control', u'X', u'N', 0, 32767, None, None, None, None, u'Horizontal coordinate of the upper left corner of the bounding rectangle of the control.'), -(u'Control', u'Y', u'N', 0, 32767, None, None, None, None, u'Vertical coordinate of the upper left corner of the bounding rectangle of the control.'), -(u'Control', u'Width', u'N', 0, 32767, None, None, None, None, u'Width of the bounding rectangle of the control.'), -(u'Control', u'Height', u'N', 0, 32767, None, None, None, None, u'Height of the bounding rectangle of the control.'), -(u'Control', u'Attributes', u'Y', 0, 2147483647, None, None, None, None, u'A 32-bit word that specifies the attribute flags to be applied to this control.'), -(u'Control', u'Text', u'Y', None, None, None, None, u'Formatted', None, u'A string used to set the initial text contained within a control (if appropriate).'), -(u'Control', u'Property', u'Y', None, None, None, None, u'Identifier', None, u'The name of a defined property to be linked to this control. '), -(u'Control', u'Control', u'N', None, None, None, None, u'Identifier', None, u'Name of the control. This name must be unique within a dialog, but can repeat on different dialogs. '), -(u'Control', u'Dialog_', u'N', None, None, u'Dialog', 1, u'Identifier', None, u'External key to the Dialog table, name of the dialog.'), -(u'Control', u'Control_Next', u'Y', None, None, u'Control', 2, u'Identifier', None, u'The name of an other control on the same dialog. This link defines the tab order of the controls. The links have to form one or more cycles!'), -(u'Control', u'Help', u'Y', None, None, None, None, u'Text', None, u'The help strings used with the button. The text is optional. '), -(u'Icon', u'Name', u'N', None, None, None, None, u'Identifier', None, u'Primary key. Name of the icon file.'), -(u'Icon', u'Data', u'N', None, None, None, None, u'Binary', None, u'Binary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.'), -(u'ListBox', u'Text', u'Y', None, None, None, None, u'Text', None, u'The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.'), -(u'ListBox', u'Property', u'N', None, None, None, None, u'Identifier', None, u'A named property to be tied to this item. All the items tied to the same property become part of the same listbox.'), -(u'ListBox', u'Value', u'N', None, None, None, None, u'Formatted', None, u'The value string associated with this item. Selecting the line will set the associated property to this value.'), -(u'ListBox', u'Order', u'N', 1, 32767, None, None, None, None, u'A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.'), -(u'ActionText', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to be described.'), -(u'ActionText', u'Description', u'Y', None, None, None, None, u'Text', None, u'Localized description displayed in progress dialog and log when action is executing.'), -(u'ActionText', u'Template', u'Y', None, None, None, None, u'Template', None, u'Optional localized format template used to format action data records for display during action execution.'), -(u'ControlCondition', u'Action', u'N', None, None, None, None, None, u'Default;Disable;Enable;Hide;Show', u'The desired action to be taken on the specified control.'), -(u'ControlCondition', u'Condition', u'N', None, None, None, None, u'Condition', None, u'A standard conditional statement that specifies under which conditions the action should be triggered.'), -(u'ControlCondition', u'Dialog_', u'N', None, None, u'Dialog', 1, u'Identifier', None, u'A foreign key to the Dialog table, name of the dialog.'), -(u'ControlCondition', u'Control_', u'N', None, None, u'Control', 2, u'Identifier', None, u'A foreign key to the Control table, name of the control.'), -(u'ControlEvent', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'A standard conditional statement that specifies under which conditions an event should be triggered.'), -(u'ControlEvent', u'Ordering', u'Y', 0, 2147483647, None, None, None, None, u'An integer used to order several events tied to the same control. Can be left blank.'), -(u'ControlEvent', u'Dialog_', u'N', None, None, u'Dialog', 1, u'Identifier', None, u'A foreign key to the Dialog table, name of the dialog.'), -(u'ControlEvent', u'Control_', u'N', None, None, u'Control', 2, u'Identifier', None, u'A foreign key to the Control table, name of the control'), -(u'ControlEvent', u'Event', u'N', None, None, None, None, u'Formatted', None, u'An identifier that specifies the type of the event that should take place when the user interacts with control specified by the first two entries.'), -(u'ControlEvent', u'Argument', u'N', None, None, None, None, u'Formatted', None, u'A value to be used as a modifier when triggering a particular event.'), -(u'Dialog', u'Width', u'N', 0, 32767, None, None, None, None, u'Width of the bounding rectangle of the dialog.'), -(u'Dialog', u'Height', u'N', 0, 32767, None, None, None, None, u'Height of the bounding rectangle of the dialog.'), -(u'Dialog', u'Attributes', u'Y', 0, 2147483647, None, None, None, None, u'A 32-bit word that specifies the attribute flags to be applied to this dialog.'), -(u'Dialog', u'Title', u'Y', None, None, None, None, u'Formatted', None, u"A text string specifying the title to be displayed in the title bar of the dialog's window."), -(u'Dialog', u'Dialog', u'N', None, None, None, None, u'Identifier', None, u'Name of the dialog.'), -(u'Dialog', u'HCentering', u'N', 0, 100, None, None, None, None, u'Horizontal position of the dialog on a 0-100 scale. 0 means left end, 100 means right end of the screen, 50 center.'), -(u'Dialog', u'VCentering', u'N', 0, 100, None, None, None, None, u'Vertical position of the dialog on a 0-100 scale. 0 means top end, 100 means bottom end of the screen, 50 center.'), -(u'Dialog', u'Control_First', u'N', None, None, u'Control', 2, u'Identifier', None, u'Defines the control that has the focus when the dialog is created.'), -(u'Dialog', u'Control_Default', u'Y', None, None, u'Control', 2, u'Identifier', None, u'Defines the default control. Hitting return is equivalent to pushing this button.'), -(u'Dialog', u'Control_Cancel', u'Y', None, None, u'Control', 2, u'Identifier', None, u'Defines the cancel control. Hitting escape or clicking on the close icon on the dialog is equivalent to pushing this button.'), -(u'EventMapping', u'Dialog_', u'N', None, None, u'Dialog', 1, u'Identifier', None, u'A foreign key to the Dialog table, name of the Dialog.'), -(u'EventMapping', u'Control_', u'N', None, None, u'Control', 2, u'Identifier', None, u'A foreign key to the Control table, name of the control.'), -(u'EventMapping', u'Event', u'N', None, None, None, None, u'Identifier', None, u'An identifier that specifies the type of the event that the control subscribes to.'), -(u'EventMapping', u'Attribute', u'N', None, None, None, None, u'Identifier', None, u'The name of the control attribute, that is set when this event is received.'), -(u'InstallExecuteSequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'InstallExecuteSequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'InstallExecuteSequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'AppSearch', u'Property', u'N', None, None, None, None, u'Identifier', None, u'The property associated with a Signature'), -(u'AppSearch', u'Signature_', u'N', None, None, u'Signature;RegLocator;IniLocator;DrLocator;CompLocator', 1, u'Identifier', None, u'The Signature_ represents a unique file signature and is also the foreign key in the Signature, RegLocator, IniLocator, CompLocator and the DrLocator tables.'), -(u'BindImage', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'The index into the File table. This must be an executable file.'), -(u'BindImage', u'Path', u'Y', None, None, None, None, u'Paths', None, u'A list of ; delimited paths that represent the paths to be searched for the import DLLS. The list is usually a list of properties each enclosed within square brackets [] .'), -(u'CCPSearch', u'Signature_', u'N', None, None, u'Signature;RegLocator;IniLocator;DrLocator;CompLocator', 1, u'Identifier', None, u'The Signature_ represents a unique file signature and is also the foreign key in the Signature, RegLocator, IniLocator, CompLocator and the DrLocator tables.'), -(u'InstallUISequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'InstallUISequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'InstallUISequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'ListView', u'Text', u'Y', None, None, None, None, u'Text', None, u'The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.'), -(u'ListView', u'Property', u'N', None, None, None, None, u'Identifier', None, u'A named property to be tied to this item. All the items tied to the same property become part of the same listview.'), -(u'ListView', u'Value', u'N', None, None, None, None, u'Identifier', None, u'The value string associated with this item. Selecting the line will set the associated property to this value.'), -(u'ListView', u'Order', u'N', 1, 32767, None, None, None, None, u'A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.'), -(u'ListView', u'Binary_', u'Y', None, None, u'Binary', 1, u'Identifier', None, u'The name of the icon to be displayed with the icon. The binary information is looked up from the Binary Table.'), -(u'RadioButton', u'X', u'N', 0, 32767, None, None, None, None, u'The horizontal coordinate of the upper left corner of the bounding rectangle of the radio button.'), -(u'RadioButton', u'Y', u'N', 0, 32767, None, None, None, None, u'The vertical coordinate of the upper left corner of the bounding rectangle of the radio button.'), -(u'RadioButton', u'Width', u'N', 0, 32767, None, None, None, None, u'The width of the button.'), -(u'RadioButton', u'Height', u'N', 0, 32767, None, None, None, None, u'The height of the button.'), -(u'RadioButton', u'Text', u'Y', None, None, None, None, u'Text', None, u'The visible title to be assigned to the radio button.'), -(u'RadioButton', u'Property', u'N', None, None, None, None, u'Identifier', None, u'A named property to be tied to this radio button. All the buttons tied to the same property become part of the same group.'), -(u'RadioButton', u'Value', u'N', None, None, None, None, u'Formatted', None, u'The value string associated with this button. Selecting the button will set the associated property to this value.'), -(u'RadioButton', u'Order', u'N', 1, 32767, None, None, None, None, u'A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.'), -(u'RadioButton', u'Help', u'Y', None, None, None, None, u'Text', None, u'The help strings used with the button. The text is optional.'), -(u'TextStyle', u'TextStyle', u'N', None, None, None, None, u'Identifier', None, u'Name of the style. The primary key of this table. This name is embedded in the texts to indicate a style change.'), -(u'TextStyle', u'FaceName', u'N', None, None, None, None, u'Text', None, u'A string indicating the name of the font used. Required. The string must be at most 31 characters long.'), -(u'TextStyle', u'Size', u'N', 0, 32767, None, None, None, None, u'The size of the font used. This size is given in our units (1/12 of the system font height). Assuming that the system font is set to 12 point size, this is equivalent to the point size.'), -(u'TextStyle', u'Color', u'Y', 0, 16777215, None, None, None, None, u'A long integer indicating the color of the string in the RGB format (Red, Green, Blue each 0-255, RGB = R + 256*G + 256^2*B).'), -(u'TextStyle', u'StyleBits', u'Y', 0, 15, None, None, None, None, u'A combination of style bits.'), -(u'UIText', u'Text', u'Y', None, None, None, None, u'Text', None, u'The localized version of the string.'), -(u'UIText', u'Key', u'N', None, None, None, None, u'Identifier', None, u'A unique key that identifies the particular string.'), -(u'_Validation', u'Table', u'N', None, None, None, None, u'Identifier', None, u'Name of table'), -(u'_Validation', u'Description', u'Y', None, None, None, None, u'Text', None, u'Description of column'), -(u'_Validation', u'Column', u'N', None, None, None, None, u'Identifier', None, u'Name of column'), -(u'_Validation', u'Nullable', u'N', None, None, None, None, None, u'Y;N;@', u'Whether the column is nullable'), -(u'_Validation', u'MinValue', u'Y', -2147483647, 2147483647, None, None, None, None, u'Minimum value allowed'), -(u'_Validation', u'MaxValue', u'Y', -2147483647, 2147483647, None, None, None, None, u'Maximum value allowed'), -(u'_Validation', u'KeyTable', u'Y', None, None, None, None, u'Identifier', None, u'For foreign key, Name of table to which data must link'), -(u'_Validation', u'KeyColumn', u'Y', 1, 32, None, None, None, None, u'Column to which foreign key connects'), -(u'_Validation', u'Category', u'Y', None, None, None, None, None, u'Text;Formatted;Template;Condition;Guid;Path;Version;Language;Identifier;Binary;UpperCase;LowerCase;Filename;Paths;AnyPath;WildCardFilename;RegPath;KeyFormatted;CustomSource;Property;Cabinet;Shortcut;URL', u'String category'), -(u'_Validation', u'Set', u'Y', None, None, None, None, u'Text', None, u'Set of values that are permitted'), -(u'AdvtUISequence', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Name of action to invoke, either in the engine or the handler DLL.'), -(u'AdvtUISequence', u'Sequence', u'Y', -4, 32767, None, None, None, None, u'Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.'), -(u'AdvtUISequence', u'Condition', u'Y', None, None, None, None, u'Condition', None, u'Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.'), -(u'AppId', u'AppId', u'N', None, None, None, None, u'Guid', None, None), -(u'AppId', u'ActivateAtStorage', u'Y', 0, 1, None, None, None, None, None), -(u'AppId', u'DllSurrogate', u'Y', None, None, None, None, u'Text', None, None), -(u'AppId', u'LocalService', u'Y', None, None, None, None, u'Text', None, None), -(u'AppId', u'RemoteServerName', u'Y', None, None, None, None, u'Formatted', None, None), -(u'AppId', u'RunAsInteractiveUser', u'Y', 0, 1, None, None, None, None, None), -(u'AppId', u'ServiceParameters', u'Y', None, None, None, None, u'Text', None, None), -(u'Feature', u'Attributes', u'N', None, None, None, None, None, u'0;1;2;4;5;6;8;9;10;16;17;18;20;21;22;24;25;26;32;33;34;36;37;38;48;49;50;52;53;54', u'Feature attributes'), -(u'Feature', u'Description', u'Y', None, None, None, None, u'Text', None, u'Longer descriptive text describing a visible feature item.'), -(u'Feature', u'Title', u'Y', None, None, None, None, u'Text', None, u'Short text identifying a visible feature item.'), -(u'Feature', u'Feature', u'N', None, None, None, None, u'Identifier', None, u'Primary key used to identify a particular feature record.'), -(u'Feature', u'Directory_', u'Y', None, None, u'Directory', 1, u'UpperCase', None, u'The name of the Directory that can be configured by the UI. A non-null value will enable the browse button.'), -(u'Feature', u'Level', u'N', 0, 32767, None, None, None, None, u'The install level at which record will be initially selected. An install level of 0 will disable an item and prevent its display.'), -(u'Feature', u'Display', u'Y', 0, 32767, None, None, None, None, u'Numeric sort order, used to force a specific display ordering.'), -(u'Feature', u'Feature_Parent', u'Y', None, None, u'Feature', 1, u'Identifier', None, u'Optional key of a parent record in the same table. If the parent is not selected, then the record will not be installed. Null indicates a root item.'), -(u'File', u'Sequence', u'N', 1, 32767, None, None, None, None, u'Sequence with respect to the media images; order must track cabinet order.'), -(u'File', u'Attributes', u'Y', 0, 32767, None, None, None, None, u'Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)'), -(u'File', u'File', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token, must match identifier in cabinet. For uncompressed files, this field is ignored.'), -(u'File', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key referencing Component that controls the file.'), -(u'File', u'FileName', u'N', None, None, None, None, u'Filename', None, u'File name used for installation, may be localized. This may contain a "short name|long name" pair.'), -(u'File', u'FileSize', u'N', 0, 2147483647, None, None, None, None, u'Size of file in bytes (long integer).'), -(u'File', u'Language', u'Y', None, None, None, None, u'Language', None, u'List of decimal language Ids, comma-separated if more than one.'), -(u'File', u'Version', u'Y', None, None, u'File', 1, u'Version', None, u'Version string for versioned files; Blank for unversioned files.'), -(u'Class', u'Attributes', u'Y', None, 32767, None, None, None, None, u'Class registration attributes.'), -(u'Class', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Required foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.'), -(u'Class', u'Description', u'Y', None, None, None, None, u'Text', None, u'Localized description for the Class.'), -(u'Class', u'Argument', u'Y', None, None, None, None, u'Formatted', None, u'optional argument for LocalServers.'), -(u'Class', u'AppId_', u'Y', None, None, u'AppId', 1, u'Guid', None, u'Optional AppID containing DCOM information for associated application (string GUID).'), -(u'Class', u'CLSID', u'N', None, None, None, None, u'Guid', None, u'The CLSID of an OLE factory.'), -(u'Class', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.'), -(u'Class', u'Context', u'N', None, None, None, None, u'Identifier', None, u'The numeric server context for this server. CLSCTX_xxxx'), -(u'Class', u'DefInprocHandler', u'Y', None, None, None, None, u'Filename', u'1;2;3', u'Optional default inproc handler. Only optionally provided if Context=CLSCTX_LOCAL_SERVER. Typically "ole32.dll" or "mapi32.dll"'), -(u'Class', u'FileTypeMask', u'Y', None, None, None, None, u'Text', None, u'Optional string containing information for the HKCRthis CLSID) key. If multiple patterns exist, they must be delimited by a semicolon, and numeric subkeys will be generated: 0,1,2...'), -(u'Class', u'Icon_', u'Y', None, None, u'Icon', 1, u'Identifier', None, u'Optional foreign key into the Icon Table, specifying the icon file associated with this CLSID. Will be written under the DefaultIcon key.'), -(u'Class', u'IconIndex', u'Y', -32767, 32767, None, None, None, None, u'Optional icon index.'), -(u'Class', u'ProgId_Default', u'Y', None, None, u'ProgId', 1, u'Text', None, u'Optional ProgId associated with this CLSID.'), -(u'Component', u'Condition', u'Y', None, None, None, None, u'Condition', None, u"A conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component."), -(u'Component', u'Attributes', u'N', None, None, None, None, None, None, u'Remote execution option, one of irsEnum'), -(u'Component', u'Component', u'N', None, None, None, None, u'Identifier', None, u'Primary key used to identify a particular component record.'), -(u'Component', u'ComponentId', u'Y', None, None, None, None, u'Guid', None, u'A string GUID unique to this component, version, and language.'), -(u'Component', u'Directory_', u'N', None, None, u'Directory', 1, u'Identifier', None, u'Required key of a Directory table record. This is actually a property name whose value contains the actual path, set either by the AppSearch action or with the default setting obtained from the Directory table.'), -(u'Component', u'KeyPath', u'Y', None, None, u'File;Registry;ODBCDataSource', 1, u'Identifier', None, u'Either the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.'), -(u'ProgId', u'Description', u'Y', None, None, None, None, u'Text', None, u'Localized description for the Program identifier.'), -(u'ProgId', u'Icon_', u'Y', None, None, u'Icon', 1, u'Identifier', None, u'Optional foreign key into the Icon Table, specifying the icon file associated with this ProgId. Will be written under the DefaultIcon key.'), -(u'ProgId', u'IconIndex', u'Y', -32767, 32767, None, None, None, None, u'Optional icon index.'), -(u'ProgId', u'ProgId', u'N', None, None, None, None, u'Text', None, u'The Program Identifier. Primary key.'), -(u'ProgId', u'Class_', u'Y', None, None, u'Class', 1, u'Guid', None, u'The CLSID of an OLE factory corresponding to the ProgId.'), -(u'ProgId', u'ProgId_Parent', u'Y', None, None, u'ProgId', 1, u'Text', None, u'The Parent Program Identifier. If specified, the ProgId column becomes a version independent prog id.'), -(u'CompLocator', u'Type', u'Y', 0, 1, None, None, None, None, u'A boolean value that determines if the registry value is a filename or a directory location.'), -(u'CompLocator', u'Signature_', u'N', None, None, None, None, u'Identifier', None, u'The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.'), -(u'CompLocator', u'ComponentId', u'N', None, None, None, None, u'Guid', None, u'A string GUID unique to this component, version, and language.'), -(u'Complus', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key referencing Component that controls the ComPlus component.'), -(u'Complus', u'ExpType', u'Y', 0, 32767, None, None, None, None, u'ComPlus component attributes.'), -(u'Directory', u'Directory', u'N', None, None, None, None, u'Identifier', None, u'Unique identifier for directory entry, primary key. If a property by this name is defined, it contains the full path to the directory.'), -(u'Directory', u'DefaultDir', u'N', None, None, None, None, u'DefaultDir', None, u"The default sub-path under parent's path."), -(u'Directory', u'Directory_Parent', u'Y', None, None, u'Directory', 1, u'Identifier', None, u'Reference to the entry in this table specifying the default parent directory. A record parented to itself or with a Null parent represents a root of the install tree.'), -(u'CreateFolder', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table.'), -(u'CreateFolder', u'Directory_', u'N', None, None, u'Directory', 1, u'Identifier', None, u'Primary key, could be foreign key into the Directory table.'), -(u'CustomAction', u'Type', u'N', 1, 16383, None, None, None, None, u'The numeric custom action type, consisting of source location, code type, entry, option flags.'), -(u'CustomAction', u'Action', u'N', None, None, None, None, u'Identifier', None, u'Primary key, name of action, normally appears in sequence table unless private use.'), -(u'CustomAction', u'Source', u'Y', None, None, None, None, u'CustomSource', None, u'The table reference of the source of the code.'), -(u'CustomAction', u'Target', u'Y', None, None, None, None, u'Formatted', None, u'Excecution parameter, depends on the type of custom action'), -(u'DrLocator', u'Signature_', u'N', None, None, None, None, u'Identifier', None, u'The Signature_ represents a unique file signature and is also the foreign key in the Signature table.'), -(u'DrLocator', u'Path', u'Y', None, None, None, None, u'AnyPath', None, u'The path on the user system. This is a either a subpath below the value of the Parent or a full path. The path may contain properties enclosed within [ ] that will be expanded.'), -(u'DrLocator', u'Depth', u'Y', 0, 32767, None, None, None, None, u'The depth below the path to which the Signature_ is recursively searched. If absent, the depth is assumed to be 0.'), -(u'DrLocator', u'Parent', u'Y', None, None, None, None, u'Identifier', None, u'The parent file signature. It is also a foreign key in the Signature table. If null and the Path column does not expand to a full path, then all the fixed drives of the user system are searched using the Path.'), -(u'DuplicateFile', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Foreign key referencing the source file to be duplicated.'), -(u'DuplicateFile', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key referencing Component that controls the duplicate file.'), -(u'DuplicateFile', u'DestFolder', u'Y', None, None, None, None, u'Identifier', None, u'Name of a property whose value is assumed to resolve to the full pathname to a destination folder.'), -(u'DuplicateFile', u'DestName', u'Y', None, None, None, None, u'Filename', None, u'Filename to be given to the duplicate file.'), -(u'DuplicateFile', u'FileKey', u'N', None, None, None, None, u'Identifier', None, u'Primary key used to identify a particular file entry'), -(u'Environment', u'Name', u'N', None, None, None, None, u'Text', None, u'The name of the environmental value.'), -(u'Environment', u'Value', u'Y', None, None, None, None, u'Formatted', None, u'The value to set in the environmental settings.'), -(u'Environment', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table referencing component that controls the installing of the environmental value.'), -(u'Environment', u'Environment', u'N', None, None, None, None, u'Identifier', None, u'Unique identifier for the environmental variable setting'), -(u'Error', u'Error', u'N', 0, 32767, None, None, None, None, u'Integer error number, obtained from header file IError(...) macros.'), -(u'Error', u'Message', u'Y', None, None, None, None, u'Template', None, u'Error formatting template, obtained from user ed. or localizers.'), -(u'Extension', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Required foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.'), -(u'Extension', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.'), -(u'Extension', u'Extension', u'N', None, None, None, None, u'Text', None, u'The extension associated with the table row.'), -(u'Extension', u'MIME_', u'Y', None, None, u'MIME', 1, u'Text', None, u'Optional Context identifier, typically "type/format" associated with the extension'), -(u'Extension', u'ProgId_', u'Y', None, None, u'ProgId', 1, u'Text', None, u'Optional ProgId associated with this extension.'), -(u'MIME', u'CLSID', u'Y', None, None, None, None, u'Guid', None, u'Optional associated CLSID.'), -(u'MIME', u'ContentType', u'N', None, None, None, None, u'Text', None, u'Primary key. Context identifier, typically "type/format".'), -(u'MIME', u'Extension_', u'N', None, None, u'Extension', 1, u'Text', None, u'Optional associated extension (without dot)'), -(u'FeatureComponents', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Foreign key into Feature table.'), -(u'FeatureComponents', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into Component table.'), -(u'FileSFPCatalog', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'File associated with the catalog'), -(u'FileSFPCatalog', u'SFPCatalog_', u'N', None, None, u'SFPCatalog', 1, u'Filename', None, u'Catalog associated with the file'), -(u'SFPCatalog', u'SFPCatalog', u'N', None, None, None, None, u'Filename', None, u'File name for the catalog.'), -(u'SFPCatalog', u'Catalog', u'N', None, None, None, None, u'Binary', None, u'SFP Catalog'), -(u'SFPCatalog', u'Dependency', u'Y', None, None, None, None, u'Formatted', None, u'Parent catalog - only used by SFP'), -(u'Font', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Primary key, foreign key into File table referencing font file.'), -(u'Font', u'FontTitle', u'Y', None, None, None, None, u'Text', None, u'Font name.'), -(u'IniFile', u'Action', u'N', None, None, None, None, None, u'0;1;3', u'The type of modification to be made, one of iifEnum'), -(u'IniFile', u'Value', u'N', None, None, None, None, u'Formatted', None, u'The value to be written.'), -(u'IniFile', u'Key', u'N', None, None, None, None, u'Formatted', None, u'The .INI file key below Section.'), -(u'IniFile', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table referencing component that controls the installing of the .INI value.'), -(u'IniFile', u'FileName', u'N', None, None, None, None, u'Filename', None, u'The .INI file name in which to write the information'), -(u'IniFile', u'IniFile', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'IniFile', u'DirProperty', u'Y', None, None, None, None, u'Identifier', None, u'Foreign key into the Directory table denoting the directory where the .INI file is.'), -(u'IniFile', u'Section', u'N', None, None, None, None, u'Formatted', None, u'The .INI file Section.'), -(u'IniLocator', u'Type', u'Y', 0, 2, None, None, None, None, u'An integer value that determines if the .INI value read is a filename or a directory location or to be used as is w/o interpretation.'), -(u'IniLocator', u'Key', u'N', None, None, None, None, u'Text', None, u'Key value (followed by an equals sign in INI file).'), -(u'IniLocator', u'Signature_', u'N', None, None, None, None, u'Identifier', None, u'The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.'), -(u'IniLocator', u'FileName', u'N', None, None, None, None, u'Filename', None, u'The .INI file name.'), -(u'IniLocator', u'Section', u'N', None, None, None, None, u'Text', None, u'Section name within in file (within square brackets in INI file).'), -(u'IniLocator', u'Field', u'Y', 0, 32767, None, None, None, None, u'The field in the .INI line. If Field is null or 0 the entire line is read.'), -(u'IsolatedComponent', u'Component_Application', u'N', None, None, u'Component', 1, u'Identifier', None, u'Key to Component table item for application'), -(u'IsolatedComponent', u'Component_Shared', u'N', None, None, u'Component', 1, u'Identifier', None, u'Key to Component table item to be isolated'), -(u'LaunchCondition', u'Condition', u'N', None, None, None, None, u'Condition', None, u'Expression which must evaluate to TRUE in order for install to commence.'), -(u'LaunchCondition', u'Description', u'N', None, None, None, None, u'Formatted', None, u'Localizable text to display when condition fails and install must abort.'), -(u'LockPermissions', u'Table', u'N', None, None, None, None, u'Identifier', u'Directory;File;Registry', u'Reference to another table name'), -(u'LockPermissions', u'Domain', u'Y', None, None, None, None, u'Formatted', None, u'Domain name for user whose permissions are being set. (usually a property)'), -(u'LockPermissions', u'LockObject', u'N', None, None, None, None, u'Identifier', None, u'Foreign key into Registry or File table'), -(u'LockPermissions', u'Permission', u'Y', -2147483647, 2147483647, None, None, None, None, u'Permission Access mask. Full Control = 268435456 (GENERIC_ALL = 0x10000000)'), -(u'LockPermissions', u'User', u'N', None, None, None, None, u'Formatted', None, u'User for permissions to be set. (usually a property)'), -(u'Media', u'Source', u'Y', None, None, None, None, u'Property', None, u'The property defining the location of the cabinet file.'), -(u'Media', u'Cabinet', u'Y', None, None, None, None, u'Cabinet', None, u'If some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.'), -(u'Media', u'DiskId', u'N', 1, 32767, None, None, None, None, u'Primary key, integer to determine sort order for table.'), -(u'Media', u'DiskPrompt', u'Y', None, None, None, None, u'Text', None, u'Disk name: the visible text actually printed on the disk. This will be used to prompt the user when this disk needs to be inserted.'), -(u'Media', u'LastSequence', u'N', 0, 32767, None, None, None, None, u'File sequence number for the last file for this media.'), -(u'Media', u'VolumeLabel', u'Y', None, None, None, None, u'Text', None, u'The label attributed to the volume.'), -(u'ModuleComponents', u'Component', u'N', None, None, u'Component', 1, u'Identifier', None, u'Component contained in the module.'), -(u'ModuleComponents', u'Language', u'N', None, None, u'ModuleSignature', 2, None, None, u'Default language ID for module (may be changed by transform).'), -(u'ModuleComponents', u'ModuleID', u'N', None, None, u'ModuleSignature', 1, u'Identifier', None, u'Module containing the component.'), -(u'ModuleSignature', u'Language', u'N', None, None, None, None, None, None, u'Default decimal language of module.'), -(u'ModuleSignature', u'Version', u'N', None, None, None, None, u'Version', None, u'Version of the module.'), -(u'ModuleSignature', u'ModuleID', u'N', None, None, None, None, u'Identifier', None, u'Module identifier (String.GUID).'), -(u'ModuleDependency', u'ModuleID', u'N', None, None, u'ModuleSignature', 1, u'Identifier', None, u'Module requiring the dependency.'), -(u'ModuleDependency', u'ModuleLanguage', u'N', None, None, u'ModuleSignature', 2, None, None, u'Language of module requiring the dependency.'), -(u'ModuleDependency', u'RequiredID', u'N', None, None, None, None, None, None, u'String.GUID of required module.'), -(u'ModuleDependency', u'RequiredLanguage', u'N', None, None, None, None, None, None, u'LanguageID of the required module.'), -(u'ModuleDependency', u'RequiredVersion', u'Y', None, None, None, None, u'Version', None, u'Version of the required version.'), -(u'ModuleExclusion', u'ModuleID', u'N', None, None, u'ModuleSignature', 1, u'Identifier', None, u'String.GUID of module with exclusion requirement.'), -(u'ModuleExclusion', u'ModuleLanguage', u'N', None, None, u'ModuleSignature', 2, None, None, u'LanguageID of module with exclusion requirement.'), -(u'ModuleExclusion', u'ExcludedID', u'N', None, None, None, None, None, None, u'String.GUID of excluded module.'), -(u'ModuleExclusion', u'ExcludedLanguage', u'N', None, None, None, None, None, None, u'Language of excluded module.'), -(u'ModuleExclusion', u'ExcludedMaxVersion', u'Y', None, None, None, None, u'Version', None, u'Maximum version of excluded module.'), -(u'ModuleExclusion', u'ExcludedMinVersion', u'Y', None, None, None, None, u'Version', None, u'Minimum version of excluded module.'), -(u'MoveFile', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'If this component is not "selected" for installation or removal, no action will be taken on the associated MoveFile entry'), -(u'MoveFile', u'DestFolder', u'N', None, None, None, None, u'Identifier', None, u'Name of a property whose value is assumed to resolve to the full path to the destination directory'), -(u'MoveFile', u'DestName', u'Y', None, None, None, None, u'Filename', None, u'Name to be given to the original file after it is moved or copied. If blank, the destination file will be given the same name as the source file'), -(u'MoveFile', u'FileKey', u'N', None, None, None, None, u'Identifier', None, u'Primary key that uniquely identifies a particular MoveFile record'), -(u'MoveFile', u'Options', u'N', 0, 1, None, None, None, None, u'Integer value specifying the MoveFile operating mode, one of imfoEnum'), -(u'MoveFile', u'SourceFolder', u'Y', None, None, None, None, u'Identifier', None, u'Name of a property whose value is assumed to resolve to the full path to the source directory'), -(u'MoveFile', u'SourceName', u'Y', None, None, None, None, u'Text', None, u"Name of the source file(s) to be moved or copied. Can contain the '*' or '?' wildcards."), -(u'MsiAssembly', u'Attributes', u'Y', None, None, None, None, None, None, u'Assembly attributes'), -(u'MsiAssembly', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Foreign key into Feature table.'), -(u'MsiAssembly', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into Component table.'), -(u'MsiAssembly', u'File_Application', u'Y', None, None, u'File', 1, u'Identifier', None, u'Foreign key into File table, denoting the application context for private assemblies. Null for global assemblies.'), -(u'MsiAssembly', u'File_Manifest', u'Y', None, None, u'File', 1, u'Identifier', None, u'Foreign key into the File table denoting the manifest file for the assembly.'), -(u'MsiAssemblyName', u'Name', u'N', None, None, None, None, u'Text', None, u'The name part of the name-value pairs for the assembly name.'), -(u'MsiAssemblyName', u'Value', u'N', None, None, None, None, u'Text', None, u'The value part of the name-value pairs for the assembly name.'), -(u'MsiAssemblyName', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into Component table.'), -(u'MsiDigitalCertificate', u'CertData', u'N', None, None, None, None, u'Binary', None, u'A certificate context blob for a signer certificate'), -(u'MsiDigitalCertificate', u'DigitalCertificate', u'N', None, None, None, None, u'Identifier', None, u'A unique identifier for the row'), -(u'MsiDigitalSignature', u'Table', u'N', None, None, None, None, None, u'Media', u'Reference to another table name (only Media table is supported)'), -(u'MsiDigitalSignature', u'DigitalCertificate_', u'N', None, None, u'MsiDigitalCertificate', 1, u'Identifier', None, u'Foreign key to MsiDigitalCertificate table identifying the signer certificate'), -(u'MsiDigitalSignature', u'Hash', u'Y', None, None, None, None, u'Binary', None, u'The encoded hash blob from the digital signature'), -(u'MsiDigitalSignature', u'SignObject', u'N', None, None, None, None, u'Text', None, u'Foreign key to Media table'), -(u'MsiFileHash', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Primary key, foreign key into File table referencing file with this hash'), -(u'MsiFileHash', u'Options', u'N', 0, 32767, None, None, None, None, u'Various options and attributes for this hash.'), -(u'MsiFileHash', u'HashPart1', u'N', None, None, None, None, None, None, u'Size of file in bytes (long integer).'), -(u'MsiFileHash', u'HashPart2', u'N', None, None, None, None, None, None, u'Size of file in bytes (long integer).'), -(u'MsiFileHash', u'HashPart3', u'N', None, None, None, None, None, None, u'Size of file in bytes (long integer).'), -(u'MsiFileHash', u'HashPart4', u'N', None, None, None, None, None, None, u'Size of file in bytes (long integer).'), -(u'MsiPatchHeaders', u'StreamRef', u'N', None, None, None, None, u'Identifier', None, u'Primary key. A unique identifier for the row.'), -(u'MsiPatchHeaders', u'Header', u'N', None, None, None, None, u'Binary', None, u'Binary stream. The patch header, used for patch validation.'), -(u'ODBCAttribute', u'Value', u'Y', None, None, None, None, u'Text', None, u'Value for ODBC driver attribute'), -(u'ODBCAttribute', u'Attribute', u'N', None, None, None, None, u'Text', None, u'Name of ODBC driver attribute'), -(u'ODBCAttribute', u'Driver_', u'N', None, None, u'ODBCDriver', 1, u'Identifier', None, u'Reference to ODBC driver in ODBCDriver table'), -(u'ODBCDriver', u'Description', u'N', None, None, None, None, u'Text', None, u'Text used as registered name for driver, non-localized'), -(u'ODBCDriver', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Reference to key driver file'), -(u'ODBCDriver', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Reference to associated component'), -(u'ODBCDriver', u'Driver', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized.internal token for driver'), -(u'ODBCDriver', u'File_Setup', u'Y', None, None, u'File', 1, u'Identifier', None, u'Optional reference to key driver setup DLL'), -(u'ODBCDataSource', u'Description', u'N', None, None, None, None, u'Text', None, u'Text used as registered name for data source'), -(u'ODBCDataSource', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Reference to associated component'), -(u'ODBCDataSource', u'DataSource', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized.internal token for data source'), -(u'ODBCDataSource', u'DriverDescription', u'N', None, None, None, None, u'Text', None, u'Reference to driver description, may be existing driver'), -(u'ODBCDataSource', u'Registration', u'N', 0, 1, None, None, None, None, u'Registration option: 0=machine, 1=user, others t.b.d.'), -(u'ODBCSourceAttribute', u'Value', u'Y', None, None, None, None, u'Text', None, u'Value for ODBC data source attribute'), -(u'ODBCSourceAttribute', u'Attribute', u'N', None, None, None, None, u'Text', None, u'Name of ODBC data source attribute'), -(u'ODBCSourceAttribute', u'DataSource_', u'N', None, None, u'ODBCDataSource', 1, u'Identifier', None, u'Reference to ODBC data source in ODBCDataSource table'), -(u'ODBCTranslator', u'Description', u'N', None, None, None, None, u'Text', None, u'Text used as registered name for translator'), -(u'ODBCTranslator', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Reference to key translator file'), -(u'ODBCTranslator', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Reference to associated component'), -(u'ODBCTranslator', u'File_Setup', u'Y', None, None, u'File', 1, u'Identifier', None, u'Optional reference to key translator setup DLL'), -(u'ODBCTranslator', u'Translator', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized.internal token for translator'), -(u'Patch', u'Sequence', u'N', 0, 32767, None, None, None, None, u'Primary key, sequence with respect to the media images; order must track cabinet order.'), -(u'Patch', u'Attributes', u'N', 0, 32767, None, None, None, None, u'Integer containing bit flags representing patch attributes'), -(u'Patch', u'File_', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token, foreign key to File table, must match identifier in cabinet.'), -(u'Patch', u'Header', u'Y', None, None, None, None, u'Binary', None, u'Binary stream. The patch header, used for patch validation.'), -(u'Patch', u'PatchSize', u'N', 0, 2147483647, None, None, None, None, u'Size of patch in bytes (long integer).'), -(u'Patch', u'StreamRef_', u'Y', None, None, None, None, u'Identifier', None, u'Identifier. Foreign key to the StreamRef column of the MsiPatchHeaders table.'), -(u'PatchPackage', u'Media_', u'N', 0, 32767, None, None, None, None, u'Foreign key to DiskId column of Media table. Indicates the disk containing the patch package.'), -(u'PatchPackage', u'PatchId', u'N', None, None, None, None, u'Guid', None, u'A unique string GUID representing this patch.'), -(u'PublishComponent', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Foreign key into the Feature table.'), -(u'PublishComponent', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table.'), -(u'PublishComponent', u'ComponentId', u'N', None, None, None, None, u'Guid', None, u'A string GUID that represents the component id that will be requested by the alien product.'), -(u'PublishComponent', u'AppData', u'Y', None, None, None, None, u'Text', None, u'This is localisable Application specific data that can be associated with a Qualified Component.'), -(u'PublishComponent', u'Qualifier', u'N', None, None, None, None, u'Text', None, u'This is defined only when the ComponentId column is an Qualified Component Id. This is the Qualifier for ProvideComponentIndirect.'), -(u'Registry', u'Name', u'Y', None, None, None, None, u'Formatted', None, u'The registry value name.'), -(u'Registry', u'Value', u'Y', None, None, None, None, u'Formatted', None, u'The registry value.'), -(u'Registry', u'Key', u'N', None, None, None, None, u'RegPath', None, u'The key for the registry value.'), -(u'Registry', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table referencing component that controls the installing of the registry value.'), -(u'Registry', u'Registry', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'Registry', u'Root', u'N', -1, 3, None, None, None, None, u'The predefined root key for the registry value, one of rrkEnum.'), -(u'RegLocator', u'Name', u'Y', None, None, None, None, u'Formatted', None, u'The registry value name.'), -(u'RegLocator', u'Type', u'Y', 0, 18, None, None, None, None, u'An integer value that determines if the registry value is a filename or a directory location or to be used as is w/o interpretation.'), -(u'RegLocator', u'Key', u'N', None, None, None, None, u'RegPath', None, u'The key for the registry value.'), -(u'RegLocator', u'Signature_', u'N', None, None, None, None, u'Identifier', None, u'The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table. If the type is 0, the registry values refers a directory, and _Signature is not a foreign key.'), -(u'RegLocator', u'Root', u'N', 0, 3, None, None, None, None, u'The predefined root key for the registry value, one of rrkEnum.'), -(u'RemoveFile', u'InstallMode', u'N', None, None, None, None, None, u'1;2;3', u'Installation option, one of iimEnum.'), -(u'RemoveFile', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key referencing Component that controls the file to be removed.'), -(u'RemoveFile', u'FileKey', u'N', None, None, None, None, u'Identifier', None, u'Primary key used to identify a particular file entry'), -(u'RemoveFile', u'FileName', u'Y', None, None, None, None, u'WildCardFilename', None, u'Name of the file to be removed.'), -(u'RemoveFile', u'DirProperty', u'N', None, None, None, None, u'Identifier', None, u'Name of a property whose value is assumed to resolve to the full pathname to the folder of the file to be removed.'), -(u'RemoveIniFile', u'Action', u'N', None, None, None, None, None, u'2;4', u'The type of modification to be made, one of iifEnum.'), -(u'RemoveIniFile', u'Value', u'Y', None, None, None, None, u'Formatted', None, u'The value to be deleted. The value is required when Action is iifIniRemoveTag'), -(u'RemoveIniFile', u'Key', u'N', None, None, None, None, u'Formatted', None, u'The .INI file key below Section.'), -(u'RemoveIniFile', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table referencing component that controls the deletion of the .INI value.'), -(u'RemoveIniFile', u'FileName', u'N', None, None, None, None, u'Filename', None, u'The .INI file name in which to delete the information'), -(u'RemoveIniFile', u'DirProperty', u'Y', None, None, None, None, u'Identifier', None, u'Foreign key into the Directory table denoting the directory where the .INI file is.'), -(u'RemoveIniFile', u'Section', u'N', None, None, None, None, u'Formatted', None, u'The .INI file Section.'), -(u'RemoveIniFile', u'RemoveIniFile', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'RemoveRegistry', u'Name', u'Y', None, None, None, None, u'Formatted', None, u'The registry value name.'), -(u'RemoveRegistry', u'Key', u'N', None, None, None, None, u'RegPath', None, u'The key for the registry value.'), -(u'RemoveRegistry', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table referencing component that controls the deletion of the registry value.'), -(u'RemoveRegistry', u'Root', u'N', -1, 3, None, None, None, None, u'The predefined root key for the registry value, one of rrkEnum'), -(u'RemoveRegistry', u'RemoveRegistry', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'ReserveCost', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Reserve a specified amount of space if this component is to be installed.'), -(u'ReserveCost', u'ReserveFolder', u'Y', None, None, None, None, u'Identifier', None, u'Name of a property whose value is assumed to resolve to the full path to the destination directory'), -(u'ReserveCost', u'ReserveKey', u'N', None, None, None, None, u'Identifier', None, u'Primary key that uniquely identifies a particular ReserveCost record'), -(u'ReserveCost', u'ReserveLocal', u'N', 0, 2147483647, None, None, None, None, u'Disk space to reserve if linked component is installed locally.'), -(u'ReserveCost', u'ReserveSource', u'N', 0, 2147483647, None, None, None, None, u'Disk space to reserve if linked component is installed to run from the source location.'), -(u'SelfReg', u'File_', u'N', None, None, u'File', 1, u'Identifier', None, u'Foreign key into the File table denoting the module that needs to be registered.'), -(u'SelfReg', u'Cost', u'Y', 0, 32767, None, None, None, None, u'The cost of registering the module.'), -(u'ServiceControl', u'Name', u'N', None, None, None, None, u'Formatted', None, u'Name of a service. /, \\, comma and space are invalid'), -(u'ServiceControl', u'Event', u'N', 0, 187, None, None, None, None, u'Bit field: Install: 0x1 = Start, 0x2 = Stop, 0x8 = Delete, Uninstall: 0x10 = Start, 0x20 = Stop, 0x80 = Delete'), -(u'ServiceControl', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Required foreign key into the Component Table that controls the startup of the service'), -(u'ServiceControl', u'ServiceControl', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'ServiceControl', u'Arguments', u'Y', None, None, None, None, u'Formatted', None, u'Arguments for the service. Separate by [~].'), -(u'ServiceControl', u'Wait', u'Y', 0, 1, None, None, None, None, u'Boolean for whether to wait for the service to fully start'), -(u'ServiceInstall', u'Name', u'N', None, None, None, None, u'Formatted', None, u'Internal Name of the Service'), -(u'ServiceInstall', u'Description', u'Y', None, None, None, None, u'Text', None, u'Description of service.'), -(u'ServiceInstall', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Required foreign key into the Component Table that controls the startup of the service'), -(u'ServiceInstall', u'Arguments', u'Y', None, None, None, None, u'Formatted', None, u'Arguments to include in every start of the service, passed to WinMain'), -(u'ServiceInstall', u'ServiceInstall', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'ServiceInstall', u'Dependencies', u'Y', None, None, None, None, u'Formatted', None, u'Other services this depends on to start. Separate by [~], and end with [~][~]'), -(u'ServiceInstall', u'DisplayName', u'Y', None, None, None, None, u'Formatted', None, u'External Name of the Service'), -(u'ServiceInstall', u'ErrorControl', u'N', -2147483647, 2147483647, None, None, None, None, u'Severity of error if service fails to start'), -(u'ServiceInstall', u'LoadOrderGroup', u'Y', None, None, None, None, u'Formatted', None, u'LoadOrderGroup'), -(u'ServiceInstall', u'Password', u'Y', None, None, None, None, u'Formatted', None, u'password to run service with. (with StartName)'), -(u'ServiceInstall', u'ServiceType', u'N', -2147483647, 2147483647, None, None, None, None, u'Type of the service'), -(u'ServiceInstall', u'StartName', u'Y', None, None, None, None, u'Formatted', None, u'User or object name to run service as'), -(u'ServiceInstall', u'StartType', u'N', 0, 4, None, None, None, None, u'Type of the service'), -(u'Shortcut', u'Name', u'N', None, None, None, None, u'Filename', None, u'The name of the shortcut to be created.'), -(u'Shortcut', u'Description', u'Y', None, None, None, None, u'Text', None, u'The description for the shortcut.'), -(u'Shortcut', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Foreign key into the Component table denoting the component whose selection gates the the shortcut creation/deletion.'), -(u'Shortcut', u'Icon_', u'Y', None, None, u'Icon', 1, u'Identifier', None, u'Foreign key into the File table denoting the external icon file for the shortcut.'), -(u'Shortcut', u'IconIndex', u'Y', -32767, 32767, None, None, None, None, u'The icon index for the shortcut.'), -(u'Shortcut', u'Directory_', u'N', None, None, u'Directory', 1, u'Identifier', None, u'Foreign key into the Directory table denoting the directory where the shortcut file is created.'), -(u'Shortcut', u'Target', u'N', None, None, None, None, u'Shortcut', None, u'The shortcut target. This is usually a property that is expanded to a file or a folder that the shortcut points to.'), -(u'Shortcut', u'Arguments', u'Y', None, None, None, None, u'Formatted', None, u'The command-line arguments for the shortcut.'), -(u'Shortcut', u'Shortcut', u'N', None, None, None, None, u'Identifier', None, u'Primary key, non-localized token.'), -(u'Shortcut', u'Hotkey', u'Y', 0, 32767, None, None, None, None, u'The hotkey for the shortcut. It has the virtual-key code for the key in the low-order byte, and the modifier flags in the high-order byte. '), -(u'Shortcut', u'ShowCmd', u'Y', None, None, None, None, None, u'1;3;7', u'The show command for the application window.The following values may be used.'), -(u'Shortcut', u'WkDir', u'Y', None, None, None, None, u'Identifier', None, u'Name of property defining location of working directory.'), -(u'Signature', u'FileName', u'N', None, None, None, None, u'Filename', None, u'The name of the file. This may contain a "short name|long name" pair.'), -(u'Signature', u'Signature', u'N', None, None, None, None, u'Identifier', None, u'The table key. The Signature represents a unique file signature.'), -(u'Signature', u'Languages', u'Y', None, None, None, None, u'Language', None, u'The languages supported by the file.'), -(u'Signature', u'MaxDate', u'Y', 0, 2147483647, None, None, None, None, u'The maximum creation date of the file.'), -(u'Signature', u'MaxSize', u'Y', 0, 2147483647, None, None, None, None, u'The maximum size of the file. '), -(u'Signature', u'MaxVersion', u'Y', None, None, None, None, u'Text', None, u'The maximum version of the file.'), -(u'Signature', u'MinDate', u'Y', 0, 2147483647, None, None, None, None, u'The minimum creation date of the file.'), -(u'Signature', u'MinSize', u'Y', 0, 2147483647, None, None, None, None, u'The minimum size of the file.'), -(u'Signature', u'MinVersion', u'Y', None, None, None, None, u'Text', None, u'The minimum version of the file.'), -(u'TypeLib', u'Feature_', u'N', None, None, u'Feature', 1, u'Identifier', None, u'Required foreign key into the Feature Table, specifying the feature to validate or install in order for the type library to be operational.'), -(u'TypeLib', u'Description', u'Y', None, None, None, None, u'Text', None, None), -(u'TypeLib', u'Component_', u'N', None, None, u'Component', 1, u'Identifier', None, u'Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.'), -(u'TypeLib', u'Directory_', u'Y', None, None, u'Directory', 1, u'Identifier', None, u'Optional. The foreign key into the Directory table denoting the path to the help file for the type library.'), -(u'TypeLib', u'Language', u'N', 0, 32767, None, None, None, None, u'The language of the library.'), -(u'TypeLib', u'Version', u'Y', 0, 16777215, None, None, None, None, u'The version of the library. The minor version is in the lower 8 bits of the integer. The major version is in the next 16 bits. '), -(u'TypeLib', u'Cost', u'Y', 0, 2147483647, None, None, None, None, u'The cost associated with the registration of the typelib. This column is currently optional.'), -(u'TypeLib', u'LibID', u'N', None, None, None, None, u'Guid', None, u'The GUID that represents the library.'), -(u'Upgrade', u'Attributes', u'N', 0, 2147483647, None, None, None, None, u'The attributes of this product set.'), -(u'Upgrade', u'Remove', u'Y', None, None, None, None, u'Formatted', None, u'The list of features to remove when uninstalling a product from this set. The default is "ALL".'), -(u'Upgrade', u'Language', u'Y', None, None, None, None, u'Language', None, u'A comma-separated list of languages for either products in this set or products not in this set.'), -(u'Upgrade', u'ActionProperty', u'N', None, None, None, None, u'UpperCase', None, u'The property to set when a product in this set is found.'), -(u'Upgrade', u'UpgradeCode', u'N', None, None, None, None, u'Guid', None, u'The UpgradeCode GUID belonging to the products in this set.'), -(u'Upgrade', u'VersionMax', u'Y', None, None, None, None, u'Text', None, u'The maximum ProductVersion of the products in this set. The set may or may not include products with this particular version.'), -(u'Upgrade', u'VersionMin', u'Y', None, None, None, None, u'Text', None, u'The minimum ProductVersion of the products in this set. The set may or may not include products with this particular version.'), -(u'Verb', u'Sequence', u'Y', 0, 32767, None, None, None, None, u'Order within the verbs for a particular extension. Also used simply to specify the default verb.'), -(u'Verb', u'Argument', u'Y', None, None, None, None, u'Formatted', None, u'Optional value for the command arguments.'), -(u'Verb', u'Extension_', u'N', None, None, u'Extension', 1, u'Text', None, u'The extension associated with the table row.'), -(u'Verb', u'Verb', u'N', None, None, None, None, u'Text', None, u'The verb for the command.'), -(u'Verb', u'Command', u'Y', None, None, None, None, u'Formatted', None, u'The command text.'), -] - -Error = [ -(0, u'{{Fatal error: }}'), -(1, u'{{Error [1]. }}'), -(2, u'Warning [1]. '), -(3, None), -(4, u'Info [1]. '), -(5, u'The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is [1]. {{The arguments are: [2], [3], [4]}}'), -(6, None), -(7, u'{{Disk full: }}'), -(8, u'Action [Time]: [1]. [2]'), -(9, u'[ProductName]'), -(10, u'{[2]}{, [3]}{, [4]}'), -(11, u'Message type: [1], Argument: [2]'), -(12, u'=== Logging started: [Date] [Time] ==='), -(13, u'=== Logging stopped: [Date] [Time] ==='), -(14, u'Action start [Time]: [1].'), -(15, u'Action ended [Time]: [1]. Return value [2].'), -(16, u'Time remaining: {[1] minutes }{[2] seconds}'), -(17, u'Out of memory. Shut down other applications before retrying.'), -(18, u'Installer is no longer responding.'), -(19, u'Installer stopped prematurely.'), -(20, u'Please wait while Windows configures [ProductName]'), -(21, u'Gathering required information...'), -(22, u'Removing older versions of this application...'), -(23, u'Preparing to remove older versions of this application...'), -(32, u'{[ProductName] }Setup completed successfully.'), -(33, u'{[ProductName] }Setup failed.'), -(1101, u'Error reading from file: [2]. {{ System error [3].}} Verify that the file exists and that you can access it.'), -(1301, u"Cannot create the file '[2]'. A directory with this name already exists. Cancel the install and try installing to a different location."), -(1302, u'Please insert the disk: [2]'), -(1303, u'The installer has insufficient privileges to access this directory: [2]. The installation cannot continue. Log on as administrator or contact your system administrator.'), -(1304, u'Error writing to file: [2]. Verify that you have access to that directory.'), -(1305, u'Error reading from file [2]. {{ System error [3].}} Verify that the file exists and that you can access it.'), -(1306, u"Another application has exclusive access to the file '[2]'. Please shut down all other applications, then click Retry."), -(1307, u'There is not enough disk space to install this file: [2]. Free some disk space and click Retry, or click Cancel to exit.'), -(1308, u'Source file not found: [2]. Verify that the file exists and that you can access it.'), -(1309, u'Error reading from file: [3]. {{ System error [2].}} Verify that the file exists and that you can access it.'), -(1310, u'Error writing to file: [3]. {{ System error [2].}} Verify that you have access to that directory.'), -(1311, u'Source file not found{{(cabinet)}}: [2]. Verify that the file exists and that you can access it.'), -(1312, u"Cannot create the directory '[2]'. A file with this name already exists. Please rename or remove the file and click retry, or click Cancel to exit."), -(1313, u'The volume [2] is currently unavailable. Please select another.'), -(1314, u"The specified path '[2]' is unavailable."), -(1315, u'Unable to write to the specified folder: [2].'), -(1316, u'A network error occurred while attempting to read from the file: [2]'), -(1317, u'An error occurred while attempting to create the directory: [2]'), -(1318, u'A network error occurred while attempting to create the directory: [2]'), -(1319, u'A network error occurred while attempting to open the source file cabinet: [2]'), -(1320, u'The specified path is too long: [2]'), -(1321, u'The Installer has insufficient privileges to modify this file: [2].'), -(1322, u"A portion of the folder path '[2]' is invalid. It is either empty or exceeds the length allowed by the system."), -(1323, u"The folder path '[2]' contains words that are not valid in folder paths."), -(1324, u"The folder path '[2]' contains an invalid character."), -(1325, u"'[2]' is not a valid short file name."), -(1326, u'Error getting file security: [3] GetLastError: [2]'), -(1327, u'Invalid Drive: [2]'), -(1328, u'Error applying patch to file [2]. It has probably been updated by other means, and can no longer be modified by this patch. For more information contact your patch vendor. {{System Error: [3]}}'), -(1329, u'A file that is required cannot be installed because the cabinet file [2] is not digitally signed. This may indicate that the cabinet file is corrupt.'), -(1330, u'A file that is required cannot be installed because the cabinet file [2] has an invalid digital signature. This may indicate that the cabinet file is corrupt.{{ Error [3] was returned by WinVerifyTrust.}}'), -(1331, u'Failed to correctly copy [2] file: CRC error.'), -(1332, u'Failed to correctly move [2] file: CRC error.'), -(1333, u'Failed to correctly patch [2] file: CRC error.'), -(1334, u"The file '[2]' cannot be installed because the file cannot be found in cabinet file '[3]'. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package."), -(1335, u"The cabinet file '[2]' required for this installation is corrupt and cannot be used. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package."), -(1336, u'There was an error creating a temporary file that is needed to complete this installation.{{ Folder: [3]. System error code: [2]}}'), -(1401, u'Could not create key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. '), -(1402, u'Could not open key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. '), -(1403, u'Could not delete value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. '), -(1404, u'Could not delete key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. '), -(1405, u'Could not read value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. '), -(1406, u'Could not write value [2] to key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel.'), -(1407, u'Could not get value names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.'), -(1408, u'Could not get sub key names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.'), -(1409, u'Could not read security information for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.'), -(1410, u'Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application.'), -(1500, u'Another installation is in progress. You must complete that installation before continuing this one.'), -(1501, u'Error accessing secured data. Please make sure the Windows Installer is configured properly and try the install again.'), -(1502, u"User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product. Your current install will now continue."), -(1503, u"User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product."), -(1601, u"Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB. Free some disk space and retry."), -(1602, u'Are you sure you want to cancel?'), -(1603, u"The file [2][3] is being held in use{ by the following process: Name: [4], Id: [5], Window Title: '[6]'}. Close that application and retry."), -(1604, u"The product '[2]' is already installed, preventing the installation of this product. The two products are incompatible."), -(1605, u"There is not enough disk space on the volume '[2]' to continue the install with recovery enabled. [3] KB are required, but only [4] KB are available. Click Ignore to continue the install without saving recovery information, click Retry to check for available space again, or click Cancel to quit the installation."), -(1606, u'Could not access network location [2].'), -(1607, u'The following applications should be closed before continuing the install:'), -(1608, u'Could not find any previously installed compliant products on the machine for installing this product.'), -(1609, u"An error occurred while applying security settings. [2] is not a valid user or group. This could be a problem with the package, or a problem connecting to a domain controller on the network. Check your network connection and click Retry, or Cancel to end the install. {{Unable to locate the user's SID, system error [3]}}"), -(1701, u'The key [2] is not valid. Verify that you entered the correct key.'), -(1702, u'The installer must restart your system before configuration of [2] can continue. Click Yes to restart now or No if you plan to manually restart later.'), -(1703, u'You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to manually restart later.'), -(1704, u'An installation for [2] is currently suspended. You must undo the changes made by that installation to continue. Do you want to undo those changes?'), -(1705, u'A previous installation for this product is in progress. You must undo the changes made by that installation to continue. Do you want to undo those changes?'), -(1706, u"An installation package for the product [2] cannot be found. Try the installation again using a valid copy of the installation package '[3]'."), -(1707, u'Installation completed successfully.'), -(1708, u'Installation failed.'), -(1709, u'Product: [2] -- [3]'), -(1710, u'You may either restore your computer to its previous state or continue the install later. Would you like to restore?'), -(1711, u'An error occurred while writing installation information to disk. Check to make sure enough disk space is available, and click Retry, or Cancel to end the install.'), -(1712, u'One or more of the files required to restore your computer to its previous state could not be found. Restoration will not be possible.'), -(1713, u'[2] cannot install one of its required products. Contact your technical support group. {{System Error: [3].}}'), -(1714, u'The older version of [2] cannot be removed. Contact your technical support group. {{System Error [3].}}'), -(1715, u'Installed [2]'), -(1716, u'Configured [2]'), -(1717, u'Removed [2]'), -(1718, u'File [2] was rejected by digital signature policy.'), -(1719, u'The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance.'), -(1720, u'There is a problem with this Windows Installer package. A script required for this install to complete could not be run. Contact your support personnel or package vendor. {{Custom action [2] script error [3], [4]: [5] Line [6], Column [7], [8] }}'), -(1721, u'There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action: [2], location: [3], command: [4] }}'), -(1722, u'There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. {{Action [2], location: [3], command: [4] }}'), -(1723, u'There is a problem with this Windows Installer package. A DLL required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action [2], entry: [3], library: [4] }}'), -(1724, u'Removal completed successfully.'), -(1725, u'Removal failed.'), -(1726, u'Advertisement completed successfully.'), -(1727, u'Advertisement failed.'), -(1728, u'Configuration completed successfully.'), -(1729, u'Configuration failed.'), -(1730, u'You must be an Administrator to remove this application. To remove this application, you can log on as an Administrator, or contact your technical support group for assistance.'), -(1801, u'The path [2] is not valid. Please specify a valid path.'), -(1802, u'Out of memory. Shut down other applications before retrying.'), -(1803, u'There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume.'), -(1804, u'There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume.'), -(1805, u'The folder [2] does not exist. Please enter a path to an existing folder.'), -(1806, u'You have insufficient privileges to read this folder.'), -(1807, u'A valid destination folder for the install could not be determined.'), -(1901, u'Error attempting to read from the source install database: [2].'), -(1902, u'Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation.'), -(1903, u'Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation.'), -(1904, u'Module [2] failed to register. HRESULT [3]. Contact your support personnel.'), -(1905, u'Module [2] failed to unregister. HRESULT [3]. Contact your support personnel.'), -(1906, u'Failed to cache package [2]. Error: [3]. Contact your support personnel.'), -(1907, u'Could not register font [2]. Verify that you have sufficient permissions to install fonts, and that the system supports this font.'), -(1908, u'Could not unregister font [2]. Verify that you that you have sufficient permissions to remove fonts.'), -(1909, u'Could not create Shortcut [2]. Verify that the destination folder exists and that you can access it.'), -(1910, u'Could not remove Shortcut [2]. Verify that the shortcut file exists and that you can access it.'), -(1911, u'Could not register type library for file [2]. Contact your support personnel.'), -(1912, u'Could not unregister type library for file [2]. Contact your support personnel.'), -(1913, u'Could not update the ini file [2][3]. Verify that the file exists and that you can access it.'), -(1914, u'Could not schedule file [2] to replace file [3] on reboot. Verify that you have write permissions to file [3].'), -(1915, u'Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.'), -(1916, u'Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.'), -(1917, u'Error removing ODBC driver: [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers.'), -(1918, u'Error installing ODBC driver: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.'), -(1919, u'Error configuring ODBC data source: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.'), -(1920, u"Service '[2]' ([3]) failed to start. Verify that you have sufficient privileges to start system services."), -(1921, u"Service '[2]' ([3]) could not be stopped. Verify that you have sufficient privileges to stop system services."), -(1922, u"Service '[2]' ([3]) could not be deleted. Verify that you have sufficient privileges to remove system services."), -(1923, u"Service '[2]' ([3]) could not be installed. Verify that you have sufficient privileges to install system services."), -(1924, u"Could not update environment variable '[2]'. Verify that you have sufficient privileges to modify environment variables."), -(1925, u'You do not have sufficient privileges to complete this installation for all users of the machine. Log on as administrator and then retry this installation.'), -(1926, u"Could not set file security for file '[3]'. Error: [2]. Verify that you have sufficient privileges to modify the security permissions for this file."), -(1927, u'Component Services (COM+ 1.0) are not installed on this computer. This installation requires Component Services in order to complete successfully. Component Services are available on Windows 2000.'), -(1928, u'Error registering COM+ Application. Contact your support personnel for more information.'), -(1929, u'Error unregistering COM+ Application. Contact your support personnel for more information.'), -(1930, u"The description for service '[2]' ([3]) could not be changed."), -(1931, u'The Windows Installer service cannot update the system file [2] because the file is protected by Windows. You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}}'), -(1932, u'The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}}'), -(1933, u'The Windows Installer service cannot update one or more protected Windows files. {{SFP Error: [2]. List of protected files:\\r\\n[3]}}'), -(1934, u'User installations are disabled via policy on the machine.'), -(1935, u'An error occured during the installation of assembly component [2]. HRESULT: [3]. {{assembly interface: [4], function: [5], assembly name: [6]}}'), -] - -tables=['AdminExecuteSequence', 'AdminUISequence', 'AdvtExecuteSequence', 'BBControl', 'Billboard', 'Binary', 'CheckBox', 'Property', 'ComboBox', 'Control', 'ListBox', 'ActionText', 'ControlCondition', 'ControlEvent', 'Dialog', 'EventMapping', 'InstallExecuteSequence', 'InstallUISequence', 'ListView', 'RadioButton', 'TextStyle', 'UIText', '_Validation', 'Error'] diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 5dd5f1a..7a79b53 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -481,27 +481,28 @@ def normpath(path): # Return an absolute path. -def abspath(path): - """Return the absolute version of a path""" - try: - from nt import _getfullpathname - except ImportError: # Not running on Windows - mock up something sensible. - global abspath - def _abspath(path): - if not isabs(path): - path = join(os.getcwd(), path) - return normpath(path) - abspath = _abspath - return _abspath(path) - - if path: # Empty path must return current working directory. - try: - path = _getfullpathname(path) - except WindowsError: - pass # Bad path - return unchanged. - else: - path = os.getcwd() - return normpath(path) +try: + from nt import _getfullpathname + +except ImportError: # not running on Windows - mock up something sensible + def abspath(path): + """Return the absolute version of a path.""" + if not isabs(path): + path = join(os.getcwd(), path) + return normpath(path) + +else: # use native Windows method on Windows + def abspath(path): + """Return the absolute version of a path.""" + + if path: # Empty path must return current working directory. + try: + path = _getfullpathname(path) + except WindowsError: + pass # Bad path - return unchanged. + else: + path = os.getcwd() + return normpath(path) # realpath is a no-op on systems without islink support realpath = abspath diff --git a/Lib/optparse.py b/Lib/optparse.py index ae3d00d..9ac987e 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -16,7 +16,7 @@ For support, use the optik-users@lists.sourceforge.net mailing list # Python developers: please do not make changes to this file, since # it is automatically generated from the Optik source code. -__version__ = "1.5a2" +__version__ = "1.5.1" __all__ = ['Option', 'SUPPRESS_HELP', @@ -35,8 +35,8 @@ __all__ = ['Option', 'BadOptionError'] __copyright__ = """ -Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2004 Python Software Foundation. All rights reserved. +Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. +Copyright (c) 2002-2006 Python Software Foundation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -67,21 +67,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import sys, os +import types import textwrap -try: - from gettext import gettext as _ -except ImportError: - _ = lambda arg: arg def _repr(self): return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) # This file was generated from: -# Id: option_parser.py 421 2004-10-26 00:45:16Z greg -# Id: option.py 422 2004-10-26 00:53:47Z greg -# Id: help.py 367 2004-07-24 23:21:21Z gward -# Id: errors.py 367 2004-07-24 23:21:21Z gward +# Id: option_parser.py 509 2006-04-20 00:58:24Z gward +# Id: option.py 509 2006-04-20 00:58:24Z gward +# Id: help.py 509 2006-04-20 00:58:24Z gward +# Id: errors.py 509 2006-04-20 00:58:24Z gward + +try: + from gettext import gettext +except ImportError: + def gettext(message): + return message +_ = gettext + class OptParseError (Exception): def __init__(self, msg): @@ -120,8 +125,25 @@ class OptionValueError (OptParseError): class BadOptionError (OptParseError): """ - Raised if an invalid or ambiguous option is seen on the command-line. + Raised if an invalid option is seen on the command line. + """ + def __init__(self, opt_str): + self.opt_str = opt_str + + def __str__(self): + return _("no such option: %s") % self.opt_str + +class AmbiguousOptionError (BadOptionError): + """ + Raised if an ambiguous option is seen on the command line. """ + def __init__(self, opt_str, possibilities): + BadOptionError.__init__(self, opt_str) + self.possibilities = possibilities + + def __str__(self): + return (_("ambiguous option: %s (%s?)") + % (self.opt_str, ", ".join(self.possibilities))) class HelpFormatter: @@ -223,15 +245,30 @@ class HelpFormatter: def format_heading(self, heading): raise NotImplementedError, "subclasses must implement" - def format_description(self, description): - if not description: - return "" - desc_width = self.width - self.current_indent + def _format_text(self, text): + """ + Format a paragraph of free-form text for inclusion in the + help output at the current indentation level. + """ + text_width = self.width - self.current_indent indent = " "*self.current_indent - return textwrap.fill(description, - desc_width, + return textwrap.fill(text, + text_width, initial_indent=indent, - subsequent_indent=indent) + "\n" + subsequent_indent=indent) + + def format_description(self, description): + if description: + return self._format_text(description) + "\n" + else: + return "" + + def format_epilog(self, epilog): + if epilog: + return "\n" + self._format_text(epilog) + "\n" + else: + return "" + def expand_default(self, option): if self.parser is None or not self.default_tag: @@ -328,7 +365,7 @@ class IndentedHelpFormatter (HelpFormatter): self, indent_increment, max_help_position, width, short_first) def format_usage(self, usage): - return _("usage: %s\n") % usage + return _("Usage: %s\n") % usage def format_heading(self, heading): return "%*s%s:\n" % (self.current_indent, "", heading) @@ -353,8 +390,27 @@ class TitledHelpFormatter (HelpFormatter): return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) -_builtin_cvt = { "int" : (int, _("integer")), - "long" : (long, _("long integer")), +def _parse_num(val, type): + if val[:2].lower() == "0x": # hexadecimal + radix = 16 + elif val[:2].lower() == "0b": # binary + radix = 2 + val = val[2:] or "0" # have to remove "0b" prefix + elif val[:1] == "0": # octal + radix = 8 + else: # decimal + radix = 10 + + return type(val, radix) + +def _parse_int(val): + return _parse_num(val, int) + +def _parse_long(val): + return _parse_num(val, long) + +_builtin_cvt = { "int" : (_parse_int, _("integer")), + "long" : (_parse_long, _("long integer")), "float" : (float, _("floating-point")), "complex" : (complex, _("complex")) } @@ -422,6 +478,7 @@ class Option: "store_true", "store_false", "append", + "append_const", "count", "callback", "help", @@ -435,6 +492,7 @@ class Option: "store_true", "store_false", "append", + "append_const", "count") # The set of actions for which it makes sense to supply a value @@ -448,6 +506,10 @@ class Option: ALWAYS_TYPED_ACTIONS = ("store", "append") + # The set of actions which take a 'const' attribute. + CONST_ACTIONS = ("store_const", + "append_const") + # The set of known types for option parsers. Again, listed here for # constructor argument validation. TYPES = ("string", "int", "long", "float", "complex", "choice") @@ -572,9 +634,17 @@ class Option: # No type given? "string" is the most sensible default. self.type = "string" else: - # Allow type objects as an alternative to their names. - if type(self.type) is type: + # Allow type objects or builtin type conversion functions + # (int, str, etc.) as an alternative to their names. (The + # complicated check of __builtin__ is only necessary for + # Python 2.1 and earlier, and is short-circuited by the + # first check on modern Pythons.) + import __builtin__ + if ( type(self.type) is types.TypeType or + (hasattr(self.type, "__name__") and + getattr(__builtin__, self.type.__name__, None) is self.type) ): self.type = self.type.__name__ + if self.type == "str": self.type = "string" @@ -589,7 +659,7 @@ class Option: if self.choices is None: raise OptionError( "must supply a list of choices for type 'choice'", self) - elif type(self.choices) not in (tuple, list): + elif type(self.choices) not in (types.TupleType, types.ListType): raise OptionError( "choices must be a list of strings ('%s' supplied)" % str(type(self.choices)).split("'")[1], self) @@ -613,7 +683,7 @@ class Option: self.dest = self._short_opts[0][1] def _check_const(self): - if self.action != "store_const" and self.const is not None: + if self.action not in self.CONST_ACTIONS and self.const is not None: raise OptionError( "'const' must not be supplied for action %r" % self.action, self) @@ -633,12 +703,12 @@ class Option: raise OptionError( "callback not callable: %r" % self.callback, self) if (self.callback_args is not None and - type(self.callback_args) is not tuple): + type(self.callback_args) is not types.TupleType): raise OptionError( "callback_args, if supplied, must be a tuple: not %r" % self.callback_args, self) if (self.callback_kwargs is not None and - type(self.callback_kwargs) is not dict): + type(self.callback_kwargs) is not types.DictType): raise OptionError( "callback_kwargs, if supplied, must be a dict: not %r" % self.callback_kwargs, self) @@ -720,6 +790,8 @@ class Option: setattr(values, dest, False) elif action == "append": values.ensure_value(dest, []).append(value) + elif action == "append_const": + values.ensure_value(dest, []).append(self.const) elif action == "count": setattr(values, dest, values.ensure_value(dest, 0) + 1) elif action == "callback": @@ -748,11 +820,9 @@ try: True, False except NameError: (True, False) = (1, 0) -try: - basestring -except NameError: - basestring = (str, unicode) +def isbasestring(x): + return isinstance(x, types.StringType) or isinstance(x, types.UnicodeType) class Values: @@ -766,16 +836,13 @@ class Values: __repr__ = _repr - def __eq__(self, other): + def __cmp__(self, other): if isinstance(other, Values): - return self.__dict__ == other.__dict__ - elif isinstance(other, dict): - return self.__dict__ == other + return cmp(self.__dict__, other.__dict__) + elif isinstance(other, types.DictType): + return cmp(self.__dict__, other) else: - return False - - def __ne__(self, other): - return not (self == other) + return -1 def _update_careful(self, dict): """ @@ -893,6 +960,13 @@ class OptionContainer: return self.description + def destroy(self): + """see OptionParser.destroy().""" + del self._short_opt + del self._long_opt + del self.defaults + + # -- Option-adding methods ----------------------------------------- def _check_conflict(self, option): @@ -926,7 +1000,7 @@ class OptionContainer: """add_option(Option) add_option(opt_str, ..., kwarg=val, ...) """ - if type(args[0]) is str: + if type(args[0]) is types.StringType: option = self.option_class(*args, **kwargs) elif len(args) == 1 and not kwargs: option = args[0] @@ -1018,6 +1092,11 @@ class OptionGroup (OptionContainer): def set_title(self, title): self.title = title + def destroy(self): + """see OptionParser.destroy().""" + OptionContainer.destroy(self) + del self.option_list + # -- Help-formatting methods --------------------------------------- def format_help(self, formatter): @@ -1044,6 +1123,8 @@ class OptionParser (OptionContainer): prog : string the name of the current program (to override os.path.basename(sys.argv[0])). + epilog : string + paragraph of help text to print after option help option_groups : [OptionGroup] list of option groups in this parser (option groups are @@ -1102,7 +1183,8 @@ class OptionParser (OptionContainer): description=None, formatter=None, add_help_option=True, - prog=None): + prog=None, + epilog=None): OptionContainer.__init__( self, option_class, conflict_handler, description) self.set_usage(usage) @@ -1114,6 +1196,7 @@ class OptionParser (OptionContainer): formatter = IndentedHelpFormatter() self.formatter = formatter self.formatter.set_parser(self) + self.epilog = epilog # Populate the option list; initial sources are the # standard_option_list class attribute, the 'option_list' @@ -1124,6 +1207,22 @@ class OptionParser (OptionContainer): self._init_parsing_state() + + def destroy(self): + """ + Declare that you are done with this OptionParser. This cleans up + reference cycles so the OptionParser (and all objects referenced by + it) can be garbage-collected promptly. After calling destroy(), the + OptionParser is unusable. + """ + OptionContainer.destroy(self) + for group in self.option_groups: + group.destroy() + del self.option_list + del self.option_groups + del self.formatter + + # -- Private methods ----------------------------------------------- # (used by our or OptionContainer's constructor) @@ -1167,7 +1266,7 @@ class OptionParser (OptionContainer): elif usage is SUPPRESS_USAGE: self.usage = None # For backwards compatibility with Optik 1.3 and earlier. - elif usage.startswith("usage:" + " "): + elif usage.lower().startswith("usage: "): self.usage = usage[7:] else: self.usage = usage @@ -1201,7 +1300,7 @@ class OptionParser (OptionContainer): defaults = self.defaults.copy() for option in self._get_all_options(): default = defaults.get(option.dest) - if isinstance(default, basestring): + if isbasestring(default): opt_str = option.get_opt_string() defaults[option.dest] = option.check_value(opt_str, default) @@ -1212,7 +1311,7 @@ class OptionParser (OptionContainer): def add_option_group(self, *args, **kwargs): # XXX lots of overlap with OptionContainer.add_option() - if type(args[0]) is str: + if type(args[0]) is types.StringType: group = OptionGroup(self, *args, **kwargs) elif len(args) == 1 and not kwargs: group = args[0] @@ -1276,7 +1375,7 @@ class OptionParser (OptionContainer): try: stop = self._process_args(largs, rargs, values) except (BadOptionError, OptionValueError), err: - self.error(err.msg) + self.error(str(err)) args = largs + rargs return self.check_values(values, args) @@ -1401,7 +1500,7 @@ class OptionParser (OptionContainer): i += 1 # we have consumed a character if not option: - self.error(_("no such option: %s") % opt) + raise BadOptionError(opt) if option.takes_value(): # Any characters left in arg? Pretend they're the # next arg, and stop consuming characters of arg. @@ -1501,7 +1600,7 @@ class OptionParser (OptionContainer): formatter = self.formatter formatter.store_option_strings(self) result = [] - result.append(formatter.format_heading(_("options"))) + result.append(formatter.format_heading(_("Options"))) formatter.indent() if self.option_list: result.append(OptionContainer.format_option_help(self, formatter)) @@ -1513,6 +1612,9 @@ class OptionParser (OptionContainer): # Drop the last "\n", or the header if no options or option groups: return "".join(result[:-1]) + def format_epilog(self, formatter): + return formatter.format_epilog(self.epilog) + def format_help(self, formatter=None): if formatter is None: formatter = self.formatter @@ -1522,6 +1624,7 @@ class OptionParser (OptionContainer): if self.description: result.append(self.format_description(formatter) + "\n") result.append(self.format_option_help(formatter)) + result.append(self.format_epilog(formatter)) return "".join(result) def print_help(self, file=None): @@ -1555,11 +1658,10 @@ def _match_abbrev(s, wordmap): if len(possibilities) == 1: return possibilities[0] elif not possibilities: - raise BadOptionError(_("no such option: %s") % s) + raise BadOptionError(s) else: # More than one possible completion: ambiguous prefix. - raise BadOptionError(_("ambiguous option: %s (%s?)") - % (s, ", ".join(possibilities))) + raise AmbiguousOptionError(s, possibilities) # Some day, there might be many Option classes. As of Optik 1.3, the diff --git a/Lib/pdb.py b/Lib/pdb.py index 5b7ea99..94f61f7 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -57,9 +57,11 @@ line_prefix = '\n-> ' # Probably a better default class Pdb(bdb.Bdb, cmd.Cmd): - def __init__(self): + def __init__(self, completekey='tab', stdin=None, stdout=None): bdb.Bdb.__init__(self) - cmd.Cmd.__init__(self) + cmd.Cmd.__init__(self, completekey, stdin, stdout) + if stdout: + self.use_rawinput = 0 self.prompt = '(Pdb) ' self.aliases = {} self.mainpyfile = '' @@ -133,7 +135,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): if self._wait_for_mainpyfile: return if self.stop_here(frame): - print '--Call--' + print >>self.stdout, '--Call--' self.interaction(frame, None) def user_line(self, frame): @@ -169,7 +171,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" frame.f_locals['__return__'] = return_value - print '--Return--' + print >>self.stdout, '--Return--' self.interaction(frame, None) def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): @@ -179,7 +181,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): if type(exc_type) == type(''): exc_type_name = exc_type else: exc_type_name = exc_type.__name__ - print exc_type_name + ':', _saferepr(exc_value) + print >>self.stdout, exc_type_name + ':', _saferepr(exc_value) self.interaction(frame, exc_traceback) # General interaction function @@ -202,7 +204,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): if type(t) == type(''): exc_type_name = t else: exc_type_name = t.__name__ - print '***', exc_type_name + ':', v + print >>self.stdout, '***', exc_type_name + ':', v def precmd(self, line): """Handle alias expansion and ';;' separator.""" @@ -280,7 +282,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): try: bnum = int(arg) except: - print "Usage : commands [bnum]\n ...\n end" + print >>self.stdout, "Usage : commands [bnum]\n ...\n end" return self.commands_bnum = bnum self.commands[bnum] = [] @@ -297,10 +299,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): # break [ ([filename:]lineno | function) [, "condition"] ] if not arg: if self.breaks: # There's at least one - print "Num Type Disp Enb Where" + print >>self.stdout, "Num Type Disp Enb Where" for bp in bdb.Breakpoint.bpbynumber: if bp: - bp.bpprint() + bp.bpprint(self.stdout) return # parse arguments; comma has lowest precedence # and cannot occur in filename @@ -319,8 +321,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): filename = arg[:colon].rstrip() f = self.lookupmodule(filename) if not f: - print '*** ', repr(filename), - print 'not found from sys.path' + print >>self.stdout, '*** ', repr(filename), + print >>self.stdout, 'not found from sys.path' return else: filename = f @@ -328,7 +330,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): try: lineno = int(arg) except ValueError, msg: - print '*** Bad lineno:', arg + print >>self.stdout, '*** Bad lineno:', arg return else: # no colon; can be lineno or function @@ -354,11 +356,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): # last thing to try (ok, filename, ln) = self.lineinfo(arg) if not ok: - print '*** The specified object', - print repr(arg), - print 'is not a function' - print ('or was not found ' - 'along sys.path.') + print >>self.stdout, '*** The specified object', + print >>self.stdout, repr(arg), + print >>self.stdout, 'is not a function' + print >>self.stdout, 'or was not found along sys.path.' return funcname = ok # ok contains a function name lineno = int(ln) @@ -369,12 +370,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): if line: # now set the break point err = self.set_break(filename, line, temporary, cond, funcname) - if err: print '***', err + if err: print >>self.stdout, '***', err else: bp = self.get_breaks(filename, line)[-1] - print "Breakpoint %d at %s:%d" % (bp.number, - bp.file, - bp.line) + print >>self.stdout, "Breakpoint %d at %s:%d" % (bp.number, + bp.file, + bp.line) # To be overridden in derived debuggers def defaultFile(self): @@ -430,13 +431,13 @@ class Pdb(bdb.Bdb, cmd.Cmd): """ line = linecache.getline(filename, lineno) if not line: - print 'End of file' + print >>self.stdout, 'End of file' return 0 line = line.strip() # Don't allow setting breakpoint at a blank line if (not line or (line[0] == '#') or (line[:3] == '"""') or line[:3] == "'''"): - print '*** Blank or comment' + print >>self.stdout, '*** Blank or comment' return 0 return lineno @@ -446,11 +447,11 @@ class Pdb(bdb.Bdb, cmd.Cmd): try: i = int(i) except ValueError: - print 'Breakpoint index %r is not a number' % i + print >>self.stdout, 'Breakpoint index %r is not a number' % i continue if not (0 <= i < len(bdb.Breakpoint.bpbynumber)): - print 'No breakpoint numbered', i + print >>self.stdout, 'No breakpoint numbered', i continue bp = bdb.Breakpoint.bpbynumber[i] @@ -463,11 +464,11 @@ class Pdb(bdb.Bdb, cmd.Cmd): try: i = int(i) except ValueError: - print 'Breakpoint index %r is not a number' % i + print >>self.stdout, 'Breakpoint index %r is not a number' % i continue if not (0 <= i < len(bdb.Breakpoint.bpbynumber)): - print 'No breakpoint numbered', i + print >>self.stdout, 'No breakpoint numbered', i continue bp = bdb.Breakpoint.bpbynumber[i] @@ -486,8 +487,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): if bp: bp.cond = cond if not cond: - print 'Breakpoint', bpnum, - print 'is now unconditional.' + print >>self.stdout, 'Breakpoint', bpnum, + print >>self.stdout, 'is now unconditional.' def do_ignore(self,arg): """arg is bp number followed by ignore count.""" @@ -506,10 +507,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): reply = reply + '%d crossings' % count else: reply = reply + '1 crossing' - print reply + ' of breakpoint %d.' % bpnum + print >>self.stdout, reply + ' of breakpoint %d.' % bpnum else: - print 'Will stop next time breakpoint', - print bpnum, 'is reached.' + print >>self.stdout, 'Will stop next time breakpoint', + print >>self.stdout, bpnum, 'is reached.' def do_clear(self, arg): """Three possibilities, tried in this order: @@ -532,22 +533,28 @@ class Pdb(bdb.Bdb, cmd.Cmd): arg = arg[i+1:] try: lineno = int(arg) - except: + except ValueError: err = "Invalid line number (%s)" % arg else: err = self.clear_break(filename, lineno) - if err: print '***', err + if err: print >>self.stdout, '***', err return numberlist = arg.split() for i in numberlist: + try: + i = int(i) + except ValueError: + print >>self.stdout, 'Breakpoint index %r is not a number' % i + continue + if not (0 <= i < len(bdb.Breakpoint.bpbynumber)): - print 'No breakpoint numbered', i + print >>self.stdout, 'No breakpoint numbered', i continue err = self.clear_bpbynumber(i) if err: - print '***', err + print >>self.stdout, '***', err else: - print 'Deleted breakpoint', i + print >>self.stdout, 'Deleted breakpoint', i do_cl = do_clear # 'c' is already an abbreviation for 'continue' def do_where(self, arg): @@ -557,7 +564,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): def do_up(self, arg): if self.curindex == 0: - print '*** Oldest frame' + print >>self.stdout, '*** Oldest frame' else: self.curindex = self.curindex - 1 self.curframe = self.stack[self.curindex][0] @@ -567,7 +574,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): def do_down(self, arg): if self.curindex + 1 == len(self.stack): - print '*** Newest frame' + print >>self.stdout, '*** Newest frame' else: self.curindex = self.curindex + 1 self.curframe = self.stack[self.curindex][0] @@ -597,12 +604,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): def do_jump(self, arg): if self.curindex + 1 != len(self.stack): - print "*** You can only jump within the bottom frame" + print >>self.stdout, "*** You can only jump within the bottom frame" return try: arg = int(arg) except ValueError: - print "*** The 'jump' command requires a line number." + print >>self.stdout, "*** The 'jump' command requires a line number." else: try: # Do the jump, fix up our copy of the stack, and display the @@ -611,7 +618,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.stack[self.curindex] = self.stack[self.curindex][0], arg self.print_stack_entry(self.stack[self.curindex]) except ValueError, e: - print '*** Jump failed:', e + print >>self.stdout, '*** Jump failed:', e do_j = do_jump def do_debug(self, arg): @@ -620,9 +627,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): locals = self.curframe.f_locals p = Pdb() p.prompt = "(%s) " % self.prompt.strip() - print "ENTERING RECURSIVE DEBUGGER" + print >>self.stdout, "ENTERING RECURSIVE DEBUGGER" sys.call_tracing(p.run, (arg, globals, locals)) - print "LEAVING RECURSIVE DEBUGGER" + print >>self.stdout, "LEAVING RECURSIVE DEBUGGER" sys.settrace(self.trace_dispatch) self.lastcmd = p.lastcmd @@ -635,7 +642,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): do_exit = do_quit def do_EOF(self, arg): - print + print >>self.stdout self._user_requested_quit = 1 self.set_quit() return 1 @@ -649,16 +656,16 @@ class Pdb(bdb.Bdb, cmd.Cmd): if co.co_flags & 8: n = n+1 for i in range(n): name = co.co_varnames[i] - print name, '=', - if name in dict: print dict[name] - else: print "*** undefined ***" + print >>self.stdout, name, '=', + if name in dict: print >>self.stdout, dict[name] + else: print >>self.stdout, "*** undefined ***" do_a = do_args def do_retval(self, arg): if '__return__' in self.curframe.f_locals: - print self.curframe.f_locals['__return__'] + print >>self.stdout, self.curframe.f_locals['__return__'] else: - print '*** Not yet returned!' + print >>self.stdout, '*** Not yet returned!' do_rv = do_retval def _getval(self, arg): @@ -670,18 +677,18 @@ class Pdb(bdb.Bdb, cmd.Cmd): if isinstance(t, str): exc_type_name = t else: exc_type_name = t.__name__ - print '***', exc_type_name + ':', repr(v) + print >>self.stdout, '***', exc_type_name + ':', repr(v) raise def do_p(self, arg): try: - print repr(self._getval(arg)) + print >>self.stdout, repr(self._getval(arg)) except: pass def do_pp(self, arg): try: - pprint.pprint(self._getval(arg)) + pprint.pprint(self._getval(arg), self.stdout) except: pass @@ -701,7 +708,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): else: first = max(1, int(x) - 5) except: - print '*** Error in argument:', repr(arg) + print >>self.stdout, '*** Error in argument:', repr(arg) return elif self.lineno is None: first = max(1, self.curframe.f_lineno - 5) @@ -715,7 +722,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): for lineno in range(first, last+1): line = linecache.getline(filename, lineno) if not line: - print '[EOF]' + print >>self.stdout, '[EOF]' break else: s = repr(lineno).rjust(3) @@ -724,7 +731,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): else: s = s + ' ' if lineno == self.curframe.f_lineno: s = s + '->' - print s + '\t' + line, + print >>self.stdout, s + '\t' + line, self.lineno = lineno except KeyboardInterrupt: pass @@ -739,23 +746,23 @@ class Pdb(bdb.Bdb, cmd.Cmd): if type(t) == type(''): exc_type_name = t else: exc_type_name = t.__name__ - print '***', exc_type_name + ':', repr(v) + print >>self.stdout, '***', exc_type_name + ':', repr(v) return code = None # Is it a function? try: code = value.func_code except: pass if code: - print 'Function', code.co_name + print >>self.stdout, 'Function', code.co_name return # Is it an instance method? try: code = value.im_func.func_code except: pass if code: - print 'Method', code.co_name + print >>self.stdout, 'Method', code.co_name return # None of the above... - print type(value) + print >>self.stdout, type(value) def do_alias(self, arg): args = arg.split() @@ -763,10 +770,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): keys = self.aliases.keys() keys.sort() for alias in keys: - print "%s = %s" % (alias, self.aliases[alias]) + print >>self.stdout, "%s = %s" % (alias, self.aliases[alias]) return if args[0] in self.aliases and len(args) == 1: - print "%s = %s" % (args[0], self.aliases[args[0]]) + print >>self.stdout, "%s = %s" % (args[0], self.aliases[args[0]]) else: self.aliases[args[0]] = ' '.join(args[1:]) @@ -777,7 +784,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): del self.aliases[args[0]] #list of all the commands making the program resume execution. - commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return', 'do_quit', 'do_jump'] + commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return', + 'do_quit', 'do_jump'] # Print a traceback starting at the top stack frame. # The most recently entered frame is printed last; @@ -797,10 +805,11 @@ class Pdb(bdb.Bdb, cmd.Cmd): def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix): frame, lineno = frame_lineno if frame is self.curframe: - print '>', + print >>self.stdout, '>', else: - print ' ', - print self.format_stack_entry(frame_lineno, prompt_prefix) + print >>self.stdout, ' ', + print >>self.stdout, self.format_stack_entry(frame_lineno, + prompt_prefix) # Help methods (derived from pdb.doc) @@ -809,7 +818,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.help_h() def help_h(self): - print """h(elp) + print >>self.stdout, """h(elp) Without argument, print the list of available commands. With a command name as argument, print help about that command "help pdb" pipes the full documentation file to the $PAGER @@ -819,7 +828,7 @@ With a command name as argument, print help about that command self.help_w() def help_w(self): - print """w(here) + print >>self.stdout, """w(here) Print a stack trace, with the most recent frame at the bottom. An arrow indicates the "current frame", which determines the context of most commands. 'bt' is an alias for this command.""" @@ -830,7 +839,7 @@ context of most commands. 'bt' is an alias for this command.""" self.help_d() def help_d(self): - print """d(own) + print >>self.stdout, """d(own) Move the current frame one level down in the stack trace (to a newer frame).""" @@ -838,7 +847,7 @@ Move the current frame one level down in the stack trace self.help_u() def help_u(self): - print """u(p) + print >>self.stdout, """u(p) Move the current frame one level up in the stack trace (to an older frame).""" @@ -846,7 +855,7 @@ Move the current frame one level up in the stack trace self.help_b() def help_b(self): - print """b(reak) ([file:]lineno | function) [, condition] + print >>self.stdout, """b(reak) ([file:]lineno | function) [, condition] With a line number argument, set a break there in the current file. With a function name, set a break at first executable line of that function. Without argument, list all breaks. If a second @@ -862,8 +871,8 @@ the .py suffix may be omitted.""" self.help_cl() def help_cl(self): - print "cl(ear) filename:lineno" - print """cl(ear) [bpnumber [bpnumber...]] + print >>self.stdout, "cl(ear) filename:lineno" + print >>self.stdout, """cl(ear) [bpnumber [bpnumber...]] With a space separated list of breakpoint numbers, clear those breakpoints. Without argument, clear all breaks (but first ask confirmation). With a filename:lineno argument, @@ -875,21 +884,21 @@ a linenumber was used instead of either filename:lineno or breakpoint numbers.""" def help_tbreak(self): - print """tbreak same arguments as break, but breakpoint is + print >>self.stdout, """tbreak same arguments as break, but breakpoint is removed when first hit.""" def help_enable(self): - print """enable bpnumber [bpnumber ...] + print >>self.stdout, """enable bpnumber [bpnumber ...] Enables the breakpoints given as a space separated list of bp numbers.""" def help_disable(self): - print """disable bpnumber [bpnumber ...] + print >>self.stdout, """disable bpnumber [bpnumber ...] Disables the breakpoints given as a space separated list of bp numbers.""" def help_ignore(self): - print """ignore bpnumber count + print >>self.stdout, """ignore bpnumber count Sets the ignore count for the given breakpoint number. A breakpoint becomes active when the ignore count is zero. When non-zero, the count is decremented each time the breakpoint is reached and the @@ -897,7 +906,7 @@ breakpoint is not disabled and any associated condition evaluates to true.""" def help_condition(self): - print """condition bpnumber str_condition + print >>self.stdout, """condition bpnumber str_condition str_condition is a string specifying an expression which must evaluate to true before the breakpoint is honored. If str_condition is absent, any existing condition is removed; @@ -907,7 +916,7 @@ i.e., the breakpoint is made unconditional.""" self.help_s() def help_s(self): - print """s(tep) + print >>self.stdout, """s(tep) Execute the current line, stop at the first possible occasion (either in a function that is called or in the current function).""" @@ -915,7 +924,7 @@ Execute the current line, stop at the first possible occasion self.help_n() def help_n(self): - print """n(ext) + print >>self.stdout, """n(ext) Continue execution until the next line in the current function is reached or it returns.""" @@ -923,7 +932,7 @@ is reached or it returns.""" self.help_r() def help_r(self): - print """r(eturn) + print >>self.stdout, """r(eturn) Continue execution until the current function returns.""" def help_continue(self): @@ -933,18 +942,18 @@ Continue execution until the current function returns.""" self.help_c() def help_c(self): - print """c(ont(inue)) + print >>self.stdout, """c(ont(inue)) Continue execution, only stop when a breakpoint is encountered.""" def help_jump(self): self.help_j() def help_j(self): - print """j(ump) lineno + print >>self.stdout, """j(ump) lineno Set the next line that will be executed.""" def help_debug(self): - print """debug code + print >>self.stdout, """debug code Enter a recursive debugger that steps through the code argument (which is an arbitrary expression or statement to be executed in the current environment).""" @@ -953,7 +962,7 @@ in the current environment).""" self.help_l() def help_l(self): - print """l(ist) [first [,last]] + print >>self.stdout, """l(ist) [first [,last]] List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing. @@ -965,19 +974,19 @@ if the second argument is less than the first, it is a count.""" self.help_a() def help_a(self): - print """a(rgs) + print >>self.stdout, """a(rgs) Print the arguments of the current function.""" def help_p(self): - print """p expression + print >>self.stdout, """p expression Print the value of the expression.""" def help_pp(self): - print """pp expression + print >>self.stdout, """pp expression Pretty-print the value of the expression.""" def help_exec(self): - print """(!) statement + print >>self.stdout, """(!) statement Execute the (one-line) statement in the context of the current stack frame. The exclamation point can be omitted unless the first word @@ -991,21 +1000,21 @@ command with a 'global' command, e.g.: self.help_q() def help_q(self): - print """q(uit) or exit - Quit from the debugger. + print >>self.stdout, """q(uit) or exit - Quit from the debugger. The program being executed is aborted.""" help_exit = help_q def help_whatis(self): - print """whatis arg + print >>self.stdout, """whatis arg Prints the type of the argument.""" def help_EOF(self): - print """EOF + print >>self.stdout, """EOF Handles the receipt of EOF as a command.""" def help_alias(self): - print """alias [name [command [parameter parameter ...] ]] + print >>self.stdout, """alias [name [command [parameter parameter ...] ]] Creates an alias called 'name' the executes 'command'. The command must *not* be enclosed in quotes. Replaceable parameters are indicated by %1, %2, and so on, while %* is replaced by all the @@ -1029,11 +1038,11 @@ alias ps pi self """ def help_unalias(self): - print """unalias name + print >>self.stdout, """unalias name Deletes the specified alias.""" def help_commands(self): - print """commands [bpnumber] + print >>self.stdout, """commands [bpnumber] (com) ... (com) end (Pdb) diff --git a/Lib/pkg_resources.py b/Lib/pkg_resources.py deleted file mode 100644 index db6cc90..0000000 --- a/Lib/pkg_resources.py +++ /dev/null @@ -1,2377 +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, new, pkgutil # XXX -from sets import ImmutableSet -from os import utime, rename, unlink # capture these to bypass sandboxing -from os import open as os_open - -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: - info = os.popen('/usr/bin/sw_vers').read().splitlines() - for line in info: - key, value = line.split(None, 1) - if key == 'ProductVersion:': - _cache.append(value.strip().split(".")) - break - else: - raise ValueError, "What?!" - 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. - """ - from distutils.util 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) - - -try: - from pkgutil import get_importer -except ImportError: - import _pkgutil as pkgutil - get_importer = pkgutil.get_importer -else: - import pkgutil - - -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,False)`` 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,[]) - - 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) - - self._added_new(dist) - - - def resolve(self, requirements, env=None, installer=None): - """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 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: - raise DistributionNotFound(req) # XXX put more info here - 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 "Couldn't 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: - 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. - """ - # XXX - - - 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(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)) - - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info,name)) - - 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): - return os.path.join(base, *resource_name.split('/')) - - 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: - 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': - return open(self.path,'rU').read() - 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() - - - -_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): - 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 file(os.path.join(path_item, entry)): - if not line.strip(): continue - for item in find_distributions(line.rstrip()): - yield item - -register_finder(pkgutil.ImpImporter, 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] = new.module(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(pkgutil.ImpImporter, 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'}.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", whic 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. - """ - 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.lstrip(), 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) - - - - - - -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, - -len(self.location or ''), self.location, self.py_version, - self.platform - ) - ) - def __cmp__(self, other): return cmp(self.hashcmp, other) - def __hash__(self): return hash(self.hashcmp) - - # 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) - for pkg in self._get_metadata('namespace_packages.txt'): - if pkg in sys.modules: declare_namespace(pkg) - - 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 not loc: return - if path is sys.path: - self.check_version_conflict() - best, pos = 0, -1 - for p,item in enumerate(path): - item = _normalize_cached(item) - if loc.startswith(item) and len(item)>best and loc<>item: - best, pos = len(item), p - if pos==-1: - if loc not in path: path.append(loc) - elif loc not in path[:pos+1]: - while loc in path: path.remove(loc) - path.insert(pos,loc) - - - def check_version_conflict(self): - if self.key=='setuptools': - 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 - - fn = getattr(sys.modules[modname], '__file__', None) - if fn and normalize_path(fn).startswith(loc): - 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]), - ImmutableSet(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 - for parsed,trans,op,ver in self.index: - action = trans[cmp(item,parsed)] - 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): - reqs = list(parse_requirements(s)) - if reqs: - if len(reqs)==1: - return reqs[0] - 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 _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/Lib/pkgutil.py b/Lib/pkgutil.py index 7316892..26c797f 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -31,7 +31,7 @@ def read_code(stream): def simplegeneric(func): """Make a trivial single-dispatch generic function""" registry = {} - def wrapper(*args,**kw): + def wrapper(*args, **kw): ob = args[0] try: cls = ob.__class__ @@ -41,18 +41,19 @@ def simplegeneric(func): mro = cls.__mro__ except AttributeError: try: - class cls(cls,object): pass + class cls(cls, object): + pass mro = cls.__mro__[1:] except TypeError: mro = object, # must be an ExtensionClass or some such :( for t in mro: if t in registry: - return registry[t](*args,**kw) + return registry[t](*args, **kw) else: - return func(*args,**kw) + return func(*args, **kw) try: wrapper.__name__ = func.__name__ - except (TypeError,AttributeError): + except (TypeError, AttributeError): pass # Python 2.3 doesn't allow functions to be renamed def register(typ, func=None): @@ -70,8 +71,9 @@ def simplegeneric(func): def walk_packages(path=None, prefix='', onerror=None): """Yield submodule names+loaders recursively, for path or sys.path""" - def seen(p,m={}): - if p in m: return True + def seen(p, m={}): + if p in m: + return True m[p] = True for importer, name, ispkg in iter_modules(path, prefix): @@ -110,7 +112,7 @@ def iter_modules(path=None, prefix=''): #@simplegeneric def iter_importer_modules(importer, prefix=''): - if not hasattr(importer,'iter_modules'): + if not hasattr(importer, 'iter_modules'): return [] return importer.iter_modules(prefix) @@ -336,13 +338,15 @@ def get_importer(path_item): pass else: importer = None - sys.path_importer_cache.setdefault(path_item,importer) + sys.path_importer_cache.setdefault(path_item, importer) - if importer is None: + # The boolean values are used for caching valid and invalid + # file paths for the built-in import machinery + if importer in (None, True, False): try: importer = ImpImporter(path_item) except ImportError: - pass + importer = None return importer @@ -377,7 +381,7 @@ def iter_importers(fullname=""): pkg = '.'.join(fullname.split('.')[:-1]) if pkg not in sys.modules: __import__(pkg) - path = getattr(sys.modules[pkg],'__path__',None) or [] + path = getattr(sys.modules[pkg], '__path__', None) or [] else: for importer in sys.meta_path: yield importer @@ -404,7 +408,7 @@ def get_loader(module_or_name): module_or_name = sys.modules[module_or_name] if isinstance(module_or_name, ModuleType): module = module_or_name - loader = getattr(module,'__loader__',None) + loader = getattr(module, '__loader__', None) if loader is not None: return loader fullname = module.__name__ diff --git a/Lib/plat-mac/bundlebuilder.py b/Lib/plat-mac/bundlebuilder.py index 03d8c81..266b845 100755 --- a/Lib/plat-mac/bundlebuilder.py +++ b/Lib/plat-mac/bundlebuilder.py @@ -145,11 +145,24 @@ class BundleBuilder(Defaults): self.message("Building %s" % repr(self.bundlepath), 1) if os.path.exists(self.bundlepath): shutil.rmtree(self.bundlepath) - os.mkdir(self.bundlepath) - self.preProcess() - self._copyFiles() - self._addMetaFiles() - self.postProcess() + if os.path.exists(self.bundlepath + '~'): + shutil.rmtree(self.bundlepath + '~') + bp = self.bundlepath + + # Create the app bundle in a temporary location and then + # rename the completed bundle. This way the Finder will + # never see an incomplete bundle (where it might pick up + # and cache the wrong meta data) + self.bundlepath = bp + '~' + try: + os.mkdir(self.bundlepath) + self.preProcess() + self._copyFiles() + self._addMetaFiles() + self.postProcess() + os.rename(self.bundlepath, bp) + finally: + self.bundlepath = bp self.message("Done.", 1) def preProcess(self): diff --git a/Lib/plat-mac/pimp.py b/Lib/plat-mac/pimp.py index 21923ec..456427c 100644 --- a/Lib/plat-mac/pimp.py +++ b/Lib/plat-mac/pimp.py @@ -21,7 +21,7 @@ import urlparse import plistlib import distutils.util import distutils.sysconfig -import md5 +import hashlib import tarfile import tempfile import shutil @@ -693,7 +693,7 @@ class PimpPackage: sys.stderr.write("Warning: no MD5Sum for %s\n" % self.fullname()) return 1 data = open(self.archiveFilename, 'rb').read() - checksum = md5.new(data).hexdigest() + checksum = hashlib.md5(data).hexdigest() return checksum == self._dict['MD5Sum'] def unpackPackageOnly(self, output=None): diff --git a/Lib/popen2.py b/Lib/popen2.py index 67ebd26..b966d4c 100644 --- a/Lib/popen2.py +++ b/Lib/popen2.py @@ -72,8 +72,9 @@ class Popen3: # In case the child hasn't been waited on, check if it's done. self.poll(_deadstate=sys.maxint) if self.sts < 0: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) + if _active: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) def _run_child(self, cmd): if isinstance(cmd, basestring): diff --git a/Lib/poplib.py b/Lib/poplib.py index 202c6e0..1cf114a 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -295,8 +295,8 @@ class POP3: m = self.timestamp.match(self.welcome) if not m: raise error_proto('-ERR APOP not supported by server') - import md5 - digest = md5.new(m.group(1)+secret).digest() + import hashlib + digest = hashlib.md5(m.group(1)+secret).digest() digest = ''.join(map(lambda x:'%02x'%ord(x), digest)) return self._shortcmd('APOP %s %s' % (user, digest)) diff --git a/Lib/rfc822.py b/Lib/rfc822.py index 871a049..d6d5e47 100644 --- a/Lib/rfc822.py +++ b/Lib/rfc822.py @@ -700,6 +700,7 @@ class AddrlistClass: break elif allowcomments and self.field[self.pos] == '(': slist.append(self.getcomment()) + continue # have already advanced pos from getcomment elif self.field[self.pos] == '\\': quote = 1 else: diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 6eb77f9..4d11aec 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -33,7 +33,6 @@ used, and this module (and the readline module) are silently inactive. """ -import readline import __builtin__ import __main__ @@ -141,4 +140,9 @@ def get_class_members(klass): ret = ret + get_class_members(base) return ret -readline.set_completer(Completer().complete) +try: + import readline +except ImportError: + pass +else: + readline.set_completer(Completer().complete) diff --git a/Lib/setuptools.egg-info/PKG-INFO b/Lib/setuptools.egg-info/PKG-INFO deleted file mode 100644 index ff5c1a1..0000000 --- a/Lib/setuptools.egg-info/PKG-INFO +++ /dev/null @@ -1,89 +0,0 @@ -Metadata-Version: 1.0 -Name: setuptools -Version: 0.7a1dev-r45536 -Summary: Download, build, install, upgrade, and uninstall Python packages -- easily! -Home-page: http://peak.telecommunity.com/DevCenter/setuptools -Author: Phillip J. Eby -Author-email: peak@eby-sarna.com -License: PSF or ZPL -Description: ``setuptools`` is a collection of enhancements to the Python ``distutils`` - (for Python 2.3.5 and up on most platforms; 64-bit platforms require a minimum - of Python 2.4) that allow you to more easily build and distribute Python - packages, especially ones that have dependencies on other packages. - - Packages built and distributed using ``setuptools`` look to the user like - ordinary Python packages based on the ``distutils``. Your users don't need to - install or even know about setuptools in order to use them, and you don't - have to include the entire setuptools package in your distributions. By - including just a single `bootstrap module`_ (an 8K .py file), your package will - automatically download and install ``setuptools`` if the user is building your - package from source and doesn't have a suitable version already installed. - - .. _bootstrap module: http://peak.telecommunity.com/dist/ez_setup.py - - Feature Highlights: - - * Automatically find/download/install/upgrade dependencies at build time using - the `EasyInstall tool `_, - which supports downloading via HTTP, FTP, Subversion, and SourceForge, and - automatically scans web pages linked from PyPI to find download links. (It's - the closest thing to CPAN currently available for Python.) - - * Create `Python Eggs `_ - - a single-file importable distribution format - - * Include data files inside your package directories, where your code can - actually use them. (Python 2.4 distutils also supports this feature, but - setuptools provides the feature for Python 2.3 packages also, and supports - accessing data files in zipped packages too.) - - * Automatically include all packages in your source tree, without listing them - individually in setup.py - - * Automatically include all relevant files in your source distributions, - without needing to create a ``MANIFEST.in`` file, and without having to force - regeneration of the ``MANIFEST`` file when your source tree changes. - - * Automatically generate wrapper scripts or Windows (console and GUI) .exe - files for any number of "main" functions in your project. (Note: this is not - a py2exe replacement; the .exe files rely on the local Python installation.) - - * Transparent Pyrex support, so that your setup.py can list ``.pyx`` files and - still work even when the end-user doesn't have Pyrex installed (as long as - you include the Pyrex-generated C in your source distribution) - - * Command aliases - create project-specific, per-user, or site-wide shortcut - names for commonly used commands and options - - * PyPI upload support - upload your source distributions and eggs to PyPI - - * Deploy your project in "development mode", such that it's available on - ``sys.path``, yet can still be edited directly from its source checkout. - - * Easily extend the distutils with new commands or ``setup()`` arguments, and - distribute/reuse your extensions for multiple projects, without copying code. - - * Create extensible applications and frameworks that automatically discover - extensions, using simple "entry points" declared in a project's setup script. - - In addition to the PyPI downloads, the development version of ``setuptools`` - is available from the `Python SVN sandbox`_, and in-development versions of the - `0.6 branch`_ are available as well. - - .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06 - - .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev - - -Keywords: CPAN PyPI distutils eggs package management -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Python Software Foundation License -Classifier: License :: OSI Approved :: Zope Public License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Archiving :: Packaging -Classifier: Topic :: System :: Systems Administration -Classifier: Topic :: Utilities diff --git a/Lib/setuptools.egg-info/entry_points.txt b/Lib/setuptools.egg-info/entry_points.txt deleted file mode 100755 index 0afe2cb..0000000 --- a/Lib/setuptools.egg-info/entry_points.txt +++ /dev/null @@ -1,51 +0,0 @@ -[distutils.setup_keywords] -dependency_links = setuptools.dist:assert_string_list -entry_points = setuptools.dist:check_entry_points -extras_require = setuptools.dist:check_extras -package_data = setuptools.dist:check_package_data -install_requires = setuptools.dist:check_requirements -include_package_data = setuptools.dist:assert_bool -exclude_package_data = setuptools.dist:check_package_data -namespace_packages = setuptools.dist:check_nsp -test_suite = setuptools.dist:check_test_suite -eager_resources = setuptools.dist:assert_string_list -zip_safe = setuptools.dist:assert_bool -test_loader = setuptools.dist:check_importable -tests_require = setuptools.dist:check_requirements - -[setuptools.file_finders] -svn_cvs = setuptools.command.sdist:_default_revctrl - -[egg_info.writers] -dependency_links.txt = setuptools.command.egg_info:overwrite_arg -requires.txt = setuptools.command.egg_info:write_requirements -PKG-INFO = setuptools.command.egg_info:write_pkg_info -eager_resources.txt = setuptools.command.egg_info:overwrite_arg -top_level.txt = setuptools.command.egg_info:write_toplevel_names -namespace_packages.txt = setuptools.command.egg_info:overwrite_arg -entry_points.txt = setuptools.command.egg_info:write_entries -depends.txt = setuptools.command.egg_info:warn_depends_obsolete - -[console_scripts] -easy_install = setuptools.command.easy_install:main -easy_install-2.5 = setuptools.command.easy_install:main - -[distutils.commands] -bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm -rotate = setuptools.command.rotate:rotate -develop = setuptools.command.develop:develop -setopt = setuptools.command.setopt:setopt -build_py = setuptools.command.build_py:build_py -saveopts = setuptools.command.saveopts:saveopts -egg_info = setuptools.command.egg_info:egg_info -install_egg_info = setuptools.command.install_egg_info:install_egg_info -alias = setuptools.command.alias:alias -easy_install = setuptools.command.easy_install:easy_install -install_scripts = setuptools.command.install_scripts:install_scripts -bdist_egg = setuptools.command.bdist_egg:bdist_egg -install = setuptools.command.install:install -test = setuptools.command.test:test -install_lib = setuptools.command.install_lib:install_lib -build_ext = setuptools.command.build_ext:build_ext -sdist = setuptools.command.sdist:sdist - diff --git a/Lib/setuptools.egg-info/top_level.txt b/Lib/setuptools.egg-info/top_level.txt deleted file mode 100644 index 4577c6a..0000000 --- a/Lib/setuptools.egg-info/top_level.txt +++ /dev/null @@ -1,3 +0,0 @@ -easy_install -pkg_resources -setuptools diff --git a/Lib/setuptools.egg-info/zip-safe b/Lib/setuptools.egg-info/zip-safe deleted file mode 100644 index e69de29..0000000 diff --git a/Lib/setuptools/__init__.py b/Lib/setuptools/__init__.py deleted file mode 100644 index 3921ce2..0000000 --- a/Lib/setuptools/__init__.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Extensions to the 'distutils' for large or complex distributions""" -from setuptools.extension import Extension, Library -from setuptools.dist import Distribution, Feature, _get_unpatched -import distutils.core, setuptools.command -from setuptools.depends import Require -from distutils.core import Command as _Command -from distutils.util import convert_path -import os.path - -__version__ = '0.7a1' -__all__ = [ - 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', - 'find_packages' -] - -bootstrap_install_from = None - -def find_packages(where='.', exclude=()): - """Return a list all Python packages found within directory 'where' - - 'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it - will be converted to the appropriate local path syntax. 'exclude' is a - sequence of package names to exclude; '*' can be used as a wildcard in the - names, such that 'foo.*' will exclude all subpackages of 'foo' (but not - 'foo' itself). - """ - out = [] - stack=[(convert_path(where), '')] - while stack: - where,prefix = stack.pop(0) - for name in os.listdir(where): - fn = os.path.join(where,name) - if (os.path.isdir(fn) and - os.path.isfile(os.path.join(fn,'__init__.py')) - ): - out.append(prefix+name); stack.append((fn,prefix+name+'.')) - for pat in exclude: - from fnmatch import fnmatchcase - out = [item for item in out if not fnmatchcase(item,pat)] - return out - -setup = distutils.core.setup - -_Command = _get_unpatched(_Command) - -class Command(_Command): - __doc__ = _Command.__doc__ - - command_consumes_arguments = False - - def __init__(self, dist, **kw): - # Add support for keyword arguments - _Command.__init__(self,dist) - for k,v in kw.items(): - setattr(self,k,v) - - def reinitialize_command(self, command, reinit_subcommands=0, **kw): - cmd = _Command.reinitialize_command(self, command, reinit_subcommands) - for k,v in kw.items(): - setattr(cmd,k,v) # update command with keywords - return cmd - -import distutils.core -distutils.core.Command = Command # we can't patch distutils.cmd, alas diff --git a/Lib/setuptools/archive_util.py b/Lib/setuptools/archive_util.py deleted file mode 100755 index dd9c684..0000000 --- a/Lib/setuptools/archive_util.py +++ /dev/null @@ -1,200 +0,0 @@ -"""Utilities for extracting common archive formats""" - - -__all__ = [ - "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", - "UnrecognizedFormat", "extraction_drivers", "unpack_directory", -] - -import zipfile, tarfile, os, shutil -from pkg_resources import ensure_directory -from distutils.errors import DistutilsError - -class UnrecognizedFormat(DistutilsError): - """Couldn't recognize the archive type""" - -def default_filter(src,dst): - """The default progress/filter callback; returns True for all files""" - return dst - - - - - - - - - - - - - - - - - - - - - - - -def unpack_archive(filename, extract_dir, progress_filter=default_filter, - drivers=None -): - """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` - - `progress_filter` is a function taking two arguments: a source path - internal to the archive ('/'-separated), and a filesystem path where it - will be extracted. The callback must return the desired extract path - (which may be the same as the one passed in), or else ``None`` to skip - that file or directory. The callback can thus be used to report on the - progress of the extraction, as well as to filter the items extracted or - alter their extraction paths. - - `drivers`, if supplied, must be a non-empty sequence of functions with the - same signature as this function (minus the `drivers` argument), that raise - ``UnrecognizedFormat`` if they do not support extracting the designated - archive type. The `drivers` are tried in sequence until one is found that - does not raise an error, or until all are exhausted (in which case - ``UnrecognizedFormat`` is raised). If you do not supply a sequence of - drivers, the module's ``extraction_drivers`` constant will be used, which - means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that - order. - """ - for driver in drivers or extraction_drivers: - try: - driver(filename, extract_dir, progress_filter) - except UnrecognizedFormat: - continue - else: - return - else: - raise UnrecognizedFormat( - "Not a recognized archive type: %s" % filename - ) - - - - - - - -def unpack_directory(filename, extract_dir, progress_filter=default_filter): - """"Unpack" a directory, using the same interface as for archives - - Raises ``UnrecognizedFormat`` if `filename` is not a directory - """ - if not os.path.isdir(filename): - raise UnrecognizedFormat("%s is not a directory" % (filename,)) - - paths = {filename:('',extract_dir)} - for base, dirs, files in os.walk(filename): - src,dst = paths[base] - for d in dirs: - paths[os.path.join(base,d)] = src+d+'/', os.path.join(dst,d) - for f in files: - name = src+f - target = os.path.join(dst,f) - target = progress_filter(src+f, target) - if not target: - continue # skip non-files - ensure_directory(target) - f = os.path.join(base,f) - shutil.copyfile(f, target) - shutil.copystat(f, target) - - - - - - - - - - - - - - - - - - -def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): - """Unpack zip `filename` to `extract_dir` - - Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined - by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation - of the `progress_filter` argument. - """ - - if not zipfile.is_zipfile(filename): - raise UnrecognizedFormat("%s is not a zip file" % (filename,)) - - z = zipfile.ZipFile(filename) - try: - for info in z.infolist(): - name = info.filename - - # don't extract absolute paths or ones with .. in them - if name.startswith('/') or '..' in name: - continue - - target = os.path.join(extract_dir, *name.split('/')) - target = progress_filter(name, target) - if not target: - continue - if name.endswith('/'): - # directory - ensure_directory(target) - else: - # file - ensure_directory(target) - data = z.read(info.filename) - f = open(target,'wb') - try: - f.write(data) - finally: - f.close() - del data - finally: - z.close() - - -def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): - """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` - - Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined - by ``tarfile.open()``). See ``unpack_archive()`` for an explanation - of the `progress_filter` argument. - """ - - try: - tarobj = tarfile.open(filename) - except tarfile.TarError: - raise UnrecognizedFormat( - "%s is not a compressed or uncompressed tar file" % (filename,) - ) - - try: - tarobj.chown = lambda *args: None # don't do any chowning! - for member in tarobj: - if member.isfile() or member.isdir(): - name = member.name - # don't extract absolute paths or ones with .. in them - if not name.startswith('/') and '..' not in name: - dst = os.path.join(extract_dir, *name.split('/')) - dst = progress_filter(name, dst) - if dst: - if dst.endswith(os.sep): - dst = dst[:-1] - tarobj._extract_member(member,dst) # XXX Ugh - return True - finally: - tarobj.close() - - - - -extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/Lib/setuptools/cli.exe b/Lib/setuptools/cli.exe deleted file mode 100755 index fc83339..0000000 Binary files a/Lib/setuptools/cli.exe and /dev/null differ diff --git a/Lib/setuptools/command/__init__.py b/Lib/setuptools/command/__init__.py deleted file mode 100644 index bff53e7..0000000 --- a/Lib/setuptools/command/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -__all__ = [ - 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', - 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', - 'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts', -] - -import sys -if sys.version>='2.5': - # In Python 2.5 and above, distutils includes its own upload command - __all__.remove('upload') - - -from distutils.command.bdist import bdist - -if 'egg' not in bdist.format_commands: - bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") - bdist.format_commands.append('egg') - -del bdist, sys diff --git a/Lib/setuptools/command/alias.py b/Lib/setuptools/command/alias.py deleted file mode 100755 index 1df474a..0000000 --- a/Lib/setuptools/command/alias.py +++ /dev/null @@ -1,79 +0,0 @@ -import distutils, os -from setuptools import Command -from distutils.util import convert_path -from distutils import log -from distutils.errors import * -from setuptools.command.setopt import edit_config, option_base, config_file - -def shquote(arg): - """Quote an argument for later parsing by shlex.split()""" - for c in '"', "'", "\\", "#": - if c in arg: return repr(arg) - if arg.split()<>[arg]: - return repr(arg) - return arg - - -class alias(option_base): - """Define a shortcut that invokes one or more commands""" - - description = "define a shortcut to invoke one or more commands" - command_consumes_arguments = True - - user_options = [ - ('remove', 'r', 'remove (unset) the alias'), - ] + option_base.user_options - - boolean_options = option_base.boolean_options + ['remove'] - - def initialize_options(self): - option_base.initialize_options(self) - self.args = None - self.remove = None - - def finalize_options(self): - option_base.finalize_options(self) - if self.remove and len(self.args)<>1: - raise DistutilsOptionError( - "Must specify exactly one argument (the alias name) when " - "using --remove" - ) - - def run(self): - aliases = self.distribution.get_option_dict('aliases') - - if not self.args: - print "Command Aliases" - print "---------------" - for alias in aliases: - print "setup.py alias", format_alias(alias, aliases) - return - - elif len(self.args)==1: - alias, = self.args - if self.remove: - command = None - elif alias in aliases: - print "setup.py alias", format_alias(alias, aliases) - return - else: - print "No alias definition found for %r" % alias - return - else: - alias = self.args[0] - command = ' '.join(map(shquote,self.args[1:])) - - edit_config(self.filename, {'aliases': {alias:command}}, self.dry_run) - - -def format_alias(name, aliases): - source, command = aliases[name] - if source == config_file('global'): - source = '--global-config ' - elif source == config_file('user'): - source = '--user-config ' - elif source == config_file('local'): - source = '' - else: - source = '--filename=%r' % source - return source+name+' '+command diff --git a/Lib/setuptools/command/bdist_egg.py b/Lib/setuptools/command/bdist_egg.py deleted file mode 100644 index 617d88d..0000000 --- a/Lib/setuptools/command/bdist_egg.py +++ /dev/null @@ -1,449 +0,0 @@ -"""setuptools.command.bdist_egg - -Build .egg distributions""" - -# This module should be kept compatible with Python 2.3 -import sys, os, marshal -from setuptools import Command -from distutils.dir_util import remove_tree, mkpath -from distutils.sysconfig import get_python_version, get_python_lib -from distutils import log -from pkg_resources import get_build_platform, Distribution -from types import CodeType -from setuptools.extension import Library - -def write_stub(resource, pyfile): - f = open(pyfile,'w') - f.write('\n'.join([ - "def __bootstrap__():", - " global __bootstrap__, __loader__, __file__", - " import sys, pkg_resources, imp", - " __file__ = pkg_resources.resource_filename(__name__,%r)" - % resource, - " del __bootstrap__, __loader__", - " imp.load_dynamic(__name__,__file__)", - "__bootstrap__()", - "" # terminal \n - ])) - f.close() - -# stub __init__.py for packages distributed without one -NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)' - - - - - - - - - - -class bdist_egg(Command): - - description = "create an \"egg\" distribution" - - user_options = [ - ('bdist-dir=', 'b', - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_build_platform()), - ('exclude-source-files', None, - "remove all .py files from the generated egg"), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ] - - boolean_options = [ - 'keep-temp', 'skip-build', 'exclude-source-files' - ] - - - - - - - - - - - - - - - - - - def initialize_options (self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.dist_dir = None - self.skip_build = 0 - self.egg_output = None - self.exclude_source_files = None - - - def finalize_options(self): - ei_cmd = self.get_finalized_command("egg_info") - self.egg_info = ei_cmd.egg_info - - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'egg') - - if self.plat_name is None: - self.plat_name = get_build_platform() - - self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) - - if self.egg_output is None: - - # Compute filename of the output egg - basename = Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version, - get_python_version(), - self.distribution.has_ext_modules() and self.plat_name - ).egg_name() - - self.egg_output = os.path.join(self.dist_dir, basename+'.egg') - - - - - - - - - def do_install_data(self): - # Hack for packages that install data to install's --install-lib - self.get_finalized_command('install').install_lib = self.bdist_dir - - site_packages = os.path.normcase(os.path.realpath(get_python_lib())) - old, self.distribution.data_files = self.distribution.data_files,[] - - for item in old: - if isinstance(item,tuple) and len(item)==2: - if os.path.isabs(item[0]): - realpath = os.path.realpath(item[0]) - normalized = os.path.normcase(realpath) - if normalized==site_packages or normalized.startswith( - site_packages+os.sep - ): - item = realpath[len(site_packages)+1:], item[1] - # XXX else: raise ??? - self.distribution.data_files.append(item) - - try: - log.info("installing package data to %s" % self.bdist_dir) - self.call_command('install_data', force=0, root=None) - finally: - self.distribution.data_files = old - - - def get_outputs(self): - return [self.egg_output] - - - def call_command(self,cmdname,**kw): - """Invoke reinitialized command `cmdname` with keyword args""" - for dirname in INSTALL_DIRECTORY_ATTRS: - kw.setdefault(dirname,self.bdist_dir) - kw.setdefault('skip_build',self.skip_build) - kw.setdefault('dry_run', self.dry_run) - cmd = self.reinitialize_command(cmdname, **kw) - self.run_command(cmdname) - return cmd - - - def run(self): - # Generate metadata first - self.run_command("egg_info") - - # We run install_lib before install_data, because some data hacks - # pull their data path from the install_lib command. - log.info("installing library code to %s" % self.bdist_dir) - instcmd = self.get_finalized_command('install') - old_root = instcmd.root; instcmd.root = None - cmd = self.call_command('install_lib', warn_dir=0) - instcmd.root = old_root - - all_outputs, ext_outputs = self.get_ext_outputs() - self.stubs = [] - to_compile = [] - for (p,ext_name) in enumerate(ext_outputs): - filename,ext = os.path.splitext(ext_name) - pyfile = os.path.join(self.bdist_dir, filename + '.py') - self.stubs.append(pyfile) - log.info("creating stub loader for %s" % ext_name) - if not self.dry_run: - write_stub(os.path.basename(ext_name), pyfile) - to_compile.append(pyfile) - ext_outputs[p] = ext_name.replace(os.sep,'/') - - to_compile.extend(self.make_init_files()) - if to_compile: - cmd.byte_compile(to_compile) - - if self.distribution.data_files: - self.do_install_data() - - # Make the EGG-INFO directory - archive_root = self.bdist_dir - egg_info = os.path.join(archive_root,'EGG-INFO') - self.mkpath(egg_info) - if self.distribution.scripts: - script_dir = os.path.join(egg_info, 'scripts') - log.info("installing scripts to %s" % script_dir) - self.call_command('install_scripts',install_dir=script_dir,no_ep=1) - - native_libs = os.path.join(self.egg_info,"native_libs.txt") - if all_outputs: - log.info("writing %s" % native_libs) - if not self.dry_run: - libs_file = open(native_libs, 'wt') - libs_file.write('\n'.join(all_outputs)) - libs_file.write('\n') - libs_file.close() - elif os.path.isfile(native_libs): - log.info("removing %s" % native_libs) - if not self.dry_run: - os.unlink(native_libs) - - for filename in os.listdir(self.egg_info): - path = os.path.join(self.egg_info,filename) - if os.path.isfile(path): - self.copy_file(path,os.path.join(egg_info,filename)) - - write_safety_flag( - os.path.join(archive_root,'EGG-INFO'), self.zip_safe() - ) - - if os.path.exists(os.path.join(self.egg_info,'depends.txt')): - log.warn( - "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." - ) - - if self.exclude_source_files: - self.zap_pyfiles() - - # Make the archive - make_zipfile(self.egg_output, archive_root, verbose=self.verbose, - dry_run=self.dry_run) - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - # Add to 'Distribution.dist_files' so that the "upload" command works - getattr(self.distribution,'dist_files',[]).append( - ('bdist_egg',get_python_version(),self.egg_output)) - - def zap_pyfiles(self): - log.info("Removing .py files from temporary directory") - for base,dirs,files in walk_egg(self.bdist_dir): - for name in files: - if name.endswith('.py'): - path = os.path.join(base,name) - log.debug("Deleting %s", path) - os.unlink(path) - - def zip_safe(self): - safe = getattr(self.distribution,'zip_safe',None) - if safe is not None: - return safe - log.warn("zip_safe flag not set; analyzing archive contents...") - return analyze_egg(self.bdist_dir, self.stubs) - - def make_init_files(self): - """Create missing package __init__ files""" - init_files = [] - for base,dirs,files in walk_egg(self.bdist_dir): - if base==self.bdist_dir: - # don't put an __init__ in the root - continue - for name in files: - if name.endswith('.py'): - if '__init__.py' not in files: - pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.') - if self.distribution.has_contents_for(pkg): - log.warn("Creating missing __init__.py for %s",pkg) - filename = os.path.join(base,'__init__.py') - if not self.dry_run: - f = open(filename,'w'); f.write(NS_PKG_STUB) - f.close() - init_files.append(filename) - break - else: - # not a package, don't traverse to subdirectories - dirs[:] = [] - - return init_files - - def get_ext_outputs(self): - """Get a list of relative paths to C extensions in the output distro""" - - all_outputs = [] - ext_outputs = [] - - paths = {self.bdist_dir:''} - for base, dirs, files in os.walk(self.bdist_dir): - for filename in files: - if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: - all_outputs.append(paths[base]+filename) - for filename in dirs: - paths[os.path.join(base,filename)] = paths[base]+filename+'/' - - if self.distribution.has_ext_modules(): - build_cmd = self.get_finalized_command('build_ext') - for ext in build_cmd.extensions: - if isinstance(ext,Library): - continue - fullname = build_cmd.get_ext_fullname(ext.name) - filename = build_cmd.get_ext_filename(fullname) - if not os.path.basename(filename).startswith('dl-'): - if os.path.exists(os.path.join(self.bdist_dir,filename)): - ext_outputs.append(filename) - - return all_outputs, ext_outputs - - -NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) - - - - - - - - - - - - -def walk_egg(egg_dir): - """Walk an unpacked egg's contents, skipping the metadata directory""" - walker = os.walk(egg_dir) - base,dirs,files = walker.next() - if 'EGG-INFO' in dirs: - dirs.remove('EGG-INFO') - yield base,dirs,files - for bdf in walker: - yield bdf - -def analyze_egg(egg_dir, stubs): - # check for existing flag in EGG-INFO - for flag,fn in safety_flags.items(): - if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)): - return flag - - safe = True - for base, dirs, files in walk_egg(egg_dir): - for name in files: - if name.endswith('.py') or name.endswith('.pyw'): - continue - elif name.endswith('.pyc') or name.endswith('.pyo'): - # always scan, even if we already know we're not safe - safe = scan_module(egg_dir, base, name, stubs) and safe - return safe - -def write_safety_flag(egg_dir, safe): - # Write or remove zip safety flag file(s) - for flag,fn in safety_flags.items(): - fn = os.path.join(egg_dir, fn) - if os.path.exists(fn): - if safe is None or bool(safe)<>flag: - os.unlink(fn) - elif safe is not None and bool(safe)==flag: - open(fn,'w').close() - -safety_flags = { - True: 'zip-safe', - False: 'not-zip-safe', -} - -def scan_module(egg_dir, base, name, stubs): - """Check whether module possibly uses unsafe-for-zipfile stuff""" - - filename = os.path.join(base,name) - if filename[:-1] in stubs: - return True # Extension module - pkg = base[len(egg_dir)+1:].replace(os.sep,'.') - module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0] - f = open(filename,'rb'); f.read(8) # skip magic & date - code = marshal.load(f); f.close() - safe = True - symbols = dict.fromkeys(iter_symbols(code)) - for bad in ['__file__', '__path__']: - if bad in symbols: - log.warn("%s: module references %s", module, bad) - safe = False - if 'inspect' in symbols: - for bad in [ - 'getsource', 'getabsfile', 'getsourcefile', 'getfile' - 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', - 'getinnerframes', 'getouterframes', 'stack', 'trace' - ]: - if bad in symbols: - log.warn("%s: module MAY be using inspect.%s", module, bad) - safe = False - if '__name__' in symbols and '__main__' in symbols and '.' not in module: - if get_python_version()>="2.4": - log.warn("%s: top-level module may be 'python -m' script", module) - safe = False - return safe - -def iter_symbols(code): - """Yield names and strings used by `code` and its nested code objects""" - for name in code.co_names: yield name - for const in code.co_consts: - if isinstance(const,basestring): - yield const - elif isinstance(const,CodeType): - for name in iter_symbols(const): - yield name - -# Attribute names of options for commands that might need to be convinced to -# install to the egg build directory - -INSTALL_DIRECTORY_ATTRS = [ - 'install_lib', 'install_dir', 'install_data', 'install_base' -] - -def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None): - """Create a zip file from all the files under 'base_dir'. The output - zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" - Python module (if available) or the InfoZIP "zip" utility (if installed - and found on the default search path). If neither tool is available, - raises DistutilsExecError. Returns the name of the output zip file. - """ - import zipfile - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) - log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - - def visit (z, dirname, names): - for name in names: - path = os.path.normpath(os.path.join(dirname, name)) - if os.path.isfile(path): - p = path[len(base_dir)+1:] - if not dry_run: - z.write(path, p) - log.debug("adding '%s'" % p) - - if compress is None: - compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits - - compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)] - if not dry_run: - z = zipfile.ZipFile(zip_filename, "w", compression=compression) - os.path.walk(base_dir, visit, z) - z.close() - else: - os.path.walk(base_dir, visit, None) - - return zip_filename diff --git a/Lib/setuptools/command/bdist_rpm.py b/Lib/setuptools/command/bdist_rpm.py deleted file mode 100755 index 00e07ac..0000000 --- a/Lib/setuptools/command/bdist_rpm.py +++ /dev/null @@ -1,37 +0,0 @@ -# This is just a kludge so that bdist_rpm doesn't guess wrong about the -# distribution name and version, if the egg_info command is going to alter -# them, and another kludge to allow you to build old-style non-egg RPMs - -from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm - -class bdist_rpm(_bdist_rpm): - - def initialize_options(self): - _bdist_rpm.initialize_options(self) - self.no_egg = None - - def run(self): - self.run_command('egg_info') # ensure distro name is up-to-date - _bdist_rpm.run(self) - - def _make_spec_file(self): - version = self.distribution.get_version() - rpmversion = version.replace('-','_') - spec = _bdist_rpm._make_spec_file(self) - line23 = '%define version '+version - line24 = '%define version '+rpmversion - spec = [ - line.replace( - "Source0: %{name}-%{version}.tar", - "Source0: %{name}-%{unmangled_version}.tar" - ).replace( - "setup.py install ", - "setup.py install --single-version-externally-managed " - ).replace( - "%setup", - "%setup -n %{name}-%{unmangled_version}" - ).replace(line23,line24) - for line in spec - ] - spec.insert(spec.index(line24)+1, "%define unmangled_version "+version) - return spec diff --git a/Lib/setuptools/command/build_ext.py b/Lib/setuptools/command/build_ext.py deleted file mode 100644 index f8551fb..0000000 --- a/Lib/setuptools/command/build_ext.py +++ /dev/null @@ -1,285 +0,0 @@ -from distutils.command.build_ext import build_ext as _du_build_ext -try: - # Attempt to use Pyrex for building extensions, if available - from Pyrex.Distutils.build_ext import build_ext as _build_ext -except ImportError: - _build_ext = _du_build_ext - -import os, sys -from distutils.file_util import copy_file -from setuptools.extension import Library -from distutils.ccompiler import new_compiler -from distutils.sysconfig import customize_compiler, get_config_var -get_config_var("LDSHARED") # make sure _config_vars is initialized -from distutils.sysconfig import _config_vars -from distutils import log -from distutils.errors import * - -have_rtld = False -use_stubs = False -libtype = 'shared' - -if sys.platform == "darwin": - use_stubs = True -elif os.name != 'nt': - try: - from dl import RTLD_NOW - have_rtld = True - use_stubs = True - except ImportError: - pass - -def if_dl(s): - if have_rtld: - return s - return '' - - - - - - -class build_ext(_build_ext): - def run(self): - """Build extensions in build directory, then copy if --inplace""" - old_inplace, self.inplace = self.inplace, 0 - _build_ext.run(self) - self.inplace = old_inplace - if old_inplace: - self.copy_extensions_to_source() - - def copy_extensions_to_source(self): - build_py = self.get_finalized_command('build_py') - for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - filename = self.get_ext_filename(fullname) - modpath = fullname.split('.') - package = '.'.join(modpath[:-1]) - package_dir = build_py.get_package_dir(package) - dest_filename = os.path.join(package_dir,os.path.basename(filename)) - src_filename = os.path.join(self.build_lib,filename) - - # Always copy, even if source is older than destination, to ensure - # that the right extensions for the current Python/platform are - # used. - copy_file( - src_filename, dest_filename, verbose=self.verbose, - dry_run=self.dry_run - ) - if ext._needs_stub: - self.write_stub(package_dir or os.curdir, ext, True) - - - if _build_ext is not _du_build_ext: - # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4 - def swig_sources(self, sources, *otherargs): - # first do any Pyrex processing - sources = _build_ext.swig_sources(self, sources) or sources - # Then do any actual SWIG stuff on the remainder - return _du_build_ext.swig_sources(self, sources, *otherargs) - - - - def get_ext_filename(self, fullname): - filename = _build_ext.get_ext_filename(self,fullname) - ext = self.ext_map[fullname] - if isinstance(ext,Library): - fn, ext = os.path.splitext(filename) - return self.shlib_compiler.library_filename(fn,libtype) - elif use_stubs and ext._links_to_dynamic: - d,fn = os.path.split(filename) - return os.path.join(d,'dl-'+fn) - else: - return filename - - def initialize_options(self): - _build_ext.initialize_options(self) - self.shlib_compiler = None - self.shlibs = [] - self.ext_map = {} - - def finalize_options(self): - _build_ext.finalize_options(self) - self.extensions = self.extensions or [] - self.check_extensions_list(self.extensions) - self.shlibs = [ext for ext in self.extensions - if isinstance(ext,Library)] - if self.shlibs: - self.setup_shlib_compiler() - for ext in self.extensions: - fullname = ext._full_name = self.get_ext_fullname(ext.name) - self.ext_map[fullname] = ext - ltd = ext._links_to_dynamic = \ - self.shlibs and self.links_to_dynamic(ext) or False - ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library) - filename = ext._file_name = self.get_ext_filename(fullname) - libdir = os.path.dirname(os.path.join(self.build_lib,filename)) - if ltd and libdir not in ext.library_dirs: - ext.library_dirs.append(libdir) - if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: - ext.runtime_library_dirs.append(os.curdir) - - - - def setup_shlib_compiler(self): - compiler = self.shlib_compiler = new_compiler( - compiler=self.compiler, dry_run=self.dry_run, force=self.force - ) - if sys.platform == "darwin": - tmp = _config_vars.copy() - try: - # XXX Help! I don't have any idea whether these are right... - _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" - _config_vars['CCSHARED'] = " -dynamiclib" - _config_vars['SO'] = ".dylib" - customize_compiler(compiler) - finally: - _config_vars.clear() - _config_vars.update(tmp) - else: - customize_compiler(compiler) - - if self.include_dirs is not None: - compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: - compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - compiler.undefine_macro(macro) - if self.libraries is not None: - compiler.set_libraries(self.libraries) - if self.library_dirs is not None: - compiler.set_library_dirs(self.library_dirs) - if self.rpath is not None: - compiler.set_runtime_library_dirs(self.rpath) - if self.link_objects is not None: - compiler.set_link_objects(self.link_objects) - - # hack so distutils' build_extension() builds a library instead - compiler.link_shared_object = link_shared_object.__get__(compiler) - - - - def get_export_symbols(self, ext): - if isinstance(ext,Library): - return ext.export_symbols - return _build_ext.get_export_symbols(self,ext) - - def build_extension(self, ext): - _compiler = self.compiler - try: - if isinstance(ext,Library): - self.compiler = self.shlib_compiler - _build_ext.build_extension(self,ext) - if ext._needs_stub: - self.write_stub( - self.get_finalized_command('build_py').build_lib, ext - ) - finally: - self.compiler = _compiler - - def links_to_dynamic(self, ext): - """Return true if 'ext' links to a dynamic lib in the same package""" - # XXX this should check to ensure the lib is actually being built - # XXX as dynamic, and not just using a locally-found version or a - # XXX static-compiled version - libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) - pkg = '.'.join(ext._full_name.split('.')[:-1]+['']) - for libname in ext.libraries: - if pkg+libname in libnames: return True - return False - - def get_outputs(self): - outputs = _build_ext.get_outputs(self) - optimize = self.get_finalized_command('build_py').optimize - for ext in self.extensions: - if ext._needs_stub: - base = os.path.join(self.build_lib, *ext._full_name.split('.')) - outputs.append(base+'.py') - outputs.append(base+'.pyc') - if optimize: - outputs.append(base+'.pyo') - return outputs - - def write_stub(self, output_dir, ext, compile=False): - log.info("writing stub loader for %s to %s",ext._full_name, output_dir) - stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py' - if compile and os.path.exists(stub_file): - raise DistutilsError(stub_file+" already exists! Please delete.") - if not self.dry_run: - f = open(stub_file,'w') - f.write('\n'.join([ - "def __bootstrap__():", - " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources, imp"+if_dl(", dl"), - " __file__ = pkg_resources.resource_filename(__name__,%r)" - % os.path.basename(ext._file_name), - " del __bootstrap__", - " if '__loader__' in globals():", - " del __loader__", - if_dl(" old_flags = sys.getdlopenflags()"), - " old_dir = os.getcwd()", - " try:", - " os.chdir(os.path.dirname(__file__))", - if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " imp.load_dynamic(__name__,__file__)", - " finally:", - if_dl(" sys.setdlopenflags(old_flags)"), - " os.chdir(old_dir)", - "__bootstrap__()", - "" # terminal \n - ])) - f.close() - if compile: - from distutils.util import byte_compile - byte_compile([stub_file], optimize=0, - force=True, dry_run=self.dry_run) - optimize = self.get_finalized_command('install_lib').optimize - if optimize > 0: - byte_compile([stub_file], optimize=optimize, - force=True, dry_run=self.dry_run) - if os.path.exists(stub_file) and not self.dry_run: - os.unlink(stub_file) - - -if use_stubs or os.name=='nt': - # Build shared libraries - # - def link_shared_object(self, 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 - ): self.link( - self.SHARED_LIBRARY, objects, output_libname, - output_dir, libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, extra_preargs, extra_postargs, - build_temp, target_lang - ) -else: - # Build static libraries everywhere else - libtype = 'static' - - def link_shared_object(self, 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 - ): - # XXX we need to either disallow these attrs on Library instances, - # or warn/abort here if set, or something... - #libraries=None, library_dirs=None, runtime_library_dirs=None, - #export_symbols=None, extra_preargs=None, extra_postargs=None, - #build_temp=None - - assert output_dir is None # distutils build_ext doesn't pass this - output_dir,filename = os.path.split(output_libname) - basename, ext = os.path.splitext(filename) - if self.library_filename("x").startswith('lib'): - # strip 'lib' prefix; this is kludgy if some platform uses - # a different prefix - basename = basename[3:] - - self.create_static_lib( - objects, basename, output_dir, debug, target_lang - ) diff --git a/Lib/setuptools/command/build_py.py b/Lib/setuptools/command/build_py.py deleted file mode 100644 index 77a9b23..0000000 --- a/Lib/setuptools/command/build_py.py +++ /dev/null @@ -1,192 +0,0 @@ -import os.path, sys, fnmatch -from distutils.command.build_py import build_py as _build_py -from distutils.util import convert_path -from glob import glob - -class build_py(_build_py): - """Enhanced 'build_py' command that includes data files with packages - - The data files are specified via a 'package_data' argument to 'setup()'. - See 'setuptools.dist.Distribution' for more details. - - Also, this version of the 'build_py' command allows you to specify both - 'py_modules' and 'packages' in the same setup operation. - """ - def finalize_options(self): - _build_py.finalize_options(self) - self.package_data = self.distribution.package_data - self.exclude_package_data = self.distribution.exclude_package_data or {} - if 'data_files' in self.__dict__: del self.__dict__['data_files'] - - def run(self): - """Build modules, packages, and copy data files to build directory""" - if not self.py_modules and not self.packages: - return - - if self.py_modules: - self.build_modules() - - if self.packages: - self.build_packages() - self.build_package_data() - - # Only compile actual .py files, using our base class' idea of what our - # output files are. - self.byte_compile(_build_py.get_outputs(self, include_bytecode=0)) - - def __getattr__(self,attr): - if attr=='data_files': # lazily compute data files - self.data_files = files = self._get_data_files(); return files - return _build_py.__getattr__(self,attr) - - def _get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" - self.analyze_manifest() - data = [] - for package in self.packages or (): - # Locate package source directory - src_dir = self.get_package_dir(package) - - # Compute package build directory - build_dir = os.path.join(*([self.build_lib] + package.split('.'))) - - # Length of path to strip from found files - plen = len(src_dir)+1 - - # Strip directory from globbed filenames - filenames = [ - file[plen:] for file in self.find_data_files(package, src_dir) - ] - data.append( (package, src_dir, build_dir, filenames) ) - return data - - def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" - globs = (self.package_data.get('', []) - + self.package_data.get(package, [])) - files = self.manifest_files.get(package, [])[:] - for pattern in globs: - # Each pattern has to be converted to a platform-specific path - files.extend(glob(os.path.join(src_dir, convert_path(pattern)))) - return self.exclude_data_files(package, src_dir, files) - - def build_package_data(self): - """Copy data files into build directory""" - lastdir = None - 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) - - - def analyze_manifest(self): - self.manifest_files = mf = {} - if not self.distribution.include_package_data: - return - src_dirs = {} - for package in self.packages or (): - # Locate package source directory - src_dirs[assert_relative(self.get_package_dir(package))] = package - - self.run_command('egg_info') - ei_cmd = self.get_finalized_command('egg_info') - for path in ei_cmd.filelist.files: - if path.endswith('.py'): - continue - d,f = os.path.split(assert_relative(path)) - prev = None - while d and d!=prev and d not in src_dirs: - prev = d - d, df = os.path.split(d) - f = os.path.join(df, f) - if d in src_dirs: - mf.setdefault(src_dirs[d],[]).append(path) - - - def get_data_files(self): pass # kludge 2.4 for lazy computation - - if sys.version<"2.4": # Python 2.4 already has this code - def get_outputs(self, include_bytecode=1): - """Return complete list of files copied to the build directory - - This includes both '.py' files and data files, as well as '.pyc' - and '.pyo' files if 'include_bytecode' is true. (This method is - needed for the 'install_lib' command to do its job properly, and to - generate a correct installation manifest.) - """ - return _build_py.get_outputs(self, include_bytecode) + [ - os.path.join(build_dir, filename) - for package, src_dir, build_dir,filenames in self.data_files - for filename in filenames - ] - - def check_package(self, package, package_dir): - """Check namespace packages' __init__ for declare_namespace""" - try: - return self.packages_checked[package] - except KeyError: - pass - - init_py = _build_py.check_package(self, package, package_dir) - self.packages_checked[package] = init_py - - if not init_py or not self.distribution.namespace_packages: - return init_py - - for pkg in self.distribution.namespace_packages: - if pkg==package or pkg.startswith(package+'.'): - break - else: - return init_py - - f = open(init_py,'rU') - if 'declare_namespace' not in f.read(): - from distutils.errors import DistutilsError - raise DistutilsError( - "Namespace package problem: %s is a namespace package, but its\n" - "__init__.py does not call declare_namespace()! Please fix it.\n" - '(See the setuptools manual under "Namespace Packages" for ' - "details.)\n" % (package,) - ) - f.close() - return init_py - - def initialize_options(self): - self.packages_checked={} - _build_py.initialize_options(self) - - - - - - - - def exclude_data_files(self, package, src_dir, files): - """Filter filenames for package's data files in 'src_dir'""" - globs = (self.exclude_package_data.get('', []) - + self.exclude_package_data.get(package, [])) - bad = [] - for pattern in globs: - bad.extend( - fnmatch.filter( - files, os.path.join(src_dir, convert_path(pattern)) - ) - ) - bad = dict.fromkeys(bad) - return [f for f in files if f not in bad] - - -def assert_relative(path): - if not os.path.isabs(path): - return path - from distutils.errors import DistutilsSetupError - raise DistutilsSetupError( -"""Error: setup script specifies an absolute path: - - %s - -setup() arguments must *always* be /-separated paths relative to the -setup.py directory, *never* absolute paths. -""" % path - ) diff --git a/Lib/setuptools/command/develop.py b/Lib/setuptools/command/develop.py deleted file mode 100755 index 7ab5b23..0000000 --- a/Lib/setuptools/command/develop.py +++ /dev/null @@ -1,116 +0,0 @@ -from setuptools.command.easy_install import easy_install -from distutils.util import convert_path -from pkg_resources import Distribution, PathMetadata, normalize_path -from distutils import log -from distutils.errors import * -import sys, os - -class develop(easy_install): - """Set up package for development""" - - description = "install package in 'development mode'" - - user_options = easy_install.user_options + [ - ("uninstall", "u", "Uninstall this source package"), - ] - - boolean_options = easy_install.boolean_options + ['uninstall'] - - command_consumes_arguments = False # override base - - def run(self): - if self.uninstall: - self.multi_version = True - self.uninstall_link() - else: - self.install_for_development() - self.warn_deprecated_options() - - def initialize_options(self): - self.uninstall = None - easy_install.initialize_options(self) - - - - - - - - - - - def finalize_options(self): - ei = self.get_finalized_command("egg_info") - if ei.broken_egg_info: - raise DistutilsError( - "Please rename %r to %r before using 'develop'" - % (ei.egg_info, ei.broken_egg_info) - ) - self.args = [ei.egg_name] - easy_install.finalize_options(self) - self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link') - self.egg_base = ei.egg_base - self.egg_path = os.path.abspath(ei.egg_base) - - # Make a distribution for the package's source - self.dist = Distribution( - normalize_path(self.egg_path), - PathMetadata(self.egg_path, os.path.abspath(ei.egg_info)), - project_name = ei.egg_name - ) - - def install_for_development(self): - # Ensure metadata is up-to-date - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') - - self.install_site_py() # ensure that target dir is site-safe - - # create an .egg-link in the installation dir, pointing to our egg - log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) - if not self.dry_run: - f = open(self.egg_link,"w") - f.write(self.egg_path) - f.close() - - # postprocess the installed distro, fixing up .pth, installing scripts, - # and handling requirements - self.process_distribution(None, self.dist) - - def uninstall_link(self): - if os.path.exists(self.egg_link): - log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) - contents = [line.rstrip() for line in file(self.egg_link)] - if contents != [self.egg_path]: - log.warn("Link points to %s: uninstall aborted", contents) - return - if not self.dry_run: - os.unlink(self.egg_link) - if not self.dry_run: - self.update_pth(self.dist) # remove any .pth link to us - if self.distribution.scripts: - # XXX should also check for entry point scripts! - log.warn("Note: you must uninstall or replace scripts manually!") - - - def install_egg_scripts(self, dist): - if dist is not self.dist: - # Installing a dependency, so fall back to normal behavior - return easy_install.install_egg_scripts(self,dist) - - # create wrapper scripts in the script dir, pointing to dist.scripts - - # new-style... - self.install_wrapper_scripts(dist) - - # ...and old-style - for script_name in self.distribution.scripts or []: - script_path = os.path.abspath(convert_path(script_name)) - script_name = os.path.basename(script_path) - f = open(script_path,'rU') - script_text = f.read() - f.close() - self.install_script(dist, script_name, script_text, script_path) diff --git a/Lib/setuptools/command/easy_install.py b/Lib/setuptools/command/easy_install.py deleted file mode 100755 index 3ddcec4..0000000 --- a/Lib/setuptools/command/easy_install.py +++ /dev/null @@ -1,1555 +0,0 @@ -#!python -"""\ -Easy Install ------------- - -A tool for doing automatic download/extract/build of distutils-based Python -packages. For detailed documentation, see the accompanying EasyInstall.txt -file, or visit the `EasyInstall home page`__. - -__ http://peak.telecommunity.com/DevCenter/EasyInstall -""" -import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random -from glob import glob -from setuptools import Command -from setuptools.sandbox import run_setup -from distutils import log, dir_util -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsArgError, DistutilsOptionError, \ - DistutilsError -from setuptools.archive_util import unpack_archive -from setuptools.package_index import PackageIndex, parse_bdist_wininst -from setuptools.package_index import URL_SCHEME -from setuptools.command import bdist_egg, egg_info -from pkg_resources import * -sys_executable = os.path.normpath(sys.executable) - -__all__ = [ - 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', - 'main', 'get_exe_prefixes', -] - -def samefile(p1,p2): - if hasattr(os.path,'samefile') and ( - os.path.exists(p1) and os.path.exists(p2) - ): - return os.path.samefile(p1,p2) - return ( - os.path.normpath(os.path.normcase(p1)) == - os.path.normpath(os.path.normcase(p2)) - ) - -class easy_install(Command): - """Manage a download/build/install process""" - description = "Find/get/install Python packages" - command_consumes_arguments = True - - user_options = [ - ('prefix=', None, "installation prefix"), - ("zip-ok", "z", "install package as a zipfile"), - ("multi-version", "m", "make apps have to require() a version"), - ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), - ("install-dir=", "d", "install package to DIR"), - ("script-dir=", "s", "install scripts to DIR"), - ("exclude-scripts", "x", "Don't install scripts"), - ("always-copy", "a", "Copy all needed packages to install dir"), - ("index-url=", "i", "base URL of Python Package Index"), - ("find-links=", "f", "additional URL(s) to search for packages"), - ("delete-conflicting", "D", "no longer needed; don't use this"), - ("ignore-conflicts-at-my-risk", None, - "no longer needed; don't use this"), - ("build-directory=", "b", - "download/extract/build in DIR; keep the results"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('record=', None, - "filename in which to record list of installed files"), - ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), - ('site-dirs=','S',"list of directories where .pth files work"), - ('editable', 'e', "Install specified packages in editable form"), - ('no-deps', 'N', "don't install dependencies"), - ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), - ] - boolean_options = [ - 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', - 'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable', - 'no-deps', - ] - negative_opt = {'always-unzip': 'zip-ok'} - create_index = PackageIndex - - - def initialize_options(self): - self.zip_ok = None - self.install_dir = self.script_dir = self.exclude_scripts = None - self.index_url = None - self.find_links = None - self.build_directory = None - self.args = None - self.optimize = self.record = None - self.upgrade = self.always_copy = self.multi_version = None - self.editable = self.no_deps = self.allow_hosts = None - self.root = self.prefix = self.no_report = None - - # Options not specifiable via command line - self.package_index = None - self.pth_file = None - self.delete_conflicting = None - self.ignore_conflicts_at_my_risk = None - self.site_dirs = None - self.installed_projects = {} - self.sitepy_installed = False - # Always read easy_install options, even if we are subclassed, or have - # an independent instance created. This ensures that defaults will - # always come from the standard configuration file(s)' "easy_install" - # section, even if this is a "develop" or "install" command, or some - # other embedding. - self._dry_run = None - self.verbose = self.distribution.verbose - self.distribution._set_command_options( - self, self.distribution.get_option_dict('easy_install') - ) - - def delete_blockers(self, blockers): - for filename in blockers: - if os.path.exists(filename) or os.path.islink(filename): - log.info("Deleting %s", filename) - if not self.dry_run: - if os.path.isdir(filename) and not os.path.islink(filename): - rmtree(filename) - else: - os.unlink(filename) - - def finalize_options(self): - self._expand('install_dir','script_dir','build_directory','site_dirs') - # If a non-default installation directory was specified, default the - # script directory to match it. - if self.script_dir is None: - self.script_dir = self.install_dir - - # Let install_dir get set by install_lib command, which in turn - # gets its info from the install command, and takes into account - # --prefix and --home and all that other crud. - self.set_undefined_options('install_lib', - ('install_dir','install_dir') - ) - # Likewise, set default script_dir from 'install_scripts.install_dir' - self.set_undefined_options('install_scripts', - ('install_dir', 'script_dir') - ) - # default --record from the install command - self.set_undefined_options('install', ('record', 'record')) - normpath = map(normalize_path, sys.path) - self.all_site_dirs = get_site_dirs() - if self.site_dirs is not None: - site_dirs = [ - os.path.expanduser(s.strip()) for s in self.site_dirs.split(',') - ] - for d in site_dirs: - if not os.path.isdir(d): - log.warn("%s (in --site-dirs) does not exist", d) - elif normalize_path(d) not in normpath: - raise DistutilsOptionError( - d+" (in --site-dirs) is not on sys.path" - ) - else: - self.all_site_dirs.append(normalize_path(d)) - self.check_site_dir() - self.index_url = self.index_url or "http://www.python.org/pypi" - self.shadow_path = self.all_site_dirs[:] - for path_item in self.install_dir, normalize_path(self.script_dir): - if path_item not in self.shadow_path: - self.shadow_path.insert(0, path_item) - - if self.allow_hosts is not None: - hosts = [s.strip() for s in self.allow_hosts.split(',')] - else: - hosts = ['*'] - - if self.package_index is None: - self.package_index = self.create_index( - self.index_url, search_path = self.shadow_path, hosts=hosts - ) - self.local_index = Environment(self.shadow_path) - - if self.find_links is not None: - if isinstance(self.find_links, basestring): - self.find_links = self.find_links.split() - else: - self.find_links = [] - - self.package_index.add_find_links(self.find_links) - self.set_undefined_options('install_lib', ('optimize','optimize')) - if not isinstance(self.optimize,int): - try: - self.optimize = int(self.optimize) - if not (0 <= self.optimize <= 2): raise ValueError - except ValueError: - raise DistutilsOptionError("--optimize must be 0, 1, or 2") - - if self.delete_conflicting and self.ignore_conflicts_at_my_risk: - raise DistutilsOptionError( - "Can't use both --delete-conflicting and " - "--ignore-conflicts-at-my-risk at the same time" - ) - if self.editable and not self.build_directory: - raise DistutilsArgError( - "Must specify a build directory (-b) when using --editable" - ) - if not self.args: - raise DistutilsArgError( - "No urls, filenames, or requirements specified (see --help)") - - self.outputs = [] - - def run(self): - if self.verbose<>self.distribution.verbose: - log.set_verbosity(self.verbose) - try: - for spec in self.args: - self.easy_install(spec, not self.no_deps) - if self.record: - outputs = self.outputs - if self.root: # strip any package prefix - root_len = len(self.root) - for counter in xrange(len(outputs)): - outputs[counter] = outputs[counter][root_len:] - from distutils import file_util - self.execute( - file_util.write_file, (self.record, outputs), - "writing list of installed files to '%s'" % - self.record - ) - self.warn_deprecated_options() - finally: - log.set_verbosity(self.distribution.verbose) - - def pseudo_tempname(self): - """Return a pseudo-tempname base in the install directory. - This code is intentionally naive; if a malicious party can write to - the target directory you're already in deep doodoo. - """ - try: - pid = os.getpid() - except: - pid = random.randint(0,sys.maxint) - return os.path.join(self.install_dir, "test-easy-install-%s" % pid) - - def warn_deprecated_options(self): - if self.delete_conflicting or self.ignore_conflicts_at_my_risk: - log.warn( - "Note: The -D, --delete-conflicting and" - " --ignore-conflicts-at-my-risk no longer have any purpose" - " and should not be used." - ) - - def check_site_dir(self): - """Verify that self.install_dir is .pth-capable dir, if needed""" - - instdir = normalize_path(self.install_dir) - pth_file = os.path.join(instdir,'easy-install.pth') - - # Is it a configured, PYTHONPATH, implicit, or explicit site dir? - is_site_dir = instdir in self.all_site_dirs - - if not is_site_dir: - # No? Then directly test whether it does .pth file processing - is_site_dir = self.check_pth_processing() - else: - # make sure we can write to target dir - testfile = self.pseudo_tempname()+'.write-test' - test_exists = os.path.exists(testfile) - try: - if test_exists: os.unlink(testfile) - open(testfile,'w').close() - os.unlink(testfile) - except (OSError,IOError): - self.cant_write_to_target() - - if not is_site_dir and not self.multi_version: - # Can't install non-multi to non-site dir - raise DistutilsError(self.no_default_version_msg()) - - if is_site_dir: - if self.pth_file is None: - self.pth_file = PthDistributions(pth_file) - else: - self.pth_file = None - - PYTHONPATH = os.environ.get('PYTHONPATH','').split(os.pathsep) - if instdir not in map(normalize_path, filter(None,PYTHONPATH)): - # only PYTHONPATH dirs need a site.py, so pretend it's there - self.sitepy_installed = True - - self.install_dir = instdir - - - def cant_write_to_target(self): - msg = """can't create or remove files in install directory - -The following error occurred while trying to add or remove files in the -installation directory: - - %s - -The installation directory you specified (via --install-dir, --prefix, or -the distutils default setting) was: - - %s -""" % (sys.exc_info()[1], self.install_dir,) - - if not os.path.exists(self.install_dir): - msg += """ -This directory does not currently exist. Please create it and try again, or -choose a different installation directory (using the -d or --install-dir -option). -""" - else: - msg += """ -Perhaps your account does not have write access to this directory? If the -installation directory is a system-owned directory, you may need to sign in -as the administrator or "root" account. If you do not have administrative -access to this machine, you may wish to choose a different installation -directory, preferably one that is listed in your PYTHONPATH environment -variable. - -For information on other options, you may wish to consult the -documentation at: - - http://peak.telecommunity.com/EasyInstall.html - -Please make the appropriate changes for your system and try again. -""" - raise DistutilsError(msg) - - - - - def check_pth_processing(self): - """Empirically verify whether .pth files are supported in inst. dir""" - instdir = self.install_dir - log.info("Checking .pth file support in %s", instdir) - pth_file = self.pseudo_tempname()+".pth" - ok_file = pth_file+'.ok' - ok_exists = os.path.exists(ok_file) - try: - if ok_exists: os.unlink(ok_file) - f = open(pth_file,'w') - except (OSError,IOError): - self.cant_write_to_target() - else: - try: - f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,)) - f.close(); f=None - executable = sys.executable - if os.name=='nt': - dirname,basename = os.path.split(executable) - alt = os.path.join(dirname,'pythonw.exe') - if basename.lower()=='python.exe' and os.path.exists(alt): - # use pythonw.exe to avoid opening a console window - executable = alt - if ' ' in executable: executable='"%s"' % executable - from distutils.spawn import spawn - spawn([executable,'-E','-c','pass'],0) - - if os.path.exists(ok_file): - log.info( - "TEST PASSED: %s appears to support .pth files", - instdir - ) - return True - finally: - if f: f.close() - if os.path.exists(ok_file): os.unlink(ok_file) - if os.path.exists(pth_file): os.unlink(pth_file) - if not self.multi_version: - log.warn("TEST FAILED: %s does NOT support .pth files", instdir) - return False - - def install_egg_scripts(self, dist): - """Write all the scripts for `dist`, unless scripts are excluded""" - - self.install_wrapper_scripts(dist) - if self.exclude_scripts or not dist.metadata_isdir('scripts'): - return - - for script_name in dist.metadata_listdir('scripts'): - self.install_script( - dist, script_name, - dist.get_metadata('scripts/'+script_name).replace('\r','\n') - ) - - def add_output(self, path): - if os.path.isdir(path): - for base, dirs, files in os.walk(path): - for filename in files: - self.outputs.append(os.path.join(base,filename)) - else: - self.outputs.append(path) - - def not_editable(self, spec): - if self.editable: - raise DistutilsArgError( - "Invalid argument %r: you can't use filenames or URLs " - "with --editable (except via the --find-links option)." - % (spec,) - ) - - def check_editable(self,spec): - if not self.editable: - return - - if os.path.exists(os.path.join(self.build_directory, spec.key)): - raise DistutilsArgError( - "%r already exists in %s; can't do a checkout there" % - (spec.key, self.build_directory) - ) - - - - def easy_install(self, spec, deps=False): - tmpdir = tempfile.mkdtemp(prefix="easy_install-") - download = None - self.install_site_py() - - try: - if not isinstance(spec,Requirement): - if URL_SCHEME(spec): - # It's a url, download it to tmpdir and process - self.not_editable(spec) - download = self.package_index.download(spec, tmpdir) - return self.install_item(None, download, tmpdir, deps, True) - - elif os.path.exists(spec): - # Existing file or directory, just process it directly - self.not_editable(spec) - return self.install_item(None, spec, tmpdir, deps, True) - else: - spec = parse_requirement_arg(spec) - - self.check_editable(spec) - dist = self.package_index.fetch_distribution( - spec, tmpdir, self.upgrade, self.editable, not self.always_copy - ) - - if dist is None: - msg = "Could not find suitable distribution for %r" % spec - if self.always_copy: - msg+=" (--always-copy skips system and development eggs)" - raise DistutilsError(msg) - elif dist.precedence==DEVELOP_DIST: - # .egg-info dists don't need installing, just process deps - self.process_distribution(spec, dist, deps, "Using") - return dist - else: - return self.install_item(spec, dist.location, tmpdir, deps) - - finally: - if os.path.exists(tmpdir): - rmtree(tmpdir) - - def install_item(self, spec, download, tmpdir, deps, install_needed=False): - - # Installation is also needed if file in tmpdir or is not an egg - install_needed = install_needed or os.path.dirname(download) == tmpdir - install_needed = install_needed or not download.endswith('.egg') - - log.info("Processing %s", os.path.basename(download)) - - if install_needed or self.always_copy: - dists = self.install_eggs(spec, download, tmpdir) - for dist in dists: - self.process_distribution(spec, dist, deps) - else: - dists = [self.check_conflicts(self.egg_distribution(download))] - self.process_distribution(spec, dists[0], deps, "Using") - - if spec is not None: - for dist in dists: - if dist in spec: - return dist - - - - - - - - - - - - - - - - - - - - - - def process_distribution(self, requirement, dist, deps=True, *info): - self.update_pth(dist) - self.package_index.add(dist) - self.local_index.add(dist) - self.install_egg_scripts(dist) - self.installed_projects[dist.key] = dist - log.warn(self.installation_report(requirement, dist, *info)) - if not deps and not self.always_copy: - return - elif requirement is not None and dist.key != requirement.key: - log.warn("Skipping dependencies for %s", dist) - return # XXX this is not the distribution we were looking for - elif requirement is None or dist not in requirement: - # if we wound up with a different version, resolve what we've got - distreq = dist.as_requirement() - requirement = requirement or distreq - requirement = Requirement( - distreq.project_name, distreq.specs, requirement.extras - ) - if dist.has_metadata('dependency_links.txt'): - self.package_index.add_find_links( - dist.get_metadata_lines('dependency_links.txt') - ) - log.info("Processing dependencies for %s", requirement) - try: - distros = WorkingSet([]).resolve( - [requirement], self.local_index, self.easy_install - ) - except DistributionNotFound, e: - raise DistutilsError( - "Could not find required distribution %s" % e.args - ) - except VersionConflict, e: - raise DistutilsError( - "Installed distribution %s conflicts with requirement %s" - % e.args - ) - if self.always_copy: - # Force all the relevant distros to be copied or activated - for dist in distros: - if dist.key not in self.installed_projects: - self.easy_install(dist.as_requirement()) - - def should_unzip(self, dist): - if self.zip_ok is not None: - return not self.zip_ok - if dist.has_metadata('not-zip-safe'): - return True - if not dist.has_metadata('zip-safe'): - return True - return False - - def maybe_move(self, spec, dist_filename, setup_base): - dst = os.path.join(self.build_directory, spec.key) - if os.path.exists(dst): - log.warn( - "%r already exists in %s; build directory %s will not be kept", - spec.key, self.build_directory, setup_base - ) - return setup_base - if os.path.isdir(dist_filename): - setup_base = dist_filename - else: - if os.path.dirname(dist_filename)==setup_base: - os.unlink(dist_filename) # get it out of the tmp dir - contents = os.listdir(setup_base) - if len(contents)==1: - dist_filename = os.path.join(setup_base,contents[0]) - if os.path.isdir(dist_filename): - # if the only thing there is a directory, move it instead - setup_base = dist_filename - ensure_directory(dst); shutil.move(setup_base, dst) - return dst - - def install_wrapper_scripts(self, dist): - if not self.exclude_scripts: - for args in get_script_args(dist): - self.write_script(*args) - - - - - - - def install_script(self, dist, script_name, script_text, dev_path=None): - """Generate a legacy script wrapper and install it""" - spec = str(dist.as_requirement()) - - if dev_path: - script_text = get_script_header(script_text) + ( - "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n" - "__requires__ = %(spec)r\n" - "from pkg_resources import require; require(%(spec)r)\n" - "del require\n" - "__file__ = %(dev_path)r\n" - "execfile(__file__)\n" - ) % locals() - else: - script_text = get_script_header(script_text) + ( - "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n" - "__requires__ = %(spec)r\n" - "import pkg_resources\n" - "pkg_resources.run_script(%(spec)r, %(script_name)r)\n" - ) % locals() - - self.write_script(script_name, script_text) - - def write_script(self, script_name, contents, mode="t", blockers=()): - """Write an executable file to the scripts directory""" - self.delete_blockers( # clean up old .py/.pyw w/o a script - [os.path.join(self.script_dir,x) for x in blockers]) - log.info("Installing %s script to %s", script_name, self.script_dir) - target = os.path.join(self.script_dir, script_name) - self.add_output(target) - - if not self.dry_run: - ensure_directory(target) - f = open(target,"w"+mode) - f.write(contents) - f.close() - try: - os.chmod(target,0755) - except (AttributeError, os.error): - pass - - def install_eggs(self, spec, dist_filename, tmpdir): - # .egg dirs or files are already built, so just return them - if dist_filename.lower().endswith('.egg'): - return [self.install_egg(dist_filename, tmpdir)] - elif dist_filename.lower().endswith('.exe'): - return [self.install_exe(dist_filename, tmpdir)] - - # Anything else, try to extract and build - setup_base = tmpdir - if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): - unpack_archive(dist_filename, tmpdir, self.unpack_progress) - elif os.path.isdir(dist_filename): - setup_base = os.path.abspath(dist_filename) - - if (setup_base.startswith(tmpdir) # something we downloaded - and self.build_directory and spec is not None - ): - setup_base = self.maybe_move(spec, dist_filename, setup_base) - - # Find the setup.py file - setup_script = os.path.join(setup_base, 'setup.py') - - if not os.path.exists(setup_script): - setups = glob(os.path.join(setup_base, '*', 'setup.py')) - if not setups: - raise DistutilsError( - "Couldn't find a setup script in %s" % dist_filename - ) - if len(setups)>1: - raise DistutilsError( - "Multiple setup scripts in %s" % dist_filename - ) - setup_script = setups[0] - - # Now run it, and return the result - if self.editable: - log.warn(self.report_editable(spec, setup_script)) - return [] - else: - return self.build_and_install(setup_script, setup_base) - - def egg_distribution(self, egg_path): - if os.path.isdir(egg_path): - metadata = PathMetadata(egg_path,os.path.join(egg_path,'EGG-INFO')) - else: - metadata = EggMetadata(zipimport.zipimporter(egg_path)) - return Distribution.from_filename(egg_path,metadata=metadata) - - def install_egg(self, egg_path, tmpdir): - destination = os.path.join(self.install_dir,os.path.basename(egg_path)) - destination = os.path.abspath(destination) - if not self.dry_run: - ensure_directory(destination) - - dist = self.egg_distribution(egg_path) - self.check_conflicts(dist) - if not samefile(egg_path, destination): - if os.path.isdir(destination) and not os.path.islink(destination): - dir_util.remove_tree(destination, dry_run=self.dry_run) - elif os.path.exists(destination): - self.execute(os.unlink,(destination,),"Removing "+destination) - uncache_zipdir(destination) - if os.path.isdir(egg_path): - if egg_path.startswith(tmpdir): - f,m = shutil.move, "Moving" - else: - f,m = shutil.copytree, "Copying" - elif self.should_unzip(dist): - self.mkpath(destination) - f,m = self.unpack_and_compile, "Extracting" - elif egg_path.startswith(tmpdir): - f,m = shutil.move, "Moving" - else: - f,m = shutil.copy2, "Copying" - - self.execute(f, (egg_path, destination), - (m+" %s to %s") % - (os.path.basename(egg_path),os.path.dirname(destination))) - - self.add_output(destination) - return self.egg_distribution(destination) - - def install_exe(self, dist_filename, tmpdir): - # See if it's valid, get data - cfg = extract_wininst_cfg(dist_filename) - if cfg is None: - raise DistutilsError( - "%s is not a valid distutils Windows .exe" % dist_filename - ) - # Create a dummy distribution object until we build the real distro - dist = Distribution(None, - project_name=cfg.get('metadata','name'), - version=cfg.get('metadata','version'), platform="win32" - ) - - # Convert the .exe to an unpacked egg - egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg') - egg_tmp = egg_path+'.tmp' - egg_info = os.path.join(egg_tmp, 'EGG-INFO') - pkg_inf = os.path.join(egg_info, 'PKG-INFO') - ensure_directory(pkg_inf) # make sure EGG-INFO dir exists - dist._provider = PathMetadata(egg_tmp, egg_info) # XXX - self.exe_to_egg(dist_filename, egg_tmp) - - # Write EGG-INFO/PKG-INFO - if not os.path.exists(pkg_inf): - f = open(pkg_inf,'w') - f.write('Metadata-Version: 1.0\n') - for k,v in cfg.items('metadata'): - if k<>'target_version': - f.write('%s: %s\n' % (k.replace('_','-').title(), v)) - f.close() - script_dir = os.path.join(egg_info,'scripts') - self.delete_blockers( # delete entry-point scripts to avoid duping - [os.path.join(script_dir,args[0]) for args in get_script_args(dist)] - ) - # Build .egg file from tmpdir - bdist_egg.make_zipfile( - egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run - ) - # install the .egg - return self.install_egg(egg_path, tmpdir) - - def exe_to_egg(self, dist_filename, egg_tmp): - """Extract a bdist_wininst to the directories an egg would use""" - # Check for .pth file and set up prefix translations - prefixes = get_exe_prefixes(dist_filename) - to_compile = [] - native_libs = [] - top_level = {} - - def process(src,dst): - for old,new in prefixes: - if src.startswith(old): - src = new+src[len(old):] - parts = src.split('/') - dst = os.path.join(egg_tmp, *parts) - dl = dst.lower() - if dl.endswith('.pyd') or dl.endswith('.dll'): - top_level[os.path.splitext(parts[0])[0]] = 1 - native_libs.append(src) - elif dl.endswith('.py') and old!='SCRIPTS/': - top_level[os.path.splitext(parts[0])[0]] = 1 - to_compile.append(dst) - return dst - if not src.endswith('.pth'): - log.warn("WARNING: can't process %s", src) - return None - - # extract, tracking .pyd/.dll->native_libs and .py -> to_compile - unpack_archive(dist_filename, egg_tmp, process) - stubs = [] - for res in native_libs: - if res.lower().endswith('.pyd'): # create stubs for .pyd's - parts = res.split('/') - resource, parts[-1] = parts[-1], parts[-1][:-1] - pyfile = os.path.join(egg_tmp, *parts) - to_compile.append(pyfile); stubs.append(pyfile) - bdist_egg.write_stub(resource, pyfile) - - self.byte_compile(to_compile) # compile .py's - bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'), - bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag - - for name in 'top_level','native_libs': - if locals()[name]: - txt = os.path.join(egg_tmp, 'EGG-INFO', name+'.txt') - if not os.path.exists(txt): - open(txt,'w').write('\n'.join(locals()[name])+'\n') - - def check_conflicts(self, dist): - """Verify that there are no conflicting "old-style" packages""" - - return dist # XXX temporarily disable until new strategy is stable - from imp import find_module, get_suffixes - from glob import glob - - blockers = [] - names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr - - exts = {'.pyc':1, '.pyo':1} # get_suffixes() might leave one out - for ext,mode,typ in get_suffixes(): - exts[ext] = 1 - - for path,files in expand_paths([self.install_dir]+self.all_site_dirs): - for filename in files: - base,ext = os.path.splitext(filename) - if base in names: - if not ext: - # no extension, check for package - try: - f, filename, descr = find_module(base, [path]) - except ImportError: - continue - else: - if f: f.close() - if filename not in blockers: - blockers.append(filename) - elif ext in exts and base!='site': # XXX ugh - blockers.append(os.path.join(path,filename)) - if blockers: - self.found_conflicts(dist, blockers) - - return dist - - def found_conflicts(self, dist, blockers): - if self.delete_conflicting: - log.warn("Attempting to delete conflicting packages:") - return self.delete_blockers(blockers) - - msg = """\ -------------------------------------------------------------------------- -CONFLICT WARNING: - -The following modules or packages have the same names as modules or -packages being installed, and will be *before* the installed packages in -Python's search path. You MUST remove all of the relevant files and -directories before you will be able to use the package(s) you are -installing: - - %s - -""" % '\n '.join(blockers) - - if self.ignore_conflicts_at_my_risk: - msg += """\ -(Note: you can run EasyInstall on '%s' with the ---delete-conflicting option to attempt deletion of the above files -and/or directories.) -""" % dist.project_name - else: - msg += """\ -Note: you can attempt this installation again with EasyInstall, and use -either the --delete-conflicting (-D) option or the ---ignore-conflicts-at-my-risk option, to either delete the above files -and directories, or to ignore the conflicts, respectively. Note that if -you ignore the conflicts, the installed package(s) may not work. -""" - msg += """\ -------------------------------------------------------------------------- -""" - sys.stderr.write(msg) - sys.stderr.flush() - if not self.ignore_conflicts_at_my_risk: - raise DistutilsError("Installation aborted due to conflicts") - - def installation_report(self, req, dist, what="Installed"): - """Helpful installation message for display to package users""" - msg = "\n%(what)s %(eggloc)s%(extras)s" - if self.multi_version and not self.no_report: - msg += """ - -Because this distribution was installed --multi-version or --install-dir, -before you can import modules from this package in an application, you -will need to 'import pkg_resources' and then use a 'require()' call -similar to one of these examples, in order to select the desired version: - - pkg_resources.require("%(name)s") # latest installed version - pkg_resources.require("%(name)s==%(version)s") # this exact version - pkg_resources.require("%(name)s>=%(version)s") # this version or higher -""" - if self.install_dir not in map(normalize_path,sys.path): - msg += """ - -Note also that the installation directory must be on sys.path at runtime for -this to work. (e.g. by being the application's script directory, by being on -PYTHONPATH, or by being added to sys.path by your code.) -""" - eggloc = dist.location - name = dist.project_name - version = dist.version - extras = '' # TODO: self.report_extras(req, dist) - return msg % locals() - - def report_editable(self, spec, setup_script): - dirname = os.path.dirname(setup_script) - python = sys.executable - return """\nExtracted editable version of %(spec)s to %(dirname)s - -If it uses setuptools in its setup script, you can activate it in -"development" mode by going to that directory and running:: - - %(python)s setup.py develop - -See the setuptools documentation for the "develop" command for more info. -""" % locals() - - def run_setup(self, setup_script, setup_base, args): - sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) - sys.modules.setdefault('distutils.command.egg_info', egg_info) - - args = list(args) - if self.verbose>2: - v = 'v' * (self.verbose - 1) - args.insert(0,'-'+v) - elif self.verbose<2: - args.insert(0,'-q') - if self.dry_run: - args.insert(0,'-n') - log.info( - "Running %s %s", setup_script[len(setup_base)+1:], ' '.join(args) - ) - try: - run_setup(setup_script, args) - except SystemExit, v: - raise DistutilsError("Setup script exited with %s" % (v.args[0],)) - - def build_and_install(self, setup_script, setup_base): - args = ['bdist_egg', '--dist-dir'] - dist_dir = tempfile.mkdtemp( - prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) - ) - try: - args.append(dist_dir) - self.run_setup(setup_script, setup_base, args) - all_eggs = Environment([dist_dir]) - eggs = [] - for key in all_eggs: - for dist in all_eggs[key]: - eggs.append(self.install_egg(dist.location, setup_base)) - if not eggs and not self.dry_run: - log.warn("No eggs found in %s (setup script problem?)", - dist_dir) - return eggs - finally: - rmtree(dist_dir) - log.set_verbosity(self.verbose) # restore our log verbosity - - def update_pth(self,dist): - if self.pth_file is None: - return - - for d in self.pth_file[dist.key]: # drop old entries - if self.multi_version or d.location != dist.location: - log.info("Removing %s from easy-install.pth file", d) - self.pth_file.remove(d) - if d.location in self.shadow_path: - self.shadow_path.remove(d.location) - - if not self.multi_version: - if dist.location in self.pth_file.paths: - log.info( - "%s is already the active version in easy-install.pth", - dist - ) - else: - log.info("Adding %s to easy-install.pth file", dist) - self.pth_file.add(dist) # add new entry - if dist.location not in self.shadow_path: - self.shadow_path.append(dist.location) - - if not self.dry_run: - - self.pth_file.save() - - if dist.key=='setuptools': - # Ensure that setuptools itself never becomes unavailable! - # XXX should this check for latest version? - filename = os.path.join(self.install_dir,'setuptools.pth') - if os.path.islink(filename): os.unlink(filename) - f = open(filename, 'wt') - f.write(self.pth_file.make_relative(dist.location)+'\n') - f.close() - - def unpack_progress(self, src, dst): - # Progress filter for unpacking - log.debug("Unpacking %s to %s", src, dst) - return dst # only unpack-and-compile skips files for dry run - - def unpack_and_compile(self, egg_path, destination): - to_compile = [] - - def pf(src,dst): - if dst.endswith('.py') and not src.startswith('EGG-INFO/'): - to_compile.append(dst) - self.unpack_progress(src,dst) - return not self.dry_run and dst or None - - unpack_archive(egg_path, destination, pf) - self.byte_compile(to_compile) - - - def byte_compile(self, to_compile): - from distutils.util import byte_compile - try: - # try to make the byte compile messages quieter - log.set_verbosity(self.verbose - 1) - - byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) - if self.optimize: - byte_compile( - to_compile, optimize=self.optimize, force=1, - dry_run=self.dry_run - ) - finally: - log.set_verbosity(self.verbose) # restore original verbosity - - - - - - - - - - - - - - - def no_default_version_msg(self): - return """bad install directory or PYTHONPATH - -You are attempting to install a package to a directory that is not -on PYTHONPATH and which Python does not read ".pth" files from. The -installation directory you specified (via --install-dir, --prefix, or -the distutils default setting) was: - - %s - -and your PYTHONPATH environment variable currently contains: - - %r - -Here are some of your options for correcting the problem: - -* You can choose a different installation directory, i.e., one that is - on PYTHONPATH or supports .pth files - -* You can add the installation directory to the PYTHONPATH environment - variable. (It must then also be on PYTHONPATH whenever you run - Python and want to use the package(s) you are installing.) - -* You can set up the installation directory to support ".pth" files by - using one of the approaches described here: - - http://peak.telecommunity.com/EasyInstall.html#custom-installation-locations - -Please make the appropriate changes for your system and try again.""" % ( - self.install_dir, os.environ.get('PYTHONPATH','') - ) - - - - - - - - - - - def install_site_py(self): - """Make sure there's a site.py in the target dir, if needed""" - - if self.sitepy_installed: - return # already did it, or don't need to - - sitepy = os.path.join(self.install_dir, "site.py") - source = resource_string("setuptools", "site-patch.py") - current = "" - - if os.path.exists(sitepy): - log.debug("Checking existing site.py in %s", self.install_dir) - current = open(sitepy,'rb').read() - if not current.startswith('def __boot():'): - raise DistutilsError( - "%s is not a setuptools-generated site.py; please" - " remove it." % sitepy - ) - - if current != source: - log.info("Creating %s", sitepy) - if not self.dry_run: - ensure_directory(sitepy) - f = open(sitepy,'wb') - f.write(source) - f.close() - self.byte_compile([sitepy]) - - self.sitepy_installed = True - - - - - - - - - - - - - INSTALL_SCHEMES = dict( - posix = dict( - install_dir = '$base/lib/python$py_version_short/site-packages', - script_dir = '$base/bin', - ), - ) - - DEFAULT_SCHEME = dict( - install_dir = '$base/Lib/site-packages', - script_dir = '$base/Scripts', - ) - - def _expand(self, *attrs): - config_vars = self.get_finalized_command('install').config_vars - - if self.prefix: - # Set default install_dir/scripts from --prefix - config_vars = config_vars.copy() - config_vars['base'] = self.prefix - scheme = self.INSTALL_SCHEMES.get(os.name,self.DEFAULT_SCHEME) - for attr,val in scheme.items(): - if getattr(self,attr,None) is None: - setattr(self,attr,val) - - from distutils.util import subst_vars - for attr in attrs: - val = getattr(self, attr) - if val is not None: - val = subst_vars(val, config_vars) - if os.name == 'posix': - val = os.path.expanduser(val) - setattr(self, attr, val) - - - - - - - - - -def get_site_dirs(): - # return a list of 'site' dirs - sitedirs = filter(None,os.environ.get('PYTHONPATH','').split(os.pathsep)) - prefixes = [sys.prefix] - if sys.exec_prefix != sys.prefix: - prefixes.append(sys.exec_prefix) - for prefix in prefixes: - if prefix: - if sys.platform in ('os2emx', 'riscos'): - sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) - elif os.sep == '/': - sitedirs.extend([os.path.join(prefix, - "lib", - "python" + sys.version[:3], - "site-packages"), - os.path.join(prefix, "lib", "site-python")]) - else: - sitedirs.extend( - [prefix, os.path.join(prefix, "lib", "site-packages")] - ) - if sys.platform == 'darwin': - # for framework builds *only* we add the standard Apple - # locations. Currently only per-user, but /Library and - # /Network/Library could be added too - if 'Python.framework' in prefix: - home = os.environ.get('HOME') - if home: - sitedirs.append( - os.path.join(home, - 'Library', - 'Python', - sys.version[:3], - 'site-packages')) - for plat_specific in (0,1): - site_lib = get_python_lib(plat_specific) - if site_lib not in sitedirs: sitedirs.append(site_lib) - - sitedirs = map(normalize_path, sitedirs) - return sitedirs - - -def expand_paths(inputs): - """Yield sys.path directories that might contain "old-style" packages""" - - seen = {} - - for dirname in inputs: - dirname = normalize_path(dirname) - if dirname in seen: - continue - - seen[dirname] = 1 - if not os.path.isdir(dirname): - continue - - files = os.listdir(dirname) - yield dirname, files - - for name in files: - if not name.endswith('.pth'): - # We only care about the .pth files - continue - if name in ('easy-install.pth','setuptools.pth'): - # Ignore .pth files that we control - continue - - # Read the .pth file - f = open(os.path.join(dirname,name)) - lines = list(yield_lines(f)) - f.close() - - # Yield existing non-dupe, non-import directory lines from it - for line in lines: - if not line.startswith("import"): - line = normalize_path(line.rstrip()) - if line not in seen: - seen[line] = 1 - if not os.path.isdir(line): - continue - yield line, os.listdir(line) - - -def extract_wininst_cfg(dist_filename): - """Extract configuration data from a bdist_wininst .exe - - Returns a ConfigParser.RawConfigParser, or None - """ - f = open(dist_filename,'rb') - try: - endrec = zipfile._EndRecData(f) - if endrec is None: - return None - - prepended = (endrec[9] - endrec[5]) - endrec[6] - if prepended < 12: # no wininst data here - return None - f.seek(prepended-12) - - import struct, StringIO, ConfigParser - tag, cfglen, bmlen = struct.unpack("egg path translations for a given .exe file""" - - prefixes = [ - ('PURELIB/', ''), - ('PLATLIB/', ''), - ('SCRIPTS/', 'EGG-INFO/scripts/') - ] - z = zipfile.ZipFile(exe_filename) - try: - for info in z.infolist(): - name = info.filename - parts = name.split('/') - if len(parts)==3 and parts[2]=='PKG-INFO': - if parts[1].endswith('.egg-info'): - prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/')) - break - if len(parts)<>2 or not name.endswith('.pth'): - continue - if name.endswith('-nspkg.pth'): - continue - if parts[0] in ('PURELIB','PLATLIB'): - for pth in yield_lines(z.read(name)): - pth = pth.strip().replace('\\','/') - if not pth.startswith('import'): - prefixes.append((('%s/%s/' % (parts[0],pth)), '')) - finally: - z.close() - - prefixes.sort(); prefixes.reverse() - return prefixes - - -def parse_requirement_arg(spec): - try: - return Requirement.parse(spec) - except ValueError: - raise DistutilsError( - "Not a URL, existing file, or requirement spec: %r" % (spec,) - ) - -class PthDistributions(Environment): - """A .pth file with Distribution paths in it""" - - dirty = False - - def __init__(self, filename): - self.filename = filename - self.basedir = normalize_path(os.path.dirname(self.filename)) - self._load(); Environment.__init__(self, [], None, None) - for path in yield_lines(self.paths): - map(self.add, find_distributions(path, True)) - - def _load(self): - self.paths = [] - saw_import = False - seen = {} - if os.path.isfile(self.filename): - for line in open(self.filename,'rt'): - if line.startswith('import'): - saw_import = True - continue - path = line.rstrip() - self.paths.append(path) - if not path.strip() or path.strip().startswith('#'): - continue - # skip non-existent paths, in case somebody deleted a package - # manually, and duplicate paths as well - path = self.paths[-1] = normalize_path( - os.path.join(self.basedir,path) - ) - if not os.path.exists(path) or path in seen: - self.paths.pop() # skip it - self.dirty = True # we cleaned up, so we're dirty now :) - continue - seen[path] = 1 - - if self.paths and not saw_import: - self.dirty = True # ensure anything we touch has import wrappers - while self.paths and not self.paths[-1].strip(): - self.paths.pop() - - def save(self): - """Write changed .pth file back to disk""" - if not self.dirty: - return - - data = '\n'.join(map(self.make_relative,self.paths)) - if data: - log.debug("Saving %s", self.filename) - data = ( - "import sys; sys.__plen = len(sys.path)\n" - "%s\n" - "import sys; new=sys.path[sys.__plen:];" - " del sys.path[sys.__plen:];" - " p=getattr(sys,'__egginsert',0); sys.path[p:p]=new;" - " sys.__egginsert = p+len(new)\n" - ) % data - - if os.path.islink(self.filename): - os.unlink(self.filename) - f = open(self.filename,'wb') - f.write(data); f.close() - - elif os.path.exists(self.filename): - log.debug("Deleting empty %s", self.filename) - os.unlink(self.filename) - - self.dirty = False - - def add(self,dist): - """Add `dist` to the distribution map""" - if dist.location not in self.paths: - self.paths.append(dist.location); self.dirty = True - Environment.add(self,dist) - - def remove(self,dist): - """Remove `dist` from the distribution map""" - while dist.location in self.paths: - self.paths.remove(dist.location); self.dirty = True - Environment.remove(self,dist) - - - def make_relative(self,path): - if normalize_path(os.path.dirname(path))==self.basedir: - return os.path.basename(path) - return path - - -def get_script_header(script_text, executable=sys_executable): - """Create a #! line, getting options (if any) from script_text""" - from distutils.command.build_scripts import first_line_re - first, rest = (script_text+'\n').split('\n',1) - match = first_line_re.match(first) - options = '' - if match: - script_text = rest - options = match.group(1) or '' - if options: - options = ' '+options - return "#!%(executable)s%(options)s\n" % locals() - - -def auto_chmod(func, arg, exc): - if func is os.remove and os.name=='nt': - os.chmod(arg, stat.S_IWRITE) - return func(arg) - exc = sys.exc_info() - raise exc[0], (exc[1][0], exc[1][1] + (" %s %s" % (func,arg))) - - -def uncache_zipdir(path): - """Ensure that the zip directory cache doesn't have stale info for path""" - from zipimport import _zip_directory_cache as zdc - if path in zdc: - del zdc[path] - else: - path = normalize_path(path) - for p in zdc: - if normalize_path(p)==path: - del zdc[p] - return - - -def get_script_args(dist, executable=sys_executable): - """Yield write_script() argument tuples for a distribution's entrypoints""" - spec = str(dist.as_requirement()) - header = get_script_header("", executable) - for group in 'console_scripts', 'gui_scripts': - for name,ep in dist.get_entry_map(group).items(): - script_text = ( - "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n" - "__requires__ = %(spec)r\n" - "import sys\n" - "from pkg_resources import load_entry_point\n" - "\n" - "sys.exit(\n" - " load_entry_point(%(spec)r, %(group)r, %(name)r)()\n" - ")\n" - ) % locals() - if sys.platform=='win32': - # On Windows, add a .py extension and an .exe launcher - if group=='gui_scripts': - ext, launcher = '-script.pyw', 'gui.exe' - old = ['.pyw'] - new_header = re.sub('(?i)python.exe','pythonw.exe',header) - else: - ext, launcher = '-script.py', 'cli.exe' - old = ['.py','.pyc','.pyo'] - new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header) - - if os.path.exists(new_header[2:-1]): - hdr = new_header - else: - hdr = header - yield (name+ext, hdr+script_text, 't', [name+x for x in old]) - yield ( - name+'.exe', resource_string('setuptools', launcher), - 'b' # write in binary mode - ) - else: - # On other platforms, we assume the right thing to do is to - # just write the stub with no extension. - yield (name, header+script_text) - -def rmtree(path, ignore_errors=False, onerror=auto_chmod): - """Recursively delete a directory tree. - - This code is taken from the Python 2.4 version of 'shutil', because - the 2.3 version doesn't really work right. - """ - if ignore_errors: - def onerror(*args): - pass - elif onerror is None: - def onerror(*args): - raise - names = [] - try: - names = os.listdir(path) - except os.error, err: - onerror(os.listdir, path, sys.exc_info()) - for name in names: - fullname = os.path.join(path, name) - try: - mode = os.lstat(fullname).st_mode - except os.error: - mode = 0 - if stat.S_ISDIR(mode): - rmtree(fullname, ignore_errors, onerror) - else: - try: - os.remove(fullname) - except os.error, err: - onerror(os.remove, fullname, sys.exc_info()) - try: - os.rmdir(path) - except os.error: - onerror(os.rmdir, path, sys.exc_info()) - - - - - - - -def main(argv=None, **kw): - from setuptools import setup - from setuptools.dist import Distribution - import distutils.core - - USAGE = """\ -usage: %(script)s [options] requirement_or_url ... - or: %(script)s --help -""" - - def gen_usage (script_name): - script = os.path.basename(script_name) - return USAGE % vars() - - def with_ei_usage(f): - old_gen_usage = distutils.core.gen_usage - try: - distutils.core.gen_usage = gen_usage - return f() - finally: - distutils.core.gen_usage = old_gen_usage - - class DistributionWithoutHelpCommands(Distribution): - def _show_help(self,*args,**kw): - with_ei_usage(lambda: Distribution._show_help(self,*args,**kw)) - - if argv is None: - argv = sys.argv[1:] - - with_ei_usage(lambda: - setup( - script_args = ['-q','easy_install', '-v']+argv, - script_name = sys.argv[0] or 'easy_install', - distclass=DistributionWithoutHelpCommands, **kw - ) - ) diff --git a/Lib/setuptools/command/egg_info.py b/Lib/setuptools/command/egg_info.py deleted file mode 100755 index b68fb39..0000000 --- a/Lib/setuptools/command/egg_info.py +++ /dev/null @@ -1,365 +0,0 @@ -"""setuptools.command.egg_info - -Create a distribution's .egg-info directory and contents""" - -# This module should be kept compatible with Python 2.3 -import os, re -from setuptools import Command -from distutils.errors import * -from distutils import log -from setuptools.command.sdist import sdist -from distutils import file_util -from distutils.util import convert_path -from distutils.filelist import FileList -from pkg_resources import parse_requirements, safe_name, parse_version, \ - safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename -from sdist import walk_revctrl - -class egg_info(Command): - description = "create a distribution's .egg-info directory" - - user_options = [ - ('egg-base=', 'e', "directory containing .egg-info directories" - " (default: top of the source tree)"), - ('tag-svn-revision', 'r', - "Add subversion revision ID to version number"), - ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), - ('tag-build=', 'b', "Specify explicit tag to add to version number"), - ] - - boolean_options = ['tag-date','tag-svn-revision'] - - def initialize_options (self): - self.egg_name = None - self.egg_version = None - self.egg_base = None - self.egg_info = None - self.tag_build = None - self.tag_svn_revision = 0 - self.tag_date = 0 - self.broken_egg_info = False - - def finalize_options (self): - self.egg_name = safe_name(self.distribution.get_name()) - self.egg_version = self.tagged_version() - - try: - list( - parse_requirements('%s==%s' % (self.egg_name,self.egg_version)) - ) - except ValueError: - raise DistutilsOptionError( - "Invalid distribution name or version syntax: %s-%s" % - (self.egg_name,self.egg_version) - ) - - if self.egg_base is None: - dirs = self.distribution.package_dir - self.egg_base = (dirs or {}).get('',os.curdir) - - self.ensure_dirname('egg_base') - self.egg_info = to_filename(self.egg_name)+'.egg-info' - if self.egg_base != os.curdir: - self.egg_info = os.path.join(self.egg_base, self.egg_info) - if '-' in self.egg_name: self.check_broken_egg_info() - - # Set package version for the benefit of dumber commands - # (e.g. sdist, bdist_wininst, etc.) - # - self.distribution.metadata.version = self.egg_version - - # If we bootstrapped around the lack of a PKG-INFO, as might be the - # case in a fresh checkout, make sure that any special tags get added - # to the version info - # - pd = self.distribution._patched_dist - if pd is not None and pd.key==self.egg_name.lower(): - pd._version = self.egg_version - pd._parsed_version = parse_version(self.egg_version) - self.distribution._patched_dist = None - - - - def write_or_delete_file(self, what, filename, data, force=False): - """Write `data` to `filename` or delete if empty - - If `data` is non-empty, this routine is the same as ``write_file()``. - If `data` is empty but not ``None``, this is the same as calling - ``delete_file(filename)`. If `data` is ``None``, then this is a no-op - unless `filename` exists, in which case a warning is issued about the - orphaned file (if `force` is false), or deleted (if `force` is true). - """ - if data: - self.write_file(what, filename, data) - elif os.path.exists(filename): - if data is None and not force: - log.warn( - "%s not set in setup(), but %s exists", what, filename - ) - return - else: - self.delete_file(filename) - - def write_file(self, what, filename, data): - """Write `data` to `filename` (if not a dry run) after announcing it - - `what` is used in a log message to identify what is being written - to the file. - """ - log.info("writing %s to %s", what, filename) - if not self.dry_run: - f = open(filename, 'wb') - f.write(data) - f.close() - - def delete_file(self, filename): - """Delete `filename` (if not a dry run) after announcing it""" - log.info("deleting %s", filename) - if not self.dry_run: - os.unlink(filename) - - - - - def run(self): - self.mkpath(self.egg_info) - installer = self.distribution.fetch_build_egg - for ep in iter_entry_points('egg_info.writers'): - writer = ep.load(installer=installer) - writer(self, ep.name, os.path.join(self.egg_info,ep.name)) - self.find_sources() - - def tagged_version(self): - version = self.distribution.get_version() - if self.tag_build: - version+=self.tag_build - if self.tag_svn_revision and ( - os.path.exists('.svn') or os.path.exists('PKG-INFO') - ): version += '-r%s' % self.get_svn_revision() - if self.tag_date: - import time; version += time.strftime("-%Y%m%d") - return safe_version(version) - - def get_svn_revision(self): - revision = 0 - urlre = re.compile('url="([^"]+)"') - revre = re.compile('committed-rev="(\d+)"') - for base,dirs,files in os.walk(os.curdir): - if '.svn' not in dirs: - dirs[:] = [] - continue # no sense walking uncontrolled subdirs - dirs.remove('.svn') - f = open(os.path.join(base,'.svn','entries')) - data = f.read() - f.close() - dirurl = urlre.search(data).group(1) # get repository URL - if base==os.curdir: - base_url = dirurl+'/' # save the root url - elif not dirurl.startswith(base_url): - dirs[:] = [] - continue # not part of the same svn tree, skip it - for match in revre.finditer(data): - revision = max(revision, int(match.group(1))) - return str(revision or get_pkg_info_revision()) - - def find_sources(self): - """Generate SOURCES.txt manifest file""" - manifest_filename = os.path.join(self.egg_info,"SOURCES.txt") - mm = manifest_maker(self.distribution) - mm.manifest = manifest_filename - mm.run() - self.filelist = mm.filelist - - def check_broken_egg_info(self): - bei = self.egg_name+'.egg-info' - if self.egg_base != os.curdir: - bei = os.path.join(self.egg_base, bei) - if os.path.exists(bei): - log.warn( - "-"*78+'\n' - "Note: Your current .egg-info directory has a '-' in its name;" - '\nthis will not work correctly with "setup.py develop".\n\n' - 'Please rename %s to %s to correct this problem.\n'+'-'*78, - bei, self.egg_info - ) - self.broken_egg_info = self.egg_info - self.egg_info = bei # make it work for now - -class FileList(FileList): - """File list that accepts only existing, platform-independent paths""" - - def append(self, item): - path = convert_path(item) - if os.path.exists(path): - self.files.append(path) - - - - - - - - - - - -class manifest_maker(sdist): - - template = "MANIFEST.in" - - def initialize_options (self): - self.use_defaults = 1 - self.prune = 1 - self.manifest_only = 1 - self.force_manifest = 1 - - def finalize_options(self): - pass - - def run(self): - self.filelist = FileList() - if not os.path.exists(self.manifest): - self.write_manifest() # it must exist so it'll get in the list - self.filelist.findall() - self.add_defaults() - if os.path.exists(self.template): - self.read_template() - self.prune_file_list() - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - def write_manifest (self): - """Write the file list in 'self.filelist' (presumably as filled in - by 'add_defaults()' and 'read_template()') to the manifest file - named by 'self.manifest'. - """ - files = self.filelist.files - if os.sep!='/': - files = [f.replace(os.sep,'/') for f in files] - self.execute(file_util.write_file, (self.manifest, files), - "writing manifest file '%s'" % self.manifest) - - - - - - def add_defaults(self): - sdist.add_defaults(self) - self.filelist.append(self.template) - self.filelist.append(self.manifest) - rcfiles = list(walk_revctrl()) - if rcfiles: - self.filelist.extend(rcfiles) - elif os.path.exists(self.manifest): - self.read_manifest() - ei_cmd = self.get_finalized_command('egg_info') - self.filelist.include_pattern("*", prefix=ei_cmd.egg_info) - - def prune_file_list (self): - build = self.get_finalized_command('build') - base_dir = self.distribution.get_fullname() - self.filelist.exclude_pattern(None, prefix=build.build_base) - self.filelist.exclude_pattern(None, prefix=base_dir) - sep = re.escape(os.sep) - self.filelist.exclude_pattern(sep+r'(RCS|CVS|\.svn)'+sep, is_regex=1) - - - - - - - - - - - - - - - - - - - - - - -def write_pkg_info(cmd, basename, filename): - log.info("writing %s", filename) - if not cmd.dry_run: - metadata = cmd.distribution.metadata - metadata.version, oldver = cmd.egg_version, metadata.version - metadata.name, oldname = cmd.egg_name, metadata.name - try: - # write unescaped data to PKG-INFO, so older pkg_resources - # can still parse it - metadata.write_pkg_info(cmd.egg_info) - finally: - metadata.name, metadata.version = oldname, oldver - - safe = getattr(cmd.distribution,'zip_safe',None) - import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe) - -def warn_depends_obsolete(cmd, basename, filename): - if os.path.exists(filename): - log.warn( - "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." - ) - - -def write_requirements(cmd, basename, filename): - dist = cmd.distribution - data = ['\n'.join(yield_lines(dist.install_requires or ()))] - for extra,reqs in (dist.extras_require or {}).items(): - data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs)))) - cmd.write_or_delete_file("requirements", filename, ''.join(data)) - -def write_toplevel_names(cmd, basename, filename): - pkgs = dict.fromkeys( - [k.split('.',1)[0] - for k in cmd.distribution.iter_distribution_names() - ] - ) - cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n') - - - -def overwrite_arg(cmd, basename, filename): - write_arg(cmd, basename, filename, True) - -def write_arg(cmd, basename, filename, force=False): - argname = os.path.splitext(basename)[0] - value = getattr(cmd.distribution, argname, None) - if value is not None: - value = '\n'.join(value)+'\n' - cmd.write_or_delete_file(argname, filename, value, force) - -def write_entries(cmd, basename, filename): - ep = cmd.distribution.entry_points - - if isinstance(ep,basestring) or ep is None: - data = ep - elif ep is not None: - data = [] - for section, contents in ep.items(): - if not isinstance(contents,basestring): - contents = EntryPoint.parse_group(section, contents) - contents = '\n'.join(map(str,contents.values())) - data.append('[%s]\n%s\n\n' % (section,contents)) - data = ''.join(data) - - cmd.write_or_delete_file('entry points', filename, data, True) - -def get_pkg_info_revision(): - # See if we can get a -r### off of PKG-INFO, in case this is an sdist of - # a subversion revision - # - if os.path.exists('PKG-INFO'): - f = open('PKG-INFO','rU') - for line in f: - match = re.match(r"Version:.*-r(\d+)\s*$", line) - if match: - return int(match.group(1)) - return 0 diff --git a/Lib/setuptools/command/install.py b/Lib/setuptools/command/install.py deleted file mode 100644 index bfb9af5..0000000 --- a/Lib/setuptools/command/install.py +++ /dev/null @@ -1,101 +0,0 @@ -import setuptools, sys -from distutils.command.install import install as _install -from distutils.errors import DistutilsArgError - -class install(_install): - """Use easy_install to install the package, w/dependencies""" - - user_options = _install.user_options + [ - ('old-and-unmanageable', None, "Try not to use this!"), - ('single-version-externally-managed', None, - "used by system package builders to create 'flat' eggs"), - ] - boolean_options = _install.boolean_options + [ - 'old-and-unmanageable', 'single-version-externally-managed', - ] - new_commands = [ - ('install_egg_info', lambda self: True), - ('install_scripts', lambda self: True), - ] - _nc = dict(new_commands) - sub_commands = [ - cmd for cmd in _install.sub_commands if cmd[0] not in _nc - ] + new_commands - - def initialize_options(self): - _install.initialize_options(self) - self.old_and_unmanageable = None - self.single_version_externally_managed = None - self.no_compile = None # make DISTUTILS_DEBUG work right! - - def finalize_options(self): - _install.finalize_options(self) - if self.root: - self.single_version_externally_managed = True - elif self.single_version_externally_managed: - if not self.root and not self.record: - raise DistutilsArgError( - "You must specify --record or --root when building system" - " packages" - ) - - def handle_extra_path(self): - # We always ignore extra_path, because we install as .egg or .egg-info - self.path_file = None - self.extra_dirs = '' - - def run(self): - # Explicit request for old-style install? Just do it - if self.old_and_unmanageable or self.single_version_externally_managed: - return _install.run(self) - - # Attempt to detect whether we were called from setup() or by another - # command. If we were called by setup(), our caller will be the - # 'run_command' method in 'distutils.dist', and *its* caller will be - # the 'run_commands' method. If we were called any other way, our - # immediate caller *might* be 'run_command', but it won't have been - # called by 'run_commands'. This is slightly kludgy, but seems to - # work. - # - caller = sys._getframe(2) - caller_module = caller.f_globals.get('__name__','') - caller_name = caller.f_code.co_name - - if caller_module != 'distutils.dist' or caller_name!='run_commands': - # We weren't called from the command line or setup(), so we - # should run in backward-compatibility mode to support bdist_* - # commands. - _install.run(self) - else: - self.do_egg_install() - - - - - - - - - - - - - def do_egg_install(self): - - from setuptools.command.easy_install import easy_install - - cmd = easy_install( - self.distribution, args="x", root=self.root, record=self.record, - ) - cmd.ensure_finalized() # finalize before bdist_egg munges install cmd - - self.run_command('bdist_egg') - args = [self.distribution.get_command_obj('bdist_egg').egg_output] - - if setuptools.bootstrap_install_from: - # Bootstrap self-installation of setuptools - args.insert(0, setuptools.bootstrap_install_from) - - cmd.args = args - cmd.run() - setuptools.bootstrap_install_from = None diff --git a/Lib/setuptools/command/install_egg_info.py b/Lib/setuptools/command/install_egg_info.py deleted file mode 100755 index 193e91a..0000000 --- a/Lib/setuptools/command/install_egg_info.py +++ /dev/null @@ -1,81 +0,0 @@ -from setuptools import Command -from setuptools.archive_util import unpack_archive -from distutils import log, dir_util -import os, shutil, pkg_resources - -class install_egg_info(Command): - """Install an .egg-info directory for the package""" - - description = "Install an .egg-info directory for the package" - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - self.set_undefined_options('install_lib',('install_dir','install_dir')) - ei_cmd = self.get_finalized_command("egg_info") - basename = pkg_resources.Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version - ).egg_name()+'.egg-info' - self.source = ei_cmd.egg_info - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - self.run_command('egg_info') - target = self.target - if os.path.isdir(self.target) and not os.path.islink(self.target): - dir_util.remove_tree(self.target, dry_run=self.dry_run) - elif os.path.exists(self.target): - self.execute(os.unlink,(self.target,),"Removing "+self.target) - if not self.dry_run: - pkg_resources.ensure_directory(self.target) - self.execute(self.copytree, (), - "Copying %s to %s" % (self.source, self.target) - ) - self.install_namespaces() - - def get_outputs(self): - return self.outputs - - def copytree(self): - # Copy the .egg-info tree to site-packages - def skimmer(src,dst): - # filter out source-control directories; note that 'src' is always - # a '/'-separated path, regardless of platform. 'dst' is a - # platform-specific path. - for skip in '.svn/','CVS/': - if src.startswith(skip) or '/'+skip in src: - return None - self.outputs.append(dst) - log.debug("Copying %s to %s", src, dst) - return dst - unpack_archive(self.source, self.target, skimmer) - - def install_namespaces(self): - nsp = (self.distribution.namespace_packages or [])[:] - if not nsp: return - nsp.sort() # set up shorter names first - filename,ext = os.path.splitext(self.target) - filename += '-nspkg.pth'; self.outputs.append(filename) - log.info("Installing %s",filename) - if not self.dry_run: - f = open(filename,'wb') - for pkg in nsp: - pth = tuple(pkg.split('.')) - f.write( - "import sys,new,os; " - "p = os.path.join(sys._getframe(1).f_locals['sitedir'], " - "*%(pth)r); " - "ie = os.path.exists(os.path.join(p,'__init__.py')); " - "m = not ie and " - "sys.modules.setdefault(%(pkg)r,new.module(%(pkg)r)); " - "mp = (m or []) and m.__dict__.setdefault('__path__',[]); " - "(p not in mp) and mp.append(p)\n" - % locals() - ) - f.close() diff --git a/Lib/setuptools/command/install_lib.py b/Lib/setuptools/command/install_lib.py deleted file mode 100644 index 96c8dfe..0000000 --- a/Lib/setuptools/command/install_lib.py +++ /dev/null @@ -1,76 +0,0 @@ -from distutils.command.install_lib import install_lib as _install_lib -import os - -class install_lib(_install_lib): - """Don't add compiled flags to filenames of non-Python files""" - - def _bytecode_filenames (self, py_filenames): - bytecode_files = [] - for py_file in py_filenames: - if not py_file.endswith('.py'): - continue - if self.compile: - bytecode_files.append(py_file + "c") - if self.optimize > 0: - bytecode_files.append(py_file + "o") - - return bytecode_files - - def run(self): - self.build() - outfiles = self.install() - if outfiles is not None: - # always compile, in case we have any extension stubs to deal with - self.byte_compile(outfiles) - - def get_exclusions(self): - exclude = {} - nsp = self.distribution.namespace_packages - - if (nsp and self.get_finalized_command('install') - .single_version_externally_managed - ): - for pkg in nsp: - parts = pkg.split('.') - while parts: - pkgdir = os.path.join(self.install_dir, *parts) - for f in '__init__.py', '__init__.pyc', '__init__.pyo': - exclude[os.path.join(pkgdir,f)] = 1 - parts.pop() - return exclude - - def copy_tree( - self, infile, outfile, - preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 - ): - assert preserve_mode and preserve_times and not preserve_symlinks - exclude = self.get_exclusions() - - if not exclude: - return _install_lib.copy_tree(self, infile, outfile) - - # Exclude namespace package __init__.py* files from the output - - from setuptools.archive_util import unpack_directory - from distutils import log - - outfiles = [] - - def pf(src, dst): - if dst in exclude: - log.warn("Skipping installation of %s (namespace package)",dst) - return False - - log.info("copying %s -> %s", src, os.path.dirname(dst)) - outfiles.append(dst) - return dst - - unpack_directory(infile, outfile, pf) - return outfiles - - def get_outputs(self): - outputs = _install_lib.get_outputs(self) - exclude = self.get_exclusions() - if exclude: - return [f for f in outputs if f not in exclude] - return outputs diff --git a/Lib/setuptools/command/install_scripts.py b/Lib/setuptools/command/install_scripts.py deleted file mode 100755 index 69558bf..0000000 --- a/Lib/setuptools/command/install_scripts.py +++ /dev/null @@ -1,56 +0,0 @@ -from distutils.command.install_scripts import install_scripts \ - as _install_scripts -from easy_install import get_script_args, sys_executable -from pkg_resources import Distribution, PathMetadata, ensure_directory -import os -from distutils import log - -class install_scripts(_install_scripts): - """Do normal script install, plus any egg_info wrapper scripts""" - - def initialize_options(self): - _install_scripts.initialize_options(self) - self.no_ep = False - - def run(self): - self.run_command("egg_info") - if self.distribution.scripts: - _install_scripts.run(self) # run first to set up self.outfiles - else: - self.outfiles = [] - if self.no_ep: - # don't install entry point scripts into .egg file! - return - - ei_cmd = self.get_finalized_command("egg_info") - dist = Distribution( - ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info), - ei_cmd.egg_name, ei_cmd.egg_version, - ) - bs_cmd = self.get_finalized_command('build_scripts') - executable = getattr(bs_cmd,'executable',sys_executable) - - for args in get_script_args(dist, executable): - self.write_script(*args) - - - - - - - - def write_script(self, script_name, contents, mode="t", *ignored): - """Write an executable file to the scripts directory""" - log.info("Installing %s script to %s", script_name, self.install_dir) - target = os.path.join(self.install_dir, script_name) - self.outfiles.append(target) - - if not self.dry_run: - ensure_directory(target) - f = open(target,"w"+mode) - f.write(contents) - f.close() - try: - os.chmod(target,0755) - except (AttributeError, os.error): - pass diff --git a/Lib/setuptools/command/rotate.py b/Lib/setuptools/command/rotate.py deleted file mode 100755 index 8aab312..0000000 --- a/Lib/setuptools/command/rotate.py +++ /dev/null @@ -1,57 +0,0 @@ -import distutils, os -from setuptools import Command -from distutils.util import convert_path -from distutils import log -from distutils.errors import * - -class rotate(Command): - """Delete older distributions""" - - description = "delete older distributions, keeping N newest files" - user_options = [ - ('match=', 'm', "patterns to match (required)"), - ('dist-dir=', 'd', "directory where the distributions are"), - ('keep=', 'k', "number of matching distributions to keep"), - ] - - boolean_options = [] - - def initialize_options(self): - self.match = None - self.dist_dir = None - self.keep = None - - def finalize_options(self): - if self.match is None: - raise DistutilsOptionError( - "Must specify one or more (comma-separated) match patterns " - "(e.g. '.zip' or '.egg')" - ) - if self.keep is None: - raise DistutilsOptionError("Must specify number of files to keep") - try: - self.keep = int(self.keep) - except ValueError: - raise DistutilsOptionError("--keep must be an integer") - if isinstance(self.match, basestring): - self.match = [ - convert_path(p.strip()) for p in self.match.split(',') - ] - self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) - - def run(self): - self.run_command("egg_info") - from glob import glob - for pattern in self.match: - pattern = self.distribution.get_name()+'*'+pattern - files = glob(os.path.join(self.dist_dir,pattern)) - files = [(os.path.getmtime(f),f) for f in files] - files.sort() - files.reverse() - - log.info("%d file(s) matching %s", len(files), pattern) - files = files[self.keep:] - for (t,f) in files: - log.info("Deleting %s", f) - if not self.dry_run: - os.unlink(f) diff --git a/Lib/setuptools/command/saveopts.py b/Lib/setuptools/command/saveopts.py deleted file mode 100755 index 9c58d72..0000000 --- a/Lib/setuptools/command/saveopts.py +++ /dev/null @@ -1,24 +0,0 @@ -import distutils, os -from setuptools import Command -from setuptools.command.setopt import edit_config, option_base - -class saveopts(option_base): - """Save command-line options to a file""" - - description = "save supplied options to setup.cfg or other config file" - - def run(self): - dist = self.distribution - commands = dist.command_options.keys() - settings = {} - - for cmd in commands: - - if cmd=='saveopts': - continue # don't save our own options! - - for opt,(src,val) in dist.get_option_dict(cmd).items(): - if src=="command line": - settings.setdefault(cmd,{})[opt] = val - - edit_config(self.filename, settings, self.dry_run) diff --git a/Lib/setuptools/command/sdist.py b/Lib/setuptools/command/sdist.py deleted file mode 100755 index 829cd3c..0000000 --- a/Lib/setuptools/command/sdist.py +++ /dev/null @@ -1,163 +0,0 @@ -from distutils.command.sdist import sdist as _sdist -from distutils.util import convert_path -import os, re, sys, pkg_resources - -entities = [ - ("<","<"), (">", ">"), (""", '"'), ("'", "'"), - ("&", "&") -] - -def unescape(data): - for old,new in entities: - data = data.replace(old,new) - return data - -def re_finder(pattern, postproc=None): - def find(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - for match in pattern.finditer(data): - path = match.group(1) - if postproc: - path = postproc(path) - yield joinpath(dirname,path) - return find - -def joinpath(prefix,suffix): - if not prefix: - return suffix - return os.path.join(prefix,suffix) - - - - - - - - - - - -def walk_revctrl(dirname=''): - """Find all files under revision control""" - for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): - for item in ep.load()(dirname): - yield item - -def _default_revctrl(dirname=''): - for path, finder in finders: - path = joinpath(dirname,path) - if os.path.isfile(path): - for path in finder(dirname,path): - if os.path.isfile(path): - yield path - elif os.path.isdir(path): - for item in _default_revctrl(path): - yield item - -def externals_finder(dirname, filename): - """Find any 'svn:externals' directories""" - found = False - f = open(filename,'rb') - for line in iter(f.readline, ''): # can't use direct iter! - parts = line.split() - if len(parts)==2: - kind,length = parts - data = f.read(int(length)) - if kind=='K' and data=='svn:externals': - found = True - elif kind=='V' and found: - f.close() - break - else: - f.close() - return - - for line in data.splitlines(): - parts = line.split() - if parts: - yield joinpath(dirname, parts[0]) - - -finders = [ - (convert_path('CVS/Entries'), - re_finder(re.compile(r"^\w?/([^/]+)/", re.M))), - (convert_path('.svn/entries'), - re_finder( - re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I), - unescape - ) - ), - (convert_path('.svn/dir-props'), externals_finder), -] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -class sdist(_sdist): - """Smart sdist that finds anything supported by revision control""" - - user_options = [ - ('formats=', None, - "formats for source distribution (comma-separated list)"), - ('keep-temp', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ('dist-dir=', 'd', - "directory to put the source distribution archive(s) in " - "[default: dist]"), - ] - - negative_opt = {} - - def run(self): - self.run_command('egg_info') - ei_cmd = self.get_finalized_command('egg_info') - self.filelist = ei_cmd.filelist - self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt')) - - self.check_metadata() - self.make_distribution() - - dist_files = getattr(self.distribution,'dist_files',[]) - for file in self.archive_files: - data = ('sdist', '', file) - if data not in dist_files: - dist_files.append(data) - - def read_template(self): - try: - _sdist.read_template(self) - except: - # grody hack to close the template file (MANIFEST.in) - # this prevents easy_install's attempt at deleting the file from - # dying and thus masking the real error - sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close() - raise diff --git a/Lib/setuptools/command/setopt.py b/Lib/setuptools/command/setopt.py deleted file mode 100755 index e0c1058..0000000 --- a/Lib/setuptools/command/setopt.py +++ /dev/null @@ -1,158 +0,0 @@ -import distutils, os -from setuptools import Command -from distutils.util import convert_path -from distutils import log -from distutils.errors import * - -__all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] - - -def config_file(kind="local"): - """Get the filename of the distutils, local, global, or per-user config - - `kind` must be one of "local", "global", or "user" - """ - if kind=='local': - return 'setup.cfg' - if kind=='global': - return os.path.join( - os.path.dirname(distutils.__file__),'distutils.cfg' - ) - if kind=='user': - dot = os.name=='posix' and '.' or '' - return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot)) - raise ValueError( - "config_file() type must be 'local', 'global', or 'user'", kind - ) - - - - - - - - - - - - - - - -def edit_config(filename, settings, dry_run=False): - """Edit a configuration file to include `settings` - - `settings` is a dictionary of dictionaries or ``None`` values, keyed by - command/section name. A ``None`` value means to delete the entire section, - while a dictionary lists settings to be changed or deleted in that section. - A setting of ``None`` means to delete that setting. - """ - from ConfigParser import RawConfigParser - log.debug("Reading configuration from %s", filename) - opts = RawConfigParser() - opts.read([filename]) - for section, options in settings.items(): - if options is None: - log.info("Deleting section [%s] from %s", section, filename) - opts.remove_section(section) - else: - if not opts.has_section(section): - log.debug("Adding new section [%s] to %s", section, filename) - opts.add_section(section) - for option,value in options.items(): - if value is None: - log.debug("Deleting %s.%s from %s", - section, option, filename - ) - opts.remove_option(section,option) - if not opts.options(section): - log.info("Deleting empty [%s] section from %s", - section, filename) - opts.remove_section(section) - else: - log.debug( - "Setting %s.%s to %r in %s", - section, option, value, filename - ) - opts.set(section,option,value) - - log.info("Writing %s", filename) - if not dry_run: - f = open(filename,'w'); opts.write(f); f.close() - -class option_base(Command): - """Abstract base class for commands that mess with config files""" - - user_options = [ - ('global-config', 'g', - "save options to the site-wide distutils.cfg file"), - ('user-config', 'u', - "save options to the current user's pydistutils.cfg file"), - ('filename=', 'f', - "configuration file to use (default=setup.cfg)"), - ] - - boolean_options = [ - 'global-config', 'user-config', - ] - - def initialize_options(self): - self.global_config = None - self.user_config = None - self.filename = None - - def finalize_options(self): - filenames = [] - if self.global_config: - filenames.append(config_file('global')) - if self.user_config: - filenames.append(config_file('user')) - if self.filename is not None: - filenames.append(self.filename) - if not filenames: - filenames.append(config_file('local')) - if len(filenames)>1: - raise DistutilsOptionError( - "Must specify only one configuration file option", - filenames - ) - self.filename, = filenames - - - - -class setopt(option_base): - """Save command-line options to a file""" - - description = "set an option in setup.cfg or another config file" - - user_options = [ - ('command=', 'c', 'command to set an option for'), - ('option=', 'o', 'option to set'), - ('set-value=', 's', 'value of the option'), - ('remove', 'r', 'remove (unset) the value'), - ] + option_base.user_options - - boolean_options = option_base.boolean_options + ['remove'] - - def initialize_options(self): - option_base.initialize_options(self) - self.command = None - self.option = None - self.set_value = None - self.remove = None - - def finalize_options(self): - option_base.finalize_options(self) - if self.command is None or self.option is None: - raise DistutilsOptionError("Must specify --command *and* --option") - if self.set_value is None and not self.remove: - raise DistutilsOptionError("Must specify --set-value or --remove") - - def run(self): - edit_config( - self.filename, { - self.command: {self.option.replace('-','_'):self.set_value} - }, - self.dry_run - ) diff --git a/Lib/setuptools/command/test.py b/Lib/setuptools/command/test.py deleted file mode 100644 index 01fca35..0000000 --- a/Lib/setuptools/command/test.py +++ /dev/null @@ -1,119 +0,0 @@ -from setuptools import Command -from distutils.errors import DistutilsOptionError -import sys -from pkg_resources import * -from unittest import TestLoader, main - -class ScanningLoader(TestLoader): - - def loadTestsFromModule(self, module): - """Return a suite of all tests cases contained in the given module - - If the module is a package, load tests from all the modules in it. - If the module has an ``additional_tests`` function, call it and add - the return value to the tests. - """ - tests = [] - if module.__name__!='setuptools.tests.doctest': # ugh - tests.append(TestLoader.loadTestsFromModule(self,module)) - - if hasattr(module, "additional_tests"): - tests.append(module.additional_tests()) - - if hasattr(module, '__path__'): - for file in resource_listdir(module.__name__, ''): - if file.endswith('.py') and file!='__init__.py': - submodule = module.__name__+'.'+file[:-3] - else: - if resource_exists( - module.__name__, file+'/__init__.py' - ): - submodule = module.__name__+'.'+file - else: - continue - tests.append(self.loadTestsFromName(submodule)) - - if len(tests)!=1: - return self.suiteClass(tests) - else: - return tests[0] # don't create a nested suite for only one return - - -class test(Command): - - """Command to run unit tests after in-place build""" - - description = "run unit tests after in-place build" - - user_options = [ - ('test-module=','m', "Run 'test_suite' in specified module"), - ('test-suite=','s', - "Test suite to run (e.g. 'some_module.test_suite')"), - ] - - def initialize_options(self): - self.test_suite = None - self.test_module = None - self.test_loader = None - - - def finalize_options(self): - - if self.test_suite is None: - if self.test_module is None: - self.test_suite = self.distribution.test_suite - else: - self.test_suite = self.test_module+".test_suite" - elif self.test_module: - raise DistutilsOptionError( - "You may specify a module or a suite, but not both" - ) - - self.test_args = [self.test_suite] - - if self.verbose: - self.test_args.insert(0,'--verbose') - if self.test_loader is None: - self.test_loader = getattr(self.distribution,'test_loader',None) - if self.test_loader is None: - self.test_loader = "setuptools.command.test:ScanningLoader" - - - - def run(self): - # Ensure metadata is up-to-date - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') - - if self.distribution.tests_require: - self.distribution.fetch_build_eggs(self.distribution.tests_require) - - if self.test_suite: - cmd = ' '.join(self.test_args) - if self.dry_run: - self.announce('skipping "unittest %s" (dry run)' % cmd) - else: - self.announce('running "unittest %s"' % cmd) - self.run_tests() - - - def run_tests(self): - import unittest - old_path = sys.path[:] - ei_cmd = self.get_finalized_command("egg_info") - path_item = normalize_path(ei_cmd.egg_base) - metadata = PathMetadata( - path_item, normalize_path(ei_cmd.egg_info) - ) - dist = Distribution(path_item, metadata, project_name=ei_cmd.egg_name) - working_set.add(dist) - require(str(dist.as_requirement())) - loader_ep = EntryPoint.parse("x="+self.test_loader) - loader_class = loader_ep.load(require=False) - unittest.main( - None, None, [unittest.__file__]+self.test_args, - testLoader = loader_class() - ) diff --git a/Lib/setuptools/command/upload.py b/Lib/setuptools/command/upload.py deleted file mode 100755 index 644c400..0000000 --- a/Lib/setuptools/command/upload.py +++ /dev/null @@ -1,178 +0,0 @@ -"""distutils.command.upload - -Implements the Distutils 'upload' subcommand (upload package to PyPI).""" - -from distutils.errors import * -from distutils.core import Command -from distutils.spawn import spawn -from distutils import log -from md5 import md5 -import os -import socket -import platform -import ConfigParser -import httplib -import base64 -import urlparse -import cStringIO as StringIO - -class upload(Command): - - description = "upload binary package to PyPI" - - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), - ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), - ] - boolean_options = ['show-response', 'sign'] - - def initialize_options(self): - self.username = '' - self.password = '' - self.repository = '' - self.show_response = 0 - self.sign = False - self.identity = None - - def finalize_options(self): - if self.identity and not self.sign: - raise DistutilsOptionError( - "Must use --sign for --identity to have meaning" - ) - if os.environ.has_key('HOME'): - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY - - def run(self): - if not self.distribution.dist_files: - raise DistutilsOptionError("No dist file created in earlier command") - for command, pyversion, filename in self.distribution.dist_files: - self.upload_file(command, pyversion, filename) - - def upload_file(self, command, pyversion, filename): - # Sign if requested - if self.sign: - gpg_args = ["gpg", "--detach-sign", "-a", filename] - if self.identity: - gpg_args[2:2] = ["--local-user", self.identity] - spawn(gpg_args, - dry_run=self.dry_run) - - # Fill in the data - content = open(filename,'rb').read() - basename = os.path.basename(filename) - comment = '' - if command=='bdist_egg' and self.distribution.has_ext_modules(): - comment = "built on %s" % platform.platform(terse=1) - data = { - ':action':'file_upload', - 'protcol_version':'1', - 'name':self.distribution.get_name(), - 'version':self.distribution.get_version(), - 'content':(basename,content), - 'filetype':command, - 'pyversion':pyversion, - 'md5_digest':md5(content).hexdigest(), - } - if command == 'bdist_rpm': - dist, version, id = platform.dist() - if dist: - comment = 'built for %s %s' % (dist, version) - elif command == 'bdist_dumb': - comment = 'built for %s' % platform.platform(terse=1) - data['comment'] = comment - - if self.sign: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - open(filename+".asc").read()) - - # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() - - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = '\n--' + boundary - end_boundary = sep_boundary + '--' - body = StringIO.StringIO() - for key, value in data.items(): - # handle multiple entries for the same name - if type(value) != type([]): - value = [value] - for value in value: - if type(value) is tuple: - fn = ';filename="%s"' % value[0] - value = value[1] - else: - fn = "" - value = str(value) - body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write(fn) - body.write("\n\n") - body.write(value) - if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) - body.write(end_boundary) - body.write("\n") - body = body.getvalue() - - self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) - - # build the Request - # We can't use urllib2 since we need to send the Basic - # auth right with the first request - schema, netloc, url, params, query, fragments = \ - urlparse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httplib.HTTPConnection(netloc) - elif schema == 'https': - http = httplib.HTTPSConnection(netloc) - else: - raise AssertionError, "unsupported schema "+schema - - data = '' - loglevel = log.INFO - try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) - except socket.error, e: - self.announce(e.msg, log.ERROR) - return - - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), - log.INFO) - else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), - log.ERROR) - if self.show_response: - print '-'*75, r.read(), '-'*75 diff --git a/Lib/setuptools/depends.py b/Lib/setuptools/depends.py deleted file mode 100644 index 68d8194..0000000 --- a/Lib/setuptools/depends.py +++ /dev/null @@ -1,239 +0,0 @@ -from __future__ import generators -import sys, imp, marshal -from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN -from distutils.version import StrictVersion, LooseVersion - -__all__ = [ - 'Require', 'find_module', 'get_module_constant', 'extract_constant' -] - -class Require: - """A prerequisite to building or installing a distribution""" - - def __init__(self,name,requested_version,module,homepage='', - attribute=None,format=None - ): - - if format is None and requested_version is not None: - format = StrictVersion - - if format is not None: - requested_version = format(requested_version) - if attribute is None: - attribute = '__version__' - - self.__dict__.update(locals()) - del self.self - - - def full_name(self): - """Return full package/distribution name, w/version""" - if self.requested_version is not None: - return '%s-%s' % (self.name,self.requested_version) - return self.name - - - def version_ok(self,version): - """Is 'version' sufficiently up-to-date?""" - return self.attribute is None or self.format is None or \ - str(version)<>"unknown" and version >= self.requested_version - - - def get_version(self, paths=None, default="unknown"): - - """Get version number of installed module, 'None', or 'default' - - Search 'paths' for module. If not found, return 'None'. If found, - return the extracted version attribute, or 'default' if no version - attribute was specified, or the value cannot be determined without - importing the module. The version is formatted according to the - requirement's version format (if any), unless it is 'None' or the - supplied 'default'. - """ - - if self.attribute is None: - try: - f,p,i = find_module(self.module,paths) - if f: f.close() - return default - except ImportError: - return None - - v = get_module_constant(self.module,self.attribute,default,paths) - - if v is not None and v is not default and self.format is not None: - return self.format(v) - - return v - - - def is_present(self,paths=None): - """Return true if dependency is present on 'paths'""" - return self.get_version(paths) is not None - - - def is_current(self,paths=None): - """Return true if dependency is present and up-to-date on 'paths'""" - version = self.get_version(paths) - if version is None: - return False - return self.version_ok(version) - - -def _iter_code(code): - - """Yield '(op,arg)' pair for each operation in code object 'code'""" - - from array import array - from dis import HAVE_ARGUMENT, EXTENDED_ARG - - bytes = array('b',code.co_code) - eof = len(code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr=HAVE_ARGUMENT: - - arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg - ptr += 3 - - if op==EXTENDED_ARG: - extended_arg = arg * 65536L - continue - - else: - arg = None - ptr += 1 - - yield op,arg - - - - - - - - - - -def find_module(module, paths=None): - """Just like 'imp.find_module()', but with package support""" - - parts = module.split('.') - - while parts: - part = parts.pop(0) - f, path, (suffix,mode,kind) = info = imp.find_module(part, paths) - - if kind==PKG_DIRECTORY: - parts = parts or ['__init__'] - paths = [path] - - elif parts: - raise ImportError("Can't find %r in %s" % (parts,module)) - - return info - - - - - - - - - - - - - - - - - - - - - - - - -def get_module_constant(module, symbol, default=-1, paths=None): - - """Find 'module' by searching 'paths', and extract 'symbol' - - Return 'None' if 'module' does not exist on 'paths', or it does not define - 'symbol'. If the module defines 'symbol' as a constant, return the - constant. Otherwise, return 'default'.""" - - try: - f, path, (suffix,mode,kind) = find_module(module,paths) - except ImportError: - # Module doesn't exist - return None - - try: - if kind==PY_COMPILED: - f.read(8) # skip magic & date - code = marshal.load(f) - elif kind==PY_FROZEN: - code = imp.get_frozen_object(module) - elif kind==PY_SOURCE: - code = compile(f.read(), path, 'exec') - else: - # Not something we can parse; we'll have to import it. :( - if module not in sys.modules: - imp.load_module(module,f,path,(suffix,mode,kind)) - return getattr(sys.modules[module],symbol,None) - - finally: - if f: - f.close() - - return extract_constant(code,symbol,default) - - - - - - - - -def extract_constant(code,symbol,default=-1): - - """Extract the constant value of 'symbol' from 'code' - - If the name 'symbol' is bound to a constant value by the Python code - object 'code', return that value. If 'symbol' is bound to an expression, - return 'default'. Otherwise, return 'None'. - - Return value is based on the first assignment to 'symbol'. 'symbol' must - be a global, or at least a non-"fast" local in the code block. That is, - only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' - must be present in 'code.co_names'. - """ - - if symbol not in code.co_names: - # name's not there, can't possibly be an assigment - return None - - name_idx = list(code.co_names).index(symbol) - - STORE_NAME = 90 - STORE_GLOBAL = 97 - LOAD_CONST = 100 - - const = default - - for op, arg in _iter_code(code): - - if op==LOAD_CONST: - const = code.co_consts[arg] - elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL): - return const - else: - const = default diff --git a/Lib/setuptools/dist.py b/Lib/setuptools/dist.py deleted file mode 100644 index f0417c1..0000000 --- a/Lib/setuptools/dist.py +++ /dev/null @@ -1,798 +0,0 @@ -__all__ = ['Distribution'] - -from distutils.core import Distribution as _Distribution -from setuptools.depends import Require -from setuptools.command.install import install -from setuptools.command.sdist import sdist -from setuptools.command.install_lib import install_lib -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os - -def _get_unpatched(cls): - """Protect against re-patching the distutils if reloaded - - Also ensures that no other distutils extension monkeypatched the distutils - first. - """ - while cls.__module__.startswith('setuptools'): - cls, = cls.__bases__ - if not cls.__module__.startswith('distutils'): - raise AssertionError( - "distutils has already been patched by %r" % cls - ) - return cls - -_Distribution = _get_unpatched(_Distribution) - -sequence = tuple, list - -def check_importable(dist, attr, value): - try: - ep = pkg_resources.EntryPoint.parse('x='+value) - assert not ep.extras - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "%r must be importable 'module:attrs' string (got %r)" - % (attr,value) - ) - - -def assert_string_list(dist, attr, value): - """Verify that value is a string list or None""" - try: - assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) - ) - -def check_nsp(dist, attr, value): - """Verify that namespace packages are valid""" - assert_string_list(dist,attr,value) - - for nsp in value: - if not dist.has_contents_for(nsp): - raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp - ) - -def check_extras(dist, attr, value): - """Verify that extras_require mapping is valid""" - try: - for k,v in value.items(): - list(pkg_resources.parse_requirements(v)) - except (TypeError,ValueError,AttributeError): - raise DistutilsSetupError( - "'extras_require' must be a dictionary whose values are " - "strings or lists of strings containing valid project/version " - "requirement specifiers." - ) - -def assert_bool(dist, attr, value): - """Verify that value is True, False, 0, or 1""" - if bool(value) != value: - raise DistutilsSetupError( - "%r must be a boolean value (got %r)" % (attr,value) - ) - - - -def check_requirements(dist, attr, value): - """Verify that install_requires is a valid requirements list""" - try: - list(pkg_resources.parse_requirements(value)) - except (TypeError,ValueError): - raise DistutilsSetupError( - "%r must be a string or list of strings " - "containing valid project/version requirement specifiers" % (attr,) - ) - -def check_entry_points(dist, attr, value): - """Verify that entry_points map is parseable""" - try: - pkg_resources.EntryPoint.parse_map(value) - except ValueError, e: - raise DistutilsSetupError(e) - - -def check_test_suite(dist, attr, value): - if not isinstance(value,basestring): - raise DistutilsSetupError("test_suite must be a string") - - -def check_package_data(dist, attr, value): - """Verify that value is a dictionary of package names to glob lists""" - if isinstance(value,dict): - for k,v in value.items(): - if not isinstance(k,str): break - try: iter(v) - except TypeError: - break - else: - return - raise DistutilsSetupError( - attr+" must be a dictionary mapping package names to lists of " - "wildcard patterns" - ) - - - - -class Distribution(_Distribution): - """Distribution with support for features, tests, and package data - - This is an enhanced version of 'distutils.dist.Distribution' that - effectively adds the following new optional keyword arguments to 'setup()': - - 'install_requires' -- a string or sequence of strings specifying project - versions that the distribution requires when installed, in the format - used by 'pkg_resources.require()'. They will be installed - automatically when the package is installed. If you wish to use - packages that are not available in PyPI, or want to give your users an - alternate download location, you can add a 'find_links' option to the - '[easy_install]' section of your project's 'setup.cfg' file, and then - setuptools will scan the listed web pages for links that satisfy the - requirements. - - 'extras_require' -- a dictionary mapping names of optional "extras" to the - additional requirement(s) that using those extras incurs. For example, - this:: - - extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) - - indicates that the distribution can optionally provide an extra - capability called "reST", but it can only be used if docutils and - reSTedit are installed. If the user installs your package using - EasyInstall and requests one of your extras, the corresponding - additional requirements will be installed if needed. - - 'features' -- a dictionary mapping option names to 'setuptools.Feature' - objects. Features are a portion of the distribution that can be - included or excluded based on user options, inter-feature dependencies, - and availability on the current system. Excluded features are omitted - from all setup commands, including source and binary distributions, so - you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may - contain the '-' (minus) sign. Features can be included or excluded - via the command line options '--with-X' and '--without-X', where 'X' is - the name of the feature. Whether a feature is included by default, and - whether you are allowed to control this from the command line, is - determined by the Feature object. See the 'Feature' class for more - information. - - 'test_suite' -- the name of a test suite to run for the 'test' command. - If the user runs 'python setup.py test', the package will be installed, - and the named test suite will be run. The format is the same as - would be used on a 'unittest.py' command line. That is, it is the - dotted name of an object to import and call to generate a test suite. - - 'package_data' -- a dictionary mapping package names to lists of filenames - or globs to use to find data files contained in the named packages. - If the dictionary has filenames or globs listed under '""' (the empty - string), those names will be searched for in every package, in addition - to any names for the specific package. Data files found using these - names/globs will be installed along with the package, in the same - location as the package. Note that globs are allowed to reference - the contents of non-package subdirectories, as long as you use '/' as - a path separator. (Globs are automatically converted to - platform-specific paths at runtime.) - - In addition to these new keywords, this class also has several new methods - for manipulating the distribution's contents. For example, the 'include()' - and 'exclude()' methods can be thought of as in-place add and subtract - commands that add or remove packages, modules, extensions, and so on from - the distribution. They are used by the feature subsystem to configure the - distribution for the included and excluded features. - """ - - _patched_dist = None - - def patch_missing_pkg_info(self, attrs): - # Fake up a replacement for the data that would normally come from - # PKG-INFO, but which might not yet be built if this is a fresh - # checkout. - # - if not attrs or 'name' not in attrs or 'version' not in attrs: - return - key = pkg_resources.safe_name(str(attrs['name'])).lower() - dist = pkg_resources.working_set.by_key.get(key) - if dist is not None and not dist.has_metadata('PKG-INFO'): - dist._version = pkg_resources.safe_version(str(attrs['version'])) - self._patched_dist = dist - - def __init__ (self, attrs=None): - have_package_data = hasattr(self, "package_data") - if not have_package_data: - self.package_data = {} - self.require_features = [] - self.features = {} - self.dist_files = [] - self.patch_missing_pkg_info(attrs) - # Make sure we have any eggs needed to interpret 'attrs' - if attrs and 'dependency_links' in attrs: - self.dependency_links = attrs.pop('dependency_links') - assert_string_list(self,'dependency_links',self.dependency_links) - if attrs and 'setup_requires' in attrs: - self.fetch_build_eggs(attrs.pop('setup_requires')) - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - if not hasattr(self,ep.name): - setattr(self,ep.name,None) - _Distribution.__init__(self,attrs) - if isinstance(self.metadata.version, (int,long,float)): - # Some people apparently take "version number" too literally :) - self.metadata.version = str(self.metadata.version) - - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - def _feature_attrname(self,name): - """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-','_') - - def fetch_build_eggs(self, requires): - """Resolve pre-setup requirements""" - from pkg_resources import working_set, parse_requirements - for dist in working_set.resolve( - parse_requirements(requires), installer=self.fetch_build_egg - ): - working_set.add(dist) - - def finalize_options(self): - _Distribution.finalize_options(self) - if self.features: - self._set_global_opts_from_features() - - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - value = getattr(self,ep.name,None) - if value is not None: - ep.require(installer=self.fetch_build_egg) - ep.load()(self, ep.name, value) - - def fetch_build_egg(self, req): - """Fetch an egg needed for building""" - try: - cmd = self._egg_fetcher - except AttributeError: - from setuptools.command.easy_install import easy_install - dist = self.__class__({'script_args':['easy_install']}) - dist.parse_config_files() - opts = dist.get_option_dict('easy_install') - keep = ( - 'find_links', 'site_dirs', 'index_url', 'optimize', - 'site_dirs', 'allow_hosts' - ) - for key in opts.keys(): - if key not in keep: - del opts[key] # don't use any other settings - if self.dependency_links: - links = self.dependency_links[:] - if 'find_links' in opts: - links = opts['find_links'][1].split() + links - opts['find_links'] = ('setup', links) - cmd = easy_install( - dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, - always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True, no_report = True - ) - cmd.ensure_finalized() - self._egg_fetcher = cmd - return cmd.easy_install(req) - - def _set_global_opts_from_features(self): - """Add --with-X/--without-X options based on optional features""" - - go = [] - no = self.negative_opt.copy() - - for name,feature in self.features.items(): - self._set_feature(name,None) - feature.validate(self) - - if feature.optional: - descr = feature.description - incdef = ' (default)' - excdef='' - if not feature.include_by_default(): - excdef, incdef = incdef, excdef - - go.append(('with-'+name, None, 'include '+descr+incdef)) - go.append(('without-'+name, None, 'exclude '+descr+excdef)) - no['without-'+name] = 'with-'+name - - self.global_options = self.feature_options = go + self.global_options - self.negative_opt = self.feature_negopt = no - - - - - - - - - - - - - - - - - - - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name,1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name,0) - - - def get_command_class(self, command): - """Pluggable version of get_command_class()""" - if command in self.cmdclass: - return self.cmdclass[command] - - for ep in pkg_resources.iter_entry_points('distutils.commands',command): - ep.require(installer=self.fetch_build_egg) - self.cmdclass[command] = cmdclass = ep.load() - return cmdclass - else: - return _Distribution.get_command_class(self, command) - - def print_commands(self): - for ep in pkg_resources.iter_entry_points('distutils.commands'): - if ep.name not in self.cmdclass: - cmdclass = ep.load(False) # don't require extras, we're not running - self.cmdclass[ep.name] = cmdclass - return _Distribution.print_commands(self) - - - - - - def _set_feature(self,name,status): - """Set feature's inclusion status""" - setattr(self,self._feature_attrname(name),status) - - def feature_is_included(self,name): - """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self,self._feature_attrname(name)) - - def include_feature(self,name): - """Request inclusion of feature named 'name'""" - - if self.feature_is_included(name)==0: - descr = self.features[name].description - raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) - self.features[name].include_in(self) - self._set_feature(name,1) - - def include(self,**attrs): - """Add items to distribution that are named in keyword arguments - - For example, 'dist.exclude(py_modules=["x"])' would add 'x' to - the distribution's 'py_modules' attribute, if it was not already - there. - - Currently, this method only supports inclusion for attributes that are - lists or tuples. If you need to add support for adding to other - attributes in this or a subclass, you can add an '_include_X' method, - where 'X' is the name of the attribute. The method will be called with - the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' - will try to call 'dist._include_foo({"bar":"baz"})', which can then - handle whatever special inclusion logic is needed. - """ - for k,v in attrs.items(): - include = getattr(self, '_include_'+k, None) - if include: - include(v) - else: - self._include_misc(k,v) - - def exclude_package(self,package): - """Remove packages, modules, and extensions in named package""" - - pfx = package+'.' - if self.packages: - self.packages = [ - p for p in self.packages - if p<>package and not p.startswith(pfx) - ] - - if self.py_modules: - self.py_modules = [ - p for p in self.py_modules - if p<>package and not p.startswith(pfx) - ] - - if self.ext_modules: - self.ext_modules = [ - p for p in self.ext_modules - if p.name<>package and not p.name.startswith(pfx) - ] - - - def has_contents_for(self,package): - """Return true if 'exclude_package(package)' would do something""" - - pfx = package+'.' - - for p in self.iter_distribution_names(): - if p==package or p.startswith(pfx): - return True - - - - - - - - - - - def _exclude_misc(self,name,value): - """Handle 'exclude()' for list/tuple attrs without a special handler""" - if not isinstance(value,sequence): - raise DistutilsSetupError( - "%s: setting must be a list or tuple (%r)" % (name, value) - ) - try: - old = getattr(self,name) - except AttributeError: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) - if old is not None and not isinstance(old,sequence): - raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" - ) - elif old: - setattr(self,name,[item for item in old if item not in value]) - - def _include_misc(self,name,value): - """Handle 'include()' for list/tuple attrs without a special handler""" - - if not isinstance(value,sequence): - raise DistutilsSetupError( - "%s: setting must be a list (%r)" % (name, value) - ) - try: - old = getattr(self,name) - except AttributeError: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) - if old is None: - setattr(self,name,value) - elif not isinstance(old,sequence): - raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" - ) - else: - setattr(self,name,old+[item for item in value if item not in old]) - - def exclude(self,**attrs): - """Remove items from distribution that are named in keyword arguments - - For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from - the distribution's 'py_modules' attribute. Excluding packages uses - the 'exclude_package()' method, so all of the package's contained - packages, modules, and extensions are also excluded. - - Currently, this method only supports exclusion from attributes that are - lists or tuples. If you need to add support for excluding from other - attributes in this or a subclass, you can add an '_exclude_X' method, - where 'X' is the name of the attribute. The method will be called with - the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' - will try to call 'dist._exclude_foo({"bar":"baz"})', which can then - handle whatever special exclusion logic is needed. - """ - for k,v in attrs.items(): - exclude = getattr(self, '_exclude_'+k, None) - if exclude: - exclude(v) - else: - self._exclude_misc(k,v) - - def _exclude_packages(self,packages): - if not isinstance(packages,sequence): - raise DistutilsSetupError( - "packages: setting must be a list or tuple (%r)" % (packages,) - ) - map(self.exclude_package, packages) - - - - - - - - - - - - - def _parse_command_opts(self, parser, args): - # Remove --with-X/--without-X options when processing command args - self.global_options = self.__class__.global_options - self.negative_opt = self.__class__.negative_opt - - # First, expand any aliases - command = args[0] - aliases = self.get_option_dict('aliases') - while command in aliases: - src,alias = aliases[command] - del aliases[command] # ensure each alias can expand only once! - import shlex - args[:1] = shlex.split(alias,True) - command = args[0] - - nargs = _Distribution._parse_command_opts(self, parser, args) - - # Handle commands that want to consume all remaining arguments - cmd_class = self.get_command_class(command) - if getattr(cmd_class,'command_consumes_arguments',None): - self.get_option_dict(command)['args'] = ("command line", nargs) - if nargs is not None: - return [] - - return nargs - - - - - - - - - - - - - - - - - def get_cmdline_options(self): - """Return a '{cmd: {opt:val}}' map of all command-line options - - Option names are all long, but do not include the leading '--', and - contain dashes rather than underscores. If the option doesn't take - an argument (e.g. '--quiet'), the 'val' is 'None'. - - Note that options provided by config files are intentionally excluded. - """ - - d = {} - - for cmd,opts in self.command_options.items(): - - for opt,(src,val) in opts.items(): - - if src != "command line": - continue - - opt = opt.replace('_','-') - - if val==0: - cmdobj = self.get_command_obj(cmd) - neg_opt = self.negative_opt.copy() - neg_opt.update(getattr(cmdobj,'negative_opt',{})) - for neg,pos in neg_opt.items(): - if pos==opt: - opt=neg - val=None - break - else: - raise AssertionError("Shouldn't be able to get here") - - elif val==1: - val = None - - d.setdefault(cmd,{})[opt] = val - - return d - - - def iter_distribution_names(self): - """Yield all packages, modules, and extension names in distribution""" - - for pkg in self.packages or (): - yield pkg - - for module in self.py_modules or (): - yield module - - for ext in self.ext_modules or (): - if isinstance(ext,tuple): - name,buildinfo = ext - yield name - else: - yield ext.name - -# Install it throughout the distutils -for module in distutils.dist, distutils.core, distutils.cmd: - module.Distribution = Distribution - - - - - - - - - - - - - - - - - - - - - - -class Feature: - """A subset of the distribution that can be excluded if unneeded/wanted - - Features are created using these keyword arguments: - - 'description' -- a short, human readable description of the feature, to - be used in error messages, and option help messages. - - 'standard' -- if true, the feature is included by default if it is - available on the current system. Otherwise, the feature is only - included if requested via a command line '--with-X' option, or if - another included feature requires it. The default setting is 'False'. - - 'available' -- if true, the feature is available for installation on the - current system. The default setting is 'True'. - - 'optional' -- if true, the feature's inclusion can be controlled from the - command line, using the '--with-X' or '--without-X' options. If - false, the feature's inclusion status is determined automatically, - based on 'availabile', 'standard', and whether any other feature - requires it. The default setting is 'True'. - - 'require_features' -- a string or sequence of strings naming features - that should also be included if this feature is included. Defaults to - empty list. May also contain 'Require' objects that should be - added/removed from the distribution. - - 'remove' -- a string or list of strings naming packages to be removed - from the distribution if this feature is *not* included. If the - feature *is* included, this argument is ignored. This argument exists - to support removing features that "crosscut" a distribution, such as - defining a 'tests' feature that removes all the 'tests' subpackages - provided by other features. The default for this argument is an empty - list. (Note: the named package(s) or modules must exist in the base - distribution when the 'setup()' function is initially called.) - - other keywords -- any other keyword arguments are saved, and passed to - the distribution's 'include()' and 'exclude()' methods when the - feature is included or excluded, respectively. So, for example, you - could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be - added or removed from the distribution as appropriate. - - A feature must include at least one 'requires', 'remove', or other - keyword argument. Otherwise, it can't affect the distribution in any way. - Note also that you can subclass 'Feature' to create your own specialized - feature types that modify the distribution in other ways when included or - excluded. See the docstrings for the various methods here for more detail. - Aside from the methods, the only feature attributes that distributions look - at are 'description' and 'optional'. - """ - def __init__(self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras - ): - - self.description = description - self.standard = standard - self.available = available - self.optional = optional - if isinstance(require_features,(str,Require)): - require_features = require_features, - - self.require_features = [ - r for r in require_features if isinstance(r,str) - ] - er = [r for r in require_features if not isinstance(r,str)] - if er: extras['require_features'] = er - - if isinstance(remove,str): - remove = remove, - self.remove = remove - self.extras = extras - - if not remove and not require_features and not extras: - raise DistutilsSetupError( - "Feature %s: must define 'require_features', 'remove', or at least one" - " of 'packages', 'py_modules', etc." - ) - - def include_by_default(self): - """Should this feature be included by default?""" - return self.available and self.standard - - def include_in(self,dist): - - """Ensure feature and its requirements are included in distribution - - You may override this in a subclass to perform additional operations on - the distribution. Note that this method may be called more than once - per feature, and so should be idempotent. - - """ - - if not self.available: - raise DistutilsPlatformError( - self.description+" is required," - "but is not available on this platform" - ) - - dist.include(**self.extras) - - for f in self.require_features: - dist.include_feature(f) - - - - def exclude_from(self,dist): - - """Ensure feature is excluded from distribution - - You may override this in a subclass to perform additional operations on - the distribution. This method will be called at most once per - feature, and only after all included features have been asked to - include themselves. - """ - - dist.exclude(**self.extras) - - if self.remove: - for item in self.remove: - dist.exclude_package(item) - - - - def validate(self,dist): - - """Verify that feature makes sense in context of distribution - - This method is called by the distribution just before it parses its - command line. It checks to ensure that the 'remove' attribute, if any, - contains only valid package/module names that are present in the base - distribution when 'setup()' is called. You may override it in a - subclass to perform any other required validation of the feature - against a target distribution. - """ - - for item in self.remove: - if not dist.has_contents_for(item): - raise DistutilsSetupError( - "%s wants to be able to remove %s, but the distribution" - " doesn't contain any packages or modules under %s" - % (self.description, item, item) - ) diff --git a/Lib/setuptools/extension.py b/Lib/setuptools/extension.py deleted file mode 100644 index cfcf55b..0000000 --- a/Lib/setuptools/extension.py +++ /dev/null @@ -1,35 +0,0 @@ -from distutils.core import Extension as _Extension -from dist import _get_unpatched -_Extension = _get_unpatched(_Extension) - -try: - from Pyrex.Distutils.build_ext import build_ext -except ImportError: - have_pyrex = False -else: - have_pyrex = True - - -class Extension(_Extension): - """Extension that uses '.c' files in place of '.pyx' files""" - - if not have_pyrex: - # convert .pyx extensions to .c - def __init__(self,*args,**kw): - _Extension.__init__(self,*args,**kw) - sources = [] - for s in self.sources: - if s.endswith('.pyx'): - sources.append(s[:-3]+'c') - else: - sources.append(s) - self.sources = sources - -class Library(Extension): - """Just like a regular Extension, but built as a library instead""" - -import sys, distutils.core, distutils.extension -distutils.core.Extension = Extension -distutils.extension.Extension = Extension -if 'distutils.command.build_ext' in sys.modules: - sys.modules['distutils.command.build_ext'].Extension = Extension diff --git a/Lib/setuptools/gui.exe b/Lib/setuptools/gui.exe deleted file mode 100755 index 63ff35f..0000000 Binary files a/Lib/setuptools/gui.exe and /dev/null differ diff --git a/Lib/setuptools/package_index.py b/Lib/setuptools/package_index.py deleted file mode 100755 index 107e222..0000000 --- a/Lib/setuptools/package_index.py +++ /dev/null @@ -1,674 +0,0 @@ -"""PyPI and direct package downloading""" - -import sys, os.path, re, urlparse, urllib2, shutil, random, socket -from pkg_resources import * -from distutils import log -from distutils.errors import DistutilsError -from md5 import md5 -from fnmatch import translate - -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() - -__all__ = [ - 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst', - 'interpret_distro_name', -] - - -def parse_bdist_wininst(name): - """Return (base,pyversion) or (None,None) for possible .exe name""" - - 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) - dists = distros_for_location(url, base, metadata) - if fragment and not dists: - match = EGG_FRAGMENT.match(fragment) - if match: - return interpret_distro_name( - url, match.group(1), metadata, precedence = CHECKOUT_DIST - ) - return dists - -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'): # 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('-') - 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 - ) - - - - - -class PackageIndex(Environment): - """A distribution index that scans web pages for download URLs""" - - def __init__(self,index_url="http://www.python.org/pypi",hosts=('*',),*args,**kw): - 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 process_url(self, url, retrieve=False): - """Evaluate a URL as a possible download, and maybe retrieve it""" - url = fix_sf_url(url) - 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) - self.fetched_urls[url] = self.fetched_urls[f.url] = True - - - if 'html' not in f.headers['content-type'].lower(): - f.close() # not html, we can't process it - return - - base = f.url # handle redirects - page = f.read() - f.close() - if url.startswith(self.index_url): - page = self.process_index(url, page) - - for match in HREF.finditer(page): - link = urlparse.urljoin(base, match.group(1)) - self.process_url(link) - - def process_filename(self, fn, nested=False): - # process filenames or directories - if not os.path.exists(fn): - self.warn("Not found: %s", url) - 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): - if 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) - - - - 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: - # 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 - - if url==self.index_url or 'Index of Packages' in page: - # process an index page into the package-page index - for match in HREF.finditer(page): - scan( urlparse.urljoin(url, match.group(1)) ) - else: - pkg,ver = scan(url) # ensure this page is in the page index - # process individual package page - for tag in ("Home Page", "Download URL"): - pos = page.find(tag) - if pos!=-1: - match = HREF.search(page,pos) - if match: - # Process the found URL - new_url = urlparse.urljoin(url, match.group(1)) - base, frag = egg_info_for_url(new_url) - if base.endswith('.py') and not frag: - if pkg and 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 - ) - - 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.warn( - "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.warn( - "Couldn't find index page for %r (maybe misspelled?)", - requirement.unsafe_name - ) - self.scan_all() - - for url in 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 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); #import pdb; pdb.set_trace() - 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 files without an '#egg=name-version'" - " suffix to enable automatic setup script generation." - ) - - dl_blocksize = 8192 - def _download_to(self, url, filename): - self.url_ok(url,True) # raises error if not allowed - 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 retry_sf_download(self, url, filename): - try: - return self._download_to(url, filename) - except: - scheme, server, path, param, query, frag = urlparse.urlparse(url) - if server!='dl.sourceforge.net': - raise - - mirror = get_sf_ip() - - while _sf_mirrors: - self.warn("Download failed: %s", sys.exc_info()[1]) - url = urlparse.urlunparse((scheme, mirror, path, param, '', frag)) - try: - return self._download_to(url, filename) - except: - _sf_mirrors.remove(mirror) # don't retry the same mirror - mirror = get_sf_ip() - - raise # fail if no mirror works - - - - - - - - - - - - - - - - - - - - - - def open_url(self, url): - try: - return urllib2.urlopen(url) - except urllib2.HTTPError, v: - return v - except urllib2.URLError, v: - raise DistutilsError("Download error: %s" % v.reason) - - - 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) - else: - headers = self.retry_sf_download(url, filename) - if 'html' in headers['content-type'].lower(): - return self._download_html(url, headers, filename, tmpdir) - else: - return filename - - def scan_url(self, url): - self.process_url(url, True) - - - def _download_html(self, url, headers, filename, tmpdir): - 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): - log.debug(msg, *args) - - def info(self, msg, *args): - log.info(msg, *args) - - def warn(self, msg, *args): - log.warn(msg, *args) - - - - - - - - - - - - -def fix_sf_url(url): - scheme, server, path, param, query, frag = urlparse.urlparse(url) - if server!='prdownloads.sourceforge.net': - return url - return urlparse.urlunparse( - (scheme, 'dl.sourceforge.net', 'sourceforge'+path, param, '', frag) - ) - -_sf_mirrors = [] - -def get_sf_ip(): - if not _sf_mirrors: - try: - _sf_mirrors[:] = socket.gethostbyname_ex('dl.sourceforge.net')[-1] - except socket.error: - # DNS-bl0ck1n9 f1r3w4llz sUx0rs! - _sf_mirrors[:] = ['dl.sourceforge.net'] - return random.choice(_sf_mirrors) diff --git a/Lib/setuptools/sandbox.py b/Lib/setuptools/sandbox.py deleted file mode 100755 index 606944b..0000000 --- a/Lib/setuptools/sandbox.py +++ /dev/null @@ -1,203 +0,0 @@ -import os, sys, __builtin__, tempfile -_os = sys.modules[os.name] -_open = open -from distutils.errors import DistutilsError -__all__ = [ - "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", -] - -def run_setup(setup_script, args): - """Run a distutils setup script, sandboxed in its directory""" - - old_dir = os.getcwd() - save_argv = sys.argv[:] - save_path = sys.path[:] - setup_dir = os.path.abspath(os.path.dirname(setup_script)) - temp_dir = os.path.join(setup_dir,'temp') - if not os.path.isdir(temp_dir): os.makedirs(temp_dir) - save_tmp = tempfile.tempdir - - try: - tempfile.tempdir = temp_dir - os.chdir(setup_dir) - try: - sys.argv[:] = [setup_script]+list(args) - sys.path.insert(0, setup_dir) - DirectorySandbox(setup_dir).run( - lambda: execfile( - "setup.py", - {'__file__':setup_script, '__name__':'__main__'} - ) - ) - except SystemExit, v: - if v.args and v.args[0]: - raise - # Normal exit, just return - finally: - os.chdir(old_dir) - sys.path[:] = save_path - sys.argv[:] = save_argv - tempfile.tempdir = save_tmp - -class AbstractSandbox: - """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" - - _active = False - - def __init__(self): - self._attrs = [ - name for name in dir(_os) - if not name.startswith('_') and hasattr(self,name) - ] - - def _copy(self, source): - for name in self._attrs: - setattr(os, name, getattr(source,name)) - - def run(self, func): - """Run 'func' under os sandboxing""" - try: - self._copy(self) - __builtin__.open = __builtin__.file = self._open - self._active = True - return func() - finally: - self._active = False - __builtin__.open = __builtin__.file = _open - self._copy(_os) - - - def _mk_dual_path_wrapper(name): - original = getattr(_os,name) - def wrap(self,src,dst,*args,**kw): - if self._active: - src,dst = self._remap_pair(name,src,dst,*args,**kw) - return original(src,dst,*args,**kw) - return wrap - - - for name in ["rename", "link", "symlink"]: - if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name) - - - def _mk_single_path_wrapper(name, original=None): - original = original or getattr(_os,name) - def wrap(self,path,*args,**kw): - if self._active: - path = self._remap_input(name,path,*args,**kw) - return original(path,*args,**kw) - return wrap - - _open = _mk_single_path_wrapper('file', _open) - for name in [ - "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", - "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", - "startfile", "mkfifo", "mknod", "pathconf", "access" - ]: - if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name) - - - def _mk_single_with_return(name): - original = getattr(_os,name) - def wrap(self,path,*args,**kw): - if self._active: - path = self._remap_input(name,path,*args,**kw) - return self._remap_output(name, original(path,*args,**kw)) - return original(path,*args,**kw) - return wrap - - for name in ['readlink', 'tempnam']: - if hasattr(_os,name): locals()[name] = _mk_single_with_return(name) - - def _mk_query(name): - original = getattr(_os,name) - def wrap(self,*args,**kw): - retval = original(*args,**kw) - if self._active: - return self._remap_output(name, retval) - return retval - return wrap - - for name in ['getcwd', 'tmpnam']: - if hasattr(_os,name): locals()[name] = _mk_query(name) - - def _validate_path(self,path): - """Called to remap or validate any path, whether input or output""" - return path - - def _remap_input(self,operation,path,*args,**kw): - """Called for path inputs""" - return self._validate_path(path) - - def _remap_output(self,operation,path): - """Called for path outputs""" - return self._validate_path(path) - - def _remap_pair(self,operation,src,dst,*args,**kw): - """Called for path pairs like rename, link, and symlink operations""" - return ( - self._remap_input(operation+'-from',src,*args,**kw), - self._remap_input(operation+'-to',dst,*args,**kw) - ) - - -class DirectorySandbox(AbstractSandbox): - """Restrict operations to a single subdirectory - pseudo-chroot""" - - write_ops = dict.fromkeys([ - "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", - "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", - ]) - - def __init__(self,sandbox): - self._sandbox = os.path.normcase(os.path.realpath(sandbox)) - self._prefix = os.path.join(self._sandbox,'') - AbstractSandbox.__init__(self) - - def _violation(self, operation, *args, **kw): - raise SandboxViolation(operation, args, kw) - - def _open(self, path, mode='r', *args, **kw): - if mode not in ('r', 'rt', 'rb', 'rU') and not self._ok(path): - self._violation("open", path, mode, *args, **kw) - return _open(path,mode,*args,**kw) - - def tmpnam(self): - self._violation("tmpnam") - - def _ok(self,path): - active = self._active - try: - self._active = False - realpath = os.path.normcase(os.path.realpath(path)) - if realpath==self._sandbox or realpath.startswith(self._prefix): - return True - finally: - self._active = active - - def _remap_input(self,operation,path,*args,**kw): - """Called for path inputs""" - if operation in self.write_ops and not self._ok(path): - self._violation(operation, os.path.realpath(path), *args, **kw) - return path - - def _remap_pair(self,operation,src,dst,*args,**kw): - """Called for path pairs like rename, link, and symlink operations""" - if not self._ok(src) or not self._ok(dst): - self._violation(operation, src, dst, *args, **kw) - return (src,dst) - - -class SandboxViolation(DistutilsError): - """A setup script attempted to modify the filesystem outside the sandbox""" - - def __str__(self): - return """SandboxViolation: %s%r %s - -The package setup script has attempted to modify files on your system -that are not within the EasyInstall build area, and has been aborted. - -This package cannot be safely installed by EasyInstall, and may not -support alternate installation locations even if you run its setup -script by hand. Please inform the package's author and the EasyInstall -maintainers to find out if a fix or workaround is available.""" % self.args diff --git a/Lib/setuptools/site-patch.py b/Lib/setuptools/site-patch.py deleted file mode 100755 index b1b27b9..0000000 --- a/Lib/setuptools/site-patch.py +++ /dev/null @@ -1,74 +0,0 @@ -def __boot(): - import sys, imp, os, os.path - PYTHONPATH = os.environ.get('PYTHONPATH') - if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH): - PYTHONPATH = [] - else: - PYTHONPATH = PYTHONPATH.split(os.pathsep) - - pic = getattr(sys,'path_importer_cache',{}) - stdpath = sys.path[len(PYTHONPATH):] - mydir = os.path.dirname(__file__) - #print "searching",stdpath,sys.path - - for item in stdpath: - if item==mydir or not item: - continue # skip if current dir. on Windows, or my own directory - importer = pic.get(item) - if importer is not None: - loader = importer.find_module('site') - if loader is not None: - # This should actually reload the current module - loader.load_module('site') - break - else: - try: - stream, path, descr = imp.find_module('site',[item]) - except ImportError: - continue - if stream is None: - continue - try: - # This should actually reload the current module - imp.load_module('site',stream,path,descr) - finally: - stream.close() - break - else: - raise ImportError("Couldn't find the real 'site' module") - - #print "loaded", __file__ - - known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp - - oldpos = getattr(sys,'__egginsert',0) # save old insertion position - sys.__egginsert = 0 # and reset the current one - - for item in PYTHONPATH: - addsitedir(item) - - sys.__egginsert += oldpos # restore effective old position - - d,nd = makepath(stdpath[0]) - insert_at = None - new_path = [] - - for item in sys.path: - p,np = makepath(item) - - if np==nd and insert_at is None: - # We've hit the first 'system' path entry, so added entries go here - insert_at = len(new_path) - - if np in known_paths or insert_at is None: - new_path.append(item) - else: - # new path after the insert point, back-insert it - new_path.insert(insert_at, item) - insert_at += 1 - - sys.path[:] = new_path - -if __name__=='site': - __boot() - del __boot diff --git a/Lib/setuptools/tests/__init__.py b/Lib/setuptools/tests/__init__.py deleted file mode 100644 index 8a767dc..0000000 --- a/Lib/setuptools/tests/__init__.py +++ /dev/null @@ -1,364 +0,0 @@ -"""Tests for the 'setuptools' package""" - -from unittest import TestSuite, TestCase, makeSuite, defaultTestLoader -import distutils.core, distutils.cmd -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.errors import DistutilsSetupError -import setuptools, setuptools.dist -from setuptools import Feature -from distutils.core import Extension -from setuptools.depends import extract_constant, get_module_constant -from setuptools.depends import find_module, Require -from distutils.version import StrictVersion, LooseVersion -from distutils.util import convert_path -import sys, os.path - -def additional_tests(): - import doctest - return doctest.DocFileSuite( - 'api_tests.txt', optionflags=doctest.ELLIPSIS, package=__name__, - ) - - -def makeSetup(**args): - """Return distribution from 'setup(**args)', without executing commands""" - - distutils.core._setup_stop_after = "commandline" - - # Don't let system command line leak into tests! - args.setdefault('script_args',['install']) - - try: - return setuptools.setup(**args) - finally: - distutils.core_setup_stop_after = None - - - - - - - -class DependsTests(TestCase): - - def testExtractConst(self): - - from setuptools.depends import extract_constant - - def f1(): - global x,y,z - x = "test" - y = z - - # unrecognized name - self.assertEqual(extract_constant(f1.func_code,'q', -1), None) - - # constant assigned - self.assertEqual(extract_constant(f1.func_code,'x', -1), "test") - - # expression assigned - self.assertEqual(extract_constant(f1.func_code,'y', -1), -1) - - # recognized name, not assigned - self.assertEqual(extract_constant(f1.func_code,'z', -1), None) - - - def testFindModule(self): - self.assertRaises(ImportError, find_module, 'no-such.-thing') - self.assertRaises(ImportError, find_module, 'setuptools.non-existent') - f,p,i = find_module('setuptools.tests'); f.close() - - def testModuleExtract(self): - from distutils import __version__ - self.assertEqual( - get_module_constant('distutils','__version__'), __version__ - ) - self.assertEqual( - get_module_constant('sys','version'), sys.version - ) - self.assertEqual( - get_module_constant('setuptools.tests','__doc__'),__doc__ - ) - - def testRequire(self): - - req = Require('Distutils','1.0.3','distutils') - - self.assertEqual(req.name, 'Distutils') - self.assertEqual(req.module, 'distutils') - self.assertEqual(req.requested_version, '1.0.3') - self.assertEqual(req.attribute, '__version__') - self.assertEqual(req.full_name(), 'Distutils-1.0.3') - - from distutils import __version__ - self.assertEqual(req.get_version(), __version__) - self.failUnless(req.version_ok('1.0.9')) - self.failIf(req.version_ok('0.9.1')) - self.failIf(req.version_ok('unknown')) - - self.failUnless(req.is_present()) - self.failUnless(req.is_current()) - - req = Require('Distutils 3000','03000','distutils',format=LooseVersion) - self.failUnless(req.is_present()) - self.failIf(req.is_current()) - self.failIf(req.version_ok('unknown')) - - req = Require('Do-what-I-mean','1.0','d-w-i-m') - self.failIf(req.is_present()) - self.failIf(req.is_current()) - - req = Require('Tests', None, 'tests', homepage="http://example.com") - self.assertEqual(req.format, None) - self.assertEqual(req.attribute, None) - self.assertEqual(req.requested_version, None) - self.assertEqual(req.full_name(), 'Tests') - self.assertEqual(req.homepage, 'http://example.com') - - paths = [os.path.dirname(p) for p in __path__] - self.failUnless(req.is_present(paths)) - self.failUnless(req.is_current(paths)) - - - -class DistroTests(TestCase): - - def setUp(self): - self.e1 = Extension('bar.ext',['bar.c']) - self.e2 = Extension('c.y', ['y.c']) - - self.dist = makeSetup( - packages=['a', 'a.b', 'a.b.c', 'b', 'c'], - py_modules=['b.d','x'], - ext_modules = (self.e1, self.e2), - package_dir = {}, - ) - - - def testDistroType(self): - self.failUnless(isinstance(self.dist,setuptools.dist.Distribution)) - - - def testExcludePackage(self): - self.dist.exclude_package('a') - self.assertEqual(self.dist.packages, ['b','c']) - - self.dist.exclude_package('b') - self.assertEqual(self.dist.packages, ['c']) - self.assertEqual(self.dist.py_modules, ['x']) - self.assertEqual(self.dist.ext_modules, [self.e1, self.e2]) - - self.dist.exclude_package('c') - self.assertEqual(self.dist.packages, []) - self.assertEqual(self.dist.py_modules, ['x']) - self.assertEqual(self.dist.ext_modules, [self.e1]) - - # test removals from unspecified options - makeSetup().exclude_package('x') - - - - - - - - def testIncludeExclude(self): - # remove an extension - self.dist.exclude(ext_modules=[self.e1]) - self.assertEqual(self.dist.ext_modules, [self.e2]) - - # add it back in - self.dist.include(ext_modules=[self.e1]) - self.assertEqual(self.dist.ext_modules, [self.e2, self.e1]) - - # should not add duplicate - self.dist.include(ext_modules=[self.e1]) - self.assertEqual(self.dist.ext_modules, [self.e2, self.e1]) - - def testExcludePackages(self): - self.dist.exclude(packages=['c','b','a']) - self.assertEqual(self.dist.packages, []) - self.assertEqual(self.dist.py_modules, ['x']) - self.assertEqual(self.dist.ext_modules, [self.e1]) - - def testEmpty(self): - dist = makeSetup() - dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) - dist = makeSetup() - dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) - - def testContents(self): - self.failUnless(self.dist.has_contents_for('a')) - self.dist.exclude_package('a') - self.failIf(self.dist.has_contents_for('a')) - - self.failUnless(self.dist.has_contents_for('b')) - self.dist.exclude_package('b') - self.failIf(self.dist.has_contents_for('b')) - - self.failUnless(self.dist.has_contents_for('c')) - self.dist.exclude_package('c') - self.failIf(self.dist.has_contents_for('c')) - - - - - def testInvalidIncludeExclude(self): - self.assertRaises(DistutilsSetupError, - self.dist.include, nonexistent_option='x' - ) - self.assertRaises(DistutilsSetupError, - self.dist.exclude, nonexistent_option='x' - ) - self.assertRaises(DistutilsSetupError, - self.dist.include, packages={'x':'y'} - ) - self.assertRaises(DistutilsSetupError, - self.dist.exclude, packages={'x':'y'} - ) - self.assertRaises(DistutilsSetupError, - self.dist.include, ext_modules={'x':'y'} - ) - self.assertRaises(DistutilsSetupError, - self.dist.exclude, ext_modules={'x':'y'} - ) - - self.assertRaises(DistutilsSetupError, - self.dist.include, package_dir=['q'] - ) - self.assertRaises(DistutilsSetupError, - self.dist.exclude, package_dir=['q'] - ) - - - - - - - - - - - - - - - -class FeatureTests(TestCase): - - def setUp(self): - self.req = Require('Distutils','1.0.3','distutils') - self.dist = makeSetup( - features={ - 'foo': Feature("foo",standard=True,require_features=['baz',self.req]), - 'bar': Feature("bar", standard=True, packages=['pkg.bar'], - py_modules=['bar_et'], remove=['bar.ext'], - ), - 'baz': Feature( - "baz", optional=False, packages=['pkg.baz'], - scripts = ['scripts/baz_it'], - libraries=[('libfoo','foo/foofoo.c')] - ), - 'dwim': Feature("DWIM", available=False, remove='bazish'), - }, - script_args=['--without-bar', 'install'], - packages = ['pkg.bar', 'pkg.foo'], - py_modules = ['bar_et', 'bazish'], - ext_modules = [Extension('bar.ext',['bar.c'])] - ) - - def testDefaults(self): - self.failIf( - Feature( - "test",standard=True,remove='x',available=False - ).include_by_default() - ) - self.failUnless( - Feature("test",standard=True,remove='x').include_by_default() - ) - # Feature must have either kwargs, removes, or require_features - self.assertRaises(DistutilsSetupError, Feature, "test") - - def testAvailability(self): - self.assertRaises( - DistutilsPlatformError, - self.dist.features['dwim'].include_in, self.dist - ) - - def testFeatureOptions(self): - dist = self.dist - self.failUnless( - ('with-dwim',None,'include DWIM') in dist.feature_options - ) - self.failUnless( - ('without-dwim',None,'exclude DWIM (default)') in dist.feature_options - ) - self.failUnless( - ('with-bar',None,'include bar (default)') in dist.feature_options - ) - self.failUnless( - ('without-bar',None,'exclude bar') in dist.feature_options - ) - self.assertEqual(dist.feature_negopt['without-foo'],'with-foo') - self.assertEqual(dist.feature_negopt['without-bar'],'with-bar') - self.assertEqual(dist.feature_negopt['without-dwim'],'with-dwim') - self.failIf('without-baz' in dist.feature_negopt) - - def testUseFeatures(self): - dist = self.dist - self.assertEqual(dist.with_foo,1) - self.assertEqual(dist.with_bar,0) - self.assertEqual(dist.with_baz,1) - self.failIf('bar_et' in dist.py_modules) - self.failIf('pkg.bar' in dist.packages) - self.failUnless('pkg.baz' in dist.packages) - self.failUnless('scripts/baz_it' in dist.scripts) - self.failUnless(('libfoo','foo/foofoo.c') in dist.libraries) - self.assertEqual(dist.ext_modules,[]) - self.assertEqual(dist.require_features, [self.req]) - - # If we ask for bar, it should fail because we explicitly disabled - # it on the command line - self.assertRaises(DistutilsOptionError, dist.include_feature, 'bar') - - def testFeatureWithInvalidRemove(self): - self.assertRaises( - SystemExit, makeSetup, features = {'x':Feature('x', remove='y')} - ) - -class TestCommandTests(TestCase): - - def testTestIsCommand(self): - test_cmd = makeSetup().get_command_obj('test') - self.failUnless(isinstance(test_cmd, distutils.cmd.Command)) - - def testLongOptSuiteWNoDefault(self): - ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite']) - ts1 = ts1.get_command_obj('test') - ts1.ensure_finalized() - self.assertEqual(ts1.test_suite, 'foo.tests.suite') - - def testDefaultSuite(self): - ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test') - ts2.ensure_finalized() - self.assertEqual(ts2.test_suite, 'bar.tests.suite') - - def testDefaultWModuleOnCmdLine(self): - ts3 = makeSetup( - test_suite='bar.tests', - script_args=['test','-m','foo.tests'] - ).get_command_obj('test') - ts3.ensure_finalized() - self.assertEqual(ts3.test_module, 'foo.tests') - self.assertEqual(ts3.test_suite, 'foo.tests.test_suite') - - def testConflictingOptions(self): - ts4 = makeSetup( - script_args=['test','-m','bar.tests', '-s','foo.tests.suite'] - ).get_command_obj('test') - self.assertRaises(DistutilsOptionError, ts4.ensure_finalized) - - def testNoSuite(self): - ts5 = makeSetup().get_command_obj('test') - ts5.ensure_finalized() - self.assertEqual(ts5.test_suite, None) diff --git a/Lib/setuptools/tests/api_tests.txt b/Lib/setuptools/tests/api_tests.txt deleted file mode 100755 index 735ad8d..0000000 --- a/Lib/setuptools/tests/api_tests.txt +++ /dev/null @@ -1,330 +0,0 @@ -Pluggable Distributions of Python Software -========================================== - -Distributions -------------- - -A "Distribution" is a collection of files that represent a "Release" of a -"Project" as of a particular point in time, denoted by a -"Version":: - - >>> import sys, pkg_resources - >>> from pkg_resources import Distribution - >>> Distribution(project_name="Foo", version="1.2") - Foo 1.2 - -Distributions have a location, which can be a filename, URL, or really anything -else you care to use:: - - >>> dist = Distribution( - ... location="http://example.com/something", - ... project_name="Bar", version="0.9" - ... ) - - >>> dist - Bar 0.9 (http://example.com/something) - - -Distributions have various introspectable attributes:: - - >>> dist.location - 'http://example.com/something' - - >>> dist.project_name - 'Bar' - - >>> dist.version - '0.9' - - >>> dist.py_version == sys.version[:3] - True - - >>> print dist.platform - None - -Including various computed attributes:: - - >>> from pkg_resources import parse_version - >>> dist.parsed_version == parse_version(dist.version) - True - - >>> dist.key # case-insensitive form of the project name - 'bar' - -Distributions are compared (and hashed) by version first:: - - >>> Distribution(version='1.0') == Distribution(version='1.0') - True - >>> Distribution(version='1.0') == Distribution(version='1.1') - False - >>> Distribution(version='1.0') < Distribution(version='1.1') - True - -but also by project name (case-insensitive), platform, Python version, -location, etc.:: - - >>> Distribution(project_name="Foo",version="1.0") == \ - ... Distribution(project_name="Foo",version="1.0") - True - - >>> Distribution(project_name="Foo",version="1.0") == \ - ... Distribution(project_name="foo",version="1.0") - True - - >>> Distribution(project_name="Foo",version="1.0") == \ - ... Distribution(project_name="Foo",version="1.1") - False - - >>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \ - ... Distribution(project_name="Foo",py_version="2.4",version="1.0") - False - - >>> Distribution(location="spam",version="1.0") == \ - ... Distribution(location="spam",version="1.0") - True - - >>> Distribution(location="spam",version="1.0") == \ - ... Distribution(location="baz",version="1.0") - False - - - -Hash and compare distribution by prio/plat - -Get version from metadata -provider capabilities -egg_name() -as_requirement() -from_location, from_filename (w/path normalization) - -Releases may have zero or more "Requirements", which indicate -what releases of another project the release requires in order to -function. A Requirement names the other project, expresses some criteria -as to what releases of that project are acceptable, and lists any "Extras" -that the requiring release may need from that project. (An Extra is an -optional feature of a Release, that can only be used if its additional -Requirements are satisfied.) - - - -The Working Set ---------------- - -A collection of active distributions is called a Working Set. Note that a -Working Set can contain any importable distribution, not just pluggable ones. -For example, the Python standard library is an importable distribution that -will usually be part of the Working Set, even though it is not pluggable. -Similarly, when you are doing development work on a project, the files you are -editing are also a Distribution. (And, with a little attention to the -directory names used, and including some additional metadata, such a -"development distribution" can be made pluggable as well.) - - >>> from pkg_resources import WorkingSet - -A working set's entries are the sys.path entries that correspond to the active -distributions. By default, the working set's entries are the items on -``sys.path``:: - - >>> ws = WorkingSet() - >>> ws.entries == sys.path - True - -But you can also create an empty working set explicitly, and add distributions -to it:: - - >>> ws = WorkingSet([]) - >>> ws.add(dist) - >>> ws.entries - ['http://example.com/something'] - >>> dist in ws - True - >>> Distribution('foo',version="") in ws - False - -And you can iterate over its distributions:: - - >>> list(ws) - [Bar 0.9 (http://example.com/something)] - -Adding the same distribution more than once is a no-op:: - - >>> ws.add(dist) - >>> list(ws) - [Bar 0.9 (http://example.com/something)] - -For that matter, adding multiple distributions for the same project also does -nothing, because a working set can only hold one active distribution per -project -- the first one added to it:: - - >>> ws.add( - ... Distribution( - ... 'http://example.com/something', project_name="Bar", - ... version="7.2" - ... ) - ... ) - >>> list(ws) - [Bar 0.9 (http://example.com/something)] - -You can append a path entry to a working set using ``add_entry()``:: - - >>> ws.entries - ['http://example.com/something'] - >>> ws.add_entry(pkg_resources.__file__) - >>> ws.entries - ['http://example.com/something', '...pkg_resources.py...'] - -Multiple additions result in multiple entries, even if the entry is already in -the working set (because ``sys.path`` can contain the same entry more than -once):: - - >>> ws.add_entry(pkg_resources.__file__) - >>> ws.entries - ['...example.com...', '...pkg_resources...', '...pkg_resources...'] - -And you can specify the path entry a distribution was found under, using the -optional second parameter to ``add()``:: - - >>> ws = WorkingSet([]) - >>> ws.add(dist,"foo") - >>> ws.entries - ['foo'] - -But even if a distribution is found under multiple path entries, it still only -shows up once when iterating the working set: - - >>> ws.add_entry(ws.entries[0]) - >>> list(ws) - [Bar 0.9 (http://example.com/something)] - -You can ask a WorkingSet to ``find()`` a distribution matching a requirement:: - - >>> from pkg_resources import Requirement - >>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None - None - - >>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution - Bar 0.9 (http://example.com/something) - -Note that asking for a conflicting version of a distribution already in a -working set triggers a ``pkg_resources.VersionConflict`` error: - - >>> ws.find(Requirement.parse("Bar==1.0")) # doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - VersionConflict: (Bar 0.9 (http://example.com/something), - Requirement.parse('Bar==1.0')) - -You can subscribe a callback function to receive notifications whenever a new -distribution is added to a working set. The callback is immediately invoked -once for each existing distribution in the working set, and then is called -again for new distributions added thereafter:: - - >>> def added(dist): print "Added", dist - >>> ws.subscribe(added) - Added Bar 0.9 - >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12") - >>> ws.add(foo12) - Added Foo 1.2 - -Note, however, that only the first distribution added for a given project name -will trigger a callback, even during the initial ``subscribe()`` callback:: - - >>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14") - >>> ws.add(foo14) # no callback, because Foo 1.2 is already active - - >>> ws = WorkingSet([]) - >>> ws.add(foo12) - >>> ws.add(foo14) - >>> ws.subscribe(added) - Added Foo 1.2 - -And adding a callback more than once has no effect, either:: - - >>> ws.subscribe(added) # no callbacks - - # and no double-callbacks on subsequent additions, either - >>> just_a_test = Distribution(project_name="JustATest", version="0.99") - >>> ws.add(just_a_test) - Added JustATest 0.99 - - -Finding Plugins ---------------- - -``WorkingSet`` objects can be used to figure out what plugins in an -``Environment`` can be loaded without any resolution errors:: - - >>> from pkg_resources import Environment - - >>> plugins = Environment([]) # normally, a list of plugin directories - >>> plugins.add(foo12) - >>> plugins.add(foo14) - >>> plugins.add(just_a_test) - -In the simplest case, we just get the newest version of each distribution in -the plugin environment:: - - >>> ws = WorkingSet([]) - >>> ws.find_plugins(plugins) - ([JustATest 0.99, Foo 1.4 (f14)], {}) - -But if there's a problem with a version conflict or missing requirements, the -method falls back to older versions, and the error info dict will contain an -exception instance for each unloadable plugin:: - - >>> ws.add(foo12) # this will conflict with Foo 1.4 - >>> ws.find_plugins(plugins) - ([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)}) - -But if you disallow fallbacks, the failed plugin will be skipped instead of -trying older versions:: - - >>> ws.find_plugins(plugins, fallback=False) - ([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)}) - - - -Platform Compatibility Rules ----------------------------- - -On the Mac, there are potential compatibility issues for modules compiled -on newer versions of Mac OS X than what the user is running. Additionally, -Mac OS X will soon have two platforms to contend with: Intel and PowerPC. - -Basic equality works as on other platforms:: - - >>> from pkg_resources import compatible_platforms as cp - >>> reqd = 'macosx-10.4-ppc' - >>> cp(reqd, reqd) - True - >>> cp("win32", reqd) - False - -Distributions made on other machine types are not compatible:: - - >>> cp("macosx-10.4-i386", reqd) - False - -Distributions made on earlier versions of the OS are compatible, as -long as they are from the same top-level version. The patchlevel version -number does not matter:: - - >>> cp("macosx-10.4-ppc", reqd) - True - >>> cp("macosx-10.3-ppc", reqd) - True - >>> cp("macosx-10.5-ppc", reqd) - False - >>> cp("macosx-9.5-ppc", reqd) - False - -Backwards compatibility for packages made via earlier versions of -setuptools is provided as well:: - - >>> cp("darwin-8.2.0-Power_Macintosh", reqd) - True - >>> cp("darwin-7.2.0-Power_Macintosh", reqd) - True - >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc") - False - diff --git a/Lib/setuptools/tests/test_resources.py b/Lib/setuptools/tests/test_resources.py deleted file mode 100644 index f32c72e..0000000 --- a/Lib/setuptools/tests/test_resources.py +++ /dev/null @@ -1,483 +0,0 @@ -from unittest import TestCase, makeSuite -from pkg_resources import * -import pkg_resources, sys -from sets import ImmutableSet - -class Metadata(EmptyProvider): - """Mock object to return metadata as if from an on-disk distribution""" - - def __init__(self,*pairs): - self.metadata = dict(pairs) - - def has_metadata(self,name): - return name in self.metadata - - def get_metadata(self,name): - return self.metadata[name] - - def get_metadata_lines(self,name): - return yield_lines(self.get_metadata(name)) - - -class DistroTests(TestCase): - - def testCollection(self): - # empty path should produce no distributions - ad = Environment([], platform=None, python=None) - self.assertEqual(list(ad), []) - self.assertEqual(ad['FooPkg'],[]) - - ad.add(Distribution.from_filename("FooPkg-1.3_1.egg")) - ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")) - ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg")) - - # Name is in there now - self.failUnless(ad['FooPkg']) - - # But only 1 package - self.assertEqual(list(ad), ['foopkg']) - - - - # Distributions sort by version - self.assertEqual( - [dist.version for dist in ad['FooPkg']], ['1.4','1.3-1','1.2'] - ) - # Removing a distribution leaves sequence alone - ad.remove(ad['FooPkg'][1]) - self.assertEqual( - [dist.version for dist in ad['FooPkg']], ['1.4','1.2'] - ) - # And inserting adds them in order - ad.add(Distribution.from_filename("FooPkg-1.9.egg")) - self.assertEqual( - [dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2'] - ) - - ws = WorkingSet([]) - foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg") - foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg") - req, = parse_requirements("FooPkg>=1.3") - - # Nominal case: no distros on path, should yield all applicable - self.assertEqual(ad.best_match(req,ws).version, '1.9') - # If a matching distro is already installed, should return only that - ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4') - - # If the first matching distro is unsuitable, it's a version conflict - ws = WorkingSet([]); ws.add(foo12); ws.add(foo14) - self.assertRaises(VersionConflict, ad.best_match, req, ws) - - # If more than one match on the path, the first one takes precedence - ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14); - self.assertEqual(ad.best_match(req,ws).version, '1.4') - - def checkFooPkg(self,d): - self.assertEqual(d.project_name, "FooPkg") - self.assertEqual(d.key, "foopkg") - self.assertEqual(d.version, "1.3-1") - self.assertEqual(d.py_version, "2.4") - self.assertEqual(d.platform, "win32") - self.assertEqual(d.parsed_version, parse_version("1.3-1")) - - def testDistroBasics(self): - d = Distribution( - "/some/path", - project_name="FooPkg",version="1.3-1",py_version="2.4",platform="win32" - ) - self.checkFooPkg(d) - - d = Distribution("/some/path") - self.assertEqual(d.py_version, sys.version[:3]) - self.assertEqual(d.platform, None) - - def testDistroParse(self): - d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg") - self.checkFooPkg(d) - d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info") - self.checkFooPkg(d) - - def testDistroMetadata(self): - d = Distribution( - "/some/path", project_name="FooPkg", py_version="2.4", platform="win32", - metadata = Metadata( - ('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n") - ) - ) - self.checkFooPkg(d) - - - def distRequires(self, txt): - return Distribution("/foo", metadata=Metadata(('depends.txt', txt))) - - def checkRequires(self, dist, txt, extras=()): - self.assertEqual( - list(dist.requires(extras)), - list(parse_requirements(txt)) - ) - - def testDistroDependsSimple(self): - for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0": - self.checkRequires(self.distRequires(v), v) - - - def testResolve(self): - ad = Environment([]); ws = WorkingSet([]) - # Resolving no requirements -> nothing to install - self.assertEqual( list(ws.resolve([],ad)), [] ) - # Request something not in the collection -> DistributionNotFound - self.assertRaises( - DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad - ) - Foo = Distribution.from_filename( - "/foo_dir/Foo-1.2.egg", - metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0")) - ) - ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg")) - - # Request thing(s) that are available -> list to activate - for i in range(3): - targets = list(ws.resolve(parse_requirements("Foo"), ad)) - self.assertEqual(targets, [Foo]) - map(ws.add,targets) - self.assertRaises(VersionConflict, ws.resolve, - parse_requirements("Foo==0.9"), ad) - ws = WorkingSet([]) # reset - - # Request an extra that causes an unresolved dependency for "Baz" - self.assertRaises( - DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad - ) - Baz = Distribution.from_filename( - "/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo")) - ) - ad.add(Baz) - - # Activation list now includes resolved dependency - self.assertEqual( - list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz] - ) - # Requests for conflicting versions produce VersionConflict - self.assertRaises( VersionConflict, - ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad - ) - - def testDistroDependsOptions(self): - d = self.distRequires(""" - Twisted>=1.5 - [docgen] - ZConfig>=2.0 - docutils>=0.3 - [fastcgi] - fcgiapp>=0.1""") - self.checkRequires(d,"Twisted>=1.5") - self.checkRequires( - d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"] - ) - self.checkRequires( - d,"Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"] - ) - self.checkRequires( - d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(), - ["docgen","fastcgi"] - ) - self.checkRequires( - d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(), - ["fastcgi", "docgen"] - ) - self.assertRaises(UnknownExtra, d.requires, ["foo"]) - - - - - - - - - - - - - - - - - -class EntryPointTests(TestCase): - - def assertfields(self, ep): - self.assertEqual(ep.name,"foo") - self.assertEqual(ep.module_name,"setuptools.tests.test_resources") - self.assertEqual(ep.attrs, ("EntryPointTests",)) - self.assertEqual(ep.extras, ("x",)) - self.failUnless(ep.load() is EntryPointTests) - self.assertEqual( - str(ep), - "foo = setuptools.tests.test_resources:EntryPointTests [x]" - ) - - def setUp(self): - self.dist = Distribution.from_filename( - "FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]'))) - - def testBasics(self): - ep = EntryPoint( - "foo", "setuptools.tests.test_resources", ["EntryPointTests"], - ["x"], self.dist - ) - self.assertfields(ep) - - def testParse(self): - s = "foo = setuptools.tests.test_resources:EntryPointTests [x]" - ep = EntryPoint.parse(s, self.dist) - self.assertfields(ep) - - ep = EntryPoint.parse("bar baz= spammity[PING]") - self.assertEqual(ep.name,"bar baz") - self.assertEqual(ep.module_name,"spammity") - self.assertEqual(ep.attrs, ()) - self.assertEqual(ep.extras, ("ping",)) - - ep = EntryPoint.parse(" fizzly = wocka:foo") - self.assertEqual(ep.name,"fizzly") - self.assertEqual(ep.module_name,"wocka") - self.assertEqual(ep.attrs, ("foo",)) - self.assertEqual(ep.extras, ()) - - def testRejects(self): - for ep in [ - "foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2", - ]: - try: EntryPoint.parse(ep) - except ValueError: pass - else: raise AssertionError("Should've been bad", ep) - - def checkSubMap(self, m): - self.assertEqual(str(m), - "{" - "'feature2': EntryPoint.parse(" - "'feature2 = another.module:SomeClass [extra1,extra2]'), " - "'feature1': EntryPoint.parse(" - "'feature1 = somemodule:somefunction')" - "}" - ) - - submap_str = """ - # define features for blah blah - feature1 = somemodule:somefunction - feature2 = another.module:SomeClass [extra1,extra2] - """ - - def testParseList(self): - self.checkSubMap(EntryPoint.parse_group("xyz", self.submap_str)) - self.assertRaises(ValueError, EntryPoint.parse_group, "x a", "foo=bar") - self.assertRaises(ValueError, EntryPoint.parse_group, "x", - ["foo=baz", "foo=bar"]) - - def testParseMap(self): - m = EntryPoint.parse_map({'xyz':self.submap_str}) - self.checkSubMap(m['xyz']) - self.assertEqual(m.keys(),['xyz']) - m = EntryPoint.parse_map("[xyz]\n"+self.submap_str) - self.checkSubMap(m['xyz']) - self.assertEqual(m.keys(),['xyz']) - self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"]) - self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str) - - -class RequirementsTests(TestCase): - - def testBasics(self): - r = Requirement.parse("Twisted>=1.2") - self.assertEqual(str(r),"Twisted>=1.2") - self.assertEqual(repr(r),"Requirement.parse('Twisted>=1.2')") - self.assertEqual(r, Requirement("Twisted", [('>=','1.2')], ())) - self.assertEqual(r, Requirement("twisTed", [('>=','1.2')], ())) - self.assertNotEqual(r, Requirement("Twisted", [('>=','2.0')], ())) - self.assertNotEqual(r, Requirement("Zope", [('>=','1.2')], ())) - self.assertNotEqual(r, Requirement("Zope", [('>=','3.0')], ())) - self.assertNotEqual(r, Requirement.parse("Twisted[extras]>=1.2")) - - def testOrdering(self): - r1 = Requirement("Twisted", [('==','1.2c1'),('>=','1.2')], ()) - r2 = Requirement("Twisted", [('>=','1.2'),('==','1.2c1')], ()) - self.assertEqual(r1,r2) - self.assertEqual(str(r1),str(r2)) - self.assertEqual(str(r2),"Twisted==1.2c1,>=1.2") - - def testBasicContains(self): - r = Requirement("Twisted", [('>=','1.2')], ()) - foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg") - twist11 = Distribution.from_filename("Twisted-1.1.egg") - twist12 = Distribution.from_filename("Twisted-1.2.egg") - self.failUnless(parse_version('1.2') in r) - self.failUnless(parse_version('1.1') not in r) - self.failUnless('1.2' in r) - self.failUnless('1.1' not in r) - self.failUnless(foo_dist not in r) - self.failUnless(twist11 not in r) - self.failUnless(twist12 in r) - - def testAdvancedContains(self): - r, = parse_requirements("Foo>=1.2,<=1.3,==1.9,>2.0,!=2.5,<3.0,==4.5") - for v in ('1.2','1.2.2','1.3','1.9','2.0.1','2.3','2.6','3.0c1','4.5'): - self.failUnless(v in r, (v,r)) - for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'): - self.failUnless(v not in r, (v,r)) - - - def testOptionsAndHashing(self): - r1 = Requirement.parse("Twisted[foo,bar]>=1.2") - r2 = Requirement.parse("Twisted[bar,FOO]>=1.2") - r3 = Requirement.parse("Twisted[BAR,FOO]>=1.2.0") - self.assertEqual(r1,r2) - self.assertEqual(r1,r3) - self.assertEqual(r1.extras, ("foo","bar")) - self.assertEqual(r2.extras, ("bar","foo")) # extras are normalized - self.assertEqual(hash(r1), hash(r2)) - self.assertEqual( - hash(r1), hash(("twisted", ((">=",parse_version("1.2")),), - ImmutableSet(["foo","bar"]))) - ) - - def testVersionEquality(self): - r1 = Requirement.parse("setuptools==0.3a2") - r2 = Requirement.parse("setuptools!=0.3a4") - d = Distribution.from_filename - - self.failIf(d("setuptools-0.3a4.egg") in r1) - self.failIf(d("setuptools-0.3a1.egg") in r1) - self.failIf(d("setuptools-0.3a4.egg") in r2) - - self.failUnless(d("setuptools-0.3a2.egg") in r1) - self.failUnless(d("setuptools-0.3a2.egg") in r2) - self.failUnless(d("setuptools-0.3a3.egg") in r2) - self.failUnless(d("setuptools-0.3a5.egg") in r2) - - - - - - - - - - - - - - -class ParseTests(TestCase): - - def testEmptyParse(self): - self.assertEqual(list(parse_requirements('')), []) - - def testYielding(self): - for inp,out in [ - ([], []), ('x',['x']), ([[]],[]), (' x\n y', ['x','y']), - (['x\n\n','y'], ['x','y']), - ]: - self.assertEqual(list(pkg_resources.yield_lines(inp)),out) - - def testSplitting(self): - self.assertEqual( - list( - pkg_resources.split_sections(""" - x - [Y] - z - - a - [b ] - # foo - c - [ d] - [q] - v - """ - ) - ), - [(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])] - ) - self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo")) - - def testSafeName(self): - self.assertEqual(safe_name("adns-python"), "adns-python") - self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils") - self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils") - self.assertEqual(safe_name("Money$$$Maker"), "Money-Maker") - self.assertNotEqual(safe_name("peak.web"), "peak-web") - - def testSafeVersion(self): - self.assertEqual(safe_version("1.2-1"), "1.2-1") - self.assertEqual(safe_version("1.2 alpha"), "1.2.alpha") - self.assertEqual(safe_version("2.3.4 20050521"), "2.3.4.20050521") - self.assertEqual(safe_version("Money$$$Maker"), "Money-Maker") - self.assertEqual(safe_version("peak.web"), "peak.web") - - def testSimpleRequirements(self): - self.assertEqual( - list(parse_requirements('Twis-Ted>=1.2-1')), - [Requirement('Twis-Ted',[('>=','1.2-1')], ())] - ) - self.assertEqual( - list(parse_requirements('Twisted >=1.2, \ # more\n<2.0')), - [Requirement('Twisted',[('>=','1.2'),('<','2.0')], ())] - ) - self.assertEqual( - Requirement.parse("FooBar==1.99a3"), - Requirement("FooBar", [('==','1.99a3')], ()) - ) - self.assertRaises(ValueError,Requirement.parse,">=2.3") - self.assertRaises(ValueError,Requirement.parse,"x\\") - self.assertRaises(ValueError,Requirement.parse,"x==2 q") - self.assertRaises(ValueError,Requirement.parse,"X==1\nY==2") - self.assertRaises(ValueError,Requirement.parse,"#") - - def testVersionEquality(self): - def c(s1,s2): - p1, p2 = parse_version(s1),parse_version(s2) - self.assertEqual(p1,p2, (s1,s2,p1,p2)) - - c('1.2-rc1', '1.2rc1') - c('0.4', '0.4.0') - c('0.4.0.0', '0.4.0') - c('0.4.0-0', '0.4-0') - c('0pl1', '0.0pl1') - c('0pre1', '0.0c1') - c('0.0.0preview1', '0c1') - c('0.0c1', '0-rc1') - c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a') - - def testVersionOrdering(self): - def c(s1,s2): - p1, p2 = parse_version(s1),parse_version(s2) - self.failUnless(p1: big-endian, std. size & alignment + !: same as > + +The remaining chars indicate types of args and must match exactly; +these can be preceded by a decimal repeat count: + x: pad byte (no data); c:char; b:signed byte; B:unsigned byte; + h:short; H:unsigned short; i:int; I:unsigned int; + l:long; L:unsigned long; f:float; d:double. +Special cases (preceding decimal count indicates length): + s:string (array of char); p: pascal string (with count byte). +Special case (only available in native format): + P:an integer type that is wide enough to hold a pointer. +Special case (not in native mode unless 'long long' in platform C): + q:long long; Q:unsigned long long +Whitespace between formats is ignored. + +The variable struct.error is an exception raised on errors. +""" +__version__ = '0.1' + +from _struct import Struct, error + +_MAXCACHE = 100 +_cache = {} + +def _compile(fmt): + # Internal: compile struct pattern + if len(_cache) >= _MAXCACHE: + _cache.clear() + s = Struct(fmt) + _cache[fmt] = s + return s + +def calcsize(fmt): + """ + Return size of C struct described by format string fmt. + See struct.__doc__ for more on format strings. + """ + try: + o = _cache[fmt] + except KeyError: + o = _compile(fmt) + return o.size + +def pack(fmt, *args): + """ + Return string containing values v1, v2, ... packed according to fmt. + See struct.__doc__ for more on format strings. + """ + try: + o = _cache[fmt] + except KeyError: + o = _compile(fmt) + return o.pack(*args) + +def pack_to(fmt, buf, offset, *args): + """ + Pack the values v2, v2, ... according to fmt, write + the packed bytes into the writable buffer buf starting at offset. + See struct.__doc__ for more on format strings. + """ + try: + o = _cache[fmt] + except KeyError: + o = _compile(fmt) + return o.pack_to(buf, offset, *args) + +def unpack(fmt, s): + """ + Unpack the string, containing packed C structure data, according + to fmt. Requires len(string)==calcsize(fmt). + See struct.__doc__ for more on format strings. + """ + try: + o = _cache[fmt] + except KeyError: + o = _compile(fmt) + return o.unpack(s) + +def unpack_from(fmt, buf, offset=0): + """ + Unpack the buffer, containing packed C structure data, according to + fmt starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt). + See struct.__doc__ for more on format strings. + """ + try: + o = _cache[fmt] + except KeyError: + o = _compile(fmt) + return o.unpack_from(buf, offset) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 3cd0df5..19fe847 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -872,7 +872,7 @@ class Popen(object): # object do the translation: It is based on stdio, which is # impossible to combine with select (unless forcing no # buffering). - if self.universal_newlines and hasattr(open, 'newlines'): + if self.universal_newlines and hasattr(file, 'newlines'): if stdout: stdout = self._translate_newlines(stdout) if stderr: @@ -1141,7 +1141,7 @@ class Popen(object): # object do the translation: It is based on stdio, which is # impossible to combine with select (unless forcing no # buffering). - if self.universal_newlines and hasattr(open, 'newlines'): + if self.universal_newlines and hasattr(file, 'newlines'): if stdout: stdout = self._translate_newlines(stdout) if stderr: diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 0b3d477..061d0f5 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -33,7 +33,7 @@ __version__ = "$Revision$" # $Source$ -version = "0.6.4" +version = "0.8.0" __author__ = "Lars Gustäbel (lars@gustaebel.de)" __date__ = "$Date$" __cvsid__ = "$Id$" @@ -132,21 +132,65 @@ TOEXEC = 0001 # execute/search by other #--------------------------------------------------------- # Some useful functions #--------------------------------------------------------- -def nts(s): - """Convert a null-terminated string buffer to a python string. + +def stn(s, length): + """Convert a python string to a null-terminated string buffer. """ - return s.rstrip(NUL) + return s[:length-1] + (length - len(s) - 1) * NUL + NUL -def calc_chksum(buf): - """Calculate the checksum for a member's header. It's a simple addition - of all bytes, treating the chksum field as if filled with spaces. - buf is a 512 byte long string buffer which holds the header. +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0200): + n = int(s.rstrip(NUL) or "0", 8) + else: + n = 0L + for i in xrange(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, posix=False): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = "%0*o" % (digits - 1, n) + NUL + else: + if posix: + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = "" + for i in xrange(digits - 1): + s = chr(n & 0377) + s + n >>= 8 + s = chr(0200) + s + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. """ - chk = 256 # chksum field is treated as blanks, - # so the initial value is 8 * ord(" ") - for c in buf[:148]: chk += ord(c) # sum up all bytes before chksum - for c in buf[156:]: chk += ord(c) # sum up all bytes after chksum - return chk + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum def copyfileobj(src, dst, length=None): """Copy length bytes from fileobj src to fileobj dst. @@ -163,13 +207,13 @@ def copyfileobj(src, dst, length=None): for b in xrange(blocks): buf = src.read(BUFSIZE) if len(buf) < BUFSIZE: - raise IOError, "end of file reached" + raise IOError("end of file reached") dst.write(buf) if remainder != 0: buf = src.read(remainder) if len(buf) < remainder: - raise IOError, "end of file reached" + raise IOError("end of file reached") dst.write(buf) return @@ -301,7 +345,7 @@ class _Stream: try: import zlib except ImportError: - raise CompressionError, "zlib module is not available" + raise CompressionError("zlib module is not available") self.zlib = zlib self.crc = zlib.crc32("") if mode == "r": @@ -313,7 +357,7 @@ class _Stream: try: import bz2 except ImportError: - raise CompressionError, "bz2 module is not available" + raise CompressionError("bz2 module is not available") if mode == "r": self.dbuf = "" self.cmp = bz2.BZ2Decompressor() @@ -389,9 +433,9 @@ class _Stream: # taken from gzip.GzipFile with some alterations if self.__read(2) != "\037\213": - raise ReadError, "not a gzip file" + raise ReadError("not a gzip file") if self.__read(1) != "\010": - raise CompressionError, "unsupported compression method" + raise CompressionError("unsupported compression method") flag = ord(self.__read(1)) self.__read(6) @@ -427,7 +471,7 @@ class _Stream: self.read(self.bufsize) self.read(remainder) else: - raise StreamError, "seeking backwards is not allowed" + raise StreamError("seeking backwards is not allowed") return self.pos def read(self, size=None): @@ -508,6 +552,69 @@ class _StreamProxy(object): self.fileobj.close() # class StreamProxy +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = "" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + b = [self.buf] + x = len(self.buf) + while x < size: + try: + raw = self.fileobj.read(self.blocksize) + data = self.bz2obj.decompress(raw) + b.append(data) + except EOFError: + break + x += len(data) + self.buf = "".join(b) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) + self.fileobj.close() +# class _BZ2Proxy + #------------------------ # Extraction file object #------------------------ @@ -581,7 +688,7 @@ class ExFileObject(object): """Read operation for regular files. """ if self.closed: - raise ValueError, "file is closed" + raise ValueError("file is closed") self.fileobj.seek(self.offset + self.pos) bytesleft = self.size - self.pos if size is None: @@ -595,7 +702,7 @@ class ExFileObject(object): """Read operation for sparse files. """ if self.closed: - raise ValueError, "file is closed" + raise ValueError("file is closed") if size is None: size = self.size - self.pos @@ -684,24 +791,24 @@ class TarInfo(object): of the member. """ - self.name = name # member name (dirnames must end with '/') - self.mode = 0666 # file permissions - self.uid = 0 # user id - self.gid = 0 # group id - self.size = 0 # file size - self.mtime = 0 # modification time - self.chksum = 0 # header checksum - self.type = REGTYPE # member type - self.linkname = "" # link name - self.uname = "user" # user name - self.gname = "group" # group name - self.devmajor = 0 #- - self.devminor = 0 #-for use with CHRTYPE and BLKTYPE - self.prefix = "" # prefix to filename or holding information - # about sparse files - - self.offset = 0 # the tar header starts here - self.offset_data = 0 # the file's data starts here + self.name = name # member name (dirnames must end with '/') + self.mode = 0666 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "user" # user name + self.gname = "group" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + self.prefix = "" # prefix to filename or information + # about sparse files + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here def __repr__(self): return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) @@ -710,95 +817,57 @@ class TarInfo(object): def frombuf(cls, buf): """Construct a TarInfo object from a 512 byte string buffer. """ + if len(buf) != BLOCKSIZE: + raise ValueError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise ValueError("empty header") + tarinfo = cls() - tarinfo.name = nts(buf[0:100]) - tarinfo.mode = int(buf[100:108], 8) - tarinfo.uid = int(buf[108:116],8) - tarinfo.gid = int(buf[116:124],8) - - # There are two possible codings for the size field we - # have to discriminate, see comment in tobuf() below. - if buf[124] != chr(0200): - tarinfo.size = long(buf[124:136], 8) - else: - tarinfo.size = 0L - for i in range(11): - tarinfo.size <<= 8 - tarinfo.size += ord(buf[125 + i]) - - tarinfo.mtime = long(buf[136:148], 8) - tarinfo.chksum = int(buf[148:156], 8) - tarinfo.type = buf[156:157] - tarinfo.linkname = nts(buf[157:257]) - tarinfo.uname = nts(buf[265:297]) - tarinfo.gname = nts(buf[297:329]) - try: - tarinfo.devmajor = int(buf[329:337], 8) - tarinfo.devminor = int(buf[337:345], 8) - except ValueError: - tarinfo.devmajor = tarinfo.devmajor = 0 + tarinfo.buf = buf + tarinfo.name = buf[0:100].rstrip(NUL) + tarinfo.mode = nti(buf[100:108]) + tarinfo.uid = nti(buf[108:116]) + tarinfo.gid = nti(buf[116:124]) + tarinfo.size = nti(buf[124:136]) + tarinfo.mtime = nti(buf[136:148]) + tarinfo.chksum = nti(buf[148:156]) + tarinfo.type = buf[156:157] + tarinfo.linkname = buf[157:257].rstrip(NUL) + tarinfo.uname = buf[265:297].rstrip(NUL) + tarinfo.gname = buf[297:329].rstrip(NUL) + tarinfo.devmajor = nti(buf[329:337]) + tarinfo.devminor = nti(buf[337:345]) tarinfo.prefix = buf[345:500] - # Some old tar programs represent a directory as a regular - # file with a trailing slash. - if tarinfo.isreg() and tarinfo.name.endswith("/"): - tarinfo.type = DIRTYPE - - # The prefix field is used for filenames > 100 in - # the POSIX standard. - # name = prefix + '/' + name - if tarinfo.type != GNUTYPE_SPARSE: - tarinfo.name = normpath(os.path.join(nts(tarinfo.prefix), tarinfo.name)) - - # Directory names should have a '/' at the end. - if tarinfo.isdir(): - tarinfo.name += "/" + if tarinfo.chksum not in calc_chksums(buf): + raise ValueError("invalid header") return tarinfo - def tobuf(self): + def tobuf(self, posix=False): """Return a tar header block as a 512 byte string. """ - # Prefer the size to be encoded as 11 octal ascii digits - # which is the most portable. If the size exceeds this - # limit (>= 8 GB), encode it as an 88-bit value which is - # a GNU tar feature. - if self.size <= MAXSIZE_MEMBER: - size = "%011o" % self.size - else: - s = self.size - size = "" - for i in range(11): - size = chr(s & 0377) + size - s >>= 8 - size = chr(0200) + size - - # The following code was contributed by Detlef Lannert. - parts = [] - for value, fieldsize in ( - (self.name, 100), - ("%07o" % (self.mode & 07777), 8), - ("%07o" % self.uid, 8), - ("%07o" % self.gid, 8), - (size, 12), - ("%011o" % self.mtime, 12), - (" ", 8), - (self.type, 1), - (self.linkname, 100), - (MAGIC, 6), - (VERSION, 2), - (self.uname, 32), - (self.gname, 32), - ("%07o" % self.devmajor, 8), - ("%07o" % self.devminor, 8), - (self.prefix, 155) - ): - l = len(value) - parts.append(value[:fieldsize] + (fieldsize - l) * NUL) - - buf = "".join(parts) - chksum = calc_chksum(buf) + parts = [ + stn(self.name, 100), + itn(self.mode & 07777, 8, posix), + itn(self.uid, 8, posix), + itn(self.gid, 8, posix), + itn(self.size, 12, posix), + itn(self.mtime, 12, posix), + " ", # checksum field + self.type, + stn(self.linkname, 100), + stn(MAGIC, 6), + stn(VERSION, 2), + stn(self.uname, 32), + stn(self.gname, 32), + itn(self.devmajor, 8, posix), + itn(self.devminor, 8, posix), + stn(self.prefix, 155) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, "".join(parts)) + chksum = calc_chksums(buf)[0] buf = buf[:148] + "%06o\0" % chksum + buf[155:] - buf += (BLOCKSIZE - len(buf)) * NUL self.buf = buf return buf @@ -857,7 +926,7 @@ class TarFile(object): self.name = name if len(mode) > 1 or mode not in "raw": - raise ValueError, "mode must be 'r', 'a' or 'w'" + raise ValueError("mode must be 'r', 'a' or 'w'") self._mode = mode self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] @@ -873,12 +942,12 @@ class TarFile(object): self.fileobj = fileobj # Init datastructures - self.closed = False - self.members = [] # list of members as TarInfo objects - self._loaded = False # flag if all members have been read - self.offset = 0L # current position in the archive file - self.inodes = {} # dictionary caching the inodes of - # archive members already added + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = 0L # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added if self._mode == "r": self.firstmember = None @@ -937,7 +1006,7 @@ class TarFile(object): """ if not name and not fileobj: - raise ValueError, "nothing to open" + raise ValueError("nothing to open") if mode in ("r", "r:*"): # Find out which *open() is appropriate for opening the file. @@ -947,7 +1016,7 @@ class TarFile(object): return func(name, "r", fileobj) except (ReadError, CompressionError): continue - raise ReadError, "file could not be opened successfully" + raise ReadError("file could not be opened successfully") elif ":" in mode: filemode, comptype = mode.split(":", 1) @@ -959,7 +1028,7 @@ class TarFile(object): if comptype in cls.OPEN_METH: func = getattr(cls, cls.OPEN_METH[comptype]) else: - raise CompressionError, "unknown compression type %r" % comptype + raise CompressionError("unknown compression type %r" % comptype) return func(name, filemode, fileobj) elif "|" in mode: @@ -968,7 +1037,7 @@ class TarFile(object): comptype = comptype or "tar" if filemode not in "rw": - raise ValueError, "mode must be 'r' or 'w'" + raise ValueError("mode must be 'r' or 'w'") t = cls(name, filemode, _Stream(name, filemode, comptype, fileobj, bufsize)) @@ -978,14 +1047,14 @@ class TarFile(object): elif mode in "aw": return cls.taropen(name, mode, fileobj) - raise ValueError, "undiscernible mode" + raise ValueError("undiscernible mode") @classmethod def taropen(cls, name, mode="r", fileobj=None): """Open uncompressed tar archive name for reading or writing. """ if len(mode) > 1 or mode not in "raw": - raise ValueError, "mode must be 'r', 'a' or 'w'" + raise ValueError("mode must be 'r', 'a' or 'w'") return cls(name, mode, fileobj) @classmethod @@ -994,13 +1063,13 @@ class TarFile(object): Appending is not allowed. """ if len(mode) > 1 or mode not in "rw": - raise ValueError, "mode must be 'r' or 'w'" + raise ValueError("mode must be 'r' or 'w'") try: import gzip gzip.GzipFile except (ImportError, AttributeError): - raise CompressionError, "gzip module is not available" + raise CompressionError("gzip module is not available") pre, ext = os.path.splitext(name) pre = os.path.basename(pre) @@ -1021,7 +1090,7 @@ class TarFile(object): gzip.GzipFile(name, mode, compresslevel, fileobj) ) except IOError: - raise ReadError, "not a gzip file" + raise ReadError("not a gzip file") t._extfileobj = False return t @@ -1031,12 +1100,12 @@ class TarFile(object): Appending is not allowed. """ if len(mode) > 1 or mode not in "rw": - raise ValueError, "mode must be 'r' or 'w'." + raise ValueError("mode must be 'r' or 'w'.") try: import bz2 except ImportError: - raise CompressionError, "bz2 module is not available" + raise CompressionError("bz2 module is not available") pre, ext = os.path.splitext(name) pre = os.path.basename(pre) @@ -1047,12 +1116,14 @@ class TarFile(object): tarname = pre + ext if fileobj is not None: - raise ValueError, "no support for external file objects" + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) try: - t = cls.taropen(tarname, mode, bz2.BZ2File(name, mode, compresslevel=compresslevel)) + t = cls.taropen(tarname, mode, fileobj) except IOError: - raise ReadError, "not a bzip2 file" + raise ReadError("not a bzip2 file") t._extfileobj = False return t @@ -1094,7 +1165,7 @@ class TarFile(object): """ tarinfo = self._getmember(name) if tarinfo is None: - raise KeyError, "filename %r not found" % name + raise KeyError("filename %r not found" % name) return tarinfo def getmembers(self): @@ -1313,15 +1384,14 @@ class TarFile(object): if tarinfo.size > MAXSIZE_MEMBER: if self.posix: - raise ValueError, "file is too large (>= 8 GB)" + raise ValueError("file is too large (>= 8 GB)") else: self._dbg(2, "tarfile: Created GNU tar largefile header") if len(tarinfo.linkname) > LENGTH_LINK: if self.posix: - raise ValueError, "linkname is too long (>%d)" \ - % (LENGTH_LINK) + raise ValueError("linkname is too long (>%d)" % (LENGTH_LINK)) else: self._create_gnulong(tarinfo.linkname, GNUTYPE_LONGLINK) tarinfo.linkname = tarinfo.linkname[:LENGTH_LINK -1] @@ -1337,8 +1407,7 @@ class TarFile(object): prefix = prefix[:-1] if not prefix or len(name) > LENGTH_NAME: - raise ValueError, "name is too long (>%d)" \ - % (LENGTH_NAME) + raise ValueError("name is too long (>%d)" % (LENGTH_NAME)) tarinfo.name = name tarinfo.prefix = prefix @@ -1347,7 +1416,7 @@ class TarFile(object): tarinfo.name = tarinfo.name[:LENGTH_NAME - 1] self._dbg(2, "tarfile: Created GNU tar extension LONGNAME") - self.fileobj.write(tarinfo.tobuf()) + self.fileobj.write(tarinfo.tobuf(self.posix)) self.offset += BLOCKSIZE # If there's data to follow, append it. @@ -1464,7 +1533,7 @@ class TarFile(object): # A small but ugly workaround for the case that someone tries # to extract a (sym)link as a file-object from a non-seekable # stream of tar blocks. - raise StreamError, "cannot extract (sym)link as file object" + raise StreamError("cannot extract (sym)link as file object") else: # A (sym)link's file object is its target's file object. return self.extractfile(self._getmember(tarinfo.linkname, @@ -1564,13 +1633,13 @@ class TarFile(object): if hasattr(os, "mkfifo"): os.mkfifo(targetpath) else: - raise ExtractError, "fifo not supported by system" + raise ExtractError("fifo not supported by system") def makedev(self, tarinfo, targetpath): """Make a character or block device called targetpath. """ if not hasattr(os, "mknod") or not hasattr(os, "makedev"): - raise ExtractError, "special devices not supported by system" + raise ExtractError("special devices not supported by system") mode = tarinfo.mode if tarinfo.isblk(): @@ -1606,7 +1675,7 @@ class TarFile(object): try: shutil.copy2(linkpath, targetpath) except EnvironmentError, e: - raise IOError, "link could not be created" + raise IOError("link could not be created") def chown(self, tarinfo, targetpath): """Set owner of targetpath according to tarinfo. @@ -1634,7 +1703,7 @@ class TarFile(object): if sys.platform != "os2emx": os.chown(targetpath, u, g) except EnvironmentError, e: - raise ExtractError, "could not change owner" + raise ExtractError("could not change owner") def chmod(self, tarinfo, targetpath): """Set file permissions of targetpath according to tarinfo. @@ -1643,7 +1712,7 @@ class TarFile(object): try: os.chmod(targetpath, tarinfo.mode) except EnvironmentError, e: - raise ExtractError, "could not change mode" + raise ExtractError("could not change mode") def utime(self, tarinfo, targetpath): """Set modification time of targetpath according to tarinfo. @@ -1657,10 +1726,9 @@ class TarFile(object): try: os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) except EnvironmentError, e: - raise ExtractError, "could not change modification time" + raise ExtractError("could not change modification time") #-------------------------------------------------------------------------- - def next(self): """Return the next member of the archive as a TarInfo object, when TarFile is opened for reading. Return None if there is no more @@ -1678,70 +1746,91 @@ class TarFile(object): buf = self.fileobj.read(BLOCKSIZE) if not buf: return None + try: tarinfo = TarInfo.frombuf(buf) - except ValueError: + + # We shouldn't rely on this checksum, because some tar programs + # calculate it differently and it is merely validating the + # header block. We could just as well skip this part, which would + # have a slight effect on performance... + if tarinfo.chksum not in calc_chksums(buf): + self._dbg(1, "tarfile: Bad Checksum %r" % tarinfo.name) + + # Set the TarInfo object's offset to the current position of the + # TarFile and set self.offset to the position where the data blocks + # should begin. + tarinfo.offset = self.offset + self.offset += BLOCKSIZE + + tarinfo = self.proc_member(tarinfo) + + except ValueError, e: if self.ignore_zeros: - if buf.count(NUL) == BLOCKSIZE: - adj = "empty" - else: - adj = "invalid" - self._dbg(2, "0x%X: %s block" % (self.offset, adj)) + self._dbg(2, "0x%X: empty or invalid block: %s" % + (self.offset, e)) self.offset += BLOCKSIZE continue else: - # Block is empty or unreadable. if self.offset == 0: - # If the first block is invalid. That does not - # look like a tar archive we can handle. - raise ReadError,"empty, unreadable or compressed file" + raise ReadError("empty, unreadable or compressed " + "file: %s" % e) return None break - # We shouldn't rely on this checksum, because some tar programs - # calculate it differently and it is merely validating the - # header block. We could just as well skip this part, which would - # have a slight effect on performance... - if tarinfo.chksum != calc_chksum(buf): - self._dbg(1, "tarfile: Bad Checksum %r" % tarinfo.name) - - # Set the TarInfo object's offset to the current position of the - # TarFile and set self.offset to the position where the data blocks - # should begin. - tarinfo.offset = self.offset - self.offset += BLOCKSIZE + # Some old tar programs represent a directory as a regular + # file with a trailing slash. + if tarinfo.isreg() and tarinfo.name.endswith("/"): + tarinfo.type = DIRTYPE - # Check if the TarInfo object has a typeflag for which a callback - # method is registered in the TYPE_METH. If so, then call it. - if tarinfo.type in self.TYPE_METH: - return self.TYPE_METH[tarinfo.type](self, tarinfo) + # The prefix field is used for filenames > 100 in + # the POSIX standard. + # name = prefix + '/' + name + tarinfo.name = normpath(os.path.join(tarinfo.prefix.rstrip(NUL), + tarinfo.name)) - tarinfo.offset_data = self.offset - if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES: - # Skip the following data blocks. - self.offset += self._block(tarinfo.size) + # Directory names should have a '/' at the end. + if tarinfo.isdir(): + tarinfo.name += "/" self.members.append(tarinfo) return tarinfo #-------------------------------------------------------------------------- - # Below are some methods which are called for special typeflags in the - # next() method, e.g. for unwrapping GNU longname/longlink blocks. They - # are registered in TYPE_METH below. You can register your own methods - # with this mapping. - # A registered method is called with a TarInfo object as only argument. - # - # During its execution the method MUST perform the following tasks: - # 1. set tarinfo.offset_data to the position where the data blocks begin, - # if there is data to follow. - # 2. set self.offset to the position where the next member's header will + # The following are methods that are called depending on the type of a + # member. The entry point is proc_member() which is called with a TarInfo + # object created from the header block from the current offset. The + # proc_member() method can be overridden in a subclass to add custom + # proc_*() methods. A proc_*() method MUST implement the following + # operations: + # 1. Set tarinfo.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set self.offset to the position where the next member's header will # begin. - # 3. append the tarinfo object to self.members, if it is supposed to appear - # as a member of the TarFile object. - # 4. return tarinfo or another valid TarInfo object. + # 3. Return tarinfo or another valid TarInfo object. + def proc_member(self, tarinfo): + """Choose the right processing method for tarinfo depending + on its type and call it. + """ + if tarinfo.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self.proc_gnulong(tarinfo) + elif tarinfo.type == GNUTYPE_SPARSE: + return self.proc_sparse(tarinfo) + else: + return self.proc_builtin(tarinfo) + + def proc_builtin(self, tarinfo): + """Process a builtin type member or an unknown member + which will be treated as a regular file. + """ + tarinfo.offset_data = self.offset + if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + self.offset += self._block(tarinfo.size) + return tarinfo def proc_gnulong(self, tarinfo): - """Evaluate the blocks that hold a GNU longname + """Process the blocks that hold a GNU longname or longlink member. """ buf = "" @@ -1752,21 +1841,27 @@ class TarFile(object): self.offset += BLOCKSIZE count -= BLOCKSIZE - # Fetch the next header - next = self.next() + # Fetch the next header and process it. + b = self.fileobj.read(BLOCKSIZE) + t = TarInfo.frombuf(b) + t.offset = self.offset + self.offset += BLOCKSIZE + next = self.proc_member(t) + # Patch the TarInfo object from the next header with + # the longname information. next.offset = tarinfo.offset if tarinfo.type == GNUTYPE_LONGNAME: - next.name = nts(buf) + next.name = buf.rstrip(NUL) elif tarinfo.type == GNUTYPE_LONGLINK: - next.linkname = nts(buf) + next.linkname = buf.rstrip(NUL) return next def proc_sparse(self, tarinfo): - """Analyze a GNU sparse header plus extra headers. + """Process a GNU sparse header plus extra headers. """ - buf = tarinfo.tobuf() + buf = tarinfo.buf sp = _ringbuffer() pos = 386 lastpos = 0L @@ -1775,8 +1870,8 @@ class TarFile(object): # first header. for i in xrange(4): try: - offset = int(buf[pos:pos + 12], 8) - numbytes = int(buf[pos + 12:pos + 24], 8) + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) except ValueError: break if offset > lastpos: @@ -1787,7 +1882,7 @@ class TarFile(object): pos += 24 isextended = ord(buf[482]) - origsize = int(buf[483:495], 8) + origsize = nti(buf[483:495]) # If the isextended flag is given, # there are extra headers to process. @@ -1797,8 +1892,8 @@ class TarFile(object): pos = 0 for i in xrange(21): try: - offset = int(buf[pos:pos + 12], 8) - numbytes = int(buf[pos + 12:pos + 24], 8) + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) except ValueError: break if offset > lastpos: @@ -1818,17 +1913,11 @@ class TarFile(object): self.offset += self._block(tarinfo.size) tarinfo.size = origsize - self.members.append(tarinfo) - return tarinfo + # Clear the prefix field so that it is not used + # as a pathname in next(). + tarinfo.prefix = "" - # The type mapping for the next() method. The keys are single character - # strings, the typeflag. The values are methods which are called when - # next() encounters such a typeflag. - TYPE_METH = { - GNUTYPE_LONGNAME: proc_gnulong, - GNUTYPE_LONGLINK: proc_gnulong, - GNUTYPE_SPARSE: proc_sparse - } + return tarinfo #-------------------------------------------------------------------------- # Little helper methods: @@ -1873,9 +1962,9 @@ class TarFile(object): corresponds to TarFile's mode. """ if self.closed: - raise IOError, "%s is closed" % self.__class__.__name__ + raise IOError("%s is closed" % self.__class__.__name__) if mode is not None and self._mode not in mode: - raise IOError, "bad operation for mode %r" % self._mode + raise IOError("bad operation for mode %r" % self._mode) def __iter__(self): """Provide an iterator object. @@ -2011,7 +2100,7 @@ class TarFileCompat: elif compression == TAR_GZIPPED: self.tarfile = TarFile.gzopen(file, mode) else: - raise ValueError, "unknown compression constant" + raise ValueError("unknown compression constant") if mode[0:1] == "r": members = self.tarfile.getmembers() for m in members: diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index 9ed92d0..58131d7 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -15,6 +15,7 @@ BaseException | | +-- IOError | | +-- OSError | | +-- WindowsError (Windows) + | | +-- VMSError (VMS) | +-- EOFError | +-- ImportError | +-- LookupError @@ -43,4 +44,4 @@ BaseException +-- SyntaxWarning +-- UserWarning +-- FutureWarning - +-- OverflowWarning [not generated by the interpreter] + +-- ImportWarning diff --git a/Lib/test/output/test_logging b/Lib/test/output/test_logging index 7be3a3e..c0d6e06 100644 --- a/Lib/test/output/test_logging +++ b/Lib/test/output/test_logging @@ -488,12 +488,12 @@ INFO:a.b.c.d:Info 5 -- log_test4 begin --------------------------------------------------- config0: ok. config1: ok. -config2: -config3: +config2: +config3: -- log_test4 end --------------------------------------------------- -- log_test5 begin --------------------------------------------------- ERROR:root:just testing -... Don't panic! +... Don't panic! -- log_test5 end --------------------------------------------------- -- logrecv output begin --------------------------------------------------- ERR -> CRITICAL: Message 0 (via logrecv.tcp.ERR) diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 85e1dea..5b9da56 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -4,7 +4,8 @@ import cPickle import pickletools import copy_reg -from test.test_support import TestFailed, have_unicode, TESTFN +from test.test_support import TestFailed, have_unicode, TESTFN, \ + run_with_locale # Tests that try a number of pickle protocols should have a # for proto in protocols: @@ -527,6 +528,11 @@ class AbstractPickleTests(unittest.TestCase): got = self.loads(p) self.assertEqual(n, got) + @run_with_locale('LC_ALL', 'de_DE', 'fr_FR') + def test_float_format(self): + # make sure that floats are formatted locale independent + self.assertEqual(self.dumps(1.2)[0:3], 'F1.') + def test_reduce(self): pass diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 566e54b..86961b0 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -25,6 +25,7 @@ Command line options: -N: nocoverdir -- Put coverage files alongside modules -L: runleaks -- run the leaks(1) command just before exit -R: huntrleaks -- search for reference leaks (needs debug build, v. slow) +-M: memlimit -- run very large memory-consuming tests If non-option arguments are present, they are names for tests to run, unless -x is given, in which case they are names for tests not to run. @@ -63,6 +64,19 @@ of times further it is run and 'fname' is the name of the file the reports are written to. These parameters all have defaults (5, 4 and "reflog.txt" respectively), so the minimal invocation is '-R ::'. +-M runs tests that require an exorbitant amount of memory. These tests +typically try to ascertain containers keep working when containing more than +2 bilion objects, and only work on 64-bit systems. The passed-in memlimit, +which is a string in the form of '2.5Gb', determines howmuch memory the +tests will limit themselves to (but they may go slightly over.) The number +shouldn't be more memory than the machine has (including swap memory). You +should also keep in mind that swap memory is generally much, much slower +than RAM, and setting memlimit to all available RAM or higher will heavily +tax the machine. On the other hand, it is no use running these tests with a +limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect +to use more than memlimit memory will be skipped. The big-memory tests +generally run very, very long. + -u is used to specify which special resource intensive tests to run, such as those requiring large file support or network connectivity. The argument is a comma-separated list of words indicating the @@ -124,6 +138,14 @@ if sys.maxint > 0x7fffffff: warnings.filterwarnings("ignore", "hex/oct constants", FutureWarning, "") +# Ignore ImportWarnings that only occur in the source tree, +# (because of modules with the same name as source-directories in Modules/) +for mod in ("ctypes", "gzip", "zipfile", "tarfile", "encodings.zlib_codec", + "test.test_zipimport", "test.test_zlib", "test.test_zipfile", + "test.test_codecs", "test.string_tests"): + warnings.filterwarnings(module=".*%s$" % (mod,), + action="ignore", category=ImportWarning) + # MacOSX (a.k.a. Darwin) has a default stack size that is too small # for deeply recursive regular expressions. We see this as crashes in # the Python test suite when running test_re.py and test_sre.py. The @@ -180,12 +202,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, generate=False, test_support.record_original_stdout(sys.stdout) try: - opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsrf:lu:t:TD:NLR:w', + opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsrf:lu:t:TD:NLR:wM:', ['help', 'verbose', 'quiet', 'generate', 'exclude', 'single', 'random', 'fromfile', 'findleaks', 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir', 'runleaks', - 'huntrleaks=', 'verbose2', + 'huntrleaks=', 'verbose2', 'memlimit=', ]) except getopt.error, msg: usage(2, msg) @@ -241,6 +263,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, generate=False, huntrleaks[1] = int(huntrleaks[1]) if len(huntrleaks[2]) == 0: huntrleaks[2] = "reflog.txt" + elif o in ('-M', '--memlimit'): + test_support.set_memlimit(a) elif o in ('-u', '--use'): u = [x.lower() for x in a.split(',')] for r in u: @@ -521,6 +545,7 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): def cleanup(): import _strptime, linecache, warnings, dircache import urlparse, urllib, urllib2, mimetypes, doctest + import struct from distutils.dir_util import _path_created _path_created.clear() warnings.filters[:] = fs @@ -537,6 +562,7 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): dircache.reset() linecache.clearcache() mimetypes._default_mime_types() + struct._cache.clear() doctest.master = None if indirect_test: def run_the_test(): diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index aab98c2..489af20 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -243,29 +243,72 @@ class CommonTest(unittest.TestCase): self.checkequal(['a', 'b', 'c d'], 'a b c d', 'split', None, 2) self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None, 3) self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None, 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None, + sys.maxint-1) self.checkequal(['a b c d'], 'a b c d', 'split', None, 0) + self.checkequal(['a b c d'], ' a b c d', 'split', None, 0) self.checkequal(['a', 'b', 'c d'], 'a b c d', 'split', None, 2) + self.checkequal([], ' ', 'split') + self.checkequal(['a'], ' a ', 'split') + self.checkequal(['a', 'b'], ' a b ', 'split') + self.checkequal(['a', 'b '], ' a b ', 'split', None, 1) + self.checkequal(['a', 'b c '], ' a b c ', 'split', None, 1) + self.checkequal(['a', 'b', 'c '], ' a b c ', 'split', None, 2) + self.checkequal(['a', 'b'], '\n\ta \t\r b \v ', 'split') + aaa = ' a '*20 + self.checkequal(['a']*20, aaa, 'split') + self.checkequal(['a'] + [aaa[4:]], aaa, 'split', None, 1) + self.checkequal(['a']*19 + ['a '], aaa, 'split', None, 19) + # by a char self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|') + self.checkequal(['a|b|c|d'], 'a|b|c|d', 'split', '|', 0) self.checkequal(['a', 'b|c|d'], 'a|b|c|d', 'split', '|', 1) self.checkequal(['a', 'b', 'c|d'], 'a|b|c|d', 'split', '|', 2) self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|', 3) self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|', 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|', + sys.maxint-2) self.checkequal(['a|b|c|d'], 'a|b|c|d', 'split', '|', 0) self.checkequal(['a', '', 'b||c||d'], 'a||b||c||d', 'split', '|', 2) self.checkequal(['endcase ', ''], 'endcase |', 'split', '|') + self.checkequal(['', ' startcase'], '| startcase', 'split', '|') + self.checkequal(['', 'bothcase', ''], '|bothcase|', 'split', '|') self.checkequal(['a', '', 'b\x00c\x00d'], 'a\x00\x00b\x00c\x00d', 'split', '\x00', 2) + self.checkequal(['a']*20, ('a|'*20)[:-1], 'split', '|') + self.checkequal(['a']*15 +['a|a|a|a|a'], + ('a|'*20)[:-1], 'split', '|', 15) + # by string self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//') self.checkequal(['a', 'b//c//d'], 'a//b//c//d', 'split', '//', 1) self.checkequal(['a', 'b', 'c//d'], 'a//b//c//d', 'split', '//', 2) self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//', 3) self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//', 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//', + sys.maxint-10) self.checkequal(['a//b//c//d'], 'a//b//c//d', 'split', '//', 0) self.checkequal(['a', '', 'b////c////d'], 'a////b////c////d', 'split', '//', 2) self.checkequal(['endcase ', ''], 'endcase test', 'split', 'test') + self.checkequal(['', ' begincase'], 'test begincase', 'split', 'test') + self.checkequal(['', ' bothcase ', ''], 'test bothcase test', + 'split', 'test') + self.checkequal(['a', 'bc'], 'abbbc', 'split', 'bb') + self.checkequal(['', ''], 'aaa', 'split', 'aaa') + self.checkequal(['aaa'], 'aaa', 'split', 'aaa', 0) + self.checkequal(['ab', 'ab'], 'abbaab', 'split', 'ba') + self.checkequal(['aaaa'], 'aaaa', 'split', 'aab') + self.checkequal([''], '', 'split', 'aaa') + self.checkequal(['aa'], 'aa', 'split', 'aaa') + self.checkequal(['A', 'bobb'], 'Abbobbbobb', 'split', 'bbobb') + self.checkequal(['A', 'B', ''], 'AbbobbBbbobb', 'split', 'bbobb') + + self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'split', 'BLAH') + self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'split', 'BLAH', 19) + self.checkequal(['a']*18 + ['aBLAHa'], ('aBLAH'*20)[:-4], + 'split', 'BLAH', 18) # mixed use of str and unicode self.checkequal([u'a', u'b', u'c d'], 'a b c d', 'split', u' ', 2) @@ -273,6 +316,10 @@ class CommonTest(unittest.TestCase): # argument type self.checkraises(TypeError, 'hello', 'split', 42, 42, 42) + # null case + self.checkraises(ValueError, 'hello', 'split', '') + self.checkraises(ValueError, 'hello', 'split', '', 0) + def test_rsplit(self): self.checkequal(['this', 'is', 'the', 'rsplit', 'function'], 'this is the rsplit function', 'rsplit') @@ -283,29 +330,75 @@ class CommonTest(unittest.TestCase): self.checkequal(['a b', 'c', 'd'], 'a b c d', 'rsplit', None, 2) self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None, 3) self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None, 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None, + sys.maxint-20) self.checkequal(['a b c d'], 'a b c d', 'rsplit', None, 0) + self.checkequal(['a b c d'], 'a b c d ', 'rsplit', None, 0) self.checkequal(['a b', 'c', 'd'], 'a b c d', 'rsplit', None, 2) + self.checkequal([], ' ', 'rsplit') + self.checkequal(['a'], ' a ', 'rsplit') + self.checkequal(['a', 'b'], ' a b ', 'rsplit') + self.checkequal([' a', 'b'], ' a b ', 'rsplit', None, 1) + self.checkequal([' a b','c'], ' a b c ', 'rsplit', + None, 1) + self.checkequal([' a', 'b', 'c'], ' a b c ', 'rsplit', + None, 2) + self.checkequal(['a', 'b'], '\n\ta \t\r b \v ', 'rsplit', None, 88) + aaa = ' a '*20 + self.checkequal(['a']*20, aaa, 'rsplit') + self.checkequal([aaa[:-4]] + ['a'], aaa, 'rsplit', None, 1) + self.checkequal([' a a'] + ['a']*18, aaa, 'rsplit', None, 18) + + # by a char self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|') self.checkequal(['a|b|c', 'd'], 'a|b|c|d', 'rsplit', '|', 1) self.checkequal(['a|b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 2) self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 3) self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', + sys.maxint-100) self.checkequal(['a|b|c|d'], 'a|b|c|d', 'rsplit', '|', 0) self.checkequal(['a||b||c', '', 'd'], 'a||b||c||d', 'rsplit', '|', 2) self.checkequal(['', ' begincase'], '| begincase', 'rsplit', '|') + self.checkequal(['endcase ', ''], 'endcase |', 'rsplit', '|') + self.checkequal(['', 'bothcase', ''], '|bothcase|', 'rsplit', '|') + self.checkequal(['a\x00\x00b', 'c', 'd'], 'a\x00\x00b\x00c\x00d', 'rsplit', '\x00', 2) + self.checkequal(['a']*20, ('a|'*20)[:-1], 'rsplit', '|') + self.checkequal(['a|a|a|a|a']+['a']*15, + ('a|'*20)[:-1], 'rsplit', '|', 15) + # by string self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//') self.checkequal(['a//b//c', 'd'], 'a//b//c//d', 'rsplit', '//', 1) self.checkequal(['a//b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 2) self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 3) self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 4) + self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', + sys.maxint-5) self.checkequal(['a//b//c//d'], 'a//b//c//d', 'rsplit', '//', 0) self.checkequal(['a////b////c', '', 'd'], 'a////b////c////d', 'rsplit', '//', 2) self.checkequal(['', ' begincase'], 'test begincase', 'rsplit', 'test') + self.checkequal(['endcase ', ''], 'endcase test', 'rsplit', 'test') + self.checkequal(['', ' bothcase ', ''], 'test bothcase test', + 'rsplit', 'test') + self.checkequal(['ab', 'c'], 'abbbc', 'rsplit', 'bb') + self.checkequal(['', ''], 'aaa', 'rsplit', 'aaa') + self.checkequal(['aaa'], 'aaa', 'rsplit', 'aaa', 0) + self.checkequal(['ab', 'ab'], 'abbaab', 'rsplit', 'ba') + self.checkequal(['aaaa'], 'aaaa', 'rsplit', 'aab') + self.checkequal([''], '', 'rsplit', 'aaa') + self.checkequal(['aa'], 'aa', 'rsplit', 'aaa') + self.checkequal(['bbob', 'A'], 'bbobbbobbA', 'rsplit', 'bbobb') + self.checkequal(['', 'B', 'A'], 'bbobbBbbobbA', 'rsplit', 'bbobb') + + self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'rsplit', 'BLAH') + self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'rsplit', 'BLAH', 19) + self.checkequal(['aBLAHa'] + ['a']*18, ('aBLAH'*20)[:-4], + 'rsplit', 'BLAH', 18) # mixed use of str and unicode self.checkequal([u'a b', u'c', u'd'], 'a b c d', 'rsplit', u' ', 2) @@ -313,6 +406,10 @@ class CommonTest(unittest.TestCase): # argument type self.checkraises(TypeError, 'hello', 'rsplit', 42, 42, 42) + # null case + self.checkraises(ValueError, 'hello', 'rsplit', '') + self.checkraises(ValueError, 'hello', 'rsplit', '', 0) + def test_strip(self): self.checkequal('hello', ' hello ', 'strip') self.checkequal('hello ', ' hello ', 'lstrip') @@ -376,6 +473,158 @@ class CommonTest(unittest.TestCase): self.checkraises(TypeError, 'hello', 'swapcase', 42) def test_replace(self): + EQ = self.checkequal + + # Operations on the empty string + EQ("", "", "replace", "", "") + + #EQ("A", "", "replace", "", "A") + # That was the correct result; this is the result we actually get + # now (for str, but not for unicode): + #EQ("", "", "replace", "", "A") + + EQ("", "", "replace", "A", "") + EQ("", "", "replace", "A", "A") + EQ("", "", "replace", "", "", 100) + EQ("", "", "replace", "", "", sys.maxint) + + # interleave (from=="", 'to' gets inserted everywhere) + EQ("A", "A", "replace", "", "") + EQ("*A*", "A", "replace", "", "*") + EQ("*1A*1", "A", "replace", "", "*1") + EQ("*-#A*-#", "A", "replace", "", "*-#") + EQ("*-A*-A*-", "AA", "replace", "", "*-") + EQ("*-A*-A*-", "AA", "replace", "", "*-", -1) + EQ("*-A*-A*-", "AA", "replace", "", "*-", sys.maxint) + EQ("*-A*-A*-", "AA", "replace", "", "*-", 4) + EQ("*-A*-A*-", "AA", "replace", "", "*-", 3) + EQ("*-A*-A", "AA", "replace", "", "*-", 2) + EQ("*-AA", "AA", "replace", "", "*-", 1) + EQ("AA", "AA", "replace", "", "*-", 0) + + # single character deletion (from=="A", to=="") + EQ("", "A", "replace", "A", "") + EQ("", "AAA", "replace", "A", "") + EQ("", "AAA", "replace", "A", "", -1) + EQ("", "AAA", "replace", "A", "", sys.maxint) + EQ("", "AAA", "replace", "A", "", 4) + EQ("", "AAA", "replace", "A", "", 3) + EQ("A", "AAA", "replace", "A", "", 2) + EQ("AA", "AAA", "replace", "A", "", 1) + EQ("AAA", "AAA", "replace", "A", "", 0) + EQ("", "AAAAAAAAAA", "replace", "A", "") + EQ("BCD", "ABACADA", "replace", "A", "") + EQ("BCD", "ABACADA", "replace", "A", "", -1) + EQ("BCD", "ABACADA", "replace", "A", "", sys.maxint) + EQ("BCD", "ABACADA", "replace", "A", "", 5) + EQ("BCD", "ABACADA", "replace", "A", "", 4) + EQ("BCDA", "ABACADA", "replace", "A", "", 3) + EQ("BCADA", "ABACADA", "replace", "A", "", 2) + EQ("BACADA", "ABACADA", "replace", "A", "", 1) + EQ("ABACADA", "ABACADA", "replace", "A", "", 0) + EQ("BCD", "ABCAD", "replace", "A", "") + EQ("BCD", "ABCADAA", "replace", "A", "") + EQ("BCD", "BCD", "replace", "A", "") + EQ("*************", "*************", "replace", "A", "") + EQ("^A^", "^"+"A"*1000+"^", "replace", "A", "", 999) + + # substring deletion (from=="the", to=="") + EQ("", "the", "replace", "the", "") + EQ("ater", "theater", "replace", "the", "") + EQ("", "thethe", "replace", "the", "") + EQ("", "thethethethe", "replace", "the", "") + EQ("aaaa", "theatheatheathea", "replace", "the", "") + EQ("that", "that", "replace", "the", "") + EQ("thaet", "thaet", "replace", "the", "") + EQ("here and re", "here and there", "replace", "the", "") + EQ("here and re and re", "here and there and there", + "replace", "the", "", sys.maxint) + EQ("here and re and re", "here and there and there", + "replace", "the", "", -1) + EQ("here and re and re", "here and there and there", + "replace", "the", "", 3) + EQ("here and re and re", "here and there and there", + "replace", "the", "", 2) + EQ("here and re and there", "here and there and there", + "replace", "the", "", 1) + EQ("here and there and there", "here and there and there", + "replace", "the", "", 0) + EQ("here and re and re", "here and there and there", "replace", "the", "") + + EQ("abc", "abc", "replace", "the", "") + EQ("abcdefg", "abcdefg", "replace", "the", "") + + # substring deletion (from=="bob", to=="") + EQ("bob", "bbobob", "replace", "bob", "") + EQ("bobXbob", "bbobobXbbobob", "replace", "bob", "") + EQ("aaaaaaa", "aaaaaaabob", "replace", "bob", "") + EQ("aaaaaaa", "aaaaaaa", "replace", "bob", "") + + # single character replace in place (len(from)==len(to)==1) + EQ("Who goes there?", "Who goes there?", "replace", "o", "o") + EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O") + EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", sys.maxint) + EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", -1) + EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", 3) + EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", 2) + EQ("WhO goes there?", "Who goes there?", "replace", "o", "O", 1) + EQ("Who goes there?", "Who goes there?", "replace", "o", "O", 0) + + EQ("Who goes there?", "Who goes there?", "replace", "a", "q") + EQ("who goes there?", "Who goes there?", "replace", "W", "w") + EQ("wwho goes there?ww", "WWho goes there?WW", "replace", "W", "w") + EQ("Who goes there!", "Who goes there?", "replace", "?", "!") + EQ("Who goes there!!", "Who goes there??", "replace", "?", "!") + + EQ("Who goes there?", "Who goes there?", "replace", ".", "!") + + # substring replace in place (len(from)==len(to) > 1) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**") + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", sys.maxint) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", -1) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 4) + EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 3) + EQ("Th** ** a tissue", "This is a tissue", "replace", "is", "**", 2) + EQ("Th** is a tissue", "This is a tissue", "replace", "is", "**", 1) + EQ("This is a tissue", "This is a tissue", "replace", "is", "**", 0) + EQ("cobob", "bobob", "replace", "bob", "cob") + EQ("cobobXcobocob", "bobobXbobobob", "replace", "bob", "cob") + EQ("bobob", "bobob", "replace", "bot", "bot") + + # replace single character (len(from)==1, len(to)>1) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK") + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", -1) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", sys.maxint) + EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", 2) + EQ("ReyKKjavik", "Reykjavik", "replace", "k", "KK", 1) + EQ("Reykjavik", "Reykjavik", "replace", "k", "KK", 0) + EQ("A----B----C----", "A.B.C.", "replace", ".", "----") + + EQ("Reykjavik", "Reykjavik", "replace", "q", "KK") + + # replace substring (len(from)>1, len(to)!=len(from)) + EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam", + "replace", "spam", "ham") + EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam", + "replace", "spam", "ham", sys.maxint) + EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam", + "replace", "spam", "ham", -1) + EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam", + "replace", "spam", "ham", 4) + EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam", + "replace", "spam", "ham", 3) + EQ("ham, ham, eggs and spam", "spam, spam, eggs and spam", + "replace", "spam", "ham", 2) + EQ("ham, spam, eggs and spam", "spam, spam, eggs and spam", + "replace", "spam", "ham", 1) + EQ("spam, spam, eggs and spam", "spam, spam, eggs and spam", + "replace", "spam", "ham", 0) + + EQ("bobob", "bobobob", "replace", "bobob", "bob") + EQ("bobobXbobob", "bobobobXbobobob", "replace", "bobob", "bob") + EQ("BOBOBOB", "BOBOBOB", "replace", "bob", "bobby") + + # self.checkequal('one@two!three!', 'one!two!three!', 'replace', '!', '@', 1) self.checkequal('onetwothree', 'one!two!three!', 'replace', '!', '') self.checkequal('one@two@three!', 'one!two!three!', 'replace', '!', '@', 2) @@ -403,6 +652,15 @@ class CommonTest(unittest.TestCase): self.checkraises(TypeError, 'hello', 'replace', 42, 'h') self.checkraises(TypeError, 'hello', 'replace', 'h', 42) + def test_replace_overflow(self): + # Check for overflow checking on 32 bit machines + if sys.maxint != 2147483647: + return + A2_16 = "A" * (2**16) + self.checkraises(OverflowError, A2_16, "replace", "", A2_16) + self.checkraises(OverflowError, A2_16, "replace", "A", A2_16) + self.checkraises(OverflowError, A2_16, "replace", "AA", A2_16+A2_16) + def test_zfill(self): self.checkequal('123', '123', 'zfill', 2) self.checkequal('123', '123', 'zfill', 3) @@ -720,6 +978,55 @@ class MixinStrUnicodeUserStringTest: else: self.checkcall(format, "__mod__", value) + def test_inplace_rewrites(self): + # Check that strings don't copy and modify cached single-character strings + self.checkequal('a', 'A', 'lower') + self.checkequal(True, 'A', 'isupper') + self.checkequal('A', 'a', 'upper') + self.checkequal(True, 'a', 'islower') + + self.checkequal('a', 'A', 'replace', 'A', 'a') + self.checkequal(True, 'A', 'isupper') + + self.checkequal('A', 'a', 'capitalize') + self.checkequal(True, 'a', 'islower') + + self.checkequal('A', 'a', 'swapcase') + self.checkequal(True, 'a', 'islower') + + self.checkequal('A', 'a', 'title') + self.checkequal(True, 'a', 'islower') + + def test_partition(self): + + self.checkequal(('this is the par', 'ti', 'tion method'), + 'this is the partition method', 'partition', 'ti') + + # from raymond's original specification + S = 'http://www.python.org' + self.checkequal(('http', '://', 'www.python.org'), S, 'partition', '://') + self.checkequal(('http://www.python.org', '', ''), S, 'partition', '?') + self.checkequal(('', 'http://', 'www.python.org'), S, 'partition', 'http://') + self.checkequal(('http://www.python.', 'org', ''), S, 'partition', 'org') + + self.checkraises(ValueError, S, 'partition', '') + self.checkraises(TypeError, S, 'partition', None) + + def test_rpartition(self): + + self.checkequal(('this is the rparti', 'ti', 'on method'), + 'this is the rpartition method', 'rpartition', 'ti') + + # from raymond's original specification + S = 'http://www.python.org' + self.checkequal(('http', '://', 'www.python.org'), S, 'rpartition', '://') + self.checkequal(('http://www.python.org', '', ''), S, 'rpartition', '?') + self.checkequal(('', 'http://', 'www.python.org'), S, 'rpartition', 'http://') + self.checkequal(('http://www.python.', 'org', ''), S, 'rpartition', 'org') + + self.checkraises(ValueError, S, 'rpartition', '') + self.checkraises(TypeError, S, 'rpartition', None) + class MixinStrStringUserStringTest: # Additional tests for 8bit strings, i.e. str, UserString and diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py new file mode 100644 index 0000000..255428f --- /dev/null +++ b/Lib/test/test_bigmem.py @@ -0,0 +1,964 @@ +from test import test_support +from test.test_support import bigmemtest, _1G, _2G + +import unittest +import operator +import string +import sys + +# Bigmem testing houserules: +# +# - Try not to allocate too many large objects. It's okay to rely on +# refcounting semantics, but don't forget that 's = create_largestring()' +# doesn't release the old 's' (if it exists) until well after its new +# value has been created. Use 'del s' before the create_largestring call. +# +# - Do *not* compare large objects using assertEquals or similar. It's a +# lengty operation and the errormessage will be utterly useless due to +# its size. To make sure whether a result has the right contents, better +# to use the strip or count methods, or compare meaningful slices. +# +# - Don't forget to test for large indices, offsets and results and such, +# in addition to large sizes. +# +# - When repeating an object (say, a substring, or a small list) to create +# a large object, make the subobject of a length that is not a power of +# 2. That way, int-wrapping problems are more easily detected. +# +# - While the bigmemtest decorator speaks of 'minsize', all tests will +# actually be called with a much smaller number too, in the normal +# test run (5Kb currently.) This is so the tests themselves get frequent +# testing Consequently, always make all large allocations based on the +# passed-in 'size', and don't rely on the size being very large. Also, +# memuse-per-size should remain sane (less than a few thousand); if your +# test uses more, adjust 'size' upward, instead. + +class StrTest(unittest.TestCase): + @bigmemtest(minsize=_2G, memuse=2) + def test_capitalize(self, size): + SUBSTR = ' abc def ghi' + s = '-' * size + SUBSTR + caps = s.capitalize() + self.assertEquals(caps[-len(SUBSTR):], + SUBSTR.capitalize()) + self.assertEquals(caps.lstrip('-'), SUBSTR) + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_center(self, size): + SUBSTR = ' abc def ghi' + s = SUBSTR.center(size) + self.assertEquals(len(s), size) + lpadsize = rpadsize = (len(s) - len(SUBSTR)) // 2 + if len(s) % 2: + lpadsize += 1 + self.assertEquals(s[lpadsize:-rpadsize], SUBSTR) + self.assertEquals(s.strip(), SUBSTR.strip()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_count(self, size): + SUBSTR = ' abc def ghi' + s = '.' * size + SUBSTR + self.assertEquals(s.count('.'), size) + s += '.' + self.assertEquals(s.count('.'), size + 1) + self.assertEquals(s.count(' '), 3) + self.assertEquals(s.count('i'), 1) + self.assertEquals(s.count('j'), 0) + + @bigmemtest(minsize=0, memuse=1) + def test_decode(self, size): + pass + + @bigmemtest(minsize=0, memuse=1) + def test_encode(self, size): + pass + + @bigmemtest(minsize=_2G, memuse=2) + def test_endswith(self, size): + SUBSTR = ' abc def ghi' + s = '-' * size + SUBSTR + self.failUnless(s.endswith(SUBSTR)) + self.failUnless(s.endswith(s)) + s2 = '...' + s + self.failUnless(s2.endswith(s)) + self.failIf(s.endswith('a' + SUBSTR)) + self.failIf(SUBSTR.endswith(s)) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_expandtabs(self, size): + s = '-' * size + tabsize = 8 + self.assertEquals(s.expandtabs(), s) + del s + slen, remainder = divmod(size, tabsize) + s = ' \t' * slen + s = s.expandtabs(tabsize) + self.assertEquals(len(s), size - remainder) + self.assertEquals(len(s.strip(' ')), 0) + + @bigmemtest(minsize=_2G, memuse=2) + def test_find(self, size): + SUBSTR = ' abc def ghi' + sublen = len(SUBSTR) + s = ''.join([SUBSTR, '-' * size, SUBSTR]) + self.assertEquals(s.find(' '), 0) + self.assertEquals(s.find(SUBSTR), 0) + self.assertEquals(s.find(' ', sublen), sublen + size) + self.assertEquals(s.find(SUBSTR, len(SUBSTR)), sublen + size) + self.assertEquals(s.find('i'), SUBSTR.find('i')) + self.assertEquals(s.find('i', sublen), + sublen + size + SUBSTR.find('i')) + self.assertEquals(s.find('i', size), + sublen + size + SUBSTR.find('i')) + self.assertEquals(s.find('j'), -1) + + @bigmemtest(minsize=_2G, memuse=2) + def test_index(self, size): + SUBSTR = ' abc def ghi' + sublen = len(SUBSTR) + s = ''.join([SUBSTR, '-' * size, SUBSTR]) + self.assertEquals(s.index(' '), 0) + self.assertEquals(s.index(SUBSTR), 0) + self.assertEquals(s.index(' ', sublen), sublen + size) + self.assertEquals(s.index(SUBSTR, sublen), sublen + size) + self.assertEquals(s.index('i'), SUBSTR.index('i')) + self.assertEquals(s.index('i', sublen), + sublen + size + SUBSTR.index('i')) + self.assertEquals(s.index('i', size), + sublen + size + SUBSTR.index('i')) + self.assertRaises(ValueError, s.index, 'j') + + @bigmemtest(minsize=_2G, memuse=2) + def test_isalnum(self, size): + SUBSTR = '123456' + s = 'a' * size + SUBSTR + self.failUnless(s.isalnum()) + s += '.' + self.failIf(s.isalnum()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_isalpha(self, size): + SUBSTR = 'zzzzzzz' + s = 'a' * size + SUBSTR + self.failUnless(s.isalpha()) + s += '.' + self.failIf(s.isalpha()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_isdigit(self, size): + SUBSTR = '123456' + s = '9' * size + SUBSTR + self.failUnless(s.isdigit()) + s += 'z' + self.failIf(s.isdigit()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_islower(self, size): + chars = ''.join([ chr(c) for c in range(255) if not chr(c).isupper() ]) + repeats = size // len(chars) + 2 + s = chars * repeats + self.failUnless(s.islower()) + s += 'A' + self.failIf(s.islower()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_isspace(self, size): + whitespace = ' \f\n\r\t\v' + repeats = size // len(whitespace) + 2 + s = whitespace * repeats + self.failUnless(s.isspace()) + s += 'j' + self.failIf(s.isspace()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_istitle(self, size): + SUBSTR = '123456' + s = ''.join(['A', 'a' * size, SUBSTR]) + self.failUnless(s.istitle()) + s += 'A' + self.failUnless(s.istitle()) + s += 'aA' + self.failIf(s.istitle()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_isupper(self, size): + chars = ''.join([ chr(c) for c in range(255) if not chr(c).islower() ]) + repeats = size // len(chars) + 2 + s = chars * repeats + self.failUnless(s.isupper()) + s += 'a' + self.failIf(s.isupper()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_join(self, size): + s = 'A' * size + x = s.join(['aaaaa', 'bbbbb']) + self.assertEquals(x.count('a'), 5) + self.assertEquals(x.count('b'), 5) + self.failUnless(x.startswith('aaaaaA')) + self.failUnless(x.endswith('Abbbbb')) + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_ljust(self, size): + SUBSTR = ' abc def ghi' + s = SUBSTR.ljust(size) + self.failUnless(s.startswith(SUBSTR + ' ')) + self.assertEquals(len(s), size) + self.assertEquals(s.strip(), SUBSTR.strip()) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_lower(self, size): + s = 'A' * size + s = s.lower() + self.assertEquals(len(s), size) + self.assertEquals(s.count('a'), size) + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_lstrip(self, size): + SUBSTR = 'abc def ghi' + s = SUBSTR.rjust(size) + self.assertEquals(len(s), size) + self.assertEquals(s.lstrip(), SUBSTR.lstrip()) + del s + s = SUBSTR.ljust(size) + self.assertEquals(len(s), size) + stripped = s.lstrip() + self.failUnless(stripped is s) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_replace(self, size): + replacement = 'a' + s = ' ' * size + s = s.replace(' ', replacement) + self.assertEquals(len(s), size) + self.assertEquals(s.count(replacement), size) + s = s.replace(replacement, ' ', size - 4) + self.assertEquals(len(s), size) + self.assertEquals(s.count(replacement), 4) + self.assertEquals(s[-10:], ' aaaa') + + @bigmemtest(minsize=_2G, memuse=2) + def test_rfind(self, size): + SUBSTR = ' abc def ghi' + sublen = len(SUBSTR) + s = ''.join([SUBSTR, '-' * size, SUBSTR]) + self.assertEquals(s.rfind(' '), sublen + size + SUBSTR.rfind(' ')) + self.assertEquals(s.rfind(SUBSTR), sublen + size) + self.assertEquals(s.rfind(' ', 0, size), SUBSTR.rfind(' ')) + self.assertEquals(s.rfind(SUBSTR, 0, sublen + size), 0) + self.assertEquals(s.rfind('i'), sublen + size + SUBSTR.rfind('i')) + self.assertEquals(s.rfind('i', 0, sublen), SUBSTR.rfind('i')) + self.assertEquals(s.rfind('i', 0, sublen + size), + SUBSTR.rfind('i')) + self.assertEquals(s.rfind('j'), -1) + + @bigmemtest(minsize=_2G, memuse=2) + def test_rindex(self, size): + SUBSTR = ' abc def ghi' + sublen = len(SUBSTR) + s = ''.join([SUBSTR, '-' * size, SUBSTR]) + self.assertEquals(s.rindex(' '), + sublen + size + SUBSTR.rindex(' ')) + self.assertEquals(s.rindex(SUBSTR), sublen + size) + self.assertEquals(s.rindex(' ', 0, sublen + size - 1), + SUBSTR.rindex(' ')) + self.assertEquals(s.rindex(SUBSTR, 0, sublen + size), 0) + self.assertEquals(s.rindex('i'), + sublen + size + SUBSTR.rindex('i')) + self.assertEquals(s.rindex('i', 0, sublen), SUBSTR.rindex('i')) + self.assertEquals(s.rindex('i', 0, sublen + size), + SUBSTR.rindex('i')) + self.assertRaises(ValueError, s.rindex, 'j') + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_rjust(self, size): + SUBSTR = ' abc def ghi' + s = SUBSTR.ljust(size) + self.failUnless(s.startswith(SUBSTR + ' ')) + self.assertEquals(len(s), size) + self.assertEquals(s.strip(), SUBSTR.strip()) + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_rstrip(self, size): + SUBSTR = ' abc def ghi' + s = SUBSTR.ljust(size) + self.assertEquals(len(s), size) + self.assertEquals(s.rstrip(), SUBSTR.rstrip()) + del s + s = SUBSTR.rjust(size) + self.assertEquals(len(s), size) + stripped = s.rstrip() + self.failUnless(stripped is s) + + # The test takes about size bytes to build a string, and then about + # sqrt(size) substrings of sqrt(size) in size and a list to + # hold sqrt(size) items. It's close but just over 2x size. + @bigmemtest(minsize=_2G, memuse=2.1) + def test_split_small(self, size): + # Crudely calculate an estimate so that the result of s.split won't + # take up an inordinate amount of memory + chunksize = int(size ** 0.5 + 2) + SUBSTR = 'a' + ' ' * chunksize + s = SUBSTR * chunksize + l = s.split() + self.assertEquals(len(l), chunksize) + self.assertEquals(set(l), set(['a'])) + del l + l = s.split('a') + self.assertEquals(len(l), chunksize + 1) + self.assertEquals(set(l), set(['', ' ' * chunksize])) + + # Allocates a string of twice size (and briefly two) and a list of + # size. Because of internal affairs, the s.split() call produces a + # list of size times the same one-character string, so we only + # suffer for the list size. (Otherwise, it'd cost another 48 times + # size in bytes!) Nevertheless, a list of size takes + # 8*size bytes. + @bigmemtest(minsize=_2G + 5, memuse=10) + def test_split_large(self, size): + s = ' a' * size + ' ' + l = s.split() + self.assertEquals(len(l), size) + self.assertEquals(set(l), set(['a'])) + del l + l = s.split('a') + self.assertEquals(len(l), size + 1) + self.assertEquals(set(l), set([' '])) + + @bigmemtest(minsize=_2G, memuse=2.1) + def test_splitlines(self, size): + # Crudely calculate an estimate so that the result of s.split won't + # take up an inordinate amount of memory + chunksize = int(size ** 0.5 + 2) // 2 + SUBSTR = ' ' * chunksize + '\n' + ' ' * chunksize + '\r\n' + s = SUBSTR * chunksize + l = s.splitlines() + self.assertEquals(len(l), chunksize * 2) + self.assertEquals(set(l), set([' ' * chunksize])) + + @bigmemtest(minsize=_2G, memuse=2) + def test_startswith(self, size): + SUBSTR = ' abc def ghi' + s = '-' * size + SUBSTR + self.failUnless(s.startswith(s)) + self.failUnless(s.startswith('-' * size)) + self.failIf(s.startswith(SUBSTR)) + + @bigmemtest(minsize=_2G, memuse=1) + def test_strip(self, size): + SUBSTR = ' abc def ghi ' + s = SUBSTR.rjust(size) + self.assertEquals(len(s), size) + self.assertEquals(s.strip(), SUBSTR.strip()) + del s + s = SUBSTR.ljust(size) + self.assertEquals(len(s), size) + self.assertEquals(s.strip(), SUBSTR.strip()) + + @bigmemtest(minsize=_2G, memuse=2) + def test_swapcase(self, size): + SUBSTR = "aBcDeFG12.'\xa9\x00" + sublen = len(SUBSTR) + repeats = size // sublen + 2 + s = SUBSTR * repeats + s = s.swapcase() + self.assertEquals(len(s), sublen * repeats) + self.assertEquals(s[:sublen * 3], SUBSTR.swapcase() * 3) + self.assertEquals(s[-sublen * 3:], SUBSTR.swapcase() * 3) + + @bigmemtest(minsize=_2G, memuse=2) + def test_title(self, size): + SUBSTR = 'SpaaHAaaAaham' + s = SUBSTR * (size // len(SUBSTR) + 2) + s = s.title() + self.failUnless(s.startswith((SUBSTR * 3).title())) + self.failUnless(s.endswith(SUBSTR.lower() * 3)) + + @bigmemtest(minsize=_2G, memuse=2) + def test_translate(self, size): + trans = string.maketrans('.aZ', '-!$') + SUBSTR = 'aZz.z.Aaz.' + sublen = len(SUBSTR) + repeats = size // sublen + 2 + s = SUBSTR * repeats + s = s.translate(trans) + self.assertEquals(len(s), repeats * sublen) + self.assertEquals(s[:sublen], SUBSTR.translate(trans)) + self.assertEquals(s[-sublen:], SUBSTR.translate(trans)) + self.assertEquals(s.count('.'), 0) + self.assertEquals(s.count('!'), repeats * 2) + self.assertEquals(s.count('z'), repeats * 3) + + @bigmemtest(minsize=_2G + 5, memuse=2) + def test_upper(self, size): + s = 'a' * size + s = s.upper() + self.assertEquals(len(s), size) + self.assertEquals(s.count('A'), size) + + @bigmemtest(minsize=_2G + 20, memuse=1) + def test_zfill(self, size): + SUBSTR = '-568324723598234' + s = SUBSTR.zfill(size) + self.failUnless(s.endswith('0' + SUBSTR[1:])) + self.failUnless(s.startswith('-0')) + self.assertEquals(len(s), size) + self.assertEquals(s.count('0'), size - len(SUBSTR)) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_format(self, size): + s = '-' * size + sf = '%s' % (s,) + self.failUnless(s == sf) + del sf + sf = '..%s..' % (s,) + self.assertEquals(len(sf), len(s) + 4) + self.failUnless(sf.startswith('..-')) + self.failUnless(sf.endswith('-..')) + del s, sf + + size //= 2 + edge = '-' * size + s = ''.join([edge, '%s', edge]) + del edge + s = s % '...' + self.assertEquals(len(s), size * 2 + 3) + self.assertEquals(s.count('.'), 3) + self.assertEquals(s.count('-'), size * 2) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_repr_small(self, size): + s = '-' * size + s = repr(s) + self.assertEquals(len(s), size + 2) + self.assertEquals(s[0], "'") + self.assertEquals(s[-1], "'") + self.assertEquals(s.count('-'), size) + del s + # repr() will create a string four times as large as this 'binary + # string', but we don't want to allocate much more than twice + # size in total. (We do extra testing in test_repr_large()) + size = size // 5 * 2 + s = '\x00' * size + s = repr(s) + self.assertEquals(len(s), size * 4 + 2) + self.assertEquals(s[0], "'") + self.assertEquals(s[-1], "'") + self.assertEquals(s.count('\\'), size) + self.assertEquals(s.count('0'), size * 2) + + @bigmemtest(minsize=_2G + 10, memuse=5) + def test_repr_large(self, size): + s = '\x00' * size + s = repr(s) + self.assertEquals(len(s), size * 4 + 2) + self.assertEquals(s[0], "'") + self.assertEquals(s[-1], "'") + self.assertEquals(s.count('\\'), size) + self.assertEquals(s.count('0'), size * 2) + + # This test is meaningful even with size < 2G, as long as the + # doubled string is > 2G (but it tests more if both are > 2G :) + @bigmemtest(minsize=_1G + 2, memuse=3) + def test_concat(self, size): + s = '.' * size + self.assertEquals(len(s), size) + s = s + s + self.assertEquals(len(s), size * 2) + self.assertEquals(s.count('.'), size * 2) + + # This test is meaningful even with size < 2G, as long as the + # repeated string is > 2G (but it tests more if both are > 2G :) + @bigmemtest(minsize=_1G + 2, memuse=3) + def test_repeat(self, size): + s = '.' * size + self.assertEquals(len(s), size) + s = s * 2 + self.assertEquals(len(s), size * 2) + self.assertEquals(s.count('.'), size * 2) + + @bigmemtest(minsize=_2G + 20, memuse=1) + def test_slice_and_getitem(self, size): + SUBSTR = '0123456789' + sublen = len(SUBSTR) + s = SUBSTR * (size // sublen) + stepsize = len(s) // 100 + stepsize = stepsize - (stepsize % sublen) + for i in range(0, len(s) - stepsize, stepsize): + self.assertEquals(s[i], SUBSTR[0]) + self.assertEquals(s[i:i + sublen], SUBSTR) + self.assertEquals(s[i:i + sublen:2], SUBSTR[::2]) + if i > 0: + self.assertEquals(s[i + sublen - 1:i - 1:-3], + SUBSTR[sublen::-3]) + # Make sure we do some slicing and indexing near the end of the + # string, too. + self.assertEquals(s[len(s) - 1], SUBSTR[-1]) + self.assertEquals(s[-1], SUBSTR[-1]) + self.assertEquals(s[len(s) - 10], SUBSTR[0]) + self.assertEquals(s[-sublen], SUBSTR[0]) + self.assertEquals(s[len(s):], '') + self.assertEquals(s[len(s) - 1:], SUBSTR[-1]) + self.assertEquals(s[-1:], SUBSTR[-1]) + self.assertEquals(s[len(s) - sublen:], SUBSTR) + self.assertEquals(s[-sublen:], SUBSTR) + self.assertEquals(len(s[:]), len(s)) + self.assertEquals(len(s[:len(s) - 5]), len(s) - 5) + self.assertEquals(len(s[5:-5]), len(s) - 10) + + self.assertRaises(IndexError, operator.getitem, s, len(s)) + self.assertRaises(IndexError, operator.getitem, s, len(s) + 1) + self.assertRaises(IndexError, operator.getitem, s, len(s) + 1<<31) + + @bigmemtest(minsize=_2G, memuse=2) + def test_contains(self, size): + SUBSTR = '0123456789' + edge = '-' * (size // 2) + s = ''.join([edge, SUBSTR, edge]) + del edge + self.failUnless(SUBSTR in s) + self.failIf(SUBSTR * 2 in s) + self.failUnless('-' in s) + self.failIf('a' in s) + s += 'a' + self.failUnless('a' in s) + + @bigmemtest(minsize=_2G + 10, memuse=2) + def test_compare(self, size): + s1 = '-' * size + s2 = '-' * size + self.failUnless(s1 == s2) + del s2 + s2 = s1 + 'a' + self.failIf(s1 == s2) + del s2 + s2 = '.' * size + self.failIf(s1 == s2) + + @bigmemtest(minsize=_2G + 10, memuse=1) + def test_hash(self, size): + # Not sure if we can do any meaningful tests here... Even if we + # start relying on the exact algorithm used, the result will be + # different depending on the size of the C 'long int'. Even this + # test is dodgy (there's no *guarantee* that the two things should + # have a different hash, even if they, in the current + # implementation, almost always do.) + s = '\x00' * size + h1 = hash(s) + del s + s = '\x00' * (size + 1) + self.failIf(h1 == hash(s)) + +class TupleTest(unittest.TestCase): + + # Tuples have a small, fixed-sized head and an array of pointers to + # data. Since we're testing 64-bit addressing, we can assume that the + # pointers are 8 bytes, and that thus that the tuples take up 8 bytes + # per size. + + # As a side-effect of testing long tuples, these tests happen to test + # having more than 2<<31 references to any given object. Hence the + # use of different types of objects as contents in different tests. + + @bigmemtest(minsize=_2G + 2, memuse=16) + def test_compare(self, size): + t1 = (u'',) * size + t2 = (u'',) * size + self.failUnless(t1 == t2) + del t2 + t2 = (u'',) * (size + 1) + self.failIf(t1 == t2) + del t2 + t2 = (1,) * size + self.failIf(t1 == t2) + + # Test concatenating into a single tuple of more than 2G in length, + # and concatenating a tuple of more than 2G in length separately, so + # the smaller test still gets run even if there isn't memory for the + # larger test (but we still let the tester know the larger test is + # skipped, in verbose mode.) + def basic_concat_test(self, size): + t = ((),) * size + self.assertEquals(len(t), size) + t = t + t + self.assertEquals(len(t), size * 2) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=24) + def test_concat_small(self, size): + return self.basic_concat_test(size) + + @bigmemtest(minsize=_2G + 2, memuse=24) + def test_concat_large(self, size): + return self.basic_concat_test(size) + + @bigmemtest(minsize=_2G // 5 + 10, memuse=8 * 5) + def test_contains(self, size): + t = (1, 2, 3, 4, 5) * size + self.assertEquals(len(t), size * 5) + self.failUnless(5 in t) + self.failIf((1, 2, 3, 4, 5) in t) + self.failIf(0 in t) + + @bigmemtest(minsize=_2G + 10, memuse=8) + def test_hash(self, size): + t1 = (0,) * size + h1 = hash(t1) + del t1 + t2 = (0,) * (size + 1) + self.failIf(h1 == hash(t2)) + + @bigmemtest(minsize=_2G + 10, memuse=8) + def test_index_and_slice(self, size): + t = (None,) * size + self.assertEquals(len(t), size) + self.assertEquals(t[-1], None) + self.assertEquals(t[5], None) + self.assertEquals(t[size - 1], None) + self.assertRaises(IndexError, operator.getitem, t, size) + self.assertEquals(t[:5], (None,) * 5) + self.assertEquals(t[-5:], (None,) * 5) + self.assertEquals(t[20:25], (None,) * 5) + self.assertEquals(t[-25:-20], (None,) * 5) + self.assertEquals(t[size - 5:], (None,) * 5) + self.assertEquals(t[size - 5:size], (None,) * 5) + self.assertEquals(t[size - 6:size - 2], (None,) * 4) + self.assertEquals(t[size:size], ()) + self.assertEquals(t[size:size+5], ()) + + # Like test_concat, split in two. + def basic_test_repeat(self, size): + t = ('',) * size + self.assertEquals(len(t), size) + t = t * 2 + self.assertEquals(len(t), size * 2) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=24) + def test_repeat_small(self, size): + return self.basic_test_repeat(size) + + @bigmemtest(minsize=_2G + 2, memuse=24) + def test_repeat_large(self, size): + return self.basic_test_repeat(size) + + # Like test_concat, split in two. + def basic_test_repr(self, size): + t = (0,) * size + s = repr(t) + # The repr of a tuple of 0's is exactly three times the tuple length. + self.assertEquals(len(s), size * 3) + self.assertEquals(s[:5], '(0, 0') + self.assertEquals(s[-5:], '0, 0)') + self.assertEquals(s.count('0'), size) + + @bigmemtest(minsize=_2G // 3 + 2, memuse=8 + 3) + def test_repr_small(self, size): + return self.basic_test_repr(size) + + @bigmemtest(minsize=_2G + 2, memuse=8 + 3) + def test_repr_large(self, size): + return self.basic_test_repr(size) + +class ListTest(unittest.TestCase): + + # Like tuples, lists have a small, fixed-sized head and an array of + # pointers to data, so 8 bytes per size. Also like tuples, we make the + # lists hold references to various objects to test their refcount + # limits. + + @bigmemtest(minsize=_2G + 2, memuse=16) + def test_compare(self, size): + l1 = [u''] * size + l2 = [u''] * size + self.failUnless(l1 == l2) + del l2 + l2 = [u''] * (size + 1) + self.failIf(l1 == l2) + del l2 + l2 = [2] * size + self.failIf(l1 == l2) + + # Test concatenating into a single list of more than 2G in length, + # and concatenating a list of more than 2G in length separately, so + # the smaller test still gets run even if there isn't memory for the + # larger test (but we still let the tester know the larger test is + # skipped, in verbose mode.) + def basic_test_concat(self, size): + l = [[]] * size + self.assertEquals(len(l), size) + l = l + l + self.assertEquals(len(l), size * 2) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=24) + def test_concat_small(self, size): + return self.basic_test_concat(size) + + @bigmemtest(minsize=_2G + 2, memuse=24) + def test_concat_large(self, size): + return self.basic_test_concat(size) + + def basic_test_inplace_concat(self, size): + l = [sys.stdout] * size + l += l + self.assertEquals(len(l), size * 2) + self.failUnless(l[0] is l[-1]) + self.failUnless(l[size - 1] is l[size + 1]) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=24) + def test_inplace_concat_small(self, size): + return self.basic_test_inplace_concat(size) + + @bigmemtest(minsize=_2G + 2, memuse=24) + def test_inplace_concat_large(self, size): + return self.basic_test_inplace_concat(size) + + @bigmemtest(minsize=_2G // 5 + 10, memuse=8 * 5) + def test_contains(self, size): + l = [1, 2, 3, 4, 5] * size + self.assertEquals(len(l), size * 5) + self.failUnless(5 in l) + self.failIf([1, 2, 3, 4, 5] in l) + self.failIf(0 in l) + + @bigmemtest(minsize=_2G + 10, memuse=8) + def test_hash(self, size): + l = [0] * size + self.failUnlessRaises(TypeError, hash, l) + + @bigmemtest(minsize=_2G + 10, memuse=8) + def test_index_and_slice(self, size): + l = [None] * size + self.assertEquals(len(l), size) + self.assertEquals(l[-1], None) + self.assertEquals(l[5], None) + self.assertEquals(l[size - 1], None) + self.assertRaises(IndexError, operator.getitem, l, size) + self.assertEquals(l[:5], [None] * 5) + self.assertEquals(l[-5:], [None] * 5) + self.assertEquals(l[20:25], [None] * 5) + self.assertEquals(l[-25:-20], [None] * 5) + self.assertEquals(l[size - 5:], [None] * 5) + self.assertEquals(l[size - 5:size], [None] * 5) + self.assertEquals(l[size - 6:size - 2], [None] * 4) + self.assertEquals(l[size:size], []) + self.assertEquals(l[size:size+5], []) + + l[size - 2] = 5 + self.assertEquals(len(l), size) + self.assertEquals(l[-3:], [None, 5, None]) + self.assertEquals(l.count(5), 1) + self.assertRaises(IndexError, operator.setitem, l, size, 6) + self.assertEquals(len(l), size) + + l[size - 7:] = [1, 2, 3, 4, 5] + size -= 2 + self.assertEquals(len(l), size) + self.assertEquals(l[-7:], [None, None, 1, 2, 3, 4, 5]) + + l[:7] = [1, 2, 3, 4, 5] + size -= 2 + self.assertEquals(len(l), size) + self.assertEquals(l[:7], [1, 2, 3, 4, 5, None, None]) + + del l[size - 1] + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(l[-1], 4) + + del l[-2:] + size -= 2 + self.assertEquals(len(l), size) + self.assertEquals(l[-1], 2) + + del l[0] + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(l[0], 2) + + del l[:2] + size -= 2 + self.assertEquals(len(l), size) + self.assertEquals(l[0], 4) + + # Like test_concat, split in two. + def basic_test_repeat(self, size): + l = [] * size + self.failIf(l) + l = [''] * size + self.assertEquals(len(l), size) + l = l * 2 + self.assertEquals(len(l), size * 2) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=24) + def test_repeat_small(self, size): + return self.basic_test_repeat(size) + + @bigmemtest(minsize=_2G + 2, memuse=24) + def test_repeat_large(self, size): + return self.basic_test_repeat(size) + + def basic_test_inplace_repeat(self, size): + l = [''] + l *= size + self.assertEquals(len(l), size) + self.failUnless(l[0] is l[-1]) + del l + + l = [''] * size + l *= 2 + self.assertEquals(len(l), size * 2) + self.failUnless(l[size - 1] is l[-1]) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=16) + def test_inplace_repeat_small(self, size): + return self.basic_test_inplace_repeat(size) + + @bigmemtest(minsize=_2G + 2, memuse=16) + def test_inplace_repeat_large(self, size): + return self.basic_test_inplace_repeat(size) + + def basic_test_repr(self, size): + l = [0] * size + s = repr(l) + # The repr of a list of 0's is exactly three times the list length. + self.assertEquals(len(s), size * 3) + self.assertEquals(s[:5], '[0, 0') + self.assertEquals(s[-5:], '0, 0]') + self.assertEquals(s.count('0'), size) + + @bigmemtest(minsize=_2G // 3 + 2, memuse=8 + 3) + def test_repr_small(self, size): + return self.basic_test_repr(size) + + @bigmemtest(minsize=_2G + 2, memuse=8 + 3) + def test_repr_large(self, size): + return self.basic_test_repr(size) + + # list overallocates ~1/8th of the total size (on first expansion) so + # the single list.append call puts memuse at 9 bytes per size. + @bigmemtest(minsize=_2G, memuse=9) + def test_append(self, size): + l = [object()] * size + l.append(object()) + self.assertEquals(len(l), size+1) + self.failUnless(l[-3] is l[-2]) + self.failIf(l[-2] is l[-1]) + + @bigmemtest(minsize=_2G // 5 + 2, memuse=8 * 5) + def test_count(self, size): + l = [1, 2, 3, 4, 5] * size + self.assertEquals(l.count(1), size) + self.assertEquals(l.count("1"), 0) + + def basic_test_extend(self, size): + l = [file] * size + l.extend(l) + self.assertEquals(len(l), size * 2) + self.failUnless(l[0] is l[-1]) + self.failUnless(l[size - 1] is l[size + 1]) + + @bigmemtest(minsize=_2G // 2 + 2, memuse=16) + def test_extend_small(self, size): + return self.basic_test_extend(size) + + @bigmemtest(minsize=_2G + 2, memuse=16) + def test_extend_large(self, size): + return self.basic_test_extend(size) + + @bigmemtest(minsize=_2G // 5 + 2, memuse=8 * 5) + def test_index(self, size): + l = [1L, 2L, 3L, 4L, 5L] * size + size *= 5 + self.assertEquals(l.index(1), 0) + self.assertEquals(l.index(5, size - 5), size - 1) + self.assertEquals(l.index(5, size - 5, size), size - 1) + self.assertRaises(ValueError, l.index, 1, size - 4, size) + self.assertRaises(ValueError, l.index, 6L) + + # This tests suffers from overallocation, just like test_append. + @bigmemtest(minsize=_2G + 10, memuse=9) + def test_insert(self, size): + l = [1.0] * size + l.insert(size - 1, "A") + size += 1 + self.assertEquals(len(l), size) + self.assertEquals(l[-3:], [1.0, "A", 1.0]) + + l.insert(size + 1, "B") + size += 1 + self.assertEquals(len(l), size) + self.assertEquals(l[-3:], ["A", 1.0, "B"]) + + l.insert(1, "C") + size += 1 + self.assertEquals(len(l), size) + self.assertEquals(l[:3], [1.0, "C", 1.0]) + self.assertEquals(l[size - 3:], ["A", 1.0, "B"]) + + @bigmemtest(minsize=_2G // 5 + 4, memuse=8 * 5) + def test_pop(self, size): + l = [u"a", u"b", u"c", u"d", u"e"] * size + size *= 5 + self.assertEquals(len(l), size) + + item = l.pop() + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(item, u"e") + self.assertEquals(l[-2:], [u"c", u"d"]) + + item = l.pop(0) + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(item, u"a") + self.assertEquals(l[:2], [u"b", u"c"]) + + item = l.pop(size - 2) + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(item, u"c") + self.assertEquals(l[-2:], [u"b", u"d"]) + + @bigmemtest(minsize=_2G + 10, memuse=8) + def test_remove(self, size): + l = [10] * size + self.assertEquals(len(l), size) + + l.remove(10) + size -= 1 + self.assertEquals(len(l), size) + + # Because of the earlier l.remove(), this append doesn't trigger + # a resize. + l.append(5) + size += 1 + self.assertEquals(len(l), size) + self.assertEquals(l[-2:], [10, 5]) + l.remove(5) + size -= 1 + self.assertEquals(len(l), size) + self.assertEquals(l[-2:], [10, 10]) + + @bigmemtest(minsize=_2G // 5 + 2, memuse=8 * 5) + def test_reverse(self, size): + l = [1, 2, 3, 4, 5] * size + l.reverse() + self.assertEquals(len(l), size * 5) + self.assertEquals(l[-5:], [5, 4, 3, 2, 1]) + self.assertEquals(l[:5], [5, 4, 3, 2, 1]) + + @bigmemtest(minsize=_2G // 5 + 2, memuse=8 * 5) + def test_sort(self, size): + l = [1, 2, 3, 4, 5] * size + l.sort() + self.assertEquals(len(l), size * 5) + self.assertEquals(l.count(1), size) + self.assertEquals(l[:10], [1] * 10) + self.assertEquals(l[-10:], [5] * 10) + +def test_main(): + test_support.run_unittest(StrTest, TupleTest, ListTest) + +if __name__ == '__main__': + if len(sys.argv) > 1: + test_support.set_memlimit(sys.argv[1]) + test_main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index ef4f407..71e2b0a 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1,7 +1,8 @@ # Python test set -- built-in functions import test.test_support, unittest -from test.test_support import fcmp, have_unicode, TESTFN, unlink, run_unittest +from test.test_support import fcmp, have_unicode, TESTFN, unlink, \ + run_unittest, run_with_locale from operator import neg import sys, warnings, cStringIO, random, UserDict @@ -528,33 +529,20 @@ class BuiltinTest(unittest.TestCase): # Implementation limitation in PyFloat_FromString() self.assertRaises(ValueError, float, unicode("1"*10000)) + @run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE') def test_float_with_comma(self): # set locale to something that doesn't use '.' for the decimal point - try: - import locale - orig_locale = locale.setlocale(locale.LC_NUMERIC) - locale.setlocale(locale.LC_NUMERIC, 'fr_FR') - except: - # if we can't set the locale, just ignore this test - return - - try: - self.assertEqual(locale.localeconv()['decimal_point'], ',') - except: - # this test is worthless, just skip it and reset the locale - locale.setlocale(locale.LC_NUMERIC, orig_locale) + import locale + if not locale.localeconv()['decimal_point'] == ',': return - try: - self.assertEqual(float(" 3,14 "), 3.14) - self.assertEqual(float(" +3,14 "), 3.14) - self.assertEqual(float(" -3,14 "), -3.14) - self.assertRaises(ValueError, float, " 0x3.1 ") - self.assertRaises(ValueError, float, " -0x3.p-1 ") - self.assertEqual(float(" 25.e-1 "), 2.5) - self.assertEqual(fcmp(float(" .25e-1 "), .025), 0) - finally: - locale.setlocale(locale.LC_NUMERIC, orig_locale) + self.assertEqual(float(" 3,14 "), 3.14) + self.assertEqual(float(" +3,14 "), 3.14) + self.assertEqual(float(" -3,14 "), -3.14) + self.assertRaises(ValueError, float, " 0x3.1 ") + self.assertRaises(ValueError, float, " -0x3.p-1 ") + self.assertEqual(float(" 25.e-1 "), 2.5) + self.assertEqual(fcmp(float(" .25e-1 "), .025), 0) def test_floatconversion(self): # Make sure that calls to __float__() work properly @@ -693,6 +681,84 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(int('0123', 0), 83) self.assertEqual(int('0x123', 16), 291) + # SF bug 1334662: int(string, base) wrong answers + # Various representations of 2**32 evaluated to 0 + # rather than 2**32 in previous versions + + self.assertEqual(int('100000000000000000000000000000000', 2), 4294967296L) + self.assertEqual(int('102002022201221111211', 3), 4294967296L) + self.assertEqual(int('10000000000000000', 4), 4294967296L) + self.assertEqual(int('32244002423141', 5), 4294967296L) + self.assertEqual(int('1550104015504', 6), 4294967296L) + self.assertEqual(int('211301422354', 7), 4294967296L) + self.assertEqual(int('40000000000', 8), 4294967296L) + self.assertEqual(int('12068657454', 9), 4294967296L) + self.assertEqual(int('4294967296', 10), 4294967296L) + self.assertEqual(int('1904440554', 11), 4294967296L) + self.assertEqual(int('9ba461594', 12), 4294967296L) + self.assertEqual(int('535a79889', 13), 4294967296L) + self.assertEqual(int('2ca5b7464', 14), 4294967296L) + self.assertEqual(int('1a20dcd81', 15), 4294967296L) + self.assertEqual(int('100000000', 16), 4294967296L) + self.assertEqual(int('a7ffda91', 17), 4294967296L) + self.assertEqual(int('704he7g4', 18), 4294967296L) + self.assertEqual(int('4f5aff66', 19), 4294967296L) + self.assertEqual(int('3723ai4g', 20), 4294967296L) + self.assertEqual(int('281d55i4', 21), 4294967296L) + self.assertEqual(int('1fj8b184', 22), 4294967296L) + self.assertEqual(int('1606k7ic', 23), 4294967296L) + self.assertEqual(int('mb994ag', 24), 4294967296L) + self.assertEqual(int('hek2mgl', 25), 4294967296L) + self.assertEqual(int('dnchbnm', 26), 4294967296L) + self.assertEqual(int('b28jpdm', 27), 4294967296L) + self.assertEqual(int('8pfgih4', 28), 4294967296L) + self.assertEqual(int('76beigg', 29), 4294967296L) + self.assertEqual(int('5qmcpqg', 30), 4294967296L) + self.assertEqual(int('4q0jto4', 31), 4294967296L) + self.assertEqual(int('4000000', 32), 4294967296L) + self.assertEqual(int('3aokq94', 33), 4294967296L) + self.assertEqual(int('2qhxjli', 34), 4294967296L) + self.assertEqual(int('2br45qb', 35), 4294967296L) + self.assertEqual(int('1z141z4', 36), 4294967296L) + + # SF bug 1334662: int(string, base) wrong answers + # Checks for proper evaluation of 2**32 + 1 + self.assertEqual(int('100000000000000000000000000000001', 2), 4294967297L) + self.assertEqual(int('102002022201221111212', 3), 4294967297L) + self.assertEqual(int('10000000000000001', 4), 4294967297L) + self.assertEqual(int('32244002423142', 5), 4294967297L) + self.assertEqual(int('1550104015505', 6), 4294967297L) + self.assertEqual(int('211301422355', 7), 4294967297L) + self.assertEqual(int('40000000001', 8), 4294967297L) + self.assertEqual(int('12068657455', 9), 4294967297L) + self.assertEqual(int('4294967297', 10), 4294967297L) + self.assertEqual(int('1904440555', 11), 4294967297L) + self.assertEqual(int('9ba461595', 12), 4294967297L) + self.assertEqual(int('535a7988a', 13), 4294967297L) + self.assertEqual(int('2ca5b7465', 14), 4294967297L) + self.assertEqual(int('1a20dcd82', 15), 4294967297L) + self.assertEqual(int('100000001', 16), 4294967297L) + self.assertEqual(int('a7ffda92', 17), 4294967297L) + self.assertEqual(int('704he7g5', 18), 4294967297L) + self.assertEqual(int('4f5aff67', 19), 4294967297L) + self.assertEqual(int('3723ai4h', 20), 4294967297L) + self.assertEqual(int('281d55i5', 21), 4294967297L) + self.assertEqual(int('1fj8b185', 22), 4294967297L) + self.assertEqual(int('1606k7id', 23), 4294967297L) + self.assertEqual(int('mb994ah', 24), 4294967297L) + self.assertEqual(int('hek2mgm', 25), 4294967297L) + self.assertEqual(int('dnchbnn', 26), 4294967297L) + self.assertEqual(int('b28jpdn', 27), 4294967297L) + self.assertEqual(int('8pfgih5', 28), 4294967297L) + self.assertEqual(int('76beigh', 29), 4294967297L) + self.assertEqual(int('5qmcpqh', 30), 4294967297L) + self.assertEqual(int('4q0jto5', 31), 4294967297L) + self.assertEqual(int('4000001', 32), 4294967297L) + self.assertEqual(int('3aokq95', 33), 4294967297L) + self.assertEqual(int('2qhxjlj', 34), 4294967297L) + self.assertEqual(int('2br45qc', 35), 4294967297L) + self.assertEqual(int('1z141z5', 36), 4294967297L) + def test_intconversion(self): # Test __int__() class Foo0: @@ -886,6 +952,81 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(ValueError, long, '53', 40) self.assertRaises(TypeError, long, 1, 12) + self.assertEqual(long('100000000000000000000000000000000', 2), + 4294967296) + self.assertEqual(long('102002022201221111211', 3), 4294967296) + self.assertEqual(long('10000000000000000', 4), 4294967296) + self.assertEqual(long('32244002423141', 5), 4294967296) + self.assertEqual(long('1550104015504', 6), 4294967296) + self.assertEqual(long('211301422354', 7), 4294967296) + self.assertEqual(long('40000000000', 8), 4294967296) + self.assertEqual(long('12068657454', 9), 4294967296) + self.assertEqual(long('4294967296', 10), 4294967296) + self.assertEqual(long('1904440554', 11), 4294967296) + self.assertEqual(long('9ba461594', 12), 4294967296) + self.assertEqual(long('535a79889', 13), 4294967296) + self.assertEqual(long('2ca5b7464', 14), 4294967296) + self.assertEqual(long('1a20dcd81', 15), 4294967296) + self.assertEqual(long('100000000', 16), 4294967296) + self.assertEqual(long('a7ffda91', 17), 4294967296) + self.assertEqual(long('704he7g4', 18), 4294967296) + self.assertEqual(long('4f5aff66', 19), 4294967296) + self.assertEqual(long('3723ai4g', 20), 4294967296) + self.assertEqual(long('281d55i4', 21), 4294967296) + self.assertEqual(long('1fj8b184', 22), 4294967296) + self.assertEqual(long('1606k7ic', 23), 4294967296) + self.assertEqual(long('mb994ag', 24), 4294967296) + self.assertEqual(long('hek2mgl', 25), 4294967296) + self.assertEqual(long('dnchbnm', 26), 4294967296) + self.assertEqual(long('b28jpdm', 27), 4294967296) + self.assertEqual(long('8pfgih4', 28), 4294967296) + self.assertEqual(long('76beigg', 29), 4294967296) + self.assertEqual(long('5qmcpqg', 30), 4294967296) + self.assertEqual(long('4q0jto4', 31), 4294967296) + self.assertEqual(long('4000000', 32), 4294967296) + self.assertEqual(long('3aokq94', 33), 4294967296) + self.assertEqual(long('2qhxjli', 34), 4294967296) + self.assertEqual(long('2br45qb', 35), 4294967296) + self.assertEqual(long('1z141z4', 36), 4294967296) + + self.assertEqual(long('100000000000000000000000000000001', 2), + 4294967297) + self.assertEqual(long('102002022201221111212', 3), 4294967297) + self.assertEqual(long('10000000000000001', 4), 4294967297) + self.assertEqual(long('32244002423142', 5), 4294967297) + self.assertEqual(long('1550104015505', 6), 4294967297) + self.assertEqual(long('211301422355', 7), 4294967297) + self.assertEqual(long('40000000001', 8), 4294967297) + self.assertEqual(long('12068657455', 9), 4294967297) + self.assertEqual(long('4294967297', 10), 4294967297) + self.assertEqual(long('1904440555', 11), 4294967297) + self.assertEqual(long('9ba461595', 12), 4294967297) + self.assertEqual(long('535a7988a', 13), 4294967297) + self.assertEqual(long('2ca5b7465', 14), 4294967297) + self.assertEqual(long('1a20dcd82', 15), 4294967297) + self.assertEqual(long('100000001', 16), 4294967297) + self.assertEqual(long('a7ffda92', 17), 4294967297) + self.assertEqual(long('704he7g5', 18), 4294967297) + self.assertEqual(long('4f5aff67', 19), 4294967297) + self.assertEqual(long('3723ai4h', 20), 4294967297) + self.assertEqual(long('281d55i5', 21), 4294967297) + self.assertEqual(long('1fj8b185', 22), 4294967297) + self.assertEqual(long('1606k7id', 23), 4294967297) + self.assertEqual(long('mb994ah', 24), 4294967297) + self.assertEqual(long('hek2mgm', 25), 4294967297) + self.assertEqual(long('dnchbnn', 26), 4294967297) + self.assertEqual(long('b28jpdn', 27), 4294967297) + self.assertEqual(long('8pfgih5', 28), 4294967297) + self.assertEqual(long('76beigh', 29), 4294967297) + self.assertEqual(long('5qmcpqh', 30), 4294967297) + self.assertEqual(long('4q0jto5', 31), 4294967297) + self.assertEqual(long('4000001', 32), 4294967297) + self.assertEqual(long('3aokq95', 33), 4294967297) + self.assertEqual(long('2qhxjlj', 34), 4294967297) + self.assertEqual(long('2br45qc', 35), 4294967297) + self.assertEqual(long('1z141z5', 36), 4294967297) + + def test_longconversion(self): # Test __long__() class Foo0: diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 018bec6..ec860d1 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -15,8 +15,11 @@ class CmdLineTest(unittest.TestCase): popen2._cleanup() return data - def exit_code(self, cmd_line): - return subprocess.call([sys.executable, cmd_line], stderr=subprocess.PIPE) + def exit_code(self, *args): + cmd_line = [sys.executable] + cmd_line.extend(args) + return subprocess.call(cmd_line, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) def test_directories(self): self.assertNotEqual(self.exit_code('.'), 0) @@ -50,6 +53,38 @@ class CmdLineTest(unittest.TestCase): version = 'Python %d.%d' % sys.version_info[:2] self.assertTrue(self.start_python('-V').startswith(version)) + def test_run_module(self): + # Test expected operation of the '-m' switch + # Switch needs an argument + self.assertNotEqual(self.exit_code('-m'), 0) + # Check we get an error for a nonexistent module + self.assertNotEqual( + self.exit_code('-m', 'fnord43520xyz'), + 0) + # Check the runpy module also gives an error for + # a nonexistent module + self.assertNotEqual( + self.exit_code('-m', 'runpy', 'fnord43520xyz'), + 0) + # All good if module is located and run successfully + self.assertEqual( + self.exit_code('-m', 'timeit', '-n', '1'), + 0) + + def test_run_code(self): + # Test expected operation of the '-c' switch + # Switch needs an argument + self.assertNotEqual(self.exit_code('-c'), 0) + # Check we get an error for an uncaught exception + self.assertNotEqual( + self.exit_code('-c', 'raise Exception'), + 0) + # All good if execution is successful + self.assertEqual( + self.exit_code('-c', 'pass'), + 0) + + def test_main(): test.test_support.run_unittest(CmdLineTest) diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index c6e56c9..159c86d 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -18,30 +18,12 @@ class PosReturn: self.pos = len(exc.object) return (u"", oldpos) -# A UnicodeEncodeError object without a start attribute -class NoStartUnicodeEncodeError(UnicodeEncodeError): - def __init__(self): - UnicodeEncodeError.__init__(self, "ascii", u"", 0, 1, "bad") - del self.start - # A UnicodeEncodeError object with a bad start attribute class BadStartUnicodeEncodeError(UnicodeEncodeError): def __init__(self): UnicodeEncodeError.__init__(self, "ascii", u"", 0, 1, "bad") self.start = [] -# A UnicodeEncodeError object without an end attribute -class NoEndUnicodeEncodeError(UnicodeEncodeError): - def __init__(self): - UnicodeEncodeError.__init__(self, "ascii", u"", 0, 1, "bad") - del self.end - -# A UnicodeEncodeError object without an object attribute -class NoObjectUnicodeEncodeError(UnicodeEncodeError): - def __init__(self): - UnicodeEncodeError.__init__(self, "ascii", u"", 0, 1, "bad") - del self.object - # A UnicodeEncodeError object with a bad object attribute class BadObjectUnicodeEncodeError(UnicodeEncodeError): def __init__(self): @@ -478,55 +460,15 @@ class CodecCallbackTest(unittest.TestCase): UnicodeError("ouch") ) self.assertRaises( - AttributeError, - codecs.replace_errors, - NoStartUnicodeEncodeError() - ) - self.assertRaises( - TypeError, - codecs.replace_errors, - BadStartUnicodeEncodeError() - ) - self.assertRaises( - AttributeError, - codecs.replace_errors, - NoEndUnicodeEncodeError() - ) - self.assertRaises( - AttributeError, - codecs.replace_errors, - NoObjectUnicodeEncodeError() - ) - self.assertRaises( TypeError, codecs.replace_errors, BadObjectUnicodeEncodeError() ) self.assertRaises( - AttributeError, - codecs.replace_errors, - NoEndUnicodeDecodeError() - ) - self.assertRaises( TypeError, codecs.replace_errors, BadObjectUnicodeDecodeError() ) - self.assertRaises( - AttributeError, - codecs.replace_errors, - NoStartUnicodeTranslateError() - ) - self.assertRaises( - AttributeError, - codecs.replace_errors, - NoEndUnicodeTranslateError() - ) - self.assertRaises( - AttributeError, - codecs.replace_errors, - NoObjectUnicodeTranslateError() - ) # With the correct exception, "replace" returns an "?" or u"\ufffd" replacement self.assertEquals( codecs.replace_errors(UnicodeEncodeError("ascii", u"\u3042", 0, 1, "ouch")), @@ -565,21 +507,6 @@ class CodecCallbackTest(unittest.TestCase): codecs.xmlcharrefreplace_errors, UnicodeTranslateError(u"\u3042", 0, 1, "ouch") ) - self.assertRaises( - AttributeError, - codecs.xmlcharrefreplace_errors, - NoStartUnicodeEncodeError() - ) - self.assertRaises( - AttributeError, - codecs.xmlcharrefreplace_errors, - NoEndUnicodeEncodeError() - ) - self.assertRaises( - AttributeError, - codecs.xmlcharrefreplace_errors, - NoObjectUnicodeEncodeError() - ) # Use the correct exception cs = (0, 1, 9, 10, 99, 100, 999, 1000, 9999, 10000, 0x3042) s = "".join(unichr(c) for c in cs) diff --git a/Lib/test/test_codecencodings_cn.py b/Lib/test/test_codecencodings_cn.py index 0638f4f..1bf8583 100644 --- a/Lib/test/test_codecencodings_cn.py +++ b/Lib/test/test_codecencodings_cn.py @@ -3,7 +3,6 @@ # test_codecencodings_cn.py # Codec encoding tests for PRC encodings. # -# $CJKCodecs: test_codecencodings_cn.py,v 1.2 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecencodings_hk.py b/Lib/test/test_codecencodings_hk.py index e7fad90..1cd020f 100644 --- a/Lib/test/test_codecencodings_hk.py +++ b/Lib/test/test_codecencodings_hk.py @@ -3,7 +3,6 @@ # test_codecencodings_hk.py # Codec encoding tests for HongKong encodings. # -# $CJKCodecs: test_codecencodings_hk.py,v 1.1 2004/07/10 17:35:20 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecencodings_jp.py b/Lib/test/test_codecencodings_jp.py index 483b7db..558598a 100644 --- a/Lib/test/test_codecencodings_jp.py +++ b/Lib/test/test_codecencodings_jp.py @@ -3,7 +3,6 @@ # test_codecencodings_jp.py # Codec encoding tests for Japanese encodings. # -# $CJKCodecs: test_codecencodings_jp.py,v 1.3 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecencodings_kr.py b/Lib/test/test_codecencodings_kr.py index 489c9f1..8139f76 100644 --- a/Lib/test/test_codecencodings_kr.py +++ b/Lib/test/test_codecencodings_kr.py @@ -3,7 +3,6 @@ # test_codecencodings_kr.py # Codec encoding tests for ROK encodings. # -# $CJKCodecs: test_codecencodings_kr.py,v 1.2 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecencodings_tw.py b/Lib/test/test_codecencodings_tw.py index fb8a4d0..7c59478 100644 --- a/Lib/test/test_codecencodings_tw.py +++ b/Lib/test/test_codecencodings_tw.py @@ -3,7 +3,6 @@ # test_codecencodings_tw.py # Codec encoding tests for ROC encodings. # -# $CJKCodecs: test_codecencodings_tw.py,v 1.2 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecmaps_cn.py b/Lib/test/test_codecmaps_cn.py index 25ecc02..8cbee76 100644 --- a/Lib/test/test_codecmaps_cn.py +++ b/Lib/test/test_codecmaps_cn.py @@ -3,7 +3,6 @@ # test_codecmaps_cn.py # Codec mapping tests for PRC encodings # -# $CJKCodecs: test_codecmaps_cn.py,v 1.3 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py index 2335c51..e7f7b96 100644 --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -3,7 +3,6 @@ # test_codecmaps_hk.py # Codec mapping tests for HongKong encodings # -# $CJKCodecs: test_codecmaps_hk.py,v 1.1 2004/07/10 17:35:20 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecmaps_jp.py b/Lib/test/test_codecmaps_jp.py index e75a5a8..08052d4 100644 --- a/Lib/test/test_codecmaps_jp.py +++ b/Lib/test/test_codecmaps_jp.py @@ -3,7 +3,6 @@ # test_codecmaps_jp.py # Codec mapping tests for Japanese encodings # -# $CJKCodecs: test_codecmaps_jp.py,v 1.3 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecmaps_kr.py b/Lib/test/test_codecmaps_kr.py index db65c01..7484a66 100644 --- a/Lib/test/test_codecmaps_kr.py +++ b/Lib/test/test_codecmaps_kr.py @@ -3,7 +3,6 @@ # test_codecmaps_kr.py # Codec mapping tests for ROK encodings # -# $CJKCodecs: test_codecmaps_kr.py,v 1.3 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_codecmaps_tw.py b/Lib/test/test_codecmaps_tw.py index 2d469b0..0b195f4 100644 --- a/Lib/test/test_codecmaps_tw.py +++ b/Lib/test/test_codecmaps_tw.py @@ -3,7 +3,6 @@ # test_codecmaps_tw.py # Codec mapping tests for ROC encodings # -# $CJKCodecs: test_codecmaps_tw.py,v 1.3 2004/06/19 06:09:55 perky Exp $ from test import test_support from test import test_multibytecodec_support diff --git a/Lib/test/test_compiler.py b/Lib/test/test_compiler.py index a59d6aa..48f1643 100644 --- a/Lib/test/test_compiler.py +++ b/Lib/test/test_compiler.py @@ -26,6 +26,7 @@ class CompilerTest(unittest.TestCase): next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL print >>sys.__stdout__, \ ' testCompileLibrary still working, be patient...' + sys.__stdout__.flush() if not basename.endswith(".py"): continue @@ -55,6 +56,9 @@ class CompilerTest(unittest.TestCase): def testYieldExpr(self): compiler.compile("def g(): yield\n\n", "", "exec") + def testDefaultArgs(self): + self.assertRaises(SyntaxError, compiler.parse, "def foo(a=1, b): pass") + def testLineNo(self): # Test that all nodes except Module have a correct lineno attribute. filename = __file__ diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 97470c7..2cf39ae 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -51,7 +51,7 @@ class ContextManagerTestCase(unittest.TestCase): @contextmanager def whee(): yield - ctx = whee().__context__() + ctx = whee() ctx.__enter__() # Calling __exit__ should not result in an exception self.failIf(ctx.__exit__(TypeError, TypeError("foo"), None)) @@ -63,7 +63,7 @@ class ContextManagerTestCase(unittest.TestCase): yield except: yield - ctx = whoo().__context__() + ctx = whoo() ctx.__enter__() self.assertRaises( RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None @@ -146,6 +146,29 @@ class NestedTestCase(unittest.TestCase): else: self.fail("Didn't raise ZeroDivisionError") + def test_nested_right_exception(self): + state = [] + @contextmanager + def a(): + yield 1 + class b(object): + def __enter__(self): + return 2 + def __exit__(self, *exc_info): + try: + raise Exception() + except: + pass + try: + with nested(a(), b()) as (x, y): + 1/0 + except ZeroDivisionError: + self.assertEqual((x, y), (1, 2)) + except Exception: + self.fail("Reraised wrong exception") + else: + self.fail("Didn't raise ZeroDivisionError") + def test_nested_b_swallows(self): @contextmanager def a(): @@ -316,12 +339,12 @@ class DecimalContextTestCase(unittest.TestCase): orig_context = ctx.copy() try: ctx.prec = save_prec = decimal.ExtendedContext.prec + 5 - with decimal.ExtendedContext: + with decimal.ExtendedContext.get_manager(): self.assertEqual(decimal.getcontext().prec, decimal.ExtendedContext.prec) self.assertEqual(decimal.getcontext().prec, save_prec) try: - with decimal.ExtendedContext: + with decimal.ExtendedContext.get_manager(): self.assertEqual(decimal.getcontext().prec, decimal.ExtendedContext.prec) 1/0 diff --git a/Lib/test/test_cookielib.py b/Lib/test/test_cookielib.py index 49e7d47..991506c 100644 --- a/Lib/test/test_cookielib.py +++ b/Lib/test/test_cookielib.py @@ -695,6 +695,22 @@ class CookieTests(TestCase): 'foo=bar; domain=friendly.org; Version="1"') self.assertEquals(len(c), 0) + def test_strict_domain(self): + # Cookies whose domain is a country-code tld like .co.uk should + # not be set if CookiePolicy.strict_domain is true. + from cookielib import CookieJar, DefaultCookiePolicy + + cp = DefaultCookiePolicy(strict_domain=True) + cj = CookieJar(policy=cp) + interact_netscape(cj, "http://example.co.uk/", 'no=problemo') + interact_netscape(cj, "http://example.co.uk/", + 'okey=dokey; Domain=.example.co.uk') + self.assertEquals(len(cj), 2) + for pseudo_tld in [".co.uk", ".org.za", ".tx.us", ".name.us"]: + interact_netscape(cj, "http://example.%s/" % pseudo_tld, + 'spam=eggs; Domain=.co.uk') + self.assertEquals(len(cj), 2) + def test_two_component_domain_ns(self): # Netscape: .www.bar.com, www.bar.com, .bar.com, bar.com, no domain # should all get accepted, as should .acme.com, acme.com and no domain diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 2528b4a..203bea1 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -1400,6 +1400,12 @@ class TestDateTime(TestDate): got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + def test_microsecond_rounding(self): + # Test whether fromtimestamp "rounds up" floats that are less + # than one microsecond smaller than an integer. + self.assertEquals(self.theclass.fromtimestamp(0.9999999), + self.theclass.fromtimestamp(1)) + def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, # and that this test will fail there. This test should diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index b17607d..443c962 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -1079,6 +1079,25 @@ output to match any substring in the actual output: ... # doctest: +NORMALIZE_WHITESPACE [0, 1, ..., 18, 19] +The SKIP flag causes an example to be skipped entirely. I.e., the +example is not run. It can be useful in contexts where doctest +examples serve as both documentation and test cases, and an example +should be included for documentation purposes, but should not be +checked (e.g., because its output is random, or depends on resources +which would be unavailable.) The SKIP flag can also be used for +'commenting out' broken examples. + + >>> import unavailable_resource # doctest: +SKIP + >>> unavailable_resource.do_something() # doctest: +SKIP + >>> unavailable_resource.blow_up() # doctest: +SKIP + Traceback (most recent call last): + ... + UncheckedBlowUpError: Nobody checks me. + + >>> import random + >>> print random.random() # doctest: +SKIP + 0.721216923889 + The REPORT_UDIFF flag causes failures that involve multi-line expected and actual outputs to be displayed using a unified diff: @@ -1281,6 +1300,26 @@ count as failures: ValueError: 2 (3, 5) +New option flags can also be registered, via register_optionflag(). Here +we reach into doctest's internals a bit. + + >>> unlikely = "UNLIKELY_OPTION_NAME" + >>> unlikely in doctest.OPTIONFLAGS_BY_NAME + False + >>> new_flag_value = doctest.register_optionflag(unlikely) + >>> unlikely in doctest.OPTIONFLAGS_BY_NAME + True + +Before 2.4.4/2.5, registering a name more than once erroneously created +more than one flag value. Here we verify that's fixed: + + >>> redundant_flag_value = doctest.register_optionflag(unlikely) + >>> redundant_flag_value == new_flag_value + True + +Clean up. + >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] + """ def option_directives(): r""" diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 7946142..8f995f7 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -81,14 +81,6 @@ try: x = undefined_variable except NameError: pass r(OverflowError) -# XXX -# Obscure: in 2.2 and 2.3, this test relied on changing OverflowWarning -# into an error, in order to trigger OverflowError. In 2.4, OverflowWarning -# should no longer be generated, so the focus of the test shifts to showing -# that OverflowError *isn't* generated. OverflowWarning should be gone -# in Python 2.5, and then the filterwarnings() call, and this comment, -# should go away. -warnings.filterwarnings("error", "", OverflowWarning, __name__) x = 1 for dummy in range(128): x += x # this simply shouldn't blow up @@ -224,3 +216,88 @@ if not sys.platform.startswith('java'): test_capi3() unlink(TESTFN) + +# test that exception attributes are happy. +try: str(u'Hello \u00E1') +except Exception, e: sampleUnicodeEncodeError = e +try: unicode('\xff') +except Exception, e: sampleUnicodeDecodeError = e +exceptionList = [ + ( BaseException, (), { 'message' : '', 'args' : () }), + ( BaseException, (1, ), { 'message' : 1, 'args' : ( 1, ) }), + ( BaseException, ('foo', ), { 'message' : 'foo', 'args' : ( 'foo', ) }), + ( BaseException, ('foo', 1), { 'message' : '', 'args' : ( 'foo', 1 ) }), + ( SystemExit, ('foo',), { 'message' : 'foo', 'args' : ( 'foo', ), + 'code' : 'foo' }), + ( IOError, ('foo',), { 'message' : 'foo', 'args' : ( 'foo', ), }), + ( IOError, ('foo', 'bar'), { 'message' : '', + 'args' : ('foo', 'bar'), }), + ( IOError, ('foo', 'bar', 'baz'), + { 'message' : '', 'args' : ('foo', 'bar'), }), + ( EnvironmentError, ('errnoStr', 'strErrorStr', 'filenameStr'), + { 'message' : '', 'args' : ('errnoStr', 'strErrorStr'), + 'strerror' : 'strErrorStr', + 'errno' : 'errnoStr', 'filename' : 'filenameStr' }), + ( EnvironmentError, (1, 'strErrorStr', 'filenameStr'), + { 'message' : '', 'args' : (1, 'strErrorStr'), + 'strerror' : 'strErrorStr', 'errno' : 1, + 'filename' : 'filenameStr' }), + ( SyntaxError, ('msgStr',), + { 'message' : 'msgStr', 'args' : ('msgStr', ), + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : None, 'lineno' : None, 'offset' : None, + 'text' : None }), + ( SyntaxError, ('msgStr', ('filenameStr', 'linenoStr', 'offsetStr', + 'textStr')), + { 'message' : '', 'args' : ('msgStr', ('filenameStr', + 'linenoStr', 'offsetStr', 'textStr' )), + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : 'filenameStr', 'lineno' : 'linenoStr', + 'offset' : 'offsetStr', 'text' : 'textStr' }), + ( SyntaxError, ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr', + 'textStr', 'print_file_and_lineStr'), + { 'message' : '', 'args' : ('msgStr', 'filenameStr', + 'linenoStr', 'offsetStr', 'textStr', + 'print_file_and_lineStr'), + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : None, 'lineno' : None, 'offset' : None, + 'text' : None }), + ( UnicodeError, (), + { 'message' : '', 'args' : (), }), + ( sampleUnicodeEncodeError, + { 'message' : '', 'args' : ('ascii', u'Hello \xe1', 6, 7, + 'ordinal not in range(128)'), + 'encoding' : 'ascii', 'object' : u'Hello \xe1', + 'start' : 6, 'reason' : 'ordinal not in range(128)' }), + ( sampleUnicodeDecodeError, + { 'message' : '', 'args' : ('ascii', '\xff', 0, 1, + 'ordinal not in range(128)'), + 'encoding' : 'ascii', 'object' : '\xff', + 'start' : 0, 'reason' : 'ordinal not in range(128)' }), + ( UnicodeTranslateError, (u"\u3042", 0, 1, "ouch"), + { 'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'), + 'object' : u'\u3042', 'reason' : 'ouch', + 'start' : 0, 'end' : 1 }), + ] +try: + exceptionList.append( + ( WindowsError, (1, 'strErrorStr', 'filenameStr'), + { 'message' : '', 'args' : (1, 'strErrorStr'), + 'strerror' : 'strErrorStr', + 'errno' : 22, 'filename' : 'filenameStr', + 'winerror' : 1 })) +except NameError: pass + +for args in exceptionList: + expected = args[-1] + try: + if len(args) == 2: raise args[0] + else: raise apply(args[0], args[1]) + except BaseException, e: + for checkArgName in expected.keys(): + if repr(getattr(e, checkArgName)) != repr(expected[checkArgName]): + raise TestFailed('Checking exception arguments, exception ' + '"%s", attribute "%s" expected %s got %s.' % + ( repr(e), checkArgName, + repr(expected[checkArgName]), + repr(getattr(e, checkArgName)) )) diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index cfc1019..ca1c6ba 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -147,7 +147,7 @@ f.close() bad_mode = "qwerty" try: open(TESTFN, bad_mode) -except IOError, msg: +except ValueError, msg: if msg[0] != 0: s = str(msg) if s.find(TESTFN) != -1 or s.find(bad_mode) == -1: diff --git a/Lib/test/test_grp.py b/Lib/test/test_grp.py index 2c3ab29..08958ba 100755 --- a/Lib/test/test_grp.py +++ b/Lib/test/test_grp.py @@ -31,7 +31,10 @@ class GroupDatabaseTestCase(unittest.TestCase): self.assertEqual(e2.gr_gid, e.gr_gid) e2 = grp.getgrnam(e.gr_name) self.check_value(e2) - self.assertEqual(e2.gr_name, e.gr_name) + # There are instances where getgrall() returns group names in + # lowercase while getgrgid() returns proper casing. + # Discovered on Ubuntu 5.04 (custom). + self.assertEqual(e2.gr_name.lower(), e.gr_name.lower()) def test_errors(self): self.assertRaises(TypeError, grp.getgrgid) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index a72b8bd..effba3c 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -205,3 +205,20 @@ def test_import_name_binding(): assert y is test.test_support, y.__name__ test_import_name_binding() + +def test_import_initless_directory_warning(): + import warnings + oldfilters = warnings.filters[:] + warnings.simplefilter('error', ImportWarning); + try: + # Just a random non-package directory we always expect to be + # somewhere in sys.path... + __import__("site-packages") + except ImportWarning: + pass + else: + raise AssertionError + finally: + warnings.filters = oldfilters + +test_import_initless_directory_warning() diff --git a/Lib/test/test_importhooks.py b/Lib/test/test_importhooks.py index 0693581..e8b4695 100644 --- a/Lib/test/test_importhooks.py +++ b/Lib/test/test_importhooks.py @@ -14,6 +14,7 @@ def get_file(): absimp = "import sub\n" relimp = "from . import sub\n" +deeprelimp = "from .... import sub\n" futimp = "from __future__ import absolute_import\n" reload_src = test_src+"""\ @@ -26,6 +27,7 @@ reload_co = compile(reload_src, "", "exec") test2_oldabs_co = compile(absimp + test_src, "", "exec") test2_newabs_co = compile(futimp + absimp + test_src, "", "exec") test2_newrel_co = compile(relimp + test_src, "", "exec") +test2_deeprel_co = compile(deeprelimp + test_src, "", "exec") test2_futrel_co = compile(futimp + relimp + test_src, "", "exec") test_path = "!!!_test_!!!" @@ -46,10 +48,11 @@ class TestImporter: "hooktestmodule": (False, test_co), "hooktestpackage": (True, test_co), "hooktestpackage.sub": (True, test_co), - "hooktestpackage.sub.subber": (False, test_co), + "hooktestpackage.sub.subber": (True, test_co), "hooktestpackage.oldabs": (False, test2_oldabs_co), "hooktestpackage.newabs": (False, test2_newabs_co), "hooktestpackage.newrel": (False, test2_newrel_co), + "hooktestpackage.sub.subber.subest": (True, test2_deeprel_co), "hooktestpackage.futrel": (False, test2_futrel_co), "sub": (False, test_co), "reloadmodule": (False, test_co), @@ -203,6 +206,12 @@ class ImportHooksTestCase(ImportHooksBaseTestCase): self.assertEqual(hooktestpackage.newrel.sub, hooktestpackage.sub) + import hooktestpackage.sub.subber.subest as subest + self.assertEqual(subest.get_name(), + "hooktestpackage.sub.subber.subest") + self.assertEqual(subest.sub, + hooktestpackage.sub) + import hooktestpackage.futrel self.assertEqual(hooktestpackage.futrel.get_name(), "hooktestpackage.futrel") diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index 1523e77..9e264b9 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -20,14 +20,14 @@ for tloc in tlocs: else: raise ImportError, "test locale not supported (tried %s)"%(', '.join(tlocs)) -def testformat(formatstr, value, grouping = 0, output=None): +def testformat(formatstr, value, grouping = 0, output=None, func=locale.format): if verbose: if output: print "%s %% %s =? %s ..." %\ (repr(formatstr), repr(value), repr(output)), else: print "%s %% %s works? ..." % (repr(formatstr), repr(value)), - result = locale.format(formatstr, value, grouping = grouping) + result = func(formatstr, value, grouping = grouping) if output and result != output: if verbose: print 'no' @@ -49,6 +49,30 @@ try: testformat("%-10.f", 4200, grouping=1, output='4%s200 ' % sep) # Invoke getpreferredencoding to make sure it does not cause exceptions, locale.getpreferredencoding() + + # === Test format() with more complex formatting strings + # test if grouping is independent from other characters in formatting string + testformat("One million is %i", 1000000, grouping=1, + output='One million is 1%s000%s000' % (sep, sep), + func=locale.format_string) + testformat("One million is %i", 1000000, grouping=1, + output='One million is 1%s000%s000' % (sep, sep), + func=locale.format_string) + # test dots in formatting string + testformat(".%f.", 1000.0, output='.1000.000000.', func=locale.format_string) + # test floats + testformat("--> %10.2f", 1000.0, grouping=1, output='--> 1%s000.00' % sep, + func=locale.format_string) + # test asterisk formats + testformat("%10.*f", (2, 1000.0), grouping=0, output=' 1000.00', + func=locale.format_string) + testformat("%*.*f", (10, 2, 1000.0), grouping=1, output=' 1%s000.00' % sep, + func=locale.format_string) + # test more-in-one + testformat("int %i float %.2f str %s", (1000, 1000.0, 'str'), grouping=1, + output='int 1%s000 float 1%s000.00 str str' % (sep, sep), + func=locale.format_string) + finally: locale.setlocale(locale.LC_NUMERIC, oldlocale) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index b689dc8..73f8288 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -28,6 +28,7 @@ import select import os, sys, string, struct, types, cPickle, cStringIO import socket, tempfile, threading, time import logging, logging.handlers, logging.config +from test.test_support import run_with_locale BANNER = "-- %-10s %-6s ---------------------------------------------------\n" @@ -657,19 +658,11 @@ def test_main_inner(): pass rootLogger.removeHandler(hdlr) +# 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. +@run_with_locale('LC_ALL', '') def test_main(): - import locale - # 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 so we can restore it at the end. - try: - original_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, '') - except (ValueError, locale.Error): - # this happens on a Solaris box which only supports "C" locale - # or a Mac OS X box which supports very little locale stuff at all - original_locale = None - # Save and restore the original root logger level across the tests. # Otherwise, e.g., if any test using cookielib runs after test_logging, # cookielib's debug-level logger tries to log messages, leading to @@ -681,8 +674,6 @@ def test_main(): try: test_main_inner() finally: - if original_locale is not None: - locale.setlocale(locale.LC_ALL, original_locale) root_logger.setLevel(original_logging_level) if __name__ == "__main__": diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 77d39a6..914a20c 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -1,15 +1,1576 @@ -import mailbox import os import time -import unittest +import stat +import socket +import email +import email.Message +import rfc822 +import re +import StringIO from test import test_support - -# cleanup earlier tests +import unittest +import mailbox +import glob try: - os.unlink(test_support.TESTFN) -except os.error: + import fcntl +except ImportError: pass + +class TestBase(unittest.TestCase): + + def _check_sample(self, msg): + # Inspect a mailbox.Message representation of the sample message + self.assert_(isinstance(msg, email.Message.Message)) + self.assert_(isinstance(msg, mailbox.Message)) + for key, value in _sample_headers.iteritems(): + self.assert_(value in msg.get_all(key)) + self.assert_(msg.is_multipart()) + self.assert_(len(msg.get_payload()) == len(_sample_payloads)) + for i, payload in enumerate(_sample_payloads): + part = msg.get_payload(i) + self.assert_(isinstance(part, email.Message.Message)) + self.assert_(not isinstance(part, mailbox.Message)) + self.assert_(part.get_payload() == payload) + + def _delete_recursively(self, target): + # Delete a file or delete a directory recursively + if os.path.isdir(target): + for path, dirs, files in os.walk(target, topdown=False): + for name in files: + os.remove(os.path.join(path, name)) + for name in dirs: + os.rmdir(os.path.join(path, name)) + os.rmdir(target) + elif os.path.exists(target): + os.remove(target) + + +class TestMailbox(TestBase): + + _factory = None # Overridden by subclasses to reuse tests + _template = 'From: foo\n\n%s' + + def setUp(self): + self._path = test_support.TESTFN + self._box = self._factory(self._path) + + def tearDown(self): + self._box.close() + self._delete_recursively(self._path) + + def test_add(self): + # Add copies of a sample message + keys = [] + keys.append(self._box.add(self._template % 0)) + self.assert_(len(self._box) == 1) + keys.append(self._box.add(mailbox.Message(_sample_message))) + self.assert_(len(self._box) == 2) + keys.append(self._box.add(email.message_from_string(_sample_message))) + self.assert_(len(self._box) == 3) + keys.append(self._box.add(StringIO.StringIO(_sample_message))) + self.assert_(len(self._box) == 4) + keys.append(self._box.add(_sample_message)) + self.assert_(len(self._box) == 5) + self.assert_(self._box.get_string(keys[0]) == self._template % 0) + for i in (1, 2, 3, 4): + self._check_sample(self._box[keys[i]]) + + def test_remove(self): + # Remove messages using remove() + self._test_remove_or_delitem(self._box.remove) + + def test_delitem(self): + # Remove messages using __delitem__() + self._test_remove_or_delitem(self._box.__delitem__) + + def _test_remove_or_delitem(self, method): + # (Used by test_remove() and test_delitem().) + key0 = self._box.add(self._template % 0) + key1 = self._box.add(self._template % 1) + self.assert_(len(self._box) == 2) + method(key0) + l = len(self._box) + self.assert_(l == 1, "actual l: %s" % l) + self.assertRaises(KeyError, lambda: self._box[key0]) + self.assertRaises(KeyError, lambda: method(key0)) + self.assert_(self._box.get_string(key1) == self._template % 1) + key2 = self._box.add(self._template % 2) + self.assert_(len(self._box) == 2) + method(key2) + l = len(self._box) + self.assert_(l == 1, "actual l: %s" % l) + self.assertRaises(KeyError, lambda: self._box[key2]) + self.assertRaises(KeyError, lambda: method(key2)) + self.assert_(self._box.get_string(key1) == self._template % 1) + method(key1) + self.assert_(len(self._box) == 0) + self.assertRaises(KeyError, lambda: self._box[key1]) + self.assertRaises(KeyError, lambda: method(key1)) + + def test_discard(self, repetitions=10): + # Discard messages + key0 = self._box.add(self._template % 0) + key1 = self._box.add(self._template % 1) + self.assert_(len(self._box) == 2) + self._box.discard(key0) + self.assert_(len(self._box) == 1) + self.assertRaises(KeyError, lambda: self._box[key0]) + self._box.discard(key0) + self.assert_(len(self._box) == 1) + self.assertRaises(KeyError, lambda: self._box[key0]) + + def test_get(self): + # Retrieve messages using get() + key0 = self._box.add(self._template % 0) + msg = self._box.get(key0) + self.assert_(msg['from'] == 'foo') + self.assert_(msg.get_payload() == '0') + self.assert_(self._box.get('foo') is None) + self.assert_(self._box.get('foo', False) is False) + self._box.close() + self._box = self._factory(self._path, factory=rfc822.Message) + key1 = self._box.add(self._template % 1) + msg = self._box.get(key1) + self.assert_(msg['from'] == 'foo') + self.assert_(msg.fp.read() == '1') + + def test_getitem(self): + # Retrieve message using __getitem__() + key0 = self._box.add(self._template % 0) + msg = self._box[key0] + self.assert_(msg['from'] == 'foo') + self.assert_(msg.get_payload() == '0') + self.assertRaises(KeyError, lambda: self._box['foo']) + self._box.discard(key0) + self.assertRaises(KeyError, lambda: self._box[key0]) + + def test_get_message(self): + # Get Message representations of messages + key0 = self._box.add(self._template % 0) + key1 = self._box.add(_sample_message) + msg0 = self._box.get_message(key0) + self.assert_(isinstance(msg0, mailbox.Message)) + self.assert_(msg0['from'] == 'foo') + self.assert_(msg0.get_payload() == '0') + self._check_sample(self._box.get_message(key1)) + + def test_get_string(self): + # Get string representations of messages + key0 = self._box.add(self._template % 0) + key1 = self._box.add(_sample_message) + self.assert_(self._box.get_string(key0) == self._template % 0) + self.assert_(self._box.get_string(key1) == _sample_message) + + def test_get_file(self): + # Get file representations of messages + key0 = self._box.add(self._template % 0) + key1 = self._box.add(_sample_message) + self.assert_(self._box.get_file(key0).read().replace(os.linesep, '\n') + == self._template % 0) + self.assert_(self._box.get_file(key1).read().replace(os.linesep, '\n') + == _sample_message) + + def test_iterkeys(self): + # Get keys using iterkeys() + self._check_iteration(self._box.iterkeys, do_keys=True, do_values=False) + + def test_keys(self): + # Get keys using keys() + self._check_iteration(self._box.keys, do_keys=True, do_values=False) + + def test_itervalues(self): + # Get values using itervalues() + self._check_iteration(self._box.itervalues, do_keys=False, + do_values=True) + + def test_iter(self): + # Get values using __iter__() + self._check_iteration(self._box.__iter__, do_keys=False, + do_values=True) + + def test_values(self): + # Get values using values() + self._check_iteration(self._box.values, do_keys=False, do_values=True) + + def test_iteritems(self): + # Get keys and values using iteritems() + self._check_iteration(self._box.iteritems, do_keys=True, + do_values=True) + + def test_items(self): + # Get keys and values using items() + self._check_iteration(self._box.items, do_keys=True, do_values=True) + + def _check_iteration(self, method, do_keys, do_values, repetitions=10): + for value in method(): + self.fail("Not empty") + keys, values = [], [] + for i in xrange(repetitions): + keys.append(self._box.add(self._template % i)) + values.append(self._template % i) + if do_keys and not do_values: + returned_keys = list(method()) + elif do_values and not do_keys: + returned_values = list(method()) + else: + returned_keys, returned_values = [], [] + for key, value in method(): + returned_keys.append(key) + returned_values.append(value) + if do_keys: + self.assert_(len(keys) == len(returned_keys)) + self.assert_(set(keys) == set(returned_keys)) + if do_values: + count = 0 + for value in returned_values: + self.assert_(value['from'] == 'foo') + self.assert_(int(value.get_payload()) < repetitions) + count += 1 + self.assert_(len(values) == count) + + def test_has_key(self): + # Check existence of keys using has_key() + self._test_has_key_or_contains(self._box.has_key) + + def test_contains(self): + # Check existence of keys using __contains__() + self._test_has_key_or_contains(self._box.__contains__) + + def _test_has_key_or_contains(self, method): + # (Used by test_has_key() and test_contains().) + self.assert_(not method('foo')) + key0 = self._box.add(self._template % 0) + self.assert_(method(key0)) + self.assert_(not method('foo')) + key1 = self._box.add(self._template % 1) + self.assert_(method(key1)) + self.assert_(method(key0)) + self.assert_(not method('foo')) + self._box.remove(key0) + self.assert_(not method(key0)) + self.assert_(method(key1)) + self.assert_(not method('foo')) + self._box.remove(key1) + self.assert_(not method(key1)) + self.assert_(not method(key0)) + self.assert_(not method('foo')) + + def test_len(self, repetitions=10): + # Get message count + keys = [] + for i in xrange(repetitions): + self.assert_(len(self._box) == i) + keys.append(self._box.add(self._template % i)) + self.assert_(len(self._box) == i + 1) + for i in xrange(repetitions): + self.assert_(len(self._box) == repetitions - i) + self._box.remove(keys[i]) + self.assert_(len(self._box) == repetitions - i - 1) + + def test_set_item(self): + # Modify messages using __setitem__() + key0 = self._box.add(self._template % 'original 0') + self.assert_(self._box.get_string(key0) == \ + self._template % 'original 0') + key1 = self._box.add(self._template % 'original 1') + self.assert_(self._box.get_string(key1) == \ + self._template % 'original 1') + self._box[key0] = self._template % 'changed 0' + self.assert_(self._box.get_string(key0) == \ + self._template % 'changed 0') + self._box[key1] = self._template % 'changed 1' + self.assert_(self._box.get_string(key1) == \ + self._template % 'changed 1') + self._box[key0] = _sample_message + self._check_sample(self._box[key0]) + self._box[key1] = self._box[key0] + self._check_sample(self._box[key1]) + self._box[key0] = self._template % 'original 0' + self.assert_(self._box.get_string(key0) == + self._template % 'original 0') + self._check_sample(self._box[key1]) + self.assertRaises(KeyError, + lambda: self._box.__setitem__('foo', 'bar')) + self.assertRaises(KeyError, lambda: self._box['foo']) + self.assert_(len(self._box) == 2) + + def test_clear(self, iterations=10): + # Remove all messages using clear() + keys = [] + for i in xrange(iterations): + self._box.add(self._template % i) + for i, key in enumerate(keys): + self.assert_(self._box.get_string(key) == self._template % i) + self._box.clear() + self.assert_(len(self._box) == 0) + for i, key in enumerate(keys): + self.assertRaises(KeyError, lambda: self._box.get_string(key)) + + def test_pop(self): + # Get and remove a message using pop() + key0 = self._box.add(self._template % 0) + self.assert_(key0 in self._box) + key1 = self._box.add(self._template % 1) + self.assert_(key1 in self._box) + self.assert_(self._box.pop(key0).get_payload() == '0') + self.assert_(key0 not in self._box) + self.assert_(key1 in self._box) + key2 = self._box.add(self._template % 2) + self.assert_(key2 in self._box) + self.assert_(self._box.pop(key2).get_payload() == '2') + self.assert_(key2 not in self._box) + self.assert_(key1 in self._box) + self.assert_(self._box.pop(key1).get_payload() == '1') + self.assert_(key1 not in self._box) + self.assert_(len(self._box) == 0) + + def test_popitem(self, iterations=10): + # Get and remove an arbitrary (key, message) using popitem() + keys = [] + for i in xrange(10): + keys.append(self._box.add(self._template % i)) + seen = [] + for i in xrange(10): + key, msg = self._box.popitem() + self.assert_(key in keys) + self.assert_(key not in seen) + seen.append(key) + self.assert_(int(msg.get_payload()) == keys.index(key)) + self.assert_(len(self._box) == 0) + for key in keys: + self.assertRaises(KeyError, lambda: self._box[key]) + + def test_update(self): + # Modify multiple messages using update() + key0 = self._box.add(self._template % 'original 0') + key1 = self._box.add(self._template % 'original 1') + key2 = self._box.add(self._template % 'original 2') + self._box.update({key0: self._template % 'changed 0', + key2: _sample_message}) + self.assert_(len(self._box) == 3) + self.assert_(self._box.get_string(key0) == + self._template % 'changed 0') + self.assert_(self._box.get_string(key1) == + self._template % 'original 1') + self._check_sample(self._box[key2]) + self._box.update([(key2, self._template % 'changed 2'), + (key1, self._template % 'changed 1'), + (key0, self._template % 'original 0')]) + self.assert_(len(self._box) == 3) + self.assert_(self._box.get_string(key0) == + self._template % 'original 0') + self.assert_(self._box.get_string(key1) == + self._template % 'changed 1') + self.assert_(self._box.get_string(key2) == + self._template % 'changed 2') + self.assertRaises(KeyError, + lambda: self._box.update({'foo': 'bar', + key0: self._template % "changed 0"})) + self.assert_(len(self._box) == 3) + self.assert_(self._box.get_string(key0) == + self._template % "changed 0") + self.assert_(self._box.get_string(key1) == + self._template % "changed 1") + self.assert_(self._box.get_string(key2) == + self._template % "changed 2") + + def test_flush(self): + # Write changes to disk + self._test_flush_or_close(self._box.flush) + + def test_lock_unlock(self): + # Lock and unlock the mailbox + self.assert_(not os.path.exists(self._get_lock_path())) + self._box.lock() + self.assert_(os.path.exists(self._get_lock_path())) + self._box.unlock() + self.assert_(not os.path.exists(self._get_lock_path())) + + def test_close(self): + # Close mailbox and flush changes to disk + self._test_flush_or_close(self._box.close) + + def _test_flush_or_close(self, method): + contents = [self._template % i for i in xrange(3)] + self._box.add(contents[0]) + self._box.add(contents[1]) + self._box.add(contents[2]) + method() + self._box = self._factory(self._path) + keys = self._box.keys() + self.assert_(len(keys) == 3) + for key in keys: + self.assert_(self._box.get_string(key) in contents) + + def test_dump_message(self): + # Write message representations to disk + for input in (email.message_from_string(_sample_message), + _sample_message, StringIO.StringIO(_sample_message)): + output = StringIO.StringIO() + self._box._dump_message(input, output) + self.assert_(output.getvalue() == + _sample_message.replace('\n', os.linesep)) + output = StringIO.StringIO() + self.assertRaises(TypeError, + lambda: self._box._dump_message(None, output)) + + def _get_lock_path(self): + # Return the path of the dot lock file. May be overridden. + return self._path + '.lock' + + +class TestMailboxSuperclass(TestBase): + + def test_notimplemented(self): + # Test that all Mailbox methods raise NotImplementedException. + box = mailbox.Mailbox('path') + self.assertRaises(NotImplementedError, lambda: box.add('')) + self.assertRaises(NotImplementedError, lambda: box.remove('')) + self.assertRaises(NotImplementedError, lambda: box.__delitem__('')) + self.assertRaises(NotImplementedError, lambda: box.discard('')) + self.assertRaises(NotImplementedError, lambda: box.__setitem__('', '')) + self.assertRaises(NotImplementedError, lambda: box.iterkeys()) + self.assertRaises(NotImplementedError, lambda: box.keys()) + self.assertRaises(NotImplementedError, lambda: box.itervalues().next()) + self.assertRaises(NotImplementedError, lambda: box.__iter__().next()) + self.assertRaises(NotImplementedError, lambda: box.values()) + self.assertRaises(NotImplementedError, lambda: box.iteritems().next()) + self.assertRaises(NotImplementedError, lambda: box.items()) + self.assertRaises(NotImplementedError, lambda: box.get('')) + self.assertRaises(NotImplementedError, lambda: box.__getitem__('')) + self.assertRaises(NotImplementedError, lambda: box.get_message('')) + self.assertRaises(NotImplementedError, lambda: box.get_string('')) + self.assertRaises(NotImplementedError, lambda: box.get_file('')) + self.assertRaises(NotImplementedError, lambda: box.has_key('')) + self.assertRaises(NotImplementedError, lambda: box.__contains__('')) + self.assertRaises(NotImplementedError, lambda: box.__len__()) + self.assertRaises(NotImplementedError, lambda: box.clear()) + self.assertRaises(NotImplementedError, lambda: box.pop('')) + self.assertRaises(NotImplementedError, lambda: box.popitem()) + self.assertRaises(NotImplementedError, lambda: box.update((('', ''),))) + self.assertRaises(NotImplementedError, lambda: box.flush()) + self.assertRaises(NotImplementedError, lambda: box.lock()) + self.assertRaises(NotImplementedError, lambda: box.unlock()) + self.assertRaises(NotImplementedError, lambda: box.close()) + + +class TestMaildir(TestMailbox): + + _factory = lambda self, path, factory=None: mailbox.Maildir(path, factory) + + def setUp(self): + TestMailbox.setUp(self) + if os.name == 'nt': + self._box.colon = '!' + + def test_add_MM(self): + # Add a MaildirMessage instance + msg = mailbox.MaildirMessage(self._template % 0) + msg.set_subdir('cur') + msg.set_info('foo') + key = self._box.add(msg) + self.assert_(os.path.exists(os.path.join(self._path, 'cur', '%s%sfoo' % + (key, self._box.colon)))) + + def test_get_MM(self): + # Get a MaildirMessage instance + msg = mailbox.MaildirMessage(self._template % 0) + msg.set_subdir('cur') + msg.set_flags('RF') + key = self._box.add(msg) + msg_returned = self._box.get_message(key) + self.assert_(isinstance(msg_returned, mailbox.MaildirMessage)) + self.assert_(msg_returned.get_subdir() == 'cur') + self.assert_(msg_returned.get_flags() == 'FR') + + def test_set_MM(self): + # Set with a MaildirMessage instance + msg0 = mailbox.MaildirMessage(self._template % 0) + msg0.set_flags('TP') + key = self._box.add(msg0) + msg_returned = self._box.get_message(key) + self.assert_(msg_returned.get_subdir() == 'new') + self.assert_(msg_returned.get_flags() == 'PT') + msg1 = mailbox.MaildirMessage(self._template % 1) + self._box[key] = msg1 + msg_returned = self._box.get_message(key) + self.assert_(msg_returned.get_subdir() == 'new') + self.assert_(msg_returned.get_flags() == '') + self.assert_(msg_returned.get_payload() == '1') + msg2 = mailbox.MaildirMessage(self._template % 2) + msg2.set_info('2,S') + self._box[key] = msg2 + self._box[key] = self._template % 3 + msg_returned = self._box.get_message(key) + self.assert_(msg_returned.get_subdir() == 'new') + self.assert_(msg_returned.get_flags() == 'S') + self.assert_(msg_returned.get_payload() == '3') + + def test_initialize_new(self): + # Initialize a non-existent mailbox + self.tearDown() + self._box = mailbox.Maildir(self._path) + self._check_basics(factory=rfc822.Message) + self._delete_recursively(self._path) + self._box = self._factory(self._path, factory=None) + self._check_basics() + + def test_initialize_existing(self): + # Initialize an existing mailbox + self.tearDown() + for subdir in '', 'tmp', 'new', 'cur': + os.mkdir(os.path.join(self._path, subdir)) + self._box = mailbox.Maildir(self._path) + self._check_basics(factory=rfc822.Message) + self._box = mailbox.Maildir(self._path, factory=None) + self._check_basics() + + def _check_basics(self, factory=None): + # (Used by test_open_new() and test_open_existing().) + self.assertEqual(self._box._path, os.path.abspath(self._path)) + self.assertEqual(self._box._factory, factory) + for subdir in '', 'tmp', 'new', 'cur': + path = os.path.join(self._path, subdir) + mode = os.stat(path)[stat.ST_MODE] + self.assert_(stat.S_ISDIR(mode), "Not a directory: '%s'" % path) + + def test_list_folders(self): + # List folders + self._box.add_folder('one') + self._box.add_folder('two') + self._box.add_folder('three') + self.assert_(len(self._box.list_folders()) == 3) + self.assert_(set(self._box.list_folders()) == + set(('one', 'two', 'three'))) + + def test_get_folder(self): + # Open folders + self._box.add_folder('foo.bar') + folder0 = self._box.get_folder('foo.bar') + folder0.add(self._template % 'bar') + self.assert_(os.path.isdir(os.path.join(self._path, '.foo.bar'))) + folder1 = self._box.get_folder('foo.bar') + self.assert_(folder1.get_string(folder1.keys()[0]) == \ + self._template % 'bar') + + def test_add_and_remove_folders(self): + # Delete folders + self._box.add_folder('one') + self._box.add_folder('two') + self.assert_(len(self._box.list_folders()) == 2) + self.assert_(set(self._box.list_folders()) == set(('one', 'two'))) + self._box.remove_folder('one') + self.assert_(len(self._box.list_folders()) == 1) + self.assert_(set(self._box.list_folders()) == set(('two',))) + self._box.add_folder('three') + self.assert_(len(self._box.list_folders()) == 2) + self.assert_(set(self._box.list_folders()) == set(('two', 'three'))) + self._box.remove_folder('three') + self.assert_(len(self._box.list_folders()) == 1) + self.assert_(set(self._box.list_folders()) == set(('two',))) + self._box.remove_folder('two') + self.assert_(len(self._box.list_folders()) == 0) + self.assert_(self._box.list_folders() == []) + + def test_clean(self): + # Remove old files from 'tmp' + foo_path = os.path.join(self._path, 'tmp', 'foo') + bar_path = os.path.join(self._path, 'tmp', 'bar') + f = open(foo_path, 'w') + f.write("@") + f.close() + f = open(bar_path, 'w') + f.write("@") + f.close() + self._box.clean() + self.assert_(os.path.exists(foo_path)) + self.assert_(os.path.exists(bar_path)) + foo_stat = os.stat(foo_path) + os.utime(foo_path, (time.time() - 129600 - 2, + foo_stat.st_mtime)) + self._box.clean() + self.assert_(not os.path.exists(foo_path)) + self.assert_(os.path.exists(bar_path)) + + def test_create_tmp(self, repetitions=10): + # Create files in tmp directory + hostname = socket.gethostname() + if '/' in hostname: + hostname = hostname.replace('/', r'\057') + if ':' in hostname: + hostname = hostname.replace(':', r'\072') + pid = os.getpid() + pattern = re.compile(r"(?P