diff options
386 files changed, 16101 insertions, 8916 deletions
diff --git a/Demo/parser/unparse.py b/Demo/parser/unparse.py index 1c6e933..751ea6f 100644 --- a/Demo/parser/unparse.py +++ b/Demo/parser/unparse.py @@ -4,6 +4,19 @@ import _ast import cStringIO import os +def interleave(inter, f, seq): + """Call f on each item in seq, calling inter() in between. + """ + seq = iter(seq) + try: + f(seq.next()) + except StopIteration: + pass + else: + for x in seq: + inter() + f(x) + class Unparser: """Methods in this class recursively traverse an AST and output source code for the abstract syntax; original formatting @@ -63,26 +76,13 @@ class Unparser: def _Import(self, t): self.fill("import ") - first = True - for a in t.names: - if first: - first = False - else: - self.write(", ") - self.write(a.name) - if a.asname: - self.write(" as "+a.asname) + interleave(lambda: self.write(", "), self.dispatch, t.names) 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) + interleave(lambda: self.write(", "), self.dispatch, t.names) # XXX(jpe) what is level for? def _Assign(self, t): @@ -99,8 +99,9 @@ class Unparser: self.dispatch(t.value) def _Return(self, t): - self.fill("return ") + self.fill("return") if t.value: + self.write(" ") self.dispatch(t.value) def _Pass(self, t): @@ -138,18 +139,16 @@ class Unparser: self.write(",") def _Global(self, t): - self.fill("global") - for i, n in enumerate(t.names): - if i != 0: - self.write(",") - self.write(" " + n) + self.fill("global ") + interleave(lambda: self.write(", "), self.write, t.names) def _Yield(self, t): - self.fill("yield") + self.write("(") + self.write("yield") if t.value: - self.write(" (") + self.write(" ") self.dispatch(t.value) - self.write(")") + self.write(")") def _Raise(self, t): self.fill('raise ') @@ -188,8 +187,9 @@ class Unparser: self.leave() def _excepthandler(self, t): - self.fill("except ") + self.fill("except") if t.type: + self.write(" ") self.dispatch(t.type) if t.name: self.write(", ") @@ -289,9 +289,7 @@ class Unparser: def _List(self, t): self.write("[") - for e in t.elts: - self.dispatch(e) - self.write(", ") + interleave(lambda: self.write(", "), self.dispatch, t.elts) self.write("]") def _ListComp(self, t): @@ -318,30 +316,31 @@ class Unparser: self.dispatch(if_clause) def _IfExp(self, t): + self.write("(") self.dispatch(t.body) self.write(" if ") self.dispatch(t.test) - if t.orelse: - self.write(" else ") - self.dispatch(t.orelse) + self.write(" else ") + self.dispatch(t.orelse) + self.write(")") def _Dict(self, t): self.write("{") - for k,v in zip(t.keys, t.values): + def writem((k, v)): self.dispatch(k) - self.write(" : ") + self.write(": ") self.dispatch(v) - self.write(", ") + interleave(lambda: self.write(", "), writem, zip(t.keys, t.values)) self.write("}") def _Tuple(self, t): - if not t.elts: - self.write("()") - return self.write("(") - for e in t.elts: - self.dispatch(e) - self.write(", ") + if len(t.elts) == 1: + (elt,) = t.elts + self.dispatch(elt) + self.write(",") + else: + interleave(lambda: self.write(", "), self.dispatch, t.elts) self.write(")") unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"} @@ -357,7 +356,7 @@ class Unparser: def _BinOp(self, t): self.write("(") self.dispatch(t.left) - self.write(")" + self.binop[t.op.__class__.__name__] + "(") + self.write(" " + self.binop[t.op.__class__.__name__] + " ") self.dispatch(t.right) self.write(")") @@ -367,17 +366,15 @@ class Unparser: self.write("(") self.dispatch(t.left) for o, e in zip(t.ops, t.comparators): - self.write(") " +self.cmpops[o.__class__.__name__] + " (") + self.write(" " + self.cmpops[o.__class__.__name__] + " ") 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) + s = " %s " % self.boolops[t.op.__class__] + interleave(lambda: self.write(s), self.dispatch, t.values) self.write(")") def _Attribute(self,t): @@ -433,10 +430,7 @@ class Unparser: self.dispatch(t.step) def _ExtSlice(self, t): - for i, d in enumerate(t.dims): - if i != 0: - self.write(': ') - self.dispatch(d) + interleave(lambda: self.write(', '), self.dispatch, t.dims) # others def _arguments(self, t): @@ -472,9 +466,14 @@ class Unparser: self.write(": ") self.dispatch(t.body) + def _alias(self, t): + self.write(t.name) + if t.asname: + self.write(" as "+t.asname) + def roundtrip(filename, output=sys.stdout): source = open(filename).read() - tree = compile(source, filename, "exec", 0x400) + tree = compile(source, filename, "exec", _ast.PyCF_ONLY_AST) Unparser(tree, output) diff --git a/Doc/api/abstract.tex b/Doc/api/abstract.tex index f292972..c5b53d7 100644 --- a/Doc/api/abstract.tex +++ b/Doc/api/abstract.tex @@ -790,7 +790,7 @@ determination. the Python statement \samp{del \var{o}[\var{i1}:\var{i2}]}. \end{cfuncdesc} -\begin{cfuncdesc}{int}{PySequence_Count}{PyObject *o, PyObject *value} +\begin{cfuncdesc}{Py_ssize_t}{PySequence_Count}{PyObject *o, PyObject *value} Return the number of occurrences of \var{value} in \var{o}, that is, return the number of keys for which \code{\var{o}[\var{key}] == \var{value}}. On failure, return \code{-1}. This is equivalent to @@ -804,7 +804,7 @@ determination. expression \samp{\var{value} in \var{o}}. \end{cfuncdesc} -\begin{cfuncdesc}{int}{PySequence_Index}{PyObject *o, PyObject *value} +\begin{cfuncdesc}{Py_ssize_t}{PySequence_Index}{PyObject *o, PyObject *value} Return the first index \var{i} for which \code{\var{o}[\var{i}] == \var{value}}. On error, return \code{-1}. This is equivalent to the Python expression \samp{\var{o}.index(\var{value})}. @@ -854,7 +854,7 @@ determination. \versionadded{2.3} \end{cfuncdesc} -\begin{cfuncdesc}{int}{PySequence_Fast_GET_SIZE}{PyObject *o} +\begin{cfuncdesc}{Py_ssize_t}{PySequence_Fast_GET_SIZE}{PyObject *o} Returns the length of \var{o}, assuming that \var{o} was returned by \cfunction{PySequence_Fast()} and that \var{o} is not \NULL. The size can also be gotten by calling diff --git a/Doc/api/concrete.tex b/Doc/api/concrete.tex index 630a726..cdf6856 100644 --- a/Doc/api/concrete.tex +++ b/Doc/api/concrete.tex @@ -442,7 +442,9 @@ booleans. The following macros are available, however. \begin{cfuncdesc}{double}{PyFloat_AsDouble}{PyObject *pyfloat} Return a C \ctype{double} representation of the contents of - \var{pyfloat}. + \var{pyfloat}. If \var{pyfloat} is not a Python floating point + object but has a \method{__float__} method, this method will first + be called to convert \var{pyfloat} into a float. \end{cfuncdesc} \begin{cfuncdesc}{double}{PyFloat_AS_DOUBLE}{PyObject *pyfloat} @@ -557,8 +559,11 @@ typedef struct { \end{cfuncdesc} \begin{cfuncdesc}{Py_complex}{PyComplex_AsCComplex}{PyObject *op} - Return the \ctype{Py_complex} value of the complex number - \var{op}. + Return the \ctype{Py_complex} value of the complex number \var{op}. + \versionchanged[If \var{op} is not a Python complex number object + but has a \method{__complex__} method, this method + will first be called to convert \var{op} to a Python + complex number object]{2.6} \end{cfuncdesc} @@ -2156,6 +2161,35 @@ def PyDict_MergeFromSeq2(a, seq2, override): \section{Other Objects \label{otherObjects}} +\subsection{Class Objects \label{classObjects}} + +\obindex{class} +Note that the class objects described here represent old-style classes, +which will go away in Python 3. When creating new types for extension +modules, you will want to work with type objects (section +\ref{typeObjects}). + +\begin{ctypedesc}{PyClassObject} + The C structure of the objects used to describe built-in classes. +\end{ctypedesc} + +\begin{cvardesc}{PyObject*}{PyClass_Type} + This is the type object for class objects; it is the same object as + \code{types.ClassType} in the Python layer. + \withsubitem{(in module types)}{\ttindex{ClassType}} +\end{cvardesc} + +\begin{cfuncdesc}{int}{PyClass_Check}{PyObject *o} + Return true if the object \var{o} is a class object, including + instances of types derived from the standard class object. Return + false in all other cases. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyClass_IsSubclass}{PyObject *klass, PyObject *base} + Return true if \var{klass} is a subclass of \var{base}. Return false in + all other cases. +\end{cfuncdesc} + \subsection{File Objects \label{fileObjects}} \obindex{file} diff --git a/Doc/api/init.tex b/Doc/api/init.tex index e380bdb..76fcf61 100644 --- a/Doc/api/init.tex +++ b/Doc/api/init.tex @@ -245,7 +245,7 @@ program name (set by \cfunction{Py_SetProgramName()} above) and some environment variables. The returned string consists of a series of directory names separated by a platform dependent delimiter - character. The delimiter character is \character{:} on \UNIX and Mac OS X, + character. The delimiter character is \character{:} on \UNIX{} and Mac OS X, \character{;} on Windows. The returned string points into static storage; the caller should not modify its value. The value is available to Python code as the list diff --git a/Doc/api/memory.tex b/Doc/api/memory.tex index 4bc2c7a..18abe98 100644 --- a/Doc/api/memory.tex +++ b/Doc/api/memory.tex @@ -100,7 +100,9 @@ are available for allocating and releasing memory from the Python heap: memory block is resized but is not freed, and the returned pointer is non-\NULL. Unless \var{p} is \NULL, it must have been returned by a previous call to \cfunction{PyMem_Malloc()} or - \cfunction{PyMem_Realloc()}. + \cfunction{PyMem_Realloc()}. If the request fails, + \cfunction{PyMem_Realloc()} returns \NULL{} and \var{p} remains a + valid pointer to the previous memory area. \end{cfuncdesc} \begin{cfuncdesc}{void}{PyMem_Free}{void *p} @@ -124,7 +126,8 @@ that \var{TYPE} refers to any C type. \begin{cfuncdesc}{\var{TYPE}*}{PyMem_Resize}{void *p, TYPE, size_t n} Same as \cfunction{PyMem_Realloc()}, but the memory block is resized to \code{(\var{n} * sizeof(\var{TYPE}))} bytes. Returns a pointer - cast to \ctype{\var{TYPE}*}. + cast to \ctype{\var{TYPE}*}. On return, \var{p} will be a pointer to + the new memory area, or \NULL{} in the event of failure. \end{cfuncdesc} \begin{cfuncdesc}{void}{PyMem_Del}{void *p} diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index af1aed3..77ad7a5 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -1316,7 +1316,7 @@ PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems) This field is inherited by static subtypes, but not by dynamic subtypes (subtypes created by a class statement); in the latter, - this field is always set to \cfunction{PyType_GenericAlloc()}, to + this field is always set to \cfunction{PyType_GenericAlloc}, to force a standard heap allocation strategy. That is also the recommended value for statically defined types. \end{cmemberdesc} diff --git a/Doc/api/utilities.tex b/Doc/api/utilities.tex index 5a6f0b0..93e3796 100644 --- a/Doc/api/utilities.tex +++ b/Doc/api/utilities.tex @@ -930,3 +930,94 @@ PyArg_ParseTuple(args, "O|O:ref", &object, &callback) If there is an error in the format string, the \exception{SystemError} exception is set and \NULL{} returned. \end{cfuncdesc} + +\section{String conversion and formatting \label{string-formatting}} + +Functions for number conversion and formatted string output. + +\begin{cfuncdesc}{int}{PyOS_snprintf}{char *str, size_t size, + const char *format, \moreargs} +Output not more than \var{size} bytes to \var{str} according to the format +string \var{format} and the extra arguments. See the \UNIX{} man +page \manpage{snprintf}{2}. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyOS_vsnprintf}{char *str, size_t size, + const char *format, va_list va} +Output not more than \var{size} bytes to \var{str} according to the format +string \var{format} and the variable argument list \var{va}. \UNIX{} +man page \manpage{vsnprintf}{2}. +\end{cfuncdesc} + +\cfunction{PyOS_snprintf} and \cfunction{PyOS_vsnprintf} wrap the +Standard C library functions \cfunction{snprintf()} and +\cfunction{vsnprintf()}. Their purpose is to guarantee consistent +behavior in corner cases, which the Standard C functions do not. + +The wrappers ensure that \var{str}[\var{size}-1] is always +\character{\textbackslash0} upon return. They never write more than +\var{size} bytes (including the trailing \character{\textbackslash0} +into str. Both functions require that \code{\var{str} != NULL}, +\code{\var{size} > 0} and \code{\var{format} != NULL}. + +If the platform doesn't have \cfunction{vsnprintf()} and the buffer +size needed to avoid truncation exceeds \var{size} by more than 512 +bytes, Python aborts with a \var{Py_FatalError}. + +The return value (\var{rv}) for these functions should be interpreted +as follows: + +\begin{itemize} + +\item When \code{0 <= \var{rv} < \var{size}}, the output conversion + was successful and \var{rv} characters were written to \var{str} + (excluding the trailing \character{\textbackslash0} byte at + \var{str}[\var{rv}]). + +\item When \code{\var{rv} >= \var{size}}, the output conversion was + truncated and a buffer with \code{\var{rv} + 1} bytes would have + been needed to succeed. \var{str}[\var{size}-1] is + \character{\textbackslash0} in this case. + +\item When \code{\var{rv} < 0}, ``something bad happened.'' + \var{str}[\var{size}-1] is \character{\textbackslash0} in this case + too, but the rest of \var{str} is undefined. The exact cause of the + error depends on the underlying platform. + +\end{itemize} + +The following functions provide locale-independent string to number +conversions. + +\begin{cfuncdesc}{double}{PyOS_ascii_strtod}{const char *nptr, char **endptr} +Convert a string to a \ctype{double}. This function behaves like the +Standard C function \cfunction{strtod()} does in the C locale. It does +this without changing the current locale, since that would not be +thread-safe. + +\cfunction{PyOS_ascii_strtod} should typically be used for reading +configuration files or other non-user input that should be locale +independent. \versionadded{2.4} + +See the \UNIX{} man page \manpage{strtod}{2} for details. + +\end{cfuncdesc} + +\begin{cfuncdesc}{char *}{PyOS_ascii_formatd}{char *buffer, size_t buf_len, + const char *format, double d} +Convert a \ctype{double} to a string using the \character{.} as the +decimal separator. \var{format} is a \cfunction{printf()}-style format +string specifying the number format. Allowed conversion characters are +\character{e}, \character{E}, \character{f}, \character{F}, +\character{g} and \character{G}. + +The return value is a pointer to \var{buffer} with the converted +string or NULL if the conversion failed. \versionadded{2.4} +\end{cfuncdesc} + +\begin{cfuncdesc}{double}{PyOS_ascii_atof}{const char *nptr} +Convert a string to a \ctype{double} in a locale-independent +way. \versionadded{2.4} + +See the \UNIX{} man page \manpage{atof}{2} for details. +\end{cfuncdesc} diff --git a/Doc/commontex/copyright.tex b/Doc/commontex/copyright.tex index 05f15a1..ce70d0c 100644 --- a/Doc/commontex/copyright.tex +++ b/Doc/commontex/copyright.tex @@ -1,4 +1,4 @@ -Copyright \copyright{} 2001-2006 Python Software Foundation. +Copyright \copyright{} 2001-2007 Python Software Foundation. All rights reserved. Copyright \copyright{} 2000 BeOpen.com. diff --git a/Doc/commontex/license.tex b/Doc/commontex/license.tex index c98a2c3..fe6485c 100644 --- a/Doc/commontex/license.tex +++ b/Doc/commontex/license.tex @@ -52,6 +52,7 @@ GPL-compatible; the table below summarizes the various releases. \linev{2.4.3}{2.4.2}{2006}{PSF}{yes} \linev{2.4.4}{2.4.3}{2006}{PSF}{yes} \linev{2.5}{2.4}{2006}{PSF}{yes} + \linev{2.5.1}{2.5}{2007}{PSF}{yes} \end{tablev} \note{GPL-compatible doesn't mean that we're distributing @@ -82,7 +83,7 @@ license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python \version{} alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., -``Copyright \copyright{} 2001-2006 Python Software Foundation; All +``Copyright \copyright{} 2001-2007 Python Software Foundation; All Rights Reserved'' are retained in Python \version{} alone or in any derivative version prepared by Licensee. diff --git a/Doc/dist/dist.tex b/Doc/dist/dist.tex index 7a0f073..928b642 100644 --- a/Doc/dist/dist.tex +++ b/Doc/dist/dist.tex @@ -1802,9 +1802,9 @@ example: setup.py foo.py \end{verbatim} -(In all diagrams in this section, \verb|<root>| will refer to the -distribution root directory.) A minimal setup script to describe this -situation would be: +(In all diagrams in this section, \var{\textless root\textgreater} +will refer to the distribution root directory.) A minimal setup script +to describe this situation would be: \begin{verbatim} from distutils.core import setup setup(name='foo', @@ -3179,7 +3179,7 @@ for any variables not found in either \var{local_vars} or \code{os.environ}. Note that this is not a fully-fledged string interpolation function. A valid \code{\$variable} can consist only of upper and lower case letters, -numbers and an underscore. No \{ \} or \( \) style quoting is available. +numbers and an underscore. No \{ \} or ( ) style quoting is available. \end{funcdesc} \begin{funcdesc}{grok_environment_error}{exc\optional{, prefix=\samp{'error: '}}} @@ -3733,7 +3733,7 @@ implementing the class \class{peel_banana}, a subclass of Subclasses of \class{Command} must define the following methods. -\begin{methoddesc}{initialize_options()} +\begin{methoddesc}[Command]{initialize_options()} Set default values for all the options that this command supports. Note that these defaults may be overridden by other commands, by the setup script, by config files, or by the @@ -3742,7 +3742,7 @@ between options; generally, \method{initialize_options()} implementations are just a bunch of \samp{self.foo = None} assignments. \end{methoddesc} -\begin{methoddesc}{finalize_options}{} +\begin{methoddesc}[Command]{finalize_options}{} Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option assignments from the command-line or from other commands have been @@ -3751,7 +3751,7 @@ done. Thus, this is the place to to code option dependencies: if \var{bar} as long as \var{foo} still has the same value it was assigned in \method{initialize_options()}. \end{methoddesc} -\begin{methoddesc}{run}{} +\begin{methoddesc}[Command]{run}{} A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in \method{initialize_options()}, customized by other commands, the setup diff --git a/Doc/ext/newtypes.tex b/Doc/ext/newtypes.tex index a485a15..5c1f0ae 100644 --- a/Doc/ext/newtypes.tex +++ b/Doc/ext/newtypes.tex @@ -489,7 +489,6 @@ this? garbage collection, there are calls that can be made to ``untrack'' the object from garbage collection, however, these calls are advanced and not covered here.} -\item \end{itemize} @@ -930,6 +929,102 @@ That's pretty much it. If we had written custom \member{tp_alloc} or collection. Most extensions will use the versions automatically provided. +\subsection{Subclassing other types} + +It is possible to create new extension types that are derived from existing +types. It is easiest to inherit from the built in types, since an extension +can easily use the \class{PyTypeObject} it needs. It can be difficult to +share these \class{PyTypeObject} structures between extension modules. + +In this example we will create a \class{Shoddy} type that inherits from +the builtin \class{list} type. The new type will be completely compatible +with regular lists, but will have an additional \method{increment()} method +that increases an internal counter. + +\begin{verbatim} +>>> import shoddy +>>> s = shoddy.Shoddy(range(3)) +>>> s.extend(s) +>>> print len(s) +6 +>>> print s.increment() +1 +>>> print s.increment() +2 +\end{verbatim} + +\verbatiminput{shoddy.c} + +As you can see, the source code closely resembles the \class{Noddy} examples in previous +sections. We will break down the main differences between them. + +\begin{verbatim} +typedef struct { + PyListObject list; + int state; +} Shoddy; +\end{verbatim} + +The primary difference for derived type objects is that the base type's +object structure must be the first value. The base type will already +include the \cfunction{PyObject_HEAD} at the beginning of its structure. + +When a Python object is a \class{Shoddy} instance, its \var{PyObject*} pointer +can be safely cast to both \var{PyListObject*} and \var{Shoddy*}. + +\begin{verbatim} +static int +Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds) +{ + if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} +\end{verbatim} + +In the \member{__init__} method for our type, we can see how to call through +to the \member{__init__} method of the base type. + +This pattern is important when writing a type with custom \member{new} and +\member{dealloc} methods. The \member{new} method should not actually create the +memory for the object with \member{tp_alloc}, that will be handled by +the base class when calling its \member{tp_new}. + +When filling out the \cfunction{PyTypeObject} for the \class{Shoddy} type, +you see a slot for \cfunction{tp_base}. Due to cross platform compiler +issues, you can't fill that field directly with the \cfunction{PyList_Type}; +it can be done later in the module's \cfunction{init} function. + +\begin{verbatim} +PyMODINIT_FUNC +initshoddy(void) +{ + PyObject *m; + + ShoddyType.tp_base = &PyList_Type; + if (PyType_Ready(&ShoddyType) < 0) + return; + + m = Py_InitModule3("shoddy", NULL, "Shoddy module"); + if (m == NULL) + return; + + Py_INCREF(&ShoddyType); + PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType); +} +\end{verbatim} + +Before calling \cfunction{PyType_Ready}, the type structure must have the +\member{tp_base} slot filled in. When we are deriving a new type, it is +not necessary to fill out the \member{tp_alloc} slot with +\cfunction{PyType_GenericNew} -- the allocate function from the base type +will be inherited. + +After that, calling \cfunction{PyType_Ready} and adding the type object +to the module is the same as with the basic \class{Noddy} examples. + + \section{Type Methods \label{dnt-type-methods}} diff --git a/Doc/ext/shoddy.c b/Doc/ext/shoddy.c new file mode 100644 index 0000000..07a4177 --- /dev/null +++ b/Doc/ext/shoddy.c @@ -0,0 +1,91 @@ +#include <Python.h> + +typedef struct { + PyListObject list; + int state; +} Shoddy; + + +static PyObject * +Shoddy_increment(Shoddy *self, PyObject *unused) +{ + self->state++; + return PyInt_FromLong(self->state); +} + + +static PyMethodDef Shoddy_methods[] = { + {"increment", (PyCFunction)Shoddy_increment, METH_NOARGS, + PyDoc_STR("increment state counter")}, + {NULL, NULL}, +}; + +static int +Shoddy_init(Shoddy *self, PyObject *args, PyObject *kwds) +{ + if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + + +static PyTypeObject ShoddyType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "shoddy.Shoddy", /* tp_name */ + sizeof(Shoddy), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + Shoddy_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)Shoddy_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyMODINIT_FUNC +initshoddy(void) +{ + PyObject *m; + + ShoddyType.tp_base = &PyList_Type; + if (PyType_Ready(&ShoddyType) < 0) + return; + + m = Py_InitModule3("shoddy", NULL, "Shoddy module"); + if (m == NULL) + return; + + Py_INCREF(&ShoddyType); + PyModule_AddObject(m, "Shoddy", (PyObject *) &ShoddyType); +} diff --git a/Doc/inst/inst.tex b/Doc/inst/inst.tex index 6db22ac..adc686e 100644 --- a/Doc/inst/inst.tex +++ b/Doc/inst/inst.tex @@ -296,7 +296,7 @@ being installed is pure Python or contains extensions (``non-pure''): \filevar{prefix} and \filevar{exec-prefix} stand for the directories that Python is installed to, and where it finds its libraries at run-time. They are always the same under Windows, and very -often the same under \UNIX and Mac OS X. You can find out what your Python +often the same under \UNIX{} and Mac OS X. You can find out what your Python installation uses for \filevar{prefix} and \filevar{exec-prefix} by running Python in interactive mode and typing a few simple commands. Under \UNIX, just type \code{python} at the shell prompt. Under diff --git a/Doc/lib/compiler.tex b/Doc/lib/compiler.tex index f0926e7..d4f4124 100644 --- a/Doc/lib/compiler.tex +++ b/Doc/lib/compiler.tex @@ -103,8 +103,7 @@ Python. In the abstract syntax tree, each node represents a syntactic construct. The root of the tree is \class{Module} object. The abstract syntax offers a higher level interface to parsed Python -source code. The \ulink{\module{parser}} -{http://www.python.org/doc/current/lib/module-parser.html} +source code. The \refmodule{parser} module and the compiler written in C for the Python interpreter use a concrete syntax tree. The concrete syntax is tied closely to the grammar description used for the Python parser. Instead of a single diff --git a/Doc/lib/email.tex b/Doc/lib/email.tex index ea12705..47727a7 100644 --- a/Doc/lib/email.tex +++ b/Doc/lib/email.tex @@ -1,4 +1,4 @@ -% Copyright (C) 2001-2006 Python Software Foundation +% Copyright (C) 2001-2007 Python Software Foundation % Author: barry@python.org (Barry Warsaw) \section{\module{email} --- @@ -239,7 +239,7 @@ Here are the differences between \module{email} version 2 and version 1: The \module{email} package was originally prototyped as a separate library called -\ulink{\module{mimelib}}{http://mimelib.sf.net/}. +\ulink{\texttt{mimelib}}{http://mimelib.sf.net/}. Changes have been made so that method names are more consistent, and some methods or modules have either been added or removed. The semantics of some of the methods diff --git a/Doc/lib/emailutil.tex b/Doc/lib/emailutil.tex index fe96473..f9fe2d4 100644 --- a/Doc/lib/emailutil.tex +++ b/Doc/lib/emailutil.tex @@ -56,7 +56,7 @@ however, some mailers don't follow that format as specified, so \code{"Mon, 20 Nov 1995 19:12:08 -0500"}. If it succeeds in parsing the date, \function{parsedate()} returns a 9-tuple that can be passed directly to \function{time.mktime()}; otherwise \code{None} will be -returned. Note that fields 6, 7, and 8 of the result tuple are not +returned. Note that indexes 6, 7, and 8 of the result tuple are not usable. \end{funcdesc} @@ -70,7 +70,7 @@ offset is the opposite of the sign of the \code{time.timezone} variable for the same timezone; the latter variable follows the \POSIX{} standard while this module follows \rfc{2822}.}. If the input string has no timezone, the last element of the tuple returned is -\code{None}. Note that fields 6, 7, and 8 of the result tuple are not +\code{None}. Note that indexes 6, 7, and 8 of the result tuple are not usable. \end{funcdesc} diff --git a/Doc/lib/libamoeba.tex b/Doc/lib/libamoeba.tex index c3274db..ce6babc 100644 --- a/Doc/lib/libamoeba.tex +++ b/Doc/lib/libamoeba.tex @@ -89,25 +89,24 @@ aa:1c:95:52:6a:fa/14(ff)/8e:ba:5b:8:11:1a % The following methods are defined for capability objects. -\setindexsubitem{(capability method)} -\begin{funcdesc}{dir_list}{} +\begin{methoddesc}[capability]{dir_list}{} Returns a list of the names of the entries in an Amoeba directory. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{b_read}{offset, maxsize} +\begin{methoddesc}[capability]{b_read}{offset, maxsize} Reads (at most) \var{maxsize} bytes from a bullet file at offset \var{offset.} The data is returned as a string. EOF is reported as an empty string. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{b_size}{} +\begin{methoddesc}[capability]{b_size}{} Returns the size of a bullet file. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{dir_append}{} +\begin{methoddesc}[capability]{dir_append}{} \funcline{dir_delete}{} \funcline{dir_lookup}{} \funcline{dir_replace}{} @@ -116,17 +115,17 @@ Like the corresponding functions, but with a path relative to the capability. (For paths beginning with a slash the capability is ignored, since this is the defined semantics for Amoeba.) -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{std_info}{} +\begin{methoddesc}[capability]{std_info}{} Returns the standard info string of the object. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{tod_gettime}{} +\begin{methoddesc}[capability]{tod_gettime}{} Returns the time (in seconds since the Epoch, in UCT, as for \POSIX) from a time server. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{tod_settime}{t} +\begin{methoddesc}[capability]{tod_settime}{t} Sets the time kept by a time server. -\end{funcdesc} +\end{methoddesc} diff --git a/Doc/lib/libasyncore.tex b/Doc/lib/libasyncore.tex index 2067839..0552896 100644 --- a/Doc/lib/libasyncore.tex +++ b/Doc/lib/libasyncore.tex @@ -46,7 +46,7 @@ service) is closed. \begin{funcdesc}{loop}{\optional{timeout\optional{, use_poll\optional{, map\optional{,count}}}}} Enter a polling loop that terminates after count passes or all open - channels have been closed. All arguments are optional. The \var(count) + channels have been closed. All arguments are optional. The \var{count} parameter defaults to None, resulting in the loop terminating only when all channels have been closed. The \var{timeout} argument sets the timeout parameter for the appropriate \function{select()} or diff --git a/Doc/lib/libbsddb.tex b/Doc/lib/libbsddb.tex index e9d7e21..6e86d24 100644 --- a/Doc/lib/libbsddb.tex +++ b/Doc/lib/libbsddb.tex @@ -2,7 +2,6 @@ Interface to Berkeley DB library} \declaremodule{extension}{bsddb} - \platform{Unix, Windows} \modulesynopsis{Interface to Berkeley DB database library} \sectionauthor{Skip Montanaro}{skip@mojam.com} @@ -46,10 +45,10 @@ portability, only the first two arguments should be used in most instances. \begin{funcdesc}{hashopen}{filename\optional{, flag\optional{, - mode\optional{, bsize\optional{, + mode\optional{, pgsize\optional{, ffactor\optional{, nelem\optional{, - cachesize\optional{, hash\optional{, - lorder}}}}}}}}} + cachesize\optional{, lorder\optional{, + hflags}}}}}}}}} Open the hash format file named \var{filename}. Files never intended to be preserved on disk may be created by passing \code{None} as the \var{filename}. The optional @@ -80,7 +79,7 @@ interpretation. \begin{funcdesc}{rnopen}{filename\optional{, flag\optional{, mode\optional{, rnflags\optional{, cachesize\optional{, pgsize\optional{, lorder\optional{, -reclen\optional{, bval\optional{, bfname}}}}}}}}}} +rlen\optional{, delim\optional{, source\optional{, pad}}}}}}}}}}} Open a DB record format file named \var{filename}. Files never intended to be preserved on disk may be created by passing \code{None} as the @@ -114,23 +113,23 @@ the same methods as dictionaries. In addition, they support the methods listed below. \versionchanged[Added dictionary methods]{2.3.1} -\begin{methoddesc}{close}{} +\begin{methoddesc}[bsddbobject]{close}{} Close the underlying file. The object can no longer be accessed. Since there is no open \method{open} method for these objects, to open the file again a new \module{bsddb} module open function must be called. \end{methoddesc} -\begin{methoddesc}{keys}{} +\begin{methoddesc}[bsddbobject]{keys}{} Return the list of keys contained in the DB file. The order of the list is unspecified and should not be relied on. In particular, the order of the list returned is different for different file formats. \end{methoddesc} -\begin{methoddesc}{has_key}{key} +\begin{methoddesc}[bsddbobject]{has_key}{key} Return \code{1} if the DB file contains the argument as a key. \end{methoddesc} -\begin{methoddesc}{set_location}{key} +\begin{methoddesc}[bsddbobject]{set_location}{key} Set the cursor to the item indicated by \var{key} and return a tuple containing the key and its value. For binary tree databases (opened using \function{btopen()}), if \var{key} does not actually exist in @@ -140,32 +139,32 @@ and return that key and value. For other databases, database. \end{methoddesc} -\begin{methoddesc}{first}{} +\begin{methoddesc}[bsddbobject]{first}{} Set the cursor to the first item in the DB file and return it. The order of keys in the file is unspecified, except in the case of B-Tree databases. This method raises \exception{bsddb.error} if the database is empty. \end{methoddesc} -\begin{methoddesc}{next}{} +\begin{methoddesc}[bsddbobject]{next}{} Set the cursor to the next item in the DB file and return it. The order of keys in the file is unspecified, except in the case of B-Tree databases. \end{methoddesc} -\begin{methoddesc}{previous}{} +\begin{methoddesc}[bsddbobject]{previous}{} Set the cursor to the previous item in the DB file and return it. The order of keys in the file is unspecified, except in the case of B-Tree databases. This is not supported on hashtable databases (those opened with \function{hashopen()}). \end{methoddesc} -\begin{methoddesc}{last}{} +\begin{methoddesc}[bsddbobject]{last}{} Set the cursor to the last item in the DB file and return it. The order of keys in the file is unspecified. This is not supported on hashtable databases (those opened with \function{hashopen()}). This method raises \exception{bsddb.error} if the database is empty. \end{methoddesc} -\begin{methoddesc}{sync}{} +\begin{methoddesc}[bsddbobject]{sync}{} Synchronize the database on disk. \end{methoddesc} diff --git a/Doc/lib/libcfgparser.tex b/Doc/lib/libcfgparser.tex index 2c08ec4..a41aee1 100644 --- a/Doc/lib/libcfgparser.tex +++ b/Doc/lib/libcfgparser.tex @@ -155,37 +155,37 @@ the \var{raw} parameter is false. This is relevant only for the \class{RawConfigParser} instances have the following methods: -\begin{methoddesc}{defaults}{} +\begin{methoddesc}[RawConfigParser]{defaults}{} Return a dictionary containing the instance-wide defaults. \end{methoddesc} -\begin{methoddesc}{sections}{} +\begin{methoddesc}[RawConfigParser]{sections}{} Return a list of the sections available; \code{DEFAULT} is not included in the list. \end{methoddesc} -\begin{methoddesc}{add_section}{section} +\begin{methoddesc}[RawConfigParser]{add_section}{section} Add a section named \var{section} to the instance. If a section by the given name already exists, \exception{DuplicateSectionError} is raised. \end{methoddesc} -\begin{methoddesc}{has_section}{section} +\begin{methoddesc}[RawConfigParser]{has_section}{section} Indicates whether the named section is present in the configuration. The \code{DEFAULT} section is not acknowledged. \end{methoddesc} -\begin{methoddesc}{options}{section} +\begin{methoddesc}[RawConfigParser]{options}{section} Returns a list of options available in the specified \var{section}. \end{methoddesc} -\begin{methoddesc}{has_option}{section, option} +\begin{methoddesc}[RawConfigParser]{has_option}{section, option} If the given section exists, and contains the given option, return \constant{True}; otherwise return \constant{False}. \versionadded{1.6} \end{methoddesc} -\begin{methoddesc}{read}{filenames} +\begin{methoddesc}[RawConfigParser]{read}{filenames} Attempt to read and parse a list of filenames, returning a list of filenames which were successfully parsed. If \var{filenames} is a string or Unicode string, it is treated as a single filename. @@ -210,28 +210,28 @@ config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')]) \versionchanged[Returns list of successfully parsed filenames]{2.4} \end{methoddesc} -\begin{methoddesc}{readfp}{fp\optional{, filename}} +\begin{methoddesc}[RawConfigParser]{readfp}{fp\optional{, filename}} Read and parse configuration data from the file or file-like object in \var{fp} (only the \method{readline()} method is used). If \var{filename} is omitted and \var{fp} has a \member{name} attribute, that is used for \var{filename}; the default is \samp{<???>}. \end{methoddesc} -\begin{methoddesc}{get}{section, option} +\begin{methoddesc}[RawConfigParser]{get}{section, option} Get an \var{option} value for the named \var{section}. \end{methoddesc} -\begin{methoddesc}{getint}{section, option} +\begin{methoddesc}[RawConfigParser]{getint}{section, option} A convenience method which coerces the \var{option} in the specified \var{section} to an integer. \end{methoddesc} -\begin{methoddesc}{getfloat}{section, option} +\begin{methoddesc}[RawConfigParser]{getfloat}{section, option} A convenience method which coerces the \var{option} in the specified \var{section} to a floating point number. \end{methoddesc} -\begin{methoddesc}{getboolean}{section, option} +\begin{methoddesc}[RawConfigParser]{getboolean}{section, option} A convenience method which coerces the \var{option} in the specified \var{section} to a Boolean value. Note that the accepted values for the option are \code{"1"}, \code{"yes"}, \code{"true"}, and \code{"on"}, @@ -241,12 +241,12 @@ string values are checked in a case-insensitive manner. Any other value will cause it to raise \exception{ValueError}. \end{methoddesc} -\begin{methoddesc}{items}{section} +\begin{methoddesc}[RawConfigParser]{items}{section} Return a list of \code{(\var{name}, \var{value})} pairs for each option in the given \var{section}. \end{methoddesc} -\begin{methoddesc}{set}{section, option, value} +\begin{methoddesc}[RawConfigParser]{set}{section, option, value} If the given section exists, set the given option to the specified value; otherwise raise \exception{NoSectionError}. While it is possible to use \class{RawConfigParser} (or \class{ConfigParser} with @@ -256,14 +256,14 @@ output to files) can only be achieved using string values. \versionadded{1.6} \end{methoddesc} -\begin{methoddesc}{write}{fileobject} +\begin{methoddesc}[RawConfigParser]{write}{fileobject} Write a representation of the configuration to the specified file object. This representation can be parsed by a future \method{read()} call. \versionadded{1.6} \end{methoddesc} -\begin{methoddesc}{remove_option}{section, option} +\begin{methoddesc}[RawConfigParser]{remove_option}{section, option} Remove the specified \var{option} from the specified \var{section}. If the section does not exist, raise \exception{NoSectionError}. If the option existed to be removed, return \constant{True}; @@ -271,13 +271,13 @@ otherwise return \constant{False}. \versionadded{1.6} \end{methoddesc} -\begin{methoddesc}{remove_section}{section} +\begin{methoddesc}[RawConfigParser]{remove_section}{section} Remove the specified \var{section} from the configuration. If the section in fact existed, return \code{True}. Otherwise return \code{False}. \end{methoddesc} -\begin{methoddesc}{optionxform}{option} +\begin{methoddesc}[RawConfigParser]{optionxform}{option} Transforms the option name \var{option} as found in an input file or as passed in by client code to the form that should be used in the internal structures. The default implementation returns a lower-case @@ -293,14 +293,14 @@ option names case sensitive. The \class{ConfigParser} class extends some methods of the \class{RawConfigParser} interface, adding some optional arguments. -\begin{methoddesc}{get}{section, option\optional{, raw\optional{, vars}}} +\begin{methoddesc}[ConfigParser]{get}{section, option\optional{, raw\optional{, vars}}} Get an \var{option} value for the named \var{section}. All the \character{\%} interpolations are expanded in the return values, based on the defaults passed into the constructor, as well as the options \var{vars} provided, unless the \var{raw} argument is true. \end{methoddesc} -\begin{methoddesc}{items}{section\optional{, raw\optional{, vars}}} +\begin{methoddesc}[ConfigParser]{items}{section\optional{, raw\optional{, vars}}} Return a list of \code{(\var{name}, \var{value})} pairs for each option in the given \var{section}. Optional arguments have the same meaning as for the \method{get()} method. @@ -313,7 +313,7 @@ same meaning as for the \method{get()} method. The \class{SafeConfigParser} class implements the same extended interface as \class{ConfigParser}, with the following addition: -\begin{methoddesc}{set}{section, option, value} +\begin{methoddesc}[SafeConfigParser]{set}{section, option, value} If the given section exists, set the given option to the specified value; otherwise raise \exception{NoSectionError}. \var{value} must be a string (\class{str} or \class{unicode}); if not, diff --git a/Doc/lib/libcgitb.tex b/Doc/lib/libcgitb.tex index c686fe6..ca9959f 100644 --- a/Doc/lib/libcgitb.tex +++ b/Doc/lib/libcgitb.tex @@ -38,7 +38,7 @@ to a file for later analysis. context\optional{, format}}}}} This function causes the \module{cgitb} module to take over the interpreter's default handling for exceptions by setting the - value of \code{\refmodule{sys}.excepthook}. + value of \member{\refmodule{sys}.excepthook}. \withsubitem{(in module sys)}{\ttindex{excepthook()}} The optional argument \var{display} defaults to \code{1} and can be set @@ -61,7 +61,7 @@ to a file for later analysis. report it using \module{cgitb}. The optional \var{info} argument should be a 3-tuple containing an exception type, exception value, and traceback object, exactly like the tuple returned by - \code{\refmodule{sys}.exc_info()}. If the \var{info} argument + \function{\refmodule{sys}.exc_info()}. If the \var{info} argument is not supplied, the current exception is obtained from - \code{\refmodule{sys}.exc_info()}. + \function{\refmodule{sys}.exc_info()}. \end{funcdesc} diff --git a/Doc/lib/libcmath.tex b/Doc/lib/libcmath.tex index 54e0cdb..f8aa45b 100644 --- a/Doc/lib/libcmath.tex +++ b/Doc/lib/libcmath.tex @@ -5,7 +5,14 @@ \modulesynopsis{Mathematical functions for complex numbers.} This module is always available. It provides access to mathematical -functions for complex numbers. The functions are: +functions for complex numbers. The functions in this module accept +integers, floating-point numbers or complex numbers as arguments. +They will also accept any Python object that has either a +\method{__complex__} or a \method{__float__} method: these methods are +used to convert the object to a complex or floating-point number, respectively, and +the function is then applied to the result of the conversion. + +The functions are: \begin{funcdesc}{acos}{x} Return the arc cosine of \var{x}. diff --git a/Doc/lib/libcmd.tex b/Doc/lib/libcmd.tex index 9fe8123..810f19c 100644 --- a/Doc/lib/libcmd.tex +++ b/Doc/lib/libcmd.tex @@ -37,7 +37,7 @@ will default to \var{sys.stdin} and \var{sys.stdout}. A \class{Cmd} instance has the following methods: -\begin{methoddesc}{cmdloop}{\optional{intro}} +\begin{methoddesc}[Cmd]{cmdloop}{\optional{intro}} Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. @@ -82,7 +82,7 @@ commands with corresponding \method{help_*()} methods), and also lists any undocumented commands. \end{methoddesc} -\begin{methoddesc}{onecmd}{str} +\begin{methoddesc}[Cmd]{onecmd}{str} Interpret the argument as though it had been typed in response to the prompt. This may be overridden, but should not normally need to be; see the \method{precmd()} and \method{postcmd()} methods for useful @@ -93,25 +93,25 @@ value of that method is returned, otherwise the return value from the \method{default()} method is returned. \end{methoddesc} -\begin{methoddesc}{emptyline}{} +\begin{methoddesc}[Cmd]{emptyline}{} Method called when an empty line is entered in response to the prompt. If this method is not overridden, it repeats the last nonempty command entered. \end{methoddesc} -\begin{methoddesc}{default}{line} +\begin{methoddesc}[Cmd]{default}{line} Method called on an input line when the command prefix is not recognized. If this method is not overridden, it prints an error message and returns. \end{methoddesc} -\begin{methoddesc}{completedefault}{text, line, begidx, endidx} +\begin{methoddesc}[Cmd]{completedefault}{text, line, begidx, endidx} Method called to complete an input line when no command-specific \method{complete_*()} method is available. By default, it returns an empty list. \end{methoddesc} -\begin{methoddesc}{precmd}{line} +\begin{methoddesc}[Cmd]{precmd}{line} Hook method executed just before the command line \var{line} is interpreted, but after the input prompt is generated and issued. This method is a stub in \class{Cmd}; it exists to be overridden by @@ -121,7 +121,7 @@ implementation may re-write the command or simply return \var{line} unchanged. \end{methoddesc} -\begin{methoddesc}{postcmd}{stop, line} +\begin{methoddesc}[Cmd]{postcmd}{stop, line} Hook method executed just after a command dispatch is finished. This method is a stub in \class{Cmd}; it exists to be overridden by subclasses. \var{line} is the command line which was executed, and @@ -133,13 +133,13 @@ corresponds to \var{stop}; returning false will cause interpretation to continue. \end{methoddesc} -\begin{methoddesc}{preloop}{} +\begin{methoddesc}[Cmd]{preloop}{} Hook method executed once when \method{cmdloop()} is called. This method is a stub in \class{Cmd}; it exists to be overridden by subclasses. \end{methoddesc} -\begin{methoddesc}{postloop}{} +\begin{methoddesc}[Cmd]{postloop}{} Hook method executed once when \method{cmdloop()} is about to return. This method is a stub in \class{Cmd}; it exists to be overridden by subclasses. @@ -147,42 +147,52 @@ subclasses. Instances of \class{Cmd} subclasses have some public instance variables: -\begin{memberdesc}{prompt} +\begin{memberdesc}[Cmd]{prompt} The prompt issued to solicit input. \end{memberdesc} -\begin{memberdesc}{identchars} +\begin{memberdesc}[Cmd]{identchars} The string of characters accepted for the command prefix. \end{memberdesc} -\begin{memberdesc}{lastcmd} +\begin{memberdesc}[Cmd]{lastcmd} The last nonempty command prefix seen. \end{memberdesc} -\begin{memberdesc}{intro} +\begin{memberdesc}[Cmd]{intro} A string to issue as an intro or banner. May be overridden by giving the \method{cmdloop()} method an argument. \end{memberdesc} -\begin{memberdesc}{doc_header} +\begin{memberdesc}[Cmd]{doc_header} The header to issue if the help output has a section for documented commands. \end{memberdesc} -\begin{memberdesc}{misc_header} +\begin{memberdesc}[Cmd]{misc_header} The header to issue if the help output has a section for miscellaneous help topics (that is, there are \method{help_*()} methods without corresponding \method{do_*()} methods). \end{memberdesc} -\begin{memberdesc}{undoc_header} +\begin{memberdesc}[Cmd]{undoc_header} The header to issue if the help output has a section for undocumented commands (that is, there are \method{do_*()} methods without corresponding \method{help_*()} methods). \end{memberdesc} -\begin{memberdesc}{ruler} +\begin{memberdesc}[Cmd]{ruler} 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}[Cmd]{use_rawinput} +A flag, defaulting to true. If true, \method{cmdloop()} uses +\function{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 628a1eb..8e14b02 100644 --- a/Doc/lib/libcode.tex +++ b/Doc/lib/libcode.tex @@ -68,7 +68,7 @@ syntax error, or raises \exception{OverflowError} or \subsection{Interactive Interpreter Objects \label{interpreter-objects}} -\begin{methoddesc}{runsource}{source\optional{, filename\optional{, symbol}}} +\begin{methoddesc}[InteractiveInterpreter]{runsource}{source\optional{, filename\optional{, symbol}}} Compile and run some source in the interpreter. Arguments are the same as for \function{compile_command()}; the default for \var{filename} is \code{'<input>'}, and for @@ -98,7 +98,7 @@ The return value can be used to decide whether to use \code{sys.ps1} or \code{sys.ps2} to prompt the next line. \end{methoddesc} -\begin{methoddesc}{runcode}{code} +\begin{methoddesc}[InteractiveInterpreter]{runcode}{code} Execute a code object. When an exception occurs, \method{showtraceback()} is called to display a traceback. All exceptions are caught except @@ -109,7 +109,7 @@ elsewhere in this code, and may not always be caught. The caller should be prepared to deal with it. \end{methoddesc} -\begin{methoddesc}{showsyntaxerror}{\optional{filename}} +\begin{methoddesc}[InteractiveInterpreter]{showsyntaxerror}{\optional{filename}} Display the syntax error that just occurred. This does not display a stack trace because there isn't one for syntax errors. If \var{filename} is given, it is stuffed into the exception instead @@ -118,13 +118,13 @@ always uses \code{'<string>'} when reading from a string. The output is written by the \method{write()} method. \end{methoddesc} -\begin{methoddesc}{showtraceback}{} +\begin{methoddesc}[InteractiveInterpreter]{showtraceback}{} Display the exception that just occurred. We remove the first stack item because it is within the interpreter object implementation. The output is written by the \method{write()} method. \end{methoddesc} -\begin{methoddesc}{write}{data} +\begin{methoddesc}[InteractiveInterpreter]{write}{data} Write a string to the standard error stream (\code{sys.stderr}). Derived classes should override this to provide the appropriate output handling as needed. @@ -138,7 +138,7 @@ The \class{InteractiveConsole} class is a subclass of \class{InteractiveInterpreter}, and so offers all the methods of the interpreter objects as well as the following additions. -\begin{methoddesc}{interact}{\optional{banner}} +\begin{methoddesc}[InteractiveConsole]{interact}{\optional{banner}} Closely emulate the interactive Python console. The optional banner argument specify the banner to print before the first interaction; by default it prints a banner similar to the one @@ -147,7 +147,7 @@ name of the console object in parentheses (so as not to confuse this with the real interpreter -- since it's so close!). \end{methoddesc} -\begin{methoddesc}{push}{line} +\begin{methoddesc}[InteractiveConsole]{push}{line} Push a line of source text to the interpreter. The line should not have a trailing newline; it may have internal newlines. The line is appended to a buffer and the interpreter's @@ -160,11 +160,11 @@ appended. The return value is \code{True} if more input is required, \method{runsource()}). \end{methoddesc} -\begin{methoddesc}{resetbuffer}{} +\begin{methoddesc}[InteractiveConsole]{resetbuffer}{} Remove any unhandled source text from the input buffer. \end{methoddesc} -\begin{methoddesc}{raw_input}{\optional{prompt}} +\begin{methoddesc}[InteractiveConsole]{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 reads from diff --git a/Doc/lib/libcodecs.tex b/Doc/lib/libcodecs.tex index 557364d..78fa24d 100644 --- a/Doc/lib/libcodecs.tex +++ b/Doc/lib/libcodecs.tex @@ -307,7 +307,7 @@ The set of allowed values can be extended via \method{register_error}. The \class{Codec} class defines these methods which also define the function interfaces of the stateless encoder and decoder: -\begin{methoddesc}{encode}{input\optional{, errors}} +\begin{methoddesc}[Codec]{encode}{input\optional{, errors}} Encodes the object \var{input} and returns a tuple (output object, length consumed). While codecs are not restricted to use with Unicode, in a Unicode context, encoding converts a Unicode object to a plain string @@ -325,7 +325,7 @@ function interfaces of the stateless encoder and decoder: empty object of the output object type in this situation. \end{methoddesc} -\begin{methoddesc}{decode}{input\optional{, errors}} +\begin{methoddesc}[Codec]{decode}{input\optional{, errors}} Decodes the object \var{input} and returns a tuple (output object, length consumed). In a Unicode context, decoding converts a plain string encoded using a particular character set encoding to a Unicode object. @@ -1233,9 +1233,8 @@ listed as operand type in the table. \lineiv{idna} {} {Unicode string} - {Implements \rfc{3490}. - \versionadded{2.3} - See also \refmodule{encodings.idna}} + {Implements \rfc{3490}, + see also \refmodule{encodings.idna}} \lineiv{mbcs} {dbcs} @@ -1250,8 +1249,7 @@ listed as operand type in the table. \lineiv{punycode} {} {Unicode string} - {Implements \rfc{3492}. - \versionadded{2.3}} + {Implements \rfc{3492}} \lineiv{quopri_codec} {quopri, quoted-printable, quotedprintable} @@ -1305,6 +1303,8 @@ listed as operand type in the table. \end{tableiv} +\versionadded[The \code{idna} and \code{punycode} encodings]{2.3} + \subsection{\module{encodings.idna} --- Internationalized Domain Names in Applications} diff --git a/Doc/lib/libcollections.tex b/Doc/lib/libcollections.tex index 5a07a2d..33ace7d 100644 --- a/Doc/lib/libcollections.tex +++ b/Doc/lib/libcollections.tex @@ -9,14 +9,16 @@ This module implements high-performance container datatypes. Currently, -there are two datatypes, deque and defaultdict. +there are two datatypes, deque and defaultdict, and one datatype factory +function, \function{NamedTuple}. Future additions may include balanced trees and ordered dictionaries. \versionchanged[Added defaultdict]{2.5} +\versionchanged[Added NamedTuple]{2.6} \subsection{\class{deque} objects \label{deque-objects}} -\begin{funcdesc}{deque}{\optional{iterable}} - Returns a new deque objected initialized left-to-right (using +\begin{classdesc}{deque}{\optional{iterable}} + Returns a new deque object initialized left-to-right (using \method{append()}) with data from \var{iterable}. If \var{iterable} is not specified, the new deque is empty. @@ -30,7 +32,7 @@ Future additions may include balanced trees and ordered dictionaries. for \samp{pop(0)} and \samp{insert(0, v)} operations which change both the size and position of the underlying data representation. \versionadded{2.4} -\end{funcdesc} +\end{classdesc} Deque objects support the following methods: @@ -219,7 +221,7 @@ def maketree(iterable): \subsection{\class{defaultdict} objects \label{defaultdict-objects}} -\begin{funcdesc}{defaultdict}{\optional{default_factory\optional{, ...}}} +\begin{classdesc}{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 writable instance variable. The remaining functionality is the same as @@ -231,7 +233,7 @@ def maketree(iterable): passed to the \class{dict} constructor, including keyword arguments. \versionadded{2.5} -\end{funcdesc} +\end{classdesc} \class{defaultdict} objects support the following method in addition to the standard \class{dict} operations: @@ -254,11 +256,11 @@ the standard \class{dict} operations: \class{defaultdict} objects support the following instance variable: -\begin{datadesc}{default_factory} +\begin{memberdesc}{default_factory} This attribute is used by the \method{__missing__} method; it is initialized from the first argument to the constructor, if present, or to \code{None}, if absent. -\end{datadesc} +\end{memberdesc} \subsubsection{\class{defaultdict} Examples \label{defaultdict-examples}} @@ -339,3 +341,51 @@ Setting the \member{default_factory} to \class{set} makes the >>> d.items() [('blue', set([2, 4])), ('red', set([1, 3]))] \end{verbatim} + + + +\subsection{\function{NamedTuple} datatype factory function \label{named-tuple-factory}} + +\begin{funcdesc}{NamedTuple}{typename, fieldnames} + Returns a new tuple subclass named \var{typename}. The new subclass is used + to create tuple-like objects that have fields accessable by attribute + lookup as well as being indexable and iterable. Instances of the subclass + also have a helpful docstring (with typename and fieldnames) and a helpful + \method{__repr__()} method which lists the tuple contents in a \code{name=value} + format. + \versionadded{2.6} + + The \var{fieldnames} are specified in a single string and are separated by spaces. + Any valid Python identifier may be used for a field name. + + Example: + \begin{verbatim} +>>> Point = NamedTuple('Point', 'x y') +>>> Point.__doc__ # docstring for the new datatype +'Point(x, y)' +>>> p = Point(11, y=22) # instantiate with positional or keyword arguments +>>> p[0] + p[1] # works just like the tuple (11, 22) +33 +>>> x, y = p # unpacks just like a tuple +>>> x, y +(11, 22) +>>> p.x + p.y # fields also accessable by name +33 +>>> p # readable __repr__ with name=value style +Point(x=11, y=22) +\end{verbatim} + + The use cases are the same as those for tuples. The named factories + assign meaning to each tuple position and allow for more readable, + self-documenting code. Named tuples can also be used to assign field names + to tuples + returned by the \module{csv} or \module{sqlite3} modules. For example: + + \begin{verbatim} +import csv +EmployeeRecord = NamedTuple('EmployeeRecord', 'name age title department paygrade') +for tup in csv.reader(open("employees.csv", "rb")): + print EmployeeRecord(*tup) +\end{verbatim} + +\end{funcdesc} diff --git a/Doc/lib/libcommands.tex b/Doc/lib/libcommands.tex index 53b8a20..fa9b464 100644 --- a/Doc/lib/libcommands.tex +++ b/Doc/lib/libcommands.tex @@ -39,6 +39,10 @@ and the return value is a string containing the command's output. Return the output of \samp{ls -ld \var{file}} as a string. This function uses the \function{getoutput()} function, and properly escapes backslashes and dollar signs in the argument. + +\deprecated{2.6}{This function is nonobvious and useless, + also the name is misleading in the presence of + \function{getstatusoutput()}.} \end{funcdesc} Example: diff --git a/Doc/lib/libconsts.tex b/Doc/lib/libconsts.tex index 1f56b05..bf36281 100644 --- a/Doc/lib/libconsts.tex +++ b/Doc/lib/libconsts.tex @@ -13,7 +13,7 @@ A small number of constants live in the built-in namespace. They are: \end{datadesc} \begin{datadesc}{None} - The sole value of \code{\refmodule{types}.NoneType}. \code{None} is + The sole value of \member{\refmodule{types}.NoneType}. \code{None} is frequently used to represent the absence of a value, as when default arguments are not passed to a function. \end{datadesc} diff --git a/Doc/lib/libcookielib.tex b/Doc/lib/libcookielib.tex index 01f2539..2ea3554 100644 --- a/Doc/lib/libcookielib.tex +++ b/Doc/lib/libcookielib.tex @@ -292,12 +292,12 @@ If there is a failure, the object's state will not be altered. \class{FileCookieJar} instances have the following public attributes: -\begin{memberdesc}{filename} +\begin{memberdesc}[FileCookieJar]{filename} Filename of default file in which to keep cookies. This attribute may be assigned to. \end{memberdesc} -\begin{memberdesc}{delayload} +\begin{memberdesc}[FileCookieJar]{delayload} If true, load cookies lazily from disk. This attribute should not be assigned to. This is only a hint, since this only affects performance, not behaviour (unless the cookies on disk are changing). @@ -400,13 +400,13 @@ In addition to implementing the methods above, implementations of the attributes, indicating which protocols should be used, and how. All of these attributes may be assigned to. -\begin{memberdesc}{netscape} +\begin{memberdesc}[CookiePolicy]{netscape} Implement Netscape protocol. \end{memberdesc} -\begin{memberdesc}{rfc2965} +\begin{memberdesc}[CookiePolicy]{rfc2965} Implement RFC 2965 protocol. \end{memberdesc} -\begin{memberdesc}{hide_cookie2} +\begin{memberdesc}[CookiePolicy]{hide_cookie2} Don't add \mailheader{Cookie2} header to requests (the presence of this header indicates to the server that we understand RFC 2965 cookies). @@ -504,7 +504,7 @@ receiving cookies. which are all initialised from the constructor arguments of the same name, and which may all be assigned to. -\begin{memberdesc}{rfc2109_as_netscape} +\begin{memberdesc}[DefaultCookiePolicy]{rfc2109_as_netscape} If true, request that the \class{CookieJar} instance downgrade RFC 2109 cookies (ie. cookies received in a \mailheader{Set-Cookie} header with a version cookie-attribute of 1) to Netscape cookies by setting @@ -517,7 +517,7 @@ RFC 2109 cookies are downgraded by default. General strictness switches: -\begin{memberdesc}{strict_domain} +\begin{memberdesc}[DefaultCookiePolicy]{strict_domain} Don't allow sites to set two-component domains with country-code top-level domains like \code{.co.uk}, \code{.gov.uk}, \code{.co.nz}.etc. This is far from perfect and isn't guaranteed to @@ -526,7 +526,7 @@ work! RFC 2965 protocol strictness switches: -\begin{memberdesc}{strict_rfc2965_unverifiable} +\begin{memberdesc}[DefaultCookiePolicy]{strict_rfc2965_unverifiable} Follow RFC 2965 rules on unverifiable transactions (usually, an unverifiable transaction is one resulting from a redirect or a request for an image hosted on another site). If this is false, cookies are @@ -535,19 +535,19 @@ for an image hosted on another site). If this is false, cookies are Netscape protocol strictness switches: -\begin{memberdesc}{strict_ns_unverifiable} +\begin{memberdesc}[DefaultCookiePolicy]{strict_ns_unverifiable} apply RFC 2965 rules on unverifiable transactions even to Netscape cookies \end{memberdesc} -\begin{memberdesc}{strict_ns_domain} +\begin{memberdesc}[DefaultCookiePolicy]{strict_ns_domain} Flags indicating how strict to be with domain-matching rules for Netscape cookies. See below for acceptable values. \end{memberdesc} -\begin{memberdesc}{strict_ns_set_initial_dollar} +\begin{memberdesc}[DefaultCookiePolicy]{strict_ns_set_initial_dollar} Ignore cookies in Set-Cookie: headers that have names starting with \code{'\$'}. \end{memberdesc} -\begin{memberdesc}{strict_ns_set_path} +\begin{memberdesc}[DefaultCookiePolicy]{strict_ns_set_path} Don't allow setting cookies whose path doesn't path-match request URI. \end{memberdesc} @@ -556,30 +556,30 @@ constructed by or-ing together (for example, \code{DomainStrictNoDots|DomainStrictNonDomain} means both flags are set). -\begin{memberdesc}{DomainStrictNoDots} +\begin{memberdesc}[DefaultCookiePolicy]{DomainStrictNoDots} When setting cookies, the 'host prefix' must not contain a dot (eg. \code{www.foo.bar.com} can't set a cookie for \code{.bar.com}, because \code{www.foo} contains a dot). \end{memberdesc} -\begin{memberdesc}{DomainStrictNonDomain} +\begin{memberdesc}[DefaultCookiePolicy]{DomainStrictNonDomain} Cookies that did not explicitly specify a \code{domain} cookie-attribute can only be returned to a domain equal to the domain that set the cookie (eg. \code{spam.example.com} won't be returned cookies from \code{example.com} that had no \code{domain} cookie-attribute). \end{memberdesc} -\begin{memberdesc}{DomainRFC2965Match} +\begin{memberdesc}[DefaultCookiePolicy]{DomainRFC2965Match} When setting cookies, require a full RFC 2965 domain-match. \end{memberdesc} The following attributes are provided for convenience, and are the most useful combinations of the above flags: -\begin{memberdesc}{DomainLiberal} +\begin{memberdesc}[DefaultCookiePolicy]{DomainLiberal} Equivalent to 0 (ie. all of the above Netscape domain strictness flags switched off). \end{memberdesc} -\begin{memberdesc}{DomainStrict} +\begin{memberdesc}[DefaultCookiePolicy]{DomainStrict} Equivalent to \code{DomainStrictNoDots|DomainStrictNonDomain}. \end{memberdesc} diff --git a/Doc/lib/libctypes.tex b/Doc/lib/libctypes.tex index 2f880f2..f19507a 100755 --- a/Doc/lib/libctypes.tex +++ b/Doc/lib/libctypes.tex @@ -7,21 +7,21 @@ \versionadded{2.5} \code{ctypes} is a foreign function library for Python. It provides C -compatible data types, and allows to call functions in dlls/shared +compatible data types, and allows calling functions in dlls/shared libraries. It can be used to wrap these libraries in pure Python. \subsection{ctypes tutorial\label{ctypes-ctypes-tutorial}} -Note: The code samples in this tutorial uses \code{doctest} to make sure +Note: The code samples in this tutorial use \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. +Note: Some code sample 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. +expect \class{c{\_}int} --- they are actually the same type. \subsubsection{Loading dynamic link libraries\label{ctypes-loading-dynamic-link-libraries}} @@ -38,7 +38,7 @@ 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 +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} @@ -1219,7 +1219,7 @@ py_cmp_func 5 7 It is quite interesting to see that the Windows \function{qsort} function needs more comparisons than the linux version! -As we can easily check, our array sorted now: +As we can easily check, our array is sorted now: \begin{verbatim} >>> for i in ia: print i, ... @@ -1242,7 +1242,7 @@ variables. An example in the Python library itself is 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 +methods of the type. \var{pythonapi} is a predefined symbol giving access to the Python C api: \begin{verbatim} >>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") @@ -2113,7 +2113,7 @@ debugging; never modify the contents of this dictionary. \end{memberdesc} -\subsubsection{Fundamental data types\label{ctypes-fundamental-data-types}} +\subsubsection{Fundamental data types\label{ctypes-fundamental-data-types-2}} \begin{classdesc*}{_SimpleCData} This non-public class is the base class of all fundamental ctypes @@ -2294,6 +2294,13 @@ a zero-terminated wide character string. The constructor accepts an integer address, or a string. \end{classdesc*} +\begin{classdesc*}{c_bool} +Represent the C \code{bool} datatype (more accurately, _Bool from C99). +Its value can be True or False, and the constructor accepts any object that +has a truth value. +\versionadded{2.6} +\end{classdesc*} + \begin{classdesc*}{HRESULT} Windows only: Represents a \class{HRESULT} value, which contains success or error information for a function or method call. diff --git a/Doc/lib/libcurses.tex b/Doc/lib/libcurses.tex index fa9f974..33dea5a 100644 --- a/Doc/lib/libcurses.tex +++ b/Doc/lib/libcurses.tex @@ -646,6 +646,16 @@ Similar to \method{border()}, but both \var{ls} and \var{rs} are corner characters are always used by this function. \end{methoddesc} +\begin{methoddesc}[window]{chgat}{\optional{y, x, } \optional{num,} attr} +Sets the attributes of \var{num} characters at the current cursor +position, or at position \code{(\var{y}, \var{x})} if supplied. If no +value of \var{num} is given or \var{num} = -1, the attribute will +be set on all the characters to the end of the line. +This function does not move the cursor. The changed line +will be touched using the \method{touchline} method so that the +contents will be redisplayed by the next window refresh. +\end{methoddesc} + \begin{methoddesc}[window]{clear}{} Like \method{erase()}, but also causes the whole window to be repainted upon next call to \method{refresh()}. @@ -1014,9 +1024,11 @@ block for \var{delay} milliseconds, and return -1 if there is still no input at the end of that time. \end{methoddesc} -\begin{methoddesc}[window]{touchline}{start, count} +\begin{methoddesc}[window]{touchline}{start, count\optional{, changed}} Pretend \var{count} lines have been changed, starting with line -\var{start}. +\var{start}. If \var{changed} is supplied, it specifies +whether the affected lines are marked as +having been changed (\var{changed}=1) or unchanged (\var{changed}=0). \end{methoddesc} \begin{methoddesc}[window]{touchwin}{} diff --git a/Doc/lib/libcursespanel.tex b/Doc/lib/libcursespanel.tex index 1f96717..14d83e3 100644 --- a/Doc/lib/libcursespanel.tex +++ b/Doc/lib/libcursespanel.tex @@ -45,52 +45,52 @@ responsible for the window's depth in the panel stack. Panel objects have the following methods: -\begin{methoddesc}{above}{} +\begin{methoddesc}[Panel]{above}{} Returns the panel above the current panel. \end{methoddesc} -\begin{methoddesc}{below}{} +\begin{methoddesc}[Panel]{below}{} Returns the panel below the current panel. \end{methoddesc} -\begin{methoddesc}{bottom}{} +\begin{methoddesc}[Panel]{bottom}{} Push the panel to the bottom of the stack. \end{methoddesc} -\begin{methoddesc}{hidden}{} +\begin{methoddesc}[Panel]{hidden}{} Returns true if the panel is hidden (not visible), false otherwise. \end{methoddesc} -\begin{methoddesc}{hide}{} +\begin{methoddesc}[Panel]{hide}{} Hide the panel. This does not delete the object, it just makes the window on screen invisible. \end{methoddesc} -\begin{methoddesc}{move}{y, x} +\begin{methoddesc}[Panel]{move}{y, x} Move the panel to the screen coordinates \code{(\var{y}, \var{x})}. \end{methoddesc} -\begin{methoddesc}{replace}{win} +\begin{methoddesc}[Panel]{replace}{win} Change the window associated with the panel to the window \var{win}. \end{methoddesc} -\begin{methoddesc}{set_userptr}{obj} +\begin{methoddesc}[Panel]{set_userptr}{obj} Set the panel's user pointer to \var{obj}. This is used to associate an arbitrary piece of data with the panel, and can be any Python object. \end{methoddesc} -\begin{methoddesc}{show}{} +\begin{methoddesc}[Panel]{show}{} Display the panel (which might have been hidden). \end{methoddesc} -\begin{methoddesc}{top}{} +\begin{methoddesc}[Panel]{top}{} Push panel to the top of the stack. \end{methoddesc} -\begin{methoddesc}{userptr}{} +\begin{methoddesc}[Panel]{userptr}{} Returns the user pointer for the panel. This might be any Python object. \end{methoddesc} -\begin{methoddesc}{window}{} +\begin{methoddesc}[Panel]{window}{} Returns the window object associated with the panel. \end{methoddesc} diff --git a/Doc/lib/libdatetime.tex b/Doc/lib/libdatetime.tex index 0d2b5bb..fb13ea7 100644 --- a/Doc/lib/libdatetime.tex +++ b/Doc/lib/libdatetime.tex @@ -1154,7 +1154,7 @@ following methods. Exactly which methods are needed depends on the uses made of aware \module{datetime} objects. If in doubt, simply implement all of them. -\begin{methoddesc}{utcoffset}{self, dt} +\begin{methoddesc}[tzinfo]{utcoffset}{self, dt} Return offset of local time from UTC, in minutes east of UTC. If local time is west of UTC, this should be negative. Note that this is intended to be the total offset from UTC; for example, if a @@ -1178,7 +1178,7 @@ implement all of them. \exception{NotImplementedError}. \end{methoddesc} -\begin{methoddesc}{dst}{self, dt} +\begin{methoddesc}[tzinfo]{dst}{self, dt} Return the daylight saving time (DST) adjustment, in minutes east of UTC, or \code{None} if DST information isn't known. Return \code{timedelta(0)} if DST is not in effect. @@ -1237,7 +1237,7 @@ implement all of them. \exception{NotImplementedError}. \end{methoddesc} -\begin{methoddesc}{tzname}{self, dt} +\begin{methoddesc}[tzinfo]{tzname}{self, dt} Return the time zone name corresponding to the \class{datetime} object \var{dt}, as a string. Nothing about string names is defined by the @@ -1278,7 +1278,7 @@ and not need worry about objects in other timezones. There is one more \class{tzinfo} method that a subclass may wish to override: -\begin{methoddesc}{fromutc}{self, dt} +\begin{methoddesc}[tzinfo]{fromutc}{self, dt} This is called from the default \class{datetime.astimezone()} implementation. When called from that, \code{\var{dt}.tzinfo} is \var{self}, and \var{dt}'s date and time members are to be viewed as diff --git a/Doc/lib/libdbhash.tex b/Doc/lib/libdbhash.tex index f5f98ea..cf44707 100644 --- a/Doc/lib/libdbhash.tex +++ b/Doc/lib/libdbhash.tex @@ -2,7 +2,6 @@ DBM-style interface to the BSD database library} \declaremodule{standard}{dbhash} - \platform{Unix, Windows} \modulesynopsis{DBM-style interface to the BSD database library.} \sectionauthor{Fred L. Drake, Jr.}{fdrake@acm.org} diff --git a/Doc/lib/libdecimal.tex b/Doc/lib/libdecimal.tex index 127eb1d..a0a257e 100644 --- a/Doc/lib/libdecimal.tex +++ b/Doc/lib/libdecimal.tex @@ -425,7 +425,7 @@ also have a number of specialized methods: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\subsection{Context objects \label{decimal-decimal}} +\subsection{Context objects \label{decimal-context}} Contexts are environments for arithmetic operations. They govern precision, set rules for rounding, determine which signals are treated as exceptions, and diff --git a/Doc/lib/libdl.tex b/Doc/lib/libdl.tex index 325724c..d4a799e 100644 --- a/Doc/lib/libdl.tex +++ b/Doc/lib/libdl.tex @@ -67,11 +67,11 @@ example of the fact that using this module is usually a bad alternative. Dl objects, as returned by \function{open()} above, have the following methods: -\begin{methoddesc}{close}{} +\begin{methoddesc}[dl]{close}{} Free all resources, except the memory. \end{methoddesc} -\begin{methoddesc}{sym}{name} +\begin{methoddesc}[dl]{sym}{name} Return the pointer for the function named \var{name}, as a number, if it exists in the referenced shared object, otherwise \code{None}. This is useful in code like: @@ -87,7 +87,7 @@ is useful in code like: \NULL{} pointer) \end{methoddesc} -\begin{methoddesc}{call}{name\optional{, arg1\optional{, arg2\ldots}}} +\begin{methoddesc}[dl]{call}{name\optional{, arg1\optional{, arg2\ldots}}} Call the function named \var{name} in the referenced shared object. The arguments must be either Python integers, which will be passed as is, Python strings, to which a pointer will be passed, diff --git a/Doc/lib/libdoctest.tex b/Doc/lib/libdoctest.tex index 957ecf4..5e28c2a 100644 --- a/Doc/lib/libdoctest.tex +++ b/Doc/lib/libdoctest.tex @@ -201,6 +201,19 @@ prohibit it by passing \code{verbose=False}. In either of those cases, \code{sys.argv} is not examined by \function{testmod()} (so passing \programopt{-v} or not has no effect). +Since Python 2.6, there is also a command line shortcut for running +\function{testmod()}. You can instruct the Python interpreter to run +the doctest module directly from the standard library and pass the module +name(s) on the command line: + +\begin{verbatim} +python -m doctest -v example.py +\end{verbatim} + +This will import \file{example.py} as a standalone module and run +\function{testmod()} on it. Note that this may not work correctly if the +file is part of a package and imports other submodules from that package. + For more information on \function{testmod()}, see section~\ref{doctest-basic-api}. @@ -267,6 +280,18 @@ Like \function{testmod()}, \function{testfile()}'s verbosity can be set with the \programopt{-v} command-line switch or with the optional keyword argument \var{verbose}. +Since Python 2.6, there is also a command line shortcut for running +\function{testfile()}. You can instruct the Python interpreter to run +the doctest module directly from the standard library and pass the file +name(s) on the command line: + +\begin{verbatim} +python -m doctest -v example.txt +\end{verbatim} + +Because the file name does not end with \file{.py}, \module{doctest} infers +that it must be run with \function{testfile()}, not \function{testmod()}. + For more information on \function{testfile()}, see section~\ref{doctest-basic-api}. @@ -1716,7 +1741,7 @@ Doctest provides several mechanisms for debugging doctest examples: >>> \end{verbatim} - \versionchanged[The ability to use \code{\refmodule{pdb}.set_trace()} + \versionchanged[The ability to use \function{\refmodule{pdb}.set_trace()} usefully inside doctests was added]{2.4} \end{itemize} @@ -1800,10 +1825,10 @@ print doctest.testsource(a, "a.f") used. If \var{pm} has a true value, the script file is run directly, and the debugger gets involved only if the script terminates via raising an unhandled exception. If it does, then post-mortem debugging is invoked, - via \code{\refmodule{pdb}.post_mortem()}, passing the traceback object + via \function{\refmodule{pdb}.post_mortem()}, passing the traceback object from the unhandled exception. If \var{pm} is not specified, or is false, the script is run under the debugger from the start, via passing an - appropriate \function{execfile()} call to \code{\refmodule{pdb}.run()}. + appropriate \function{execfile()} call to \function{\refmodule{pdb}.run()}. \versionadded{2.3} diff --git a/Doc/lib/libdocxmlrpc.tex b/Doc/lib/libdocxmlrpc.tex index 2f1e6ca..f93b3b2 100644 --- a/Doc/lib/libdocxmlrpc.tex +++ b/Doc/lib/libdocxmlrpc.tex @@ -14,8 +14,12 @@ HTTP GET requests. Servers can either be free standing, using \class{DocXMLRPCServer}, or embedded in a CGI environment, using \class{DocCGIXMLRPCRequestHandler}. -\begin{classdesc}{DocXMLRPCServer}{addr\optional{, - requestHandler\optional{, logRequests}}} +\begin{classdesc}{DocXMLRPCServer}{addr\optional{, + requestHandler\optional{, + logRequests\optional{, + allow_none\optional{, + encoding\optional{, + bind_and_activate}}}}}} Create a new server instance. All parameters have the same meaning as for \class{SimpleXMLRPCServer.SimpleXMLRPCServer}; @@ -47,14 +51,14 @@ requests are handled as XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style HTML documentation. This allows a server to provide its own web-based documentation. -\begin{methoddesc}{set_server_title}{server_title} +\begin{methoddesc}[DocXMLRPCServer]{set_server_title}{server_title} Set the title used in the generated HTML documentation. This title will be used inside the HTML "title" element. \end{methoddesc} -\begin{methoddesc}{set_server_name}{server_name} +\begin{methoddesc}[DocXMLRPCServer]{set_server_name}{server_name} Set the name used in the generated HTML documentation. This name will appear at the top of the generated documentation inside a "h1" @@ -63,7 +67,7 @@ element. \end{methoddesc} -\begin{methoddesc}{set_server_documentation}{server_documentation} +\begin{methoddesc}[DocXMLRPCServer]{set_server_documentation}{server_documentation} Set the description used in the generated HTML documentation. This description will appear as a paragraph, below the server name, in the @@ -80,14 +84,14 @@ are handled as XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style HTML documentation. This allows a server to provide its own web-based documentation. -\begin{methoddesc}{set_server_title}{server_title} +\begin{methoddesc}[DocCGIXMLRPCRequestHandler]{set_server_title}{server_title} Set the title used in the generated HTML documentation. This title will be used inside the HTML "title" element. \end{methoddesc} -\begin{methoddesc}{set_server_name}{server_name} +\begin{methoddesc}[DocCGIXMLRPCRequestHandler]{set_server_name}{server_name} Set the name used in the generated HTML documentation. This name will appear at the top of the generated documentation inside a "h1" @@ -96,7 +100,7 @@ element. \end{methoddesc} -\begin{methoddesc}{set_server_documentation}{server_documentation} +\begin{methoddesc}[DocCGIXMLRPCRequestHandler]{set_server_documentation}{server_documentation} Set the description used in the generated HTML documentation. This description will appear as a paragraph, below the server name, in the diff --git a/Doc/lib/libdumbdbm.tex b/Doc/lib/libdumbdbm.tex index 4495774..d0917be 100644 --- a/Doc/lib/libdumbdbm.tex +++ b/Doc/lib/libdumbdbm.tex @@ -57,7 +57,7 @@ only when the database has to be created. It defaults to octal In addition to the methods provided by the \class{UserDict.DictMixin} class, \class{dumbdbm} objects provide the following methods. -\begin{methoddesc}{sync}{} +\begin{methoddesc}[dumbdbm]{sync}{} Synchronize the on-disk directory and data files. This method is called by the \method{sync} method of \class{Shelve} objects. \end{methoddesc} diff --git a/Doc/lib/libetree.tex b/Doc/lib/libetree.tex index f769c63..6f20ee3 100644 --- a/Doc/lib/libetree.tex +++ b/Doc/lib/libetree.tex @@ -144,12 +144,12 @@ Returns a tuple containing an Element instance and a dictionary. Element objects returned by Element or SubElement have the following methods and attributes. -\begin{memberdesc}{tag} +\begin{memberdesc}[Element]{tag} A string identifying what kind of data this element represents (the element type, in other words). \end{memberdesc} -\begin{memberdesc}{text} +\begin{memberdesc}[Element]{text} The \var{text} attribute can be used to hold additional data associated with the element. As the name implies this attribute is usually a string but may be any @@ -158,7 +158,7 @@ If the element is created from an XML file the attribute will contain any text found between the element tags. \end{memberdesc} -\begin{memberdesc}{tail} +\begin{memberdesc}[Element]{tail} The \var{tail} attribute can be used to hold additional data associated with the element. This attribute is usually a string but may be any application-specific object. @@ -166,7 +166,7 @@ If the element is created from an XML file the attribute will contain any text found after the element's end tag and before the next tag. \end{memberdesc} -\begin{memberdesc}{attrib} +\begin{memberdesc}[Element]{attrib} A dictionary containing the element's attributes. Note that while the \var{attrib} value is always a real mutable Python dictionary, an ElementTree implementation may choose to use another @@ -177,52 +177,52 @@ dictionary methods below whenever possible. The following dictionary-like methods work on the element attributes. -\begin{methoddesc}{clear}{} +\begin{methoddesc}[Element]{clear}{} Resets an element. This function removes all subelements, clears all attributes, and sets the text and tail attributes to None. \end{methoddesc} -\begin{methoddesc}{get}{key\optional{, default=None}} +\begin{methoddesc}[Element]{get}{key\optional{, default=None}} Gets the element attribute named \var{key}. Returns the attribute value, or \var{default} if the attribute was not found. \end{methoddesc} -\begin{methoddesc}{items}{} +\begin{methoddesc}[Element]{items}{} Returns the element attributes as a sequence of (name, value) pairs. The attributes are returned in an arbitrary order. \end{methoddesc} -\begin{methoddesc}{keys}{} +\begin{methoddesc}[Element]{keys}{} Returns the elements attribute names as a list. The names are returned in an arbitrary order. \end{methoddesc} -\begin{methoddesc}{set}{key, value} +\begin{methoddesc}[Element]{set}{key, value} Set the attribute \var{key} on the element to \var{value}. \end{methoddesc} The following methods work on the element's children (subelements). -\begin{methoddesc}{append}{subelement} +\begin{methoddesc}[Element]{append}{subelement} Adds the element \var{subelement} to the end of this elements internal list of subelements. \end{methoddesc} -\begin{methoddesc}{find}{match} +\begin{methoddesc}[Element]{find}{match} Finds the first subelement matching \var{match}. \var{match} may be a tag name or path. Returns an element instance or \code{None}. \end{methoddesc} -\begin{methoddesc}{findall}{match} +\begin{methoddesc}[Element]{findall}{match} Finds all subelements matching \var{match}. \var{match} may be a tag name or path. Returns an iterable yielding all matching elements in document order. \end{methoddesc} -\begin{methoddesc}{findtext}{condition\optional{, default=None}} +\begin{methoddesc}[Element]{findtext}{condition\optional{, default=None}} Finds text for the first subelement matching \var{condition}. \var{condition} may be a tag name or path. Returns the text content of the first matching element, or @@ -230,11 +230,11 @@ Returns the text content of the first matching element, or matching element has no text content an empty string is returned. \end{methoddesc} -\begin{methoddesc}{getchildren}{} +\begin{methoddesc}[Element]{getchildren}{} Returns all subelements. The elements are returned in document order. \end{methoddesc} -\begin{methoddesc}{getiterator}{\optional{tag=None}} +\begin{methoddesc}[Element]{getiterator}{\optional{tag=None}} Creates a tree iterator with the current element as the root. The iterator iterates over this element and all elements below it that match the given tag. If tag @@ -243,16 +243,16 @@ Returns an iterable that provides element objects in document (depth first) order. \end{methoddesc} -\begin{methoddesc}{insert}{index, element} +\begin{methoddesc}[Element]{insert}{index, element} Inserts a subelement at the given position in this element. \end{methoddesc} -\begin{methoddesc}{makeelement}{tag, attrib} +\begin{methoddesc}[Element]{makeelement}{tag, attrib} Creates a new element object of the same type as this element. Do not call this method, use the SubElement factory function instead. \end{methoddesc} -\begin{methoddesc}{remove}{subelement} +\begin{methoddesc}[Element]{remove}{subelement} Removes \var{subelement} from the element. Unlike the findXXX methods this method compares elements based on the instance identity, not on tag value or contents. diff --git a/Doc/lib/libfm.tex b/Doc/lib/libfm.tex index 0b429e0..0a0c237 100644 --- a/Doc/lib/libfm.tex +++ b/Doc/lib/libfm.tex @@ -55,40 +55,39 @@ Returns the current font search path. Font handle objects support the following operations: -\setindexsubitem{(font handle method)} -\begin{funcdesc}{scalefont}{factor} +\begin{methoddesc}[font handle]{scalefont}{factor} Returns a handle for a scaled version of this font. Calls \code{fmscalefont(\var{fh}, \var{factor})}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{setfont}{} +\begin{methoddesc}[font handle]{setfont}{} Makes this font the current font. Note: the effect is undone silently when the font handle object is deleted. Calls \code{fmsetfont(\var{fh})}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{getfontname}{} +\begin{methoddesc}[font handle]{getfontname}{} Returns this font's name. Calls \code{fmgetfontname(\var{fh})}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{getcomment}{} +\begin{methoddesc}[font handle]{getcomment}{} Returns the comment string associated with this font. Raises an exception if there is none. Calls \code{fmgetcomment(\var{fh})}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{getfontinfo}{} +\begin{methoddesc}[font handle]{getfontinfo}{} Returns a tuple giving some pertinent data about this font. This is an interface to \code{fmgetfontinfo()}. The returned tuple contains the following numbers: \code{(}\var{printermatched}, \var{fixed_width}, \var{xorig}, \var{yorig}, \var{xsize}, \var{ysize}, \var{height}, \var{nglyphs}\code{)}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{getstrwidth}{string} +\begin{methoddesc}[font handle]{getstrwidth}{string} Returns the width, in pixels, of \var{string} when drawn in this font. Calls \code{fmgetstrwidth(\var{fh}, \var{string})}. -\end{funcdesc} +\end{methoddesc} diff --git a/Doc/lib/libfnmatch.tex b/Doc/lib/libfnmatch.tex index fc4b97a..1ac46bd 100644 --- a/Doc/lib/libfnmatch.tex +++ b/Doc/lib/libfnmatch.tex @@ -36,6 +36,19 @@ lower- or upper-case before the comparison is performed. If you require a case-sensitive comparison regardless of whether that's standard for your operating system, use \function{fnmatchcase()} instead. + +This example will print all file names in the current directory with the +extension \code{.txt}: + +\begin{verbatim} +import fnmatch +import os + +for file in os.listdir('.'): + if fnmatch.fnmatch(file, '*.txt'): + print file +\end{verbatim} + \end{funcdesc} \begin{funcdesc}{fnmatchcase}{filename, pattern} @@ -50,6 +63,24 @@ implemented more efficiently. \versionadded{2.2} \end{funcdesc} +\begin{funcdesc}{translate}{pattern} +Return the shell-style \var{pattern} converted to a regular +expression. + +Example: + +\begin{verbatim} +>>> import fnmatch, re +>>> +>>> regex = fnmatch.translate('*.txt') +>>> regex +'.*\\.txt$' +>>> reobj = re.compile(regex) +>>> print reobj.match('foobar.txt') +<_sre.SRE_Match object at 0x...> +\end{verbatim} +\end{funcdesc} + \begin{seealso} \seemodule{glob}{\UNIX{} shell-style path expansion.} \end{seealso} diff --git a/Doc/lib/libftplib.tex b/Doc/lib/libftplib.tex index 58d16cb..98d7e80 100644 --- a/Doc/lib/libftplib.tex +++ b/Doc/lib/libftplib.tex @@ -37,12 +37,15 @@ dr-xr-srwt 105 ftp-usr pdmaint 1536 Mar 21 14:32 .. The module defines the following items: \begin{classdesc}{FTP}{\optional{host\optional{, user\optional{, - passwd\optional{, acct}}}}} + passwd\optional{, acct\optional{, timeout}}}}}} Return a new instance of the \class{FTP} class. When \var{host} is given, the method call \code{connect(\var{host})} is made. When \var{user} is given, additionally the method call \code{login(\var{user}, \var{passwd}, \var{acct})} is made (where \var{passwd} and \var{acct} default to the empty string when not given). +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if is not specified, or passed as None, the global +default timeout setting will be used). \end{classdesc} \begin{datadesc}{all_errors} @@ -92,7 +95,7 @@ which is used followed by \samp{lines} for the text version or \class{FTP} instances have the following methods: -\begin{methoddesc}{set_debuglevel}{level} +\begin{methoddesc}[FTP]{set_debuglevel}{level} Set the instance's debugging level. This controls the amount of debugging output printed. The default, \code{0}, produces no debugging output. A value of \code{1} produces a moderate amount of @@ -101,22 +104,28 @@ debugging output, generally a single line per request. A value of logging each line sent and received on the control connection. \end{methoddesc} -\begin{methoddesc}{connect}{host\optional{, port}} +\begin{methoddesc}[FTP]{connect}{host\optional{, port\optional{, timeout}}} Connect to the given host and port. The default port number is \code{21}, as specified by the FTP protocol specification. It is rarely needed to specify a different port number. This function should be called only once for each instance; it should not be called at all if a host was given when the instance was created. All other methods can only be used after a connection has been made. + +The optional \var{timeout} parameter specifies a timeout in seconds for +the connection attempt. If is not specified, or passed as None, the +object timeout is used (the timeout that you passed when instantiating the +class); if the object timeout is also None, the global default timeout +setting will be used. \end{methoddesc} -\begin{methoddesc}{getwelcome}{} +\begin{methoddesc}[FTP]{getwelcome}{} Return the welcome message sent by the server in reply to the initial connection. (This message sometimes contains disclaimers or help information that may be relevant to the user.) \end{methoddesc} -\begin{methoddesc}{login}{\optional{user\optional{, passwd\optional{, acct}}}} +\begin{methoddesc}[FTP]{login}{\optional{user\optional{, passwd\optional{, acct}}}} Log in as the given \var{user}. The \var{passwd} and \var{acct} parameters are optional and default to the empty string. If no \var{user} is specified, it defaults to \code{'anonymous'}. If @@ -128,23 +137,23 @@ instance was created. Most FTP commands are only allowed after the client has logged in. \end{methoddesc} -\begin{methoddesc}{abort}{} +\begin{methoddesc}[FTP]{abort}{} Abort a file transfer that is in progress. Using this does not always work, but it's worth a try. \end{methoddesc} -\begin{methoddesc}{sendcmd}{command} +\begin{methoddesc}[FTP]{sendcmd}{command} Send a simple command string to the server and return the response string. \end{methoddesc} -\begin{methoddesc}{voidcmd}{command} +\begin{methoddesc}[FTP]{voidcmd}{command} Send a simple command string to the server and handle the response. Return nothing if a response code in the range 200--299 is received. Raise an exception otherwise. \end{methoddesc} -\begin{methoddesc}{retrbinary}{command, +\begin{methoddesc}[FTP]{retrbinary}{command, callback\optional{, maxblocksize\optional{, rest}}} Retrieve a file in binary transfer mode. \var{command} should be an appropriate \samp{RETR} command: \code{'RETR \var{filename}'}. @@ -157,7 +166,7 @@ read on the low-level socket object created to do the actual transfer same thing as in the \method{transfercmd()} method. \end{methoddesc} -\begin{methoddesc}{retrlines}{command\optional{, callback}} +\begin{methoddesc}[FTP]{retrlines}{command\optional{, callback}} Retrieve a file or directory listing in \ASCII{} transfer mode. \var{command} should be an appropriate \samp{RETR} command (see \method{retrbinary()}) or a \samp{LIST} command (usually just the string @@ -166,13 +175,13 @@ with the trailing CRLF stripped. The default \var{callback} prints the line to \code{sys.stdout}. \end{methoddesc} -\begin{methoddesc}{set_pasv}{boolean} +\begin{methoddesc}[FTP]{set_pasv}{boolean} Enable ``passive'' mode if \var{boolean} is true, other disable passive mode. (In Python 2.0 and before, passive mode was off by default; in Python 2.1 and later, it is on by default.) \end{methoddesc} -\begin{methoddesc}{storbinary}{command, file\optional{, blocksize}} +\begin{methoddesc}[FTP]{storbinary}{command, file\optional{, blocksize}} Store a file in binary transfer mode. \var{command} should be an appropriate \samp{STOR} command: \code{"STOR \var{filename}"}. \var{file} is an open file object which is read until \EOF{} using its @@ -181,14 +190,14 @@ data to be stored. The \var{blocksize} argument defaults to 8192. \versionchanged[default for \var{blocksize} added]{2.1} \end{methoddesc} -\begin{methoddesc}{storlines}{command, file} +\begin{methoddesc}[FTP]{storlines}{command, file} Store a file in \ASCII{} transfer mode. \var{command} should be an appropriate \samp{STOR} command (see \method{storbinary()}). Lines are read until \EOF{} from the open file object \var{file} using its \method{readline()} method to provide the data to be stored. \end{methoddesc} -\begin{methoddesc}{transfercmd}{cmd\optional{, rest}} +\begin{methoddesc}[FTP]{transfercmd}{cmd\optional{, rest}} Initiate a transfer over the data connection. If the transfer is active, send a \samp{EPRT} or \samp{PORT} command and the transfer command specified by \var{cmd}, and accept the connection. If the server is passive, @@ -210,7 +219,7 @@ not recognize the \samp{REST} command, an simply call \method{transfercmd()} without a \var{rest} argument. \end{methoddesc} -\begin{methoddesc}{ntransfercmd}{cmd\optional{, rest}} +\begin{methoddesc}[FTP]{ntransfercmd}{cmd\optional{, rest}} Like \method{transfercmd()}, but returns a tuple of the data connection and the expected size of the data. If the expected size could not be computed, \code{None} will be returned as the expected @@ -218,14 +227,14 @@ size. \var{cmd} and \var{rest} means the same thing as in \method{transfercmd()}. \end{methoddesc} -\begin{methoddesc}{nlst}{argument\optional{, \ldots}} +\begin{methoddesc}[FTP]{nlst}{argument\optional{, \ldots}} Return a list of files as returned by the \samp{NLST} command. The optional \var{argument} is a directory to list (default is the current server directory). Multiple arguments can be used to pass non-standard options to the \samp{NLST} command. \end{methoddesc} -\begin{methoddesc}{dir}{argument\optional{, \ldots}} +\begin{methoddesc}[FTP]{dir}{argument\optional{, \ldots}} Produce a directory listing as returned by the \samp{LIST} command, printing it to standard output. The optional \var{argument} is a directory to list (default is the current server directory). Multiple @@ -235,41 +244,41 @@ command. If the last argument is a function, it is used as a prints to \code{sys.stdout}. This method returns \code{None}. \end{methoddesc} -\begin{methoddesc}{rename}{fromname, toname} +\begin{methoddesc}[FTP]{rename}{fromname, toname} Rename file \var{fromname} on the server to \var{toname}. \end{methoddesc} -\begin{methoddesc}{delete}{filename} +\begin{methoddesc}[FTP]{delete}{filename} Remove the file named \var{filename} from the server. If successful, returns the text of the response, otherwise raises \exception{error_perm} on permission errors or \exception{error_reply} on other errors. \end{methoddesc} -\begin{methoddesc}{cwd}{pathname} +\begin{methoddesc}[FTP]{cwd}{pathname} Set the current directory on the server. \end{methoddesc} -\begin{methoddesc}{mkd}{pathname} +\begin{methoddesc}[FTP]{mkd}{pathname} Create a new directory on the server. \end{methoddesc} -\begin{methoddesc}{pwd}{} +\begin{methoddesc}[FTP]{pwd}{} Return the pathname of the current directory on the server. \end{methoddesc} -\begin{methoddesc}{rmd}{dirname} +\begin{methoddesc}[FTP]{rmd}{dirname} Remove the directory named \var{dirname} on the server. \end{methoddesc} -\begin{methoddesc}{size}{filename} +\begin{methoddesc}[FTP]{size}{filename} Request the size of the file named \var{filename} on the server. On success, the size of the file is returned as an integer, otherwise \code{None} is returned. Note that the \samp{SIZE} command is not standardized, but is supported by many common server implementations. \end{methoddesc} -\begin{methoddesc}{quit}{} +\begin{methoddesc}[FTP]{quit}{} Send a \samp{QUIT} command to the server and close the connection. This is the ``polite'' way to close a connection, but it may raise an exception of the server reponds with an error to the @@ -278,7 +287,7 @@ method which renders the \class{FTP} instance useless for subsequent calls (see below). \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[FTP]{close}{} Close the connection unilaterally. This should not be applied to an already closed connection such as after a successful call to \method{quit()}. After this call the \class{FTP} instance should not diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex index b488ce4..0900317 100644 --- a/Doc/lib/libfuncs.tex +++ b/Doc/lib/libfuncs.tex @@ -175,15 +175,15 @@ class C: \code{\var{x} > \var{y}}. \end{funcdesc} -\begin{funcdesc}{compile}{string, filename, kind\optional{, +\begin{funcdesc}{compile}{source, filename, mode\optional{, flags\optional{, dont_inherit}}} - Compile the \var{string} into a code object. Code objects can be + Compile the \var{source} into a code object. Code objects can be executed by a call to \function{exec()} or evaluated by a call to \function{eval()}. The \var{filename} argument should give the file from which the code was read; pass some recognizable value if it wasn't read from a file (\code{'<string>'} is commonly used). - The \var{kind} argument specifies what kind of code must be - compiled; it can be \code{'exec'} if \var{string} consists of a + The \var{mode} argument specifies what kind of code must be + compiled; it can be \code{'exec'} if \var{source} consists of a sequence of statements, \code{'eval'} if it consists of a single expression, or \code{'single'} if it consists of a single interactive statement (in the latter case, expression statements @@ -198,7 +198,7 @@ class C: The optional arguments \var{flags} and \var{dont_inherit} (which are new in Python 2.2) control which future statements (see - \pep{236}) affect the compilation of \var{string}. If neither is + \pep{236}) affect the compilation of \var{source}. If neither is present (or both are zero) the code is compiled with those future statements that are in effect in the code that is calling compile. If the \var{flags} argument is given and \var{dont_inherit} is not @@ -582,8 +582,9 @@ class C: \begin{funcdesc}{isinstance}{object, classinfo} Return true if the \var{object} argument is an instance of the \var{classinfo} argument, or of a (direct or indirect) subclass - thereof. Also return true if \var{classinfo} is a type object and - \var{object} is an object of that type. If \var{object} is not a + thereof. Also return true if \var{classinfo} is a type object + (new-style class) and \var{object} is an object of that type or of a + (direct or indirect) subclass thereof. If \var{object} is not a class instance or an object of the given type, the function always returns false. If \var{classinfo} is neither a class object nor a type object, it may be a tuple of class or type objects, or may @@ -641,6 +642,11 @@ class C: \warning{The contents of this dictionary should not be modified; changes may not affect the values of local variables used by the interpreter.} + + Free variables are returned by \var{locals} when it is called in + a function block. Modifications of free variables may not affect + the values used by the interpreter. Free variables are not + returned in class blocks. \end{funcdesc} \begin{funcdesc}{long}{\optional{x\optional{, radix}}} @@ -980,7 +986,7 @@ except NameError: \begin{funcdesc}{reversed}{seq} Return a reverse iterator. \var{seq} must be an object which - supports the sequence protocol (the __len__() method and the + supports the sequence protocol (the \method{__len__()} method and the \method{__getitem__()} method with integer arguments starting at \code{0}). \versionadded{2.4} @@ -1148,7 +1154,7 @@ class C(B): as detailed below. \end{funcdesc} -\begin{funcdesc}{type}{name, bases, dict} +\begin{funcdescni}{type}{name, bases, dict} Return a new type object. This is essentially a dynamic form of the \keyword{class} statement. The \var{name} string is the class name and becomes the \member{__name__} attribute; the \var{bases} tuple @@ -1165,7 +1171,7 @@ class C(B): >>> X = type('X', (object,), dict(a=1)) \end{verbatim} \versionadded{2.2} -\end{funcdesc} +\end{funcdescni} \begin{funcdesc}{unichr}{i} Return the Unicode string of one character whose Unicode code is the diff --git a/Doc/lib/libgettext.tex b/Doc/lib/libgettext.tex index 5c7c6b9..6aee255 100644 --- a/Doc/lib/libgettext.tex +++ b/Doc/lib/libgettext.tex @@ -102,9 +102,9 @@ If no translation is found, return \var{singular} if \var{n} is 1; return \var{plural} otherwise. The Plural formula is taken from the catalog header. It is a C or -Python expression that has a free variable n; the expression evaluates +Python expression that has a free variable \var{n}; the expression evaluates to the index of the plural in the catalog. See the GNU gettext -documentation for the precise syntax to be used in .po files, and the +documentation for the precise syntax to be used in \file{.po} files and the formulas for a variety of languages. \versionadded{2.3} diff --git a/Doc/lib/libhmac.tex b/Doc/lib/libhmac.tex index 5ca24d1..5329cb5 100644 --- a/Doc/lib/libhmac.tex +++ b/Doc/lib/libhmac.tex @@ -15,7 +15,7 @@ This module implements the HMAC algorithm as described by \rfc{2104}. Return a new hmac object. If \var{msg} is present, the method call \code{update(\var{msg})} is made. \var{digestmod} is the digest constructor or module for the HMAC object to use. It defaults to - the \code{\refmodule{hashlib}.md5} constructor. \note{The md5 hash + the \function{\refmodule{hashlib}.md5} constructor. \note{The md5 hash has known weaknesses but remains the default for backwards compatibility. Choose a better one for your application.} \end{funcdesc} diff --git a/Doc/lib/libhotshot.tex b/Doc/lib/libhotshot.tex index ae089c2..0004e37 100644 --- a/Doc/lib/libhotshot.tex +++ b/Doc/lib/libhotshot.tex @@ -48,25 +48,25 @@ information). Profile objects have the following methods: -\begin{methoddesc}{addinfo}{key, value} +\begin{methoddesc}[Profile]{addinfo}{key, value} Add an arbitrary labelled value to the profile output. \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[Profile]{close}{} Close the logfile and terminate the profiler. \end{methoddesc} -\begin{methoddesc}{fileno}{} +\begin{methoddesc}[Profile]{fileno}{} Return the file descriptor of the profiler's log file. \end{methoddesc} -\begin{methoddesc}{run}{cmd} +\begin{methoddesc}[Profile]{run}{cmd} Profile an \function{exec()}-compatible string in the script environment. The globals from the \refmodule[main]{__main__} module are used as both the globals and locals for the script. \end{methoddesc} -\begin{methoddesc}{runcall}{func, *args, **keywords} +\begin{methoddesc}[Profile]{runcall}{func, *args, **keywords} Profile a single call of a callable. Additional positional and keyword arguments may be passed along; the result of the call is returned, and exceptions are @@ -75,16 +75,16 @@ disabled on the way out. \end{methoddesc} -\begin{methoddesc}{runctx}{cmd, globals, locals} +\begin{methoddesc}[Profile]{runctx}{cmd, globals, locals} Profile an \function{exec()}-compatible string in a specific environment. The string is compiled before profiling begins. \end{methoddesc} -\begin{methoddesc}{start}{} +\begin{methoddesc}[Profile]{start}{} Start the profiler. \end{methoddesc} -\begin{methoddesc}{stop}{} +\begin{methoddesc}[Profile]{stop}{} Stop the profiler. \end{methoddesc} diff --git a/Doc/lib/libhtmllib.tex b/Doc/lib/libhtmllib.tex index a84dd85..e51dfcb 100644 --- a/Doc/lib/libhtmllib.tex +++ b/Doc/lib/libhtmllib.tex @@ -96,11 +96,11 @@ error while parsing. In addition to tag methods, the \class{HTMLParser} class provides some additional methods and instance variables for use within tag methods. -\begin{memberdesc}{formatter} +\begin{memberdesc}[HTMLParser]{formatter} This is the formatter instance associated with the parser. \end{memberdesc} -\begin{memberdesc}{nofill} +\begin{memberdesc}[HTMLParser]{nofill} Boolean flag which should be true when whitespace should not be collapsed, or false when it should be. In general, this should only be true when character data is to be treated as ``preformatted'' text, @@ -109,7 +109,7 @@ affects the operation of \method{handle_data()} and \method{save_end()}. \end{memberdesc} -\begin{methoddesc}{anchor_bgn}{href, name, type} +\begin{methoddesc}[HTMLParser]{anchor_bgn}{href, name, type} This method is called at the start of an anchor region. The arguments correspond to the attributes of the \code{<A>} tag with the same names. The default implementation maintains a list of hyperlinks @@ -118,27 +118,27 @@ document. The list of hyperlinks is available as the data attribute \member{anchorlist}. \end{methoddesc} -\begin{methoddesc}{anchor_end}{} +\begin{methoddesc}[HTMLParser]{anchor_end}{} This method is called at the end of an anchor region. The default implementation adds a textual footnote marker using an index into the list of hyperlinks created by \method{anchor_bgn()}. \end{methoddesc} -\begin{methoddesc}{handle_image}{source, alt\optional{, ismap\optional{, +\begin{methoddesc}[HTMLParser]{handle_image}{source, alt\optional{, ismap\optional{, align\optional{, width\optional{, height}}}}} This method is called to handle images. The default implementation simply passes the \var{alt} value to the \method{handle_data()} method. \end{methoddesc} -\begin{methoddesc}{save_bgn}{} +\begin{methoddesc}[HTMLParser]{save_bgn}{} Begins saving character data in a buffer instead of sending it to the formatter object. Retrieve the stored data via \method{save_end()}. Use of the \method{save_bgn()} / \method{save_end()} pair may not be nested. \end{methoddesc} -\begin{methoddesc}{save_end}{} +\begin{methoddesc}[HTMLParser]{save_end}{} Ends buffering character data and returns all data saved since the preceding call to \method{save_bgn()}. If the \member{nofill} flag is false, whitespace is collapsed to single spaces. A call to this diff --git a/Doc/lib/libhtmlparser.tex b/Doc/lib/libhtmlparser.tex index 52f8409..5e99f27 100644 --- a/Doc/lib/libhtmlparser.tex +++ b/Doc/lib/libhtmlparser.tex @@ -75,14 +75,18 @@ This method is called to handle the start of a tag. It is intended to be overridden by a derived class; the base class implementation does nothing. -The \var{tag} argument is the name of the tag converted to -lower case. The \var{attrs} argument is a list of \code{(\var{name}, -\var{value})} pairs containing the attributes found inside the tag's -\code{<>} brackets. The \var{name} will be translated to lower case -and double quotes and backslashes in the \var{value} have been -interpreted. For instance, for the tag \code{<A -HREF="http://www.cwi.nl/">}, this method would be called as +The \var{tag} argument is the name of the tag converted to lower case. +The \var{attrs} argument is a list of \code{(\var{name}, \var{value})} +pairs containing the attributes found inside the tag's \code{<>} +brackets. The \var{name} will be translated to lower case, and quotes +in the \var{value} have been removed, and character and entity +references have been replaced. For instance, for the tag \code{<A + HREF="http://www.cwi.nl/">}, this method would be called as \samp{handle_starttag('a', [('href', 'http://www.cwi.nl/')])}. + +\versionchanged[All entity references from htmlentitydefs are now +replaced in the attribute values]{2.6} + \end{methoddesc} \begin{methoddesc}{handle_startendtag}{tag, attrs} diff --git a/Doc/lib/libhttplib.tex b/Doc/lib/libhttplib.tex index 557ee3d..5fd48c1 100644 --- a/Doc/lib/libhttplib.tex +++ b/Doc/lib/libhttplib.tex @@ -26,18 +26,27 @@ that use HTTP and HTTPS. The module provides the following classes: -\begin{classdesc}{HTTPConnection}{host\optional{, port}} +\begin{classdesc}{HTTPConnection}{host\optional{, port\optional{, + strict\optional{, timeout}}}} An \class{HTTPConnection} instance represents one transaction with an HTTP server. It should be instantiated passing it a host and optional port number. If no port number is passed, the port is extracted from the host string if it has the form \code{\var{host}:\var{port}}, else the default HTTP port (80) is -used. For example, the following calls all create instances that connect to +used. When True, the optional parameter \var{strict} +causes \code{BadStatusLine} to be raised if the status line can't be parsed +as a valid HTTP/1.0 or 1.1 status line. If the optional \var{timeout} +parameter is given, connection attempts will timeout after that many +seconds (if it is not given or \code{None}, the global default +timeout setting is used). + +For example, the following calls all create instances that connect to the server at the same host and port: \begin{verbatim} >>> h1 = httplib.HTTPConnection('www.cwi.nl') >>> h2 = httplib.HTTPConnection('www.cwi.nl:80') >>> h3 = httplib.HTTPConnection('www.cwi.nl', 80) +>>> h3 = httplib.HTTPConnection('www.cwi.nl', 80, timeout=10) \end{verbatim} \versionadded{2.0} \end{classdesc} @@ -300,7 +309,7 @@ Example: \code{httplib.responses[httplib.NOT_FOUND]} is \code{'Not Found'}. \class{HTTPConnection} instances have the following methods: -\begin{methoddesc}{request}{method, url\optional{, body\optional{, headers}}} +\begin{methoddesc}[HTTPConnection]{request}{method, url\optional{, body\optional{, headers}}} This will send a request to the server using the HTTP request method \var{method} and the selector \var{url}. If the \var{body} argument is present, it should be a string of data to send after the headers are finished. @@ -314,24 +323,24 @@ with the request. \versionchanged[\var{body} can be a file object]{2.6} \end{methoddesc} -\begin{methoddesc}{getresponse}{} +\begin{methoddesc}[HTTPConnection]{getresponse}{} Should be called after a request is sent to get the response from the server. Returns an \class{HTTPResponse} instance. \note{Note that you must have read the whole response before you can send a new request to the server.} \end{methoddesc} -\begin{methoddesc}{set_debuglevel}{level} +\begin{methoddesc}[HTTPConnection]{set_debuglevel}{level} Set the debugging level (the amount of debugging output printed). The default debug level is \code{0}, meaning no debugging output is printed. \end{methoddesc} -\begin{methoddesc}{connect}{} +\begin{methoddesc}[HTTPConnection]{connect}{} Connect to the server specified when the object was created. \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[HTTPConnection]{close}{} Close the connection to the server. \end{methoddesc} @@ -339,7 +348,7 @@ As an alternative to using the \method{request()} method described above, you can also send your request step by step, by using the four functions below. -\begin{methoddesc}{putrequest}{request, selector\optional{, +\begin{methoddesc}[HTTPConnection]{putrequest}{request, selector\optional{, skip\_host\optional{, skip_accept_encoding}}} This should be the first call after the connection to the server has been made. It sends a line to the server consisting of the @@ -351,18 +360,18 @@ with non-False values. \versionchanged[\var{skip_accept_encoding} argument added]{2.4} \end{methoddesc} -\begin{methoddesc}{putheader}{header, argument\optional{, ...}} +\begin{methoddesc}[HTTPConnection]{putheader}{header, argument\optional{, ...}} Send an \rfc{822}-style header to the server. It sends a line to the server consisting of the header, a colon and a space, and the first argument. If more arguments are given, continuation lines are sent, each consisting of a tab and an argument. \end{methoddesc} -\begin{methoddesc}{endheaders}{} +\begin{methoddesc}[HTTPConnection]{endheaders}{} Send a blank line to the server, signalling the end of the headers. \end{methoddesc} -\begin{methoddesc}{send}{data} +\begin{methoddesc}[HTTPConnection]{send}{data} Send data to the server. This should be used directly only after the \method{endheaders()} method has been called and before \method{getresponse()} is called. @@ -372,34 +381,34 @@ Send data to the server. This should be used directly only after the \class{HTTPResponse} instances have the following methods and attributes: -\begin{methoddesc}{read}{\optional{amt}} +\begin{methoddesc}[HTTPResponse]{read}{\optional{amt}} Reads and returns the response body, or up to the next \var{amt} bytes. \end{methoddesc} -\begin{methoddesc}{getheader}{name\optional{, default}} +\begin{methoddesc}[HTTPResponse]{getheader}{name\optional{, default}} Get the contents of the header \var{name}, or \var{default} if there is no matching header. \end{methoddesc} -\begin{methoddesc}{getheaders}{} +\begin{methoddesc}[HTTPResponse]{getheaders}{} Return a list of (header, value) tuples. \versionadded{2.4} \end{methoddesc} -\begin{datadesc}{msg} +\begin{memberdesc}[HTTPResponse]{msg} A \class{mimetools.Message} instance containing the response headers. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{version} +\begin{memberdesc}[HTTPResponse]{version} HTTP protocol version used by server. 10 for HTTP/1.0, 11 for HTTP/1.1. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{status} +\begin{memberdesc}[HTTPResponse]{status} Status code returned by server. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{reason} +\begin{memberdesc}[HTTPResponse]{reason} Reason phrase returned by server. -\end{datadesc} +\end{memberdesc} \subsection{Examples \label{httplib-examples}} diff --git a/Doc/lib/libimaplib.tex b/Doc/lib/libimaplib.tex index 7658bc9..e34caaa 100644 --- a/Doc/lib/libimaplib.tex +++ b/Doc/lib/libimaplib.tex @@ -153,11 +153,11 @@ can contain an asterisk to indicate an infinite upper bound An \class{IMAP4} instance has the following methods: -\begin{methoddesc}{append}{mailbox, flags, date_time, message} +\begin{methoddesc}[IMAP4]{append}{mailbox, flags, date_time, message} Append \var{message} to named mailbox. \end{methoddesc} -\begin{methoddesc}{authenticate}{mechanism, authobject} +\begin{methoddesc}[IMAP4]{authenticate}{mechanism, authobject} Authenticate command --- requires response processing. \var{mechanism} specifies which authentication mechanism is to be @@ -176,115 +176,115 @@ data = authobject(response) be sent instead. \end{methoddesc} -\begin{methoddesc}{check}{} +\begin{methoddesc}[IMAP4]{check}{} Checkpoint mailbox on server. \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[IMAP4]{close}{} Close currently selected mailbox. Deleted messages are removed from writable mailbox. This is the recommended command before \samp{LOGOUT}. \end{methoddesc} -\begin{methoddesc}{copy}{message_set, new_mailbox} +\begin{methoddesc}[IMAP4]{copy}{message_set, new_mailbox} Copy \var{message_set} messages onto end of \var{new_mailbox}. \end{methoddesc} -\begin{methoddesc}{create}{mailbox} +\begin{methoddesc}[IMAP4]{create}{mailbox} Create new mailbox named \var{mailbox}. \end{methoddesc} -\begin{methoddesc}{delete}{mailbox} +\begin{methoddesc}[IMAP4]{delete}{mailbox} Delete old mailbox named \var{mailbox}. \end{methoddesc} -\begin{methoddesc}{deleteacl}{mailbox, who} +\begin{methoddesc}[IMAP4]{deleteacl}{mailbox, who} Delete the ACLs (remove any rights) set for who on mailbox. \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}{expunge}{} +\begin{methoddesc}[IMAP4]{expunge}{} Permanently remove deleted items from selected mailbox. Generates an \samp{EXPUNGE} response for each deleted message. Returned data contains a list of \samp{EXPUNGE} message numbers in order received. \end{methoddesc} -\begin{methoddesc}{fetch}{message_set, message_parts} +\begin{methoddesc}[IMAP4]{fetch}{message_set, message_parts} Fetch (parts of) messages. \var{message_parts} should be a string of message part names enclosed within parentheses, eg: \samp{"(UID BODY[TEXT])"}. Returned data are tuples of message part envelope and data. \end{methoddesc} -\begin{methoddesc}{getacl}{mailbox} +\begin{methoddesc}[IMAP4]{getacl}{mailbox} Get the \samp{ACL}s for \var{mailbox}. The method is non-standard, but is supported by the \samp{Cyrus} server. \end{methoddesc} -\begin{methoddesc}{getannotation}{mailbox, entry, attribute} +\begin{methoddesc}[IMAP4]{getannotation}{mailbox, entry, attribute} Retrieve the specified \samp{ANNOTATION}s for \var{mailbox}. The method is non-standard, but is supported by the \samp{Cyrus} server. \versionadded{2.5} \end{methoddesc} -\begin{methoddesc}{getquota}{root} +\begin{methoddesc}[IMAP4]{getquota}{root} Get the \samp{quota} \var{root}'s resource usage and limits. This method is part of the IMAP4 QUOTA extension defined in rfc2087. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{getquotaroot}{mailbox} +\begin{methoddesc}[IMAP4]{getquotaroot}{mailbox} Get the list of \samp{quota} \samp{roots} for the named \var{mailbox}. This method is part of the IMAP4 QUOTA extension defined in rfc2087. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{list}{\optional{directory\optional{, pattern}}} +\begin{methoddesc}[IMAP4]{list}{\optional{directory\optional{, pattern}}} List mailbox names in \var{directory} matching \var{pattern}. \var{directory} defaults to the top-level mail folder, and \var{pattern} defaults to match anything. Returned data contains a list of \samp{LIST} responses. \end{methoddesc} -\begin{methoddesc}{login}{user, password} +\begin{methoddesc}[IMAP4]{login}{user, password} Identify the client using a plaintext password. The \var{password} will be quoted. \end{methoddesc} -\begin{methoddesc}{login_cram_md5}{user, password} +\begin{methoddesc}[IMAP4]{login_cram_md5}{user, password} Force use of \samp{CRAM-MD5} authentication when identifying the client to protect the password. Will only work if the server \samp{CAPABILITY} response includes the phrase \samp{AUTH=CRAM-MD5}. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{logout}{} +\begin{methoddesc}[IMAP4]{logout}{} Shutdown connection to server. Returns server \samp{BYE} response. \end{methoddesc} -\begin{methoddesc}{lsub}{\optional{directory\optional{, pattern}}} +\begin{methoddesc}[IMAP4]{lsub}{\optional{directory\optional{, pattern}}} List subscribed mailbox names in directory matching pattern. \var{directory} defaults to the top level directory and \var{pattern} defaults to match any mailbox. Returned data are tuples of message part envelope and data. \end{methoddesc} -\begin{methoddesc}{myrights}{mailbox} +\begin{methoddesc}[IMAP4]{myrights}{mailbox} Show my ACLs for a mailbox (i.e. the rights that I have on mailbox). \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}{namespace}{} +\begin{methoddesc}[IMAP4]{namespace}{} Returns IMAP namespaces as defined in RFC2342. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{noop}{} +\begin{methoddesc}[IMAP4]{noop}{} Send \samp{NOOP} to server. \end{methoddesc} -\begin{methoddesc}{open}{host, port} +\begin{methoddesc}[IMAP4]{open}{host, port} Opens socket to \var{port} at \var{host}. The connection objects established by this method will be used in the \code{read}, \code{readline}, \code{send}, and @@ -292,42 +292,42 @@ data = authobject(response) You may override this method. \end{methoddesc} -\begin{methoddesc}{partial}{message_num, message_part, start, length} +\begin{methoddesc}[IMAP4]{partial}{message_num, message_part, start, length} Fetch truncated part of a message. Returned data is a tuple of message part envelope and data. \end{methoddesc} -\begin{methoddesc}{proxyauth}{user} +\begin{methoddesc}[IMAP4]{proxyauth}{user} Assume authentication as \var{user}. Allows an authorised administrator to proxy into any user's mailbox. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{read}{size} +\begin{methoddesc}[IMAP4]{read}{size} Reads \var{size} bytes from the remote server. You may override this method. \end{methoddesc} -\begin{methoddesc}{readline}{} +\begin{methoddesc}[IMAP4]{readline}{} Reads one line from the remote server. You may override this method. \end{methoddesc} -\begin{methoddesc}{recent}{} +\begin{methoddesc}[IMAP4]{recent}{} Prompt server for an update. Returned data is \code{None} if no new messages, else value of \samp{RECENT} response. \end{methoddesc} -\begin{methoddesc}{rename}{oldmailbox, newmailbox} +\begin{methoddesc}[IMAP4]{rename}{oldmailbox, newmailbox} Rename mailbox named \var{oldmailbox} to \var{newmailbox}. \end{methoddesc} -\begin{methoddesc}{response}{code} +\begin{methoddesc}[IMAP4]{response}{code} Return data for response \var{code} if received, or \code{None}. Returns the given code, instead of the usual type. \end{methoddesc} -\begin{methoddesc}{search}{charset, criterion\optional{, ...}} +\begin{methoddesc}[IMAP4]{search}{charset, criterion\optional{, ...}} Search mailbox for matching messages. \var{charset} may be \code{None}, in which case no \samp{CHARSET} will be specified in the request to the server. The IMAP protocol requires that at least one @@ -345,45 +345,45 @@ typ, msgnums = M.search(None, '(FROM "LDJ")') \end{verbatim} \end{methoddesc} -\begin{methoddesc}{select}{\optional{mailbox\optional{, readonly}}} +\begin{methoddesc}[IMAP4]{select}{\optional{mailbox\optional{, readonly}}} Select a mailbox. Returned data is the count of messages in \var{mailbox} (\samp{EXISTS} response). The default \var{mailbox} is \code{'INBOX'}. If the \var{readonly} flag is set, modifications to the mailbox are not allowed. \end{methoddesc} -\begin{methoddesc}{send}{data} +\begin{methoddesc}[IMAP4]{send}{data} Sends \code{data} to the remote server. You may override this method. \end{methoddesc} -\begin{methoddesc}{setacl}{mailbox, who, what} +\begin{methoddesc}[IMAP4]{setacl}{mailbox, who, what} Set an \samp{ACL} for \var{mailbox}. The method is non-standard, but is supported by the \samp{Cyrus} server. \end{methoddesc} -\begin{methoddesc}{setannotation}{mailbox, entry, attribute\optional{, ...}} +\begin{methoddesc}[IMAP4]{setannotation}{mailbox, entry, attribute\optional{, ...}} Set \samp{ANNOTATION}s for \var{mailbox}. The method is non-standard, but is supported by the \samp{Cyrus} server. \versionadded{2.5} \end{methoddesc} -\begin{methoddesc}{setquota}{root, limits} +\begin{methoddesc}[IMAP4]{setquota}{root, limits} Set the \samp{quota} \var{root}'s resource \var{limits}. This method is part of the IMAP4 QUOTA extension defined in rfc2087. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{shutdown}{} +\begin{methoddesc}[IMAP4]{shutdown}{} Close connection established in \code{open}. You may override this method. \end{methoddesc} -\begin{methoddesc}{socket}{} +\begin{methoddesc}[IMAP4]{socket}{} Returns socket instance used to connect to server. \end{methoddesc} -\begin{methoddesc}{sort}{sort_criteria, charset, search_criterion\optional{, ...}} +\begin{methoddesc}[IMAP4]{sort}{sort_criteria, charset, search_criterion\optional{, ...}} The \code{sort} command is a variant of \code{search} with sorting semantics for the results. Returned data contains a space separated list of matching message numbers. @@ -402,11 +402,11 @@ typ, msgnums = M.search(None, '(FROM "LDJ")') This is an \samp{IMAP4rev1} extension command. \end{methoddesc} -\begin{methoddesc}{status}{mailbox, names} +\begin{methoddesc}[IMAP4]{status}{mailbox, names} Request named status conditions for \var{mailbox}. \end{methoddesc} -\begin{methoddesc}{store}{message_set, command, flag_list} +\begin{methoddesc}[IMAP4]{store}{message_set, command, flag_list} Alters flag dispositions for messages in mailbox. \var{command} is specified by section 6.4.6 of \rfc{2060} as being one of "FLAGS", "+FLAGS", or "-FLAGS", optionally with a suffix of ".SILENT". @@ -421,11 +421,11 @@ M.expunge() \end{verbatim} \end{methoddesc} -\begin{methoddesc}{subscribe}{mailbox} +\begin{methoddesc}[IMAP4]{subscribe}{mailbox} Subscribe to new mailbox. \end{methoddesc} -\begin{methoddesc}{thread}{threading_algorithm, charset, +\begin{methoddesc}[IMAP4]{thread}{threading_algorithm, charset, search_criterion\optional{, ...}} The \code{thread} command is a variant of \code{search} with threading semantics for the results. Returned data contains a space @@ -448,18 +448,18 @@ M.expunge() This is an \samp{IMAP4rev1} extension command. \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}{uid}{command, arg\optional{, ...}} +\begin{methoddesc}[IMAP4]{uid}{command, arg\optional{, ...}} Execute command args with messages identified by UID, rather than message number. Returns response appropriate to command. At least one argument must be supplied; if none are provided, the server will return an error and an exception will be raised. \end{methoddesc} -\begin{methoddesc}{unsubscribe}{mailbox} +\begin{methoddesc}[IMAP4]{unsubscribe}{mailbox} Unsubscribe from old mailbox. \end{methoddesc} -\begin{methoddesc}{xatom}{name\optional{, arg\optional{, ...}}} +\begin{methoddesc}[IMAP4]{xatom}{name\optional{, arg\optional{, ...}}} Allow simple extension commands notified by server in \samp{CAPABILITY} response. \end{methoddesc} @@ -467,7 +467,7 @@ M.expunge() Instances of \class{IMAP4_SSL} have just one additional method: -\begin{methoddesc}{ssl}{} +\begin{methoddesc}[IMAP4_SSL]{ssl}{} Returns SSLObject instance used for the secure connection with the server. \end{methoddesc} @@ -475,12 +475,12 @@ Instances of \class{IMAP4_SSL} have just one additional method: The following attributes are defined on instances of \class{IMAP4}: -\begin{memberdesc}{PROTOCOL_VERSION} +\begin{memberdesc}[IMAP4]{PROTOCOL_VERSION} The most recent supported protocol in the \samp{CAPABILITY} response from the server. \end{memberdesc} -\begin{memberdesc}{debug} +\begin{memberdesc}[IMAP4]{debug} Integer value to control debugging output. The initialize value is taken from the module variable \code{Debug}. Values greater than three trace each command. diff --git a/Doc/lib/liblogging.tex b/Doc/lib/liblogging.tex index f9c1d72..dabab53 100644 --- a/Doc/lib/liblogging.tex +++ b/Doc/lib/liblogging.tex @@ -364,13 +364,13 @@ Loggers have the following attributes and methods. Note that Loggers are never instantiated directly, but always through the module-level function \function{logging.getLogger(name)}. -\begin{datadesc}{propagate} +\begin{memberdesc}[Logger]{propagate} If this evaluates to false, logging messages are not passed by this logger or by child loggers to higher level (ancestor) loggers. The constructor sets this attribute to 1. -\end{datadesc} +\end{memberdesc} -\begin{methoddesc}{setLevel}{lvl} +\begin{methoddesc}[Logger]{setLevel}{lvl} Sets the threshold for this logger to \var{lvl}. Logging messages which are less severe than \var{lvl} will be ignored. When a logger is created, the level is set to \constant{NOTSET} (which causes all messages @@ -393,21 +393,21 @@ messages will be processed. Otherwise, the root's level will be used as the effective level. \end{methoddesc} -\begin{methoddesc}{isEnabledFor}{lvl} +\begin{methoddesc}[Logger]{isEnabledFor}{lvl} Indicates if a message of severity \var{lvl} would be processed by this logger. This method checks first the module-level level set by \function{logging.disable(lvl)} and then the logger's effective level as determined by \method{getEffectiveLevel()}. \end{methoddesc} -\begin{methoddesc}{getEffectiveLevel}{} +\begin{methoddesc}[Logger]{getEffectiveLevel}{} Indicates the effective level for this logger. If a value other than \constant{NOTSET} has been set using \method{setLevel()}, it is returned. Otherwise, the hierarchy is traversed towards the root until a value other than \constant{NOTSET} is found, and that value is returned. \end{methoddesc} -\begin{methoddesc}{debug}{msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{debug}{msg\optional{, *args\optional{, **kwargs}}} Logs a message with level \constant{DEBUG} on this logger. The \var{msg} is the message format string, and the \var{args} are the arguments which are merged into \var{msg} using the string formatting @@ -462,67 +462,67 @@ above example). In such circumstances, it is likely that specialized \end{methoddesc} -\begin{methoddesc}{info}{msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{info}{msg\optional{, *args\optional{, **kwargs}}} Logs a message with level \constant{INFO} on this logger. The arguments are interpreted as for \method{debug()}. \end{methoddesc} -\begin{methoddesc}{warning}{msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{warning}{msg\optional{, *args\optional{, **kwargs}}} Logs a message with level \constant{WARNING} on this logger. The arguments are interpreted as for \method{debug()}. \end{methoddesc} -\begin{methoddesc}{error}{msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{error}{msg\optional{, *args\optional{, **kwargs}}} Logs a message with level \constant{ERROR} on this logger. The arguments are interpreted as for \method{debug()}. \end{methoddesc} -\begin{methoddesc}{critical}{msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{critical}{msg\optional{, *args\optional{, **kwargs}}} Logs a message with level \constant{CRITICAL} on this logger. The arguments are interpreted as for \method{debug()}. \end{methoddesc} -\begin{methoddesc}{log}{lvl, msg\optional{, *args\optional{, **kwargs}}} +\begin{methoddesc}[Logger]{log}{lvl, msg\optional{, *args\optional{, **kwargs}}} Logs a message with integer level \var{lvl} on this logger. The other arguments are interpreted as for \method{debug()}. \end{methoddesc} -\begin{methoddesc}{exception}{msg\optional{, *args}} +\begin{methoddesc}[Logger]{exception}{msg\optional{, *args}} Logs a message with level \constant{ERROR} on this logger. The arguments are interpreted as for \method{debug()}. Exception info is added to the logging message. This method should only be called from an exception handler. \end{methoddesc} -\begin{methoddesc}{addFilter}{filt} +\begin{methoddesc}[Logger]{addFilter}{filt} Adds the specified filter \var{filt} to this logger. \end{methoddesc} -\begin{methoddesc}{removeFilter}{filt} +\begin{methoddesc}[Logger]{removeFilter}{filt} Removes the specified filter \var{filt} from this logger. \end{methoddesc} -\begin{methoddesc}{filter}{record} +\begin{methoddesc}[Logger]{filter}{record} Applies this logger's filters to the record and returns a true value if the record is to be processed. \end{methoddesc} -\begin{methoddesc}{addHandler}{hdlr} +\begin{methoddesc}[Logger]{addHandler}{hdlr} Adds the specified handler \var{hdlr} to this logger. \end{methoddesc} -\begin{methoddesc}{removeHandler}{hdlr} +\begin{methoddesc}[Logger]{removeHandler}{hdlr} Removes the specified handler \var{hdlr} from this logger. \end{methoddesc} -\begin{methoddesc}{findCaller}{} +\begin{methoddesc}[Logger]{findCaller}{} Finds the caller's source filename and line number. Returns the filename, line number and function name as a 3-element tuple. \versionchanged[The function name was added. In earlier versions, the filename and line number were returned as a 2-element tuple.]{2.5} \end{methoddesc} -\begin{methoddesc}{handle}{record} +\begin{methoddesc}[Logger]{handle}{record} Handles a record by passing it to all handlers associated with this logger and its ancestors (until a false value of \var{propagate} is found). This method is used for unpickled records received from a socket, as well @@ -530,8 +530,8 @@ as those created locally. Logger-level filtering is applied using \method{filter()}. \end{methoddesc} -\begin{methoddesc}{makeRecord}{name, lvl, fn, lno, msg, args, exc_info - \optional{, func, extra}} +\begin{methoddesc}[Logger]{makeRecord}{name, lvl, fn, lno, msg, args, exc_info + \optional{, func, extra}} This is a factory method which can be overridden in subclasses to create specialized \class{LogRecord} instances. \versionchanged[\var{func} and \var{extra} were added]{2.5} @@ -875,66 +875,66 @@ Handlers have the following attributes and methods. Note that base for more useful subclasses. However, the \method{__init__()} method in subclasses needs to call \method{Handler.__init__()}. -\begin{methoddesc}{__init__}{level=\constant{NOTSET}} +\begin{methoddesc}[Handler]{__init__}{level=\constant{NOTSET}} Initializes the \class{Handler} instance by setting its level, setting the list of filters to the empty list and creating a lock (using \method{createLock()}) for serializing access to an I/O mechanism. \end{methoddesc} -\begin{methoddesc}{createLock}{} +\begin{methoddesc}[Handler]{createLock}{} Initializes a thread lock which can be used to serialize access to underlying I/O functionality which may not be threadsafe. \end{methoddesc} -\begin{methoddesc}{acquire}{} +\begin{methoddesc}[Handler]{acquire}{} Acquires the thread lock created with \method{createLock()}. \end{methoddesc} -\begin{methoddesc}{release}{} +\begin{methoddesc}[Handler]{release}{} Releases the thread lock acquired with \method{acquire()}. \end{methoddesc} -\begin{methoddesc}{setLevel}{lvl} +\begin{methoddesc}[Handler]{setLevel}{lvl} Sets the threshold for this handler to \var{lvl}. Logging messages which are less severe than \var{lvl} will be ignored. When a handler is created, the level is set to \constant{NOTSET} (which causes all messages to be processed). \end{methoddesc} -\begin{methoddesc}{setFormatter}{form} +\begin{methoddesc}[Handler]{setFormatter}{form} Sets the \class{Formatter} for this handler to \var{form}. \end{methoddesc} -\begin{methoddesc}{addFilter}{filt} +\begin{methoddesc}[Handler]{addFilter}{filt} Adds the specified filter \var{filt} to this handler. \end{methoddesc} -\begin{methoddesc}{removeFilter}{filt} +\begin{methoddesc}[Handler]{removeFilter}{filt} Removes the specified filter \var{filt} from this handler. \end{methoddesc} -\begin{methoddesc}{filter}{record} +\begin{methoddesc}[Handler]{filter}{record} Applies this handler's filters to the record and returns a true value if the record is to be processed. \end{methoddesc} -\begin{methoddesc}{flush}{} +\begin{methoddesc}[Handler]{flush}{} Ensure all logging output has been flushed. This version does nothing and is intended to be implemented by subclasses. \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[Handler]{close}{} Tidy up any resources used by the handler. This version does nothing and is intended to be implemented by subclasses. \end{methoddesc} -\begin{methoddesc}{handle}{record} +\begin{methoddesc}[Handler]{handle}{record} Conditionally emits the specified logging record, depending on filters which may have been added to the handler. Wraps the actual emission of the record with acquisition/release of the I/O thread lock. \end{methoddesc} -\begin{methoddesc}{handleError}{record} +\begin{methoddesc}[Handler]{handleError}{record} This method should be called from handlers when an exception is encountered during an \method{emit()} call. By default it does nothing, which means that exceptions get silently ignored. This is what is @@ -945,12 +945,12 @@ handler if you wish. The specified record is the one which was being processed when the exception occurred. \end{methoddesc} -\begin{methoddesc}{format}{record} +\begin{methoddesc}[Handler]{format}{record} Do formatting for a record - if a formatter is set, use it. Otherwise, use the default formatter for the module. \end{methoddesc} -\begin{methoddesc}{emit}{record} +\begin{methoddesc}[Handler]{emit}{record} Do whatever it takes to actually log the specified logging record. This version is intended to be implemented by subclasses and so raises a \exception{NotImplementedError}. @@ -1138,9 +1138,6 @@ and \var{port}. Closes the socket. \end{methoddesc} -\begin{methoddesc}{handleError}{} -\end{methoddesc} - \begin{methoddesc}{emit}{} Pickles the record's attribute dictionary and writes it to the socket in binary format. If there is an error with the socket, silently drops the diff --git a/Doc/lib/libmailbox.tex b/Doc/lib/libmailbox.tex index 24765c8..c3e7ffd 100644 --- a/Doc/lib/libmailbox.tex +++ b/Doc/lib/libmailbox.tex @@ -1203,7 +1203,6 @@ correspond: \end{tableii} \subsection{Exceptions} -\label{mailbox-deprecated} The following exception classes are defined in the \module{mailbox} module: @@ -1247,7 +1246,7 @@ in preference to them. Older mailbox objects support only iteration and provide a single public method: -\begin{methoddesc}{next}{} +\begin{methoddesc}[oldmailbox]{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 @@ -1286,13 +1285,13 @@ For maximum portability, messages in a \UNIX-style mailbox are separated by any line that begins exactly with the string \code{'From '} (note the trailing space) if preceded by exactly two newlines. Because of the wide-range of variations in practice, nothing else on -the From_ line should be considered. However, the current +the \samp{From_} line should be considered. However, the current implementation doesn't check for the leading two newlines. This is usually fine for most applications. The \class{UnixMailbox} class implements a more strict version of -From_ line checking, using a regular expression that usually correctly -matched From_ delimiters. It considers delimiter line to be separated +\samp{From_} line checking, using a regular expression that usually correctly +matched \samp{From_} delimiters. It considers delimiter line to be separated by \samp{From \var{name} \var{time}} lines. For maximum portability, use the \class{PortableUnixMailbox} class instead. This class is identical to \class{UnixMailbox} except that individual messages are diff --git a/Doc/lib/libmimetools.tex b/Doc/lib/libmimetools.tex index 5e800af..3e4bd4b 100644 --- a/Doc/lib/libmimetools.tex +++ b/Doc/lib/libmimetools.tex @@ -76,7 +76,7 @@ open file \var{output}. The block size is currently fixed at 8192. The \class{Message} class defines the following methods in addition to the \class{rfc822.Message} methods: -\begin{methoddesc}{getplist}{} +\begin{methoddesc}[Message]{getplist}{} Return the parameter list of the \mailheader{Content-Type} header. This is a list of strings. For parameters of the form \samp{\var{key}=\var{value}}, \var{key} is converted to lower case but @@ -86,34 +86,34 @@ This is a list of strings. For parameters of the form 'spam=2', 'Spam']}. \end{methoddesc} -\begin{methoddesc}{getparam}{name} +\begin{methoddesc}[Message]{getparam}{name} Return the \var{value} of the first parameter (as returned by \method{getplist()}) of the form \samp{\var{name}=\var{value}} for the given \var{name}. If \var{value} is surrounded by quotes of the form `\code{<}...\code{>}' or `\code{"}...\code{"}', these are removed. \end{methoddesc} -\begin{methoddesc}{getencoding}{} +\begin{methoddesc}[Message]{getencoding}{} Return the encoding specified in the \mailheader{Content-Transfer-Encoding} message header. If no such header exists, return \code{'7bit'}. The encoding is converted to lower case. \end{methoddesc} -\begin{methoddesc}{gettype}{} +\begin{methoddesc}[Message]{gettype}{} Return the message type (of the form \samp{\var{type}/\var{subtype}}) as specified in the \mailheader{Content-Type} header. If no such header exists, return \code{'text/plain'}. The type is converted to lower case. \end{methoddesc} -\begin{methoddesc}{getmaintype}{} +\begin{methoddesc}[Message]{getmaintype}{} Return the main type as specified in the \mailheader{Content-Type} header. If no such header exists, return \code{'text'}. The main type is converted to lower case. \end{methoddesc} -\begin{methoddesc}{getsubtype}{} +\begin{methoddesc}[Message]{getsubtype}{} Return the subtype as specified in the \mailheader{Content-Type} header. If no such header exists, return \code{'plain'}. The subtype is converted to lower case. diff --git a/Doc/lib/libmimetypes.tex b/Doc/lib/libmimetypes.tex index 6c46d6f..af99f08 100644 --- a/Doc/lib/libmimetypes.tex +++ b/Doc/lib/libmimetypes.tex @@ -178,49 +178,49 @@ An example usage of the module: \class{MimeTypes} instances provide an interface which is very like that of the \refmodule{mimetypes} module. -\begin{datadesc}{suffix_map} +\begin{memberdesc}[MimeTypes]{suffix_map} Dictionary mapping suffixes to suffixes. This is used to allow recognition of encoded files for which the encoding and the type are indicated by the same extension. For example, the \file{.tgz} extension is mapped to \file{.tar.gz} to allow the encoding and type to be recognized separately. This is initially a copy of the global \code{suffix_map} defined in the module. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{encodings_map} +\begin{memberdesc}[MimeTypes]{encodings_map} Dictionary mapping filename extensions to encoding types. This is initially a copy of the global \code{encodings_map} defined in the module. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{types_map} +\begin{memberdesc}[MimeTypes]{types_map} Dictionary mapping filename extensions to MIME types. This is initially a copy of the global \code{types_map} defined in the module. -\end{datadesc} +\end{memberdesc} -\begin{datadesc}{common_types} +\begin{memberdesc}[MimeTypes]{common_types} Dictionary mapping filename extensions to non-standard, but commonly found MIME types. This is initially a copy of the global \code{common_types} defined in the module. -\end{datadesc} +\end{memberdesc} -\begin{methoddesc}{guess_extension}{type\optional{, strict}} +\begin{methoddesc}[MimeTypes]{guess_extension}{type\optional{, strict}} Similar to the \function{guess_extension()} function, using the tables stored as part of the object. \end{methoddesc} -\begin{methoddesc}{guess_type}{url\optional{, strict}} +\begin{methoddesc}[MimeTypes]{guess_type}{url\optional{, strict}} Similar to the \function{guess_type()} function, using the tables stored as part of the object. \end{methoddesc} -\begin{methoddesc}{read}{path} +\begin{methoddesc}[MimeTypes]{read}{path} Load MIME information from a file named \var{path}. This uses \method{readfp()} to parse the file. \end{methoddesc} -\begin{methoddesc}{readfp}{file} +\begin{methoddesc}[MimeTypes]{readfp}{file} Load MIME type information from an open file. The file must have the format of the standard \file{mime.types} files. \end{methoddesc} diff --git a/Doc/lib/libmimewriter.tex b/Doc/lib/libmimewriter.tex index a9cc09f..74bd9bb 100644 --- a/Doc/lib/libmimewriter.tex +++ b/Doc/lib/libmimewriter.tex @@ -31,7 +31,7 @@ writing. Note that a \class{StringIO} object could also be used. \class{MimeWriter} instances have the following methods: -\begin{methoddesc}{addheader}{key, value\optional{, prefix}} +\begin{methoddesc}[MimeWriter]{addheader}{key, value\optional{, prefix}} Add a header line to the MIME message. The \var{key} is the name of the header, where the \var{value} obviously provides the value of the header. The optional argument \var{prefix} determines where the header @@ -39,14 +39,14 @@ is inserted; \samp{0} means append at the end, \samp{1} is insert at the start. The default is to append. \end{methoddesc} -\begin{methoddesc}{flushheaders}{} +\begin{methoddesc}[MimeWriter]{flushheaders}{} Causes all headers accumulated so far to be written out (and forgotten). This is useful if you don't need a body part at all, e.g.\ for a subpart of type \mimetype{message/rfc822} that's (mis)used to store some header-like information. \end{methoddesc} -\begin{methoddesc}{startbody}{ctype\optional{, plist\optional{, prefix}}} +\begin{methoddesc}[MimeWriter]{startbody}{ctype\optional{, plist\optional{, prefix}}} Returns a file-like object which can be used to write to the body of the message. The content-type is set to the provided \var{ctype}, and the optional parameter \var{plist} provides @@ -55,8 +55,8 @@ functions as in \method{addheader()} except that the default is to insert at the start. \end{methoddesc} -\begin{methoddesc}{startmultipartbody}{subtype\optional{, - boundary\optional{, plist\optional{, prefix}}}} +\begin{methoddesc}[MimeWriter]{startmultipartbody}{subtype\optional{, + boundary\optional{, plist\optional{, prefix}}}} Returns a file-like object which can be used to write to the body of the message. Additionally, this method initializes the multi-part code, where \var{subtype} provides the multipart subtype, @@ -66,7 +66,7 @@ multi-part code, where \var{subtype} provides the multipart subtype, created using \method{nextpart()}. \end{methoddesc} -\begin{methoddesc}{nextpart}{} +\begin{methoddesc}[MimeWriter]{nextpart}{} Returns a new instance of \class{MimeWriter} which represents an individual part in a multipart message. This may be used to write the part as well as used for creating recursively complex multipart @@ -74,7 +74,7 @@ messages. The message must first be initialized with \method{startmultipartbody()} before using \method{nextpart()}. \end{methoddesc} -\begin{methoddesc}{lastpart}{} +\begin{methoddesc}[MimeWriter]{lastpart}{} This is used to designate the last part of a multipart message, and should \emph{always} be used when writing multipart messages. \end{methoddesc} diff --git a/Doc/lib/libmmap.tex b/Doc/lib/libmmap.tex index 3763d4f..345aeea 100644 --- a/Doc/lib/libmmap.tex +++ b/Doc/lib/libmmap.tex @@ -89,18 +89,18 @@ the underlying file. \versionchanged[To map anonymous memory, Memory-mapped file objects support the following methods: -\begin{methoddesc}{close}{} +\begin{methoddesc}[mmap]{close}{} Close the file. Subsequent calls to other methods of the object will result in an exception being raised. \end{methoddesc} -\begin{methoddesc}{find}{string\optional{, start}} +\begin{methoddesc}[mmap]{find}{string\optional{, start}} Returns the lowest index in the object where the substring \var{string} is found. Returns \code{-1} on failure. \var{start} is the index at which the search begins, and defaults to zero. \end{methoddesc} -\begin{methoddesc}{flush}{\optional{offset, size}} +\begin{methoddesc}[mmap]{flush}{\optional{offset, size}} Flushes changes made to the in-memory copy of a file back to disk. Without use of this call there is no guarantee that changes are written back before the object is destroyed. If \var{offset} and @@ -109,36 +109,36 @@ Memory-mapped file objects support the following methods: is flushed. \end{methoddesc} -\begin{methoddesc}{move}{\var{dest}, \var{src}, \var{count}} +\begin{methoddesc}[mmap]{move}{\var{dest}, \var{src}, \var{count}} Copy the \var{count} bytes starting at offset \var{src} to the destination index \var{dest}. If the mmap was created with \constant{ACCESS_READ}, then calls to move will throw a \exception{TypeError} exception. \end{methoddesc} -\begin{methoddesc}{read}{\var{num}} +\begin{methoddesc}[mmap]{read}{\var{num}} Return a string containing up to \var{num} bytes starting from the current file position; the file position is updated to point after the bytes that were returned. \end{methoddesc} -\begin{methoddesc}{read_byte}{} +\begin{methoddesc}[mmap]{read_byte}{} Returns a string of length 1 containing the character at the current file position, and advances the file position by 1. \end{methoddesc} -\begin{methoddesc}{readline}{} +\begin{methoddesc}[mmap]{readline}{} Returns a single line, starting at the current file position and up to the next newline. \end{methoddesc} -\begin{methoddesc}{resize}{\var{newsize}} +\begin{methoddesc}[mmap]{resize}{\var{newsize}} Resizes the map and the underlying file, if any. If the mmap was created with \constant{ACCESS_READ} or \constant{ACCESS_COPY}, resizing the map will throw a \exception{TypeError} exception. \end{methoddesc} -\begin{methoddesc}{seek}{pos\optional{, whence}} +\begin{methoddesc}[mmap]{seek}{pos\optional{, whence}} Set the file's current position. \var{whence} argument is optional and defaults to \code{os.SEEK_SET} or \code{0} (absolute file positioning); other values are \code{os.SEEK_CUR} or \code{1} (seek @@ -146,16 +146,16 @@ Memory-mapped file objects support the following methods: (seek relative to the file's end). \end{methoddesc} -\begin{methoddesc}{size}{} +\begin{methoddesc}[mmap]{size}{} Return the length of the file, which can be larger than the size of the memory-mapped area. \end{methoddesc} -\begin{methoddesc}{tell}{} +\begin{methoddesc}[mmap]{tell}{} Returns the current position of the file pointer. \end{methoddesc} -\begin{methoddesc}{write}{\var{string}} +\begin{methoddesc}[mmap]{write}{\var{string}} Write the bytes in \var{string} into memory at the current position of the file pointer; the file position is updated to point after the bytes that were written. If the mmap was created with @@ -163,7 +163,7 @@ Memory-mapped file objects support the following methods: \exception{TypeError} exception. \end{methoddesc} -\begin{methoddesc}{write_byte}{\var{byte}} +\begin{methoddesc}[mmap]{write_byte}{\var{byte}} Write the single-character string \var{byte} into memory at the current position of the file pointer; the file position is advanced by \code{1}. If the mmap was created with \constant{ACCESS_READ}, diff --git a/Doc/lib/libmsilib.tex b/Doc/lib/libmsilib.tex index 13d5556..075103a 100644 --- a/Doc/lib/libmsilib.tex +++ b/Doc/lib/libmsilib.tex @@ -121,17 +121,17 @@ MSI routines, and standard table structures. \subsection{Database Objects\label{database-objects}} -\begin{methoddesc}{OpenView}{sql} +\begin{methoddesc}[Database]{OpenView}{sql} Return a view object, by calling \cfunction{MSIDatabaseOpenView}. \var{sql} is the SQL statement to execute. \end{methoddesc} -\begin{methoddesc}{Commit}{} +\begin{methoddesc}[Database]{Commit}{} Commit the changes pending in the current transaction, by calling \cfunction{MSIDatabaseCommit}. \end{methoddesc} -\begin{methoddesc}{GetSummaryInformation}{count} +\begin{methoddesc}[Database]{GetSummaryInformation}{count} Return a new summary information object, by calling \cfunction{MsiGetSummaryInformation}. \var{count} is the maximum number of updated values. @@ -145,24 +145,24 @@ MSI routines, and standard table structures. \subsection{View Objects\label{view-objects}} -\begin{methoddesc}{Execute}{\optional{params=None}} +\begin{methoddesc}[View]{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} +\begin{methoddesc}[View]{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}{} +\begin{methoddesc}[View]{Fetch}{} Return a result record of the query, through calling \cfunction{MsiViewFetch}. \end{methoddesc} -\begin{methoddesc}{Modify}{kind, data} +\begin{methoddesc}[View]{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}, @@ -174,7 +174,7 @@ MSI routines, and standard table structures. \var{data} must be a record describing the new data. \end{methoddesc} -\begin{methoddesc}{Close}{} +\begin{methoddesc}[View]{Close}{} Close the view, through \cfunction{MsiViewClose}. \end{methoddesc} @@ -188,7 +188,7 @@ MSI routines, and standard table structures. \subsection{Summary Information Objects\label{summary-objects}} -\begin{methoddesc}{GetProperty}{field} +\begin{methoddesc}[SummaryInformation]{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 @@ -200,19 +200,19 @@ MSI routines, and standard table structures. \code{PID_APPNAME}, or \code{PID_SECURITY}. \end{methoddesc} -\begin{methoddesc}{GetPropertyCount}{} +\begin{methoddesc}[SummaryInformation]{GetPropertyCount}{} Return the number of summary properties, through \cfunction{MsiSummaryInfoGetPropertyCount}. \end{methoddesc} -\begin{methoddesc}{SetProperty}{field, value} +\begin{methoddesc}[SummaryInformation]{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}{} +\begin{methoddesc}[SummaryInformation]{Persist}{} Write the modified properties to the summary information stream, using \cfunction{MsiSummaryInfoPersist}. \end{methoddesc} @@ -226,27 +226,27 @@ MSI routines, and standard table structures. \subsection{Record Objects\label{record-objects}} -\begin{methoddesc}{GetFieldCount}{} +\begin{methoddesc}[Record]{GetFieldCount}{} Return the number of fields of the record, through \cfunction{MsiRecordGetFieldCount}. \end{methoddesc} -\begin{methoddesc}{SetString}{field, value} +\begin{methoddesc}[Record]{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} +\begin{methoddesc}[Record]{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} +\begin{methoddesc}[Record]{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}{} +\begin{methoddesc}[Record]{ClearData}{} Set all fields of the record to 0, through \cfunction{MsiRecordClearData}. \end{methoddesc} @@ -274,7 +274,7 @@ the string inside the exception will contain more detail. \var{name} is the name of the CAB file in the MSI file. \end{classdesc} -\begin{methoddesc}[CAB]{append}{full, logical} +\begin{methoddesc}[CAB]{append}{full, file, 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. @@ -283,7 +283,7 @@ the string inside the exception will contain more detail. new name of the file inside the CAB file. \end{methoddesc} -\begin{methoddesc}[CAB]{append}{database} +\begin{methoddesc}[CAB]{commit}{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. diff --git a/Doc/lib/libmultifile.tex b/Doc/lib/libmultifile.tex index 4348327..f3f0af7 100644 --- a/Doc/lib/libmultifile.tex +++ b/Doc/lib/libmultifile.tex @@ -48,7 +48,7 @@ own pattern for section-divider and end-marker lines. A \class{MultiFile} instance has the following methods: -\begin{methoddesc}{readline}{str} +\begin{methoddesc}[MultiFile]{readline}{str} Read a line. If the line is data (not a section-divider or end-marker or real EOF) return it. If the line matches the most-recently-stacked boundary, return \code{''} and set \code{self.last} to 1 or 0 according as @@ -58,33 +58,33 @@ underlying stream object, the method raises \exception{Error} unless all boundaries have been popped. \end{methoddesc} -\begin{methoddesc}{readlines}{str} +\begin{methoddesc}[MultiFile]{readlines}{str} Return all lines remaining in this part as a list of strings. \end{methoddesc} -\begin{methoddesc}{read}{} +\begin{methoddesc}[MultiFile]{read}{} Read all lines, up to the next section. Return them as a single (multiline) string. Note that this doesn't take a size argument! \end{methoddesc} -\begin{methoddesc}{seek}{pos\optional{, whence}} +\begin{methoddesc}[MultiFile]{seek}{pos\optional{, whence}} Seek. Seek indices are relative to the start of the current section. The \var{pos} and \var{whence} arguments are interpreted as for a file seek. \end{methoddesc} -\begin{methoddesc}{tell}{} +\begin{methoddesc}[MultiFile]{tell}{} Return the file position relative to the start of the current section. \end{methoddesc} -\begin{methoddesc}{next}{} +\begin{methoddesc}[MultiFile]{next}{} Skip lines to the next section (that is, read lines until a section-divider or end-marker has been consumed). Return true if there is such a section, false if an end-marker is seen. Re-enable the most-recently-pushed boundary. \end{methoddesc} -\begin{methoddesc}{is_data}{str} +\begin{methoddesc}[MultiFile]{is_data}{str} Return true if \var{str} is data and false if it might be a section boundary. As written, it tests for a prefix other than \code{'-}\code{-'} at start of line (which all MIME boundaries have) but it is declared so @@ -95,7 +95,7 @@ boundary tests; if it always returns false it will merely slow processing, not cause it to fail. \end{methoddesc} -\begin{methoddesc}{push}{str} +\begin{methoddesc}[MultiFile]{push}{str} Push a boundary string. When a decorated version of this boundary is found as an input line, it will be interpreted as a section-divider or end-marker (depending on the decoration, see \rfc{2045}). All subsequent @@ -108,12 +108,12 @@ most-recently-pushed boundary will return EOF; encountering any other boundary will raise an error. \end{methoddesc} -\begin{methoddesc}{pop}{} +\begin{methoddesc}[MultiFile]{pop}{} Pop a section boundary. This boundary will no longer be interpreted as EOF. \end{methoddesc} -\begin{methoddesc}{section_divider}{str} +\begin{methoddesc}[MultiFile]{section_divider}{str} Turn a boundary into a section-divider line. By default, this method prepends \code{'-}\code{-'} (which MIME section boundaries have) but it is declared so it can be overridden in derived classes. This @@ -121,7 +121,7 @@ method need not append LF or CR-LF, as comparison with the result ignores trailing whitespace. \end{methoddesc} -\begin{methoddesc}{end_marker}{str} +\begin{methoddesc}[MultiFile]{end_marker}{str} Turn a boundary string into an end-marker line. By default, this method prepends \code{'-}\code{-'} and appends \code{'-}\code{-'} (like a MIME-multipart end-of-message marker) but it is declared so it can be @@ -131,11 +131,11 @@ CR-LF, as comparison with the result ignores trailing whitespace. Finally, \class{MultiFile} instances have two public instance variables: -\begin{memberdesc}{level} +\begin{memberdesc}[MultiFile]{level} Nesting depth of the current part. \end{memberdesc} -\begin{memberdesc}{last} +\begin{memberdesc}[MultiFile]{last} True if the last end-of-file was for an end-of-message marker. \end{memberdesc} diff --git a/Doc/lib/libmutex.tex b/Doc/lib/libmutex.tex index 4893690..8c35c96 100644 --- a/Doc/lib/libmutex.tex +++ b/Doc/lib/libmutex.tex @@ -35,23 +35,23 @@ acquired. \class{mutex} objects have following methods: -\begin{methoddesc}{test}{} +\begin{methoddesc}[mutex]{test}{} Check whether the mutex is locked. \end{methoddesc} -\begin{methoddesc}{testandset}{} +\begin{methoddesc}[mutex]{testandset}{} ``Atomic'' test-and-set, grab the lock if it is not set, and return \code{True}, otherwise, return \code{False}. \end{methoddesc} -\begin{methoddesc}{lock}{function, argument} +\begin{methoddesc}[mutex]{lock}{function, argument} Execute \code{\var{function}(\var{argument})}, unless the mutex is locked. In the case it is locked, place the function and argument on the queue. See \method{unlock} for explanation of when \code{\var{function}(\var{argument})} is executed in that case. \end{methoddesc} -\begin{methoddesc}{unlock}{} +\begin{methoddesc}[mutex]{unlock}{} Unlock the mutex if queue is empty, otherwise execute the first element in the queue. \end{methoddesc} diff --git a/Doc/lib/libnetrc.tex b/Doc/lib/libnetrc.tex index f2a0c1c..f867b34 100644 --- a/Doc/lib/libnetrc.tex +++ b/Doc/lib/libnetrc.tex @@ -35,7 +35,7 @@ of the error, \member{filename} is the name of the source file, and A \class{netrc} instance has the following methods: -\begin{methoddesc}{authenticators}{host} +\begin{methoddesc}[netrc]{authenticators}{host} Return a 3-tuple \code{(\var{login}, \var{account}, \var{password})} of authenticators for \var{host}. If the netrc file did not contain an entry for the given host, return the tuple associated with @@ -43,20 +43,20 @@ the `default' entry. If neither matching host nor default entry is available, return \code{None}. \end{methoddesc} -\begin{methoddesc}{__repr__}{} +\begin{methoddesc}[netrc]{__repr__}{} Dump the class data as a string in the format of a netrc file. (This discards comments and may reorder the entries.) \end{methoddesc} Instances of \class{netrc} have public instance variables: -\begin{memberdesc}{hosts} +\begin{memberdesc}[netrc]{hosts} Dictionary mapping host names to \code{(\var{login}, \var{account}, \var{password})} tuples. The `default' entry, if any, is represented as a pseudo-host by that name. \end{memberdesc} -\begin{memberdesc}{macros} +\begin{memberdesc}[netrc]{macros} Dictionary mapping macro names to string lists. \end{memberdesc} diff --git a/Doc/lib/libnntplib.tex b/Doc/lib/libnntplib.tex index 10330ed..22236f4 100644 --- a/Doc/lib/libnntplib.tex +++ b/Doc/lib/libnntplib.tex @@ -121,13 +121,13 @@ If the server's response indicates an error, the method raises one of the above exceptions. -\begin{methoddesc}{getwelcome}{} +\begin{methoddesc}[NNTP]{getwelcome}{} Return the welcome message sent by the server in reply to the initial connection. (This message sometimes contains disclaimers or help information that may be relevant to the user.) \end{methoddesc} -\begin{methoddesc}{set_debuglevel}{level} +\begin{methoddesc}[NNTP]{set_debuglevel}{level} Set the instance's debugging level. This controls the amount of debugging output printed. The default, \code{0}, produces no debugging output. A value of \code{1} produces a moderate amount of debugging @@ -137,7 +137,7 @@ logging each line sent and received on the connection (including message text). \end{methoddesc} -\begin{methoddesc}{newgroups}{date, time, \optional{file}} +\begin{methoddesc}[NNTP]{newgroups}{date, time, \optional{file}} Send a \samp{NEWGROUPS} command. The \var{date} argument should be a string of the form \code{'\var{yy}\var{mm}\var{dd}'} indicating the date, and \var{time} should be a string of the form @@ -152,7 +152,7 @@ calling \method{write()} on it to store the lines of the command output. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{newnews}{group, date, time, \optional{file}} +\begin{methoddesc}[NNTP]{newnews}{group, date, time, \optional{file}} Send a \samp{NEWNEWS} command. Here, \var{group} is a group name or \code{'*'}, and \var{date} and \var{time} have the same meaning as for \method{newgroups()}. Return a pair \code{(\var{response}, @@ -165,7 +165,7 @@ calling \method{write()} on it to store the lines of the command output. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{list}{\optional{file}} +\begin{methoddesc}[NNTP]{list}{\optional{file}} Send a \samp{LIST} command. Return a pair \code{(\var{response}, \var{list})} where \var{list} is a list of tuples. Each tuple has the form \code{(\var{group}, \var{last}, \var{first}, \var{flag})}, where @@ -182,7 +182,7 @@ calling \method{write()} on it to store the lines of the command output. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{descriptions}{grouppattern} +\begin{methoddesc}[NNTP]{descriptions}{grouppattern} Send a \samp{LIST NEWSGROUPS} command, where \var{grouppattern} is a wildmat string as specified in RFC2980 (it's essentially the same as DOS or UNIX shell wildcard strings). Return a pair \code{(\var{response}, @@ -192,7 +192,7 @@ shell wildcard strings). Return a pair \code{(\var{response}, \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}{description}{group} +\begin{methoddesc}[NNTP]{description}{group} Get a description for a single group \var{group}. If more than one group matches (if 'group' is a real wildmat string), return the first match. If no group matches, return an empty string. @@ -203,7 +203,7 @@ needed, use \method{descriptions()}. \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}{group}{name} +\begin{methoddesc}[NNTP]{group}{name} Send a \samp{GROUP} command, where \var{name} is the group name. Return a tuple \code{(\var{response}, \var{count}, \var{first}, \var{last}, \var{name})} where \var{count} is the (estimated) number @@ -212,7 +212,7 @@ the group, \var{last} is the last article number in the group, and \var{name} is the group name. The numbers are returned as strings. \end{methoddesc} -\begin{methoddesc}{help}{\optional{file}} +\begin{methoddesc}[NNTP]{help}{\optional{file}} Send a \samp{HELP} command. Return a pair \code{(\var{response}, \var{list})} where \var{list} is a list of help strings. If the \var{file} parameter is supplied, then the output of the @@ -223,7 +223,7 @@ calling \method{write()} on it to store the lines of the command output. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{stat}{id} +\begin{methoddesc}[NNTP]{stat}{id} Send a \samp{STAT} command, where \var{id} is the message id (enclosed in \character{<} and \character{>}) or an article number (as a string). Return a triple \code{(\var{response}, \var{number}, \var{id})} where @@ -231,15 +231,15 @@ Return a triple \code{(\var{response}, \var{number}, \var{id})} where message id (enclosed in \character{<} and \character{>}). \end{methoddesc} -\begin{methoddesc}{next}{} +\begin{methoddesc}[NNTP]{next}{} Send a \samp{NEXT} command. Return as for \method{stat()}. \end{methoddesc} -\begin{methoddesc}{last}{} +\begin{methoddesc}[NNTP]{last}{} Send a \samp{LAST} command. Return as for \method{stat()}. \end{methoddesc} -\begin{methoddesc}{head}{id} +\begin{methoddesc}[NNTP]{head}{id} Send a \samp{HEAD} command, where \var{id} has the same meaning as for \method{stat()}. Return a tuple \code{(\var{response}, \var{number}, \var{id}, \var{list})} @@ -248,7 +248,7 @@ and \var{list} is a list of the article's headers (an uninterpreted list of lines, without trailing newlines). \end{methoddesc} -\begin{methoddesc}{body}{id,\optional{file}} +\begin{methoddesc}[NNTP]{body}{id,\optional{file}} Send a \samp{BODY} command, where \var{id} has the same meaning as for \method{stat()}. If the \var{file} parameter is supplied, then the body is stored in a file. If \var{file} is a string, then @@ -259,16 +259,16 @@ Return as for \method{head()}. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{article}{id} +\begin{methoddesc}[NNTP]{article}{id} Send an \samp{ARTICLE} command, where \var{id} has the same meaning as for \method{stat()}. Return as for \method{head()}. \end{methoddesc} -\begin{methoddesc}{slave}{} +\begin{methoddesc}[NNTP]{slave}{} Send a \samp{SLAVE} command. Return the server's \var{response}. \end{methoddesc} -\begin{methoddesc}{xhdr}{header, string, \optional{file}} +\begin{methoddesc}[NNTP]{xhdr}{header, string, \optional{file}} Send an \samp{XHDR} command. This command is not defined in the RFC but is a common extension. The \var{header} argument is a header keyword, e.g. \code{'subject'}. The \var{string} argument should have @@ -286,7 +286,7 @@ calling \method{write()} on it to store the lines of the command output. If \var{file} is supplied, then the returned \var{list} is an empty list. \end{methoddesc} -\begin{methoddesc}{post}{file} +\begin{methoddesc}[NNTP]{post}{file} Post an article using the \samp{POST} command. The \var{file} argument is an open file object which is read until EOF using its \method{readline()} method. It should be a well-formed news article, @@ -294,14 +294,14 @@ including the required headers. The \method{post()} method automatically escapes lines beginning with \samp{.}. \end{methoddesc} -\begin{methoddesc}{ihave}{id, file} +\begin{methoddesc}[NNTP]{ihave}{id, file} Send an \samp{IHAVE} command. \var{id} is a message id (enclosed in \character{<} and \character{>}). If the response is not an error, treat \var{file} exactly as for the \method{post()} method. \end{methoddesc} -\begin{methoddesc}{date}{} +\begin{methoddesc}[NNTP]{date}{} Return a triple \code{(\var{response}, \var{date}, \var{time})}, containing the current date and time in a form suitable for the \method{newnews()} and \method{newgroups()} methods. @@ -309,7 +309,7 @@ This is an optional NNTP extension, and may not be supported by all servers. \end{methoddesc} -\begin{methoddesc}{xgtitle}{name, \optional{file}} +\begin{methoddesc}[NNTP]{xgtitle}{name, \optional{file}} Process an \samp{XGTITLE} command, returning a pair \code{(\var{response}, \var{list})}, where \var{list} is a list of tuples containing \code{(\var{name}, \var{title})}. @@ -327,7 +327,7 @@ RFC2980 says ``It is suggested that this extension be deprecated''. Use \method{descriptions()} or \method{description()} instead. \end{methoddesc} -\begin{methoddesc}{xover}{start, end, \optional{file}} +\begin{methoddesc}[NNTP]{xover}{start, end, \optional{file}} Return a pair \code{(\var{resp}, \var{list})}. \var{list} is a list of tuples, one for each article in the range delimited by the \var{start} and \var{end} article numbers. Each tuple is of the form @@ -343,13 +343,13 @@ This is an optional NNTP extension, and may not be supported by all servers. \end{methoddesc} -\begin{methoddesc}{xpath}{id} +\begin{methoddesc}[NNTP]{xpath}{id} Return a pair \code{(\var{resp}, \var{path})}, where \var{path} is the directory path to the article with message ID \var{id}. This is an optional NNTP extension, and may not be supported by all servers. \end{methoddesc} -\begin{methoddesc}{quit}{} +\begin{methoddesc}[NNTP]{quit}{} Send a \samp{QUIT} command and close the connection. Once this method has been called, no other methods of the NNTP object should be called. \end{methoddesc} diff --git a/Doc/lib/liboptparse.tex b/Doc/lib/liboptparse.tex index df96dd4..dd618c8 100644 --- a/Doc/lib/liboptparse.tex +++ b/Doc/lib/liboptparse.tex @@ -518,7 +518,7 @@ program, i.e. \code{os.path.basename(sys.argv{[}0])}. The expanded string is then printed before the detailed option help. If you don't supply a usage string, \module{optparse} uses a bland but sensible -default: ``\code{usage: {\%}prog {[}options]"}, which is fine if your script +default: \code{"usage: {\%}prog {[}options]"}, which is fine if your script doesn't take any positional arguments. \item {} diff --git a/Doc/lib/libos.tex b/Doc/lib/libos.tex index cbb35f3..638ed6b 100644 --- a/Doc/lib/libos.tex +++ b/Doc/lib/libos.tex @@ -1233,7 +1233,8 @@ Availability: Macintosh, \UNIX, Windows. \end{funcdesc} \begin{funcdesc}{walk}{top\optional{, topdown\code{=True} - \optional{, onerror\code{=None}}}} + \optional{, onerror\code{=None}\optional{, + followlinks\code{=False}}}}} \index{directory!walking} \index{directory!traversal} \function{walk()} generates the file names in a directory tree, by @@ -1273,6 +1274,18 @@ report the error to continue with the walk, or raise the exception to abort the walk. Note that the filename is available as the \code{filename} attribute of the exception object. +By default, \function{walk()} will not walk down into symbolic links that +resolve to directories. Set \var{followlinks} to True to visit directories +pointed to by symlinks, on systems that support them. + +\versionadded[The \var{followlinks} parameter]{2.6} + +\begin{notice} +Be aware that setting \var{followlinks} to true can lead to infinite recursion +if a link points to a parent directory of itself. \function{walk()} does not +keep track of the directories it visited already. +\end{notice} + \begin{notice} If you pass a relative pathname, don't change the current working directory between resumptions of \function{walk()}. \function{walk()} @@ -1280,15 +1293,6 @@ never changes the current directory, and assumes that its caller doesn't either. \end{notice} -\begin{notice} -On systems that support symbolic links, links to subdirectories appear -in \var{dirnames} lists, but \function{walk()} will not visit them -(infinite loops are hard to avoid when following symbolic links). -To visit linked directories, you can identify them with -\code{os.path.islink(\var{path})}, and invoke \code{walk(\var{path})} -on each directly. -\end{notice} - This example displays the number of bytes taken by non-directory files in each directory under the starting directory, except that it doesn't look under any CVS subdirectory: diff --git a/Doc/lib/libpdb.tex b/Doc/lib/libpdb.tex index 778a137..45e778c 100644 --- a/Doc/lib/libpdb.tex +++ b/Doc/lib/libpdb.tex @@ -378,6 +378,14 @@ command with a \samp{global} command on the same line, e.g.: (Pdb) \end{verbatim} +\item[run \optional{\var{args} ...}] +Restart the debugged python program. If an argument is supplied, it is +splitted with "shlex" and the result is used as the new sys.argv. +History, breakpoints, actions and debugger options are preserved. +"restart" is an alias for "run". + +\versionadded{2.6} + \item[q(uit)] Quit from the debugger. diff --git a/Doc/lib/libpipes.tex b/Doc/lib/libpipes.tex index 1815e09..de25fb5 100644 --- a/Doc/lib/libpipes.tex +++ b/Doc/lib/libpipes.tex @@ -39,21 +39,21 @@ Example: Template objects following methods: -\begin{methoddesc}{reset}{} +\begin{methoddesc}[Template]{reset}{} Restore a pipeline template to its initial state. \end{methoddesc} -\begin{methoddesc}{clone}{} +\begin{methoddesc}[Template]{clone}{} Return a new, equivalent, pipeline template. \end{methoddesc} -\begin{methoddesc}{debug}{flag} +\begin{methoddesc}[Template]{debug}{flag} If \var{flag} is true, turn debugging on. Otherwise, turn debugging off. When debugging is on, commands to be executed are printed, and the shell is given \code{set -x} command to be more verbose. \end{methoddesc} -\begin{methoddesc}{append}{cmd, kind} +\begin{methoddesc}[Template]{append}{cmd, kind} Append a new action at the end. The \var{cmd} variable must be a valid bourne shell command. The \var{kind} variable consists of two letters. @@ -68,17 +68,17 @@ command writes a file on the command line) or \code{'.'} (which means the command does not write anything, and hence must be last.) \end{methoddesc} -\begin{methoddesc}{prepend}{cmd, kind} +\begin{methoddesc}[Template]{prepend}{cmd, kind} Add a new action at the beginning. See \method{append()} for explanations of the arguments. \end{methoddesc} -\begin{methoddesc}{open}{file, mode} +\begin{methoddesc}[Template]{open}{file, mode} Return a file-like object, open to \var{file}, but read from or written to by the pipeline. Note that only one of \code{'r'}, \code{'w'} may be given. \end{methoddesc} -\begin{methoddesc}{copy}{infile, outfile} +\begin{methoddesc}[Template]{copy}{infile, outfile} Copy \var{infile} to \var{outfile} through the pipe. \end{methoddesc} diff --git a/Doc/lib/libplatform.tex b/Doc/lib/libplatform.tex index 810e08b..a2f1913 100644 --- a/Doc/lib/libplatform.tex +++ b/Doc/lib/libplatform.tex @@ -80,6 +80,22 @@ Returns a string identifying the compiler used for compiling Python. \end{funcdesc} +\begin{funcdesc}{python_branch}{} + Returns a string identifying the Python implementation SCM branch. + \versionadded{2.6} +\end{funcdesc} + +\begin{funcdesc}{python_implementation}{} + Returns a string identifying the Python implementation. + Possible return values are: 'CPython', 'IronPython', 'Jython' + \versionadded{2.6} +\end{funcdesc} + +\begin{funcdesc}{python_revision}{} + Returns a string identifying the Python implementation SCM revision. + \versionadded{2.6} +\end{funcdesc} + \begin{funcdesc}{python_version}{} Returns the Python version as string \code{'major.minor.patchlevel'} @@ -205,6 +221,7 @@ which defaults to the args given as parameters. \end{funcdesc} +% Document linux_distribution()? \begin{funcdesc}{libc_ver}{executable=sys.executable, lib='', version='', chunksize=2048} diff --git a/Doc/lib/libpopen2.tex b/Doc/lib/libpopen2.tex index fa0c1a6..322a9b9 100644 --- a/Doc/lib/libpopen2.tex +++ b/Doc/lib/libpopen2.tex @@ -2,7 +2,6 @@ Subprocesses with accessible I/O streams} \declaremodule{standard}{popen2} - \platform{Unix, Windows} \modulesynopsis{Subprocesses with accessible standard I/O streams.} \sectionauthor{Drew Csillag}{drew_csillag@geocities.com} @@ -85,12 +84,12 @@ using \function{popen4()}. Instances of the \class{Popen3} and \class{Popen4} classes have the following methods: -\begin{methoddesc}{poll}{} +\begin{methoddesc}[Popen3]{poll}{} Returns \code{-1} if child process hasn't completed yet, or its return code otherwise. \end{methoddesc} -\begin{methoddesc}{wait}{} +\begin{methoddesc}[Popen3]{wait}{} Waits for and returns the status code of the child process. The status code encodes both the return code of the process and information about whether it exited using the \cfunction{exit()} @@ -102,24 +101,24 @@ status code are defined in the \refmodule{os} module; see section The following attributes are also available: -\begin{memberdesc}{fromchild} +\begin{memberdesc}[Popen3]{fromchild} A file object that provides output from the child process. For \class{Popen4} instances, this will provide both the standard output and standard error streams. \end{memberdesc} -\begin{memberdesc}{tochild} +\begin{memberdesc}[Popen3]{tochild} A file object that provides input to the child process. \end{memberdesc} -\begin{memberdesc}{childerr} +\begin{memberdesc}[Popen3]{childerr} A file object that provides error output from the child process, if \var{capturestderr} was true for the constructor, otherwise \code{None}. This will always be \code{None} for \class{Popen4} instances. \end{memberdesc} -\begin{memberdesc}{pid} +\begin{memberdesc}[Popen3]{pid} The process ID of the child process. \end{memberdesc} diff --git a/Doc/lib/libpoplib.tex b/Doc/lib/libpoplib.tex index 25570ae..7b2c4a1 100644 --- a/Doc/lib/libpoplib.tex +++ b/Doc/lib/libpoplib.tex @@ -23,15 +23,18 @@ servers that use SSL as an underlying protocol layer. Note that POP3, though widely supported, is obsolescent. The implementation quality of POP3 servers varies widely, and too many are quite poor. If your mailserver supports IMAP, you would be better off -using the \code{\refmodule{imaplib}.\class{IMAP4}} class, as IMAP +using the \class{\refmodule{imaplib}.IMAP4} class, as IMAP servers tend to be better implemented. A single class is provided by the \module{poplib} module: -\begin{classdesc}{POP3}{host\optional{, port}} +\begin{classdesc}{POP3}{host\optional{, port\optional{, timeout}}} This class implements the actual POP3 protocol. The connection is created when the instance is initialized. If \var{port} is omitted, the standard POP3 port (110) is used. +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global default +timeout setting will be used). \end{classdesc} \begin{classdesc}{POP3_SSL}{host\optional{, port\optional{, keyfile\optional{, certfile}}}} @@ -47,8 +50,9 @@ certificate chain file for the SSL connection. One exception is defined as an attribute of the \module{poplib} module: \begin{excdesc}{error_proto} -Exception raised on any errors. The reason for the exception is -passed to the constructor as a string. +Exception raised on any errors from this module (errors from +\module{socket} module are not caught). The reason for the exception +is passed to the constructor as a string. \end{excdesc} \begin{seealso} @@ -70,7 +74,7 @@ in lower-case; most return the response text sent by the server. An \class{POP3} instance has the following methods: -\begin{methoddesc}{set_debuglevel}{level} +\begin{methoddesc}[POP3]{set_debuglevel}{level} Set the instance's debugging level. This controls the amount of debugging output printed. The default, \code{0}, produces no debugging output. A value of \code{1} produces a moderate amount of @@ -79,64 +83,64 @@ debugging output, generally a single line per request. A value of logging each line sent and received on the control connection. \end{methoddesc} -\begin{methoddesc}{getwelcome}{} +\begin{methoddesc}[POP3]{getwelcome}{} Returns the greeting string sent by the POP3 server. \end{methoddesc} -\begin{methoddesc}{user}{username} +\begin{methoddesc}[POP3]{user}{username} Send user command, response should indicate that a password is required. \end{methoddesc} -\begin{methoddesc}{pass_}{password} +\begin{methoddesc}[POP3]{pass_}{password} Send password, response includes message count and mailbox size. Note: the mailbox on the server is locked until \method{quit()} is called. \end{methoddesc} -\begin{methoddesc}{apop}{user, secret} +\begin{methoddesc}[POP3]{apop}{user, secret} Use the more secure APOP authentication to log into the POP3 server. \end{methoddesc} -\begin{methoddesc}{rpop}{user} +\begin{methoddesc}[POP3]{rpop}{user} Use RPOP authentication (similar to UNIX r-commands) to log into POP3 server. \end{methoddesc} -\begin{methoddesc}{stat}{} +\begin{methoddesc}[POP3]{stat}{} Get mailbox status. The result is a tuple of 2 integers: \code{(\var{message count}, \var{mailbox size})}. \end{methoddesc} -\begin{methoddesc}{list}{\optional{which}} +\begin{methoddesc}[POP3]{list}{\optional{which}} Request message list, result is in the form \code{(\var{response}, ['mesg_num octets', ...], \var{octets})}. If \var{which} is set, it is the message to list. \end{methoddesc} -\begin{methoddesc}{retr}{which} +\begin{methoddesc}[POP3]{retr}{which} Retrieve whole message number \var{which}, and set its seen flag. Result is in form \code{(\var{response}, ['line', ...], \var{octets})}. \end{methoddesc} -\begin{methoddesc}{dele}{which} +\begin{methoddesc}[POP3]{dele}{which} Flag message number \var{which} for deletion. On most servers deletions are not actually performed until QUIT (the major exception is Eudora QPOP, which deliberately violates the RFCs by doing pending deletes on any disconnect). \end{methoddesc} -\begin{methoddesc}{rset}{} +\begin{methoddesc}[POP3]{rset}{} Remove any deletion marks for the mailbox. \end{methoddesc} -\begin{methoddesc}{noop}{} +\begin{methoddesc}[POP3]{noop}{} Do nothing. Might be used as a keep-alive. \end{methoddesc} -\begin{methoddesc}{quit}{} +\begin{methoddesc}[POP3]{quit}{} Signoff: commit changes, unlock mailbox, drop connection. \end{methoddesc} -\begin{methoddesc}{top}{which, howmuch} +\begin{methoddesc}[POP3]{top}{which, howmuch} Retrieves the message header plus \var{howmuch} lines of the message after the header of message number \var{which}. Result is in form \code{(\var{response}, ['line', ...], \var{octets})}. @@ -148,7 +152,7 @@ Test this method by hand against the POP3 servers you will use before trusting it. \end{methoddesc} -\begin{methoddesc}{uidl}{\optional{which}} +\begin{methoddesc}[POP3]{uidl}{\optional{which}} Return message digest (unique id) list. If \var{which} is specified, result contains the unique id for that message in the form \code{'\var{response}\ \var{mesgnum}\ \var{uid}}, diff --git a/Doc/lib/libposixfile.tex b/Doc/lib/libposixfile.tex index 62861f0..5c86f3e 100644 --- a/Doc/lib/libposixfile.tex +++ b/Doc/lib/libposixfile.tex @@ -62,8 +62,7 @@ The \module{posixfile} module defines the following functions: The posixfile object defines the following additional methods: -\setindexsubitem{(posixfile method)} -\begin{funcdesc}{lock}{fmt, \optional{len\optional{, start\optional{, whence}}}} +\begin{methoddesc}[posixfile]{lock}{fmt, \optional{len\optional{, start\optional{, whence}}}} Lock the specified section of the file that the file object is referring to. The format is explained below in a table. The \var{len} argument specifies the length of the @@ -74,9 +73,9 @@ The posixfile object defines the following additional methods: \constant{SEEK_CUR} or \constant{SEEK_END}. The default is \constant{SEEK_SET}. For more information about the arguments refer to the \manpage{fcntl}{2} manual page on your system. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{flags}{\optional{flags}} +\begin{methoddesc}[posixfile]{flags}{\optional{flags}} Set the specified flags for the file that the file object is referring to. The new flags are ORed with the old flags, unless specified otherwise. The format is explained below in a table. Without @@ -84,25 +83,25 @@ The posixfile object defines the following additional methods: a string indicating the current flags is returned (this is the same as the \samp{?} modifier). For more information about the flags refer to the \manpage{fcntl}{2} manual page on your system. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{dup}{} +\begin{methoddesc}[posixfile]{dup}{} Duplicate the file object and the underlying file pointer and file descriptor. The resulting object behaves as if it were newly opened. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{dup2}{fd} +\begin{methoddesc}[posixfile]{dup2}{fd} Duplicate the file object and the underlying file pointer and file descriptor. The new object will have the given file descriptor. Otherwise the resulting object behaves as if it were newly opened. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{file}{} +\begin{methoddesc}[posixfile]{file}{} Return the standard file object that the posixfile object is based on. This is sometimes necessary for functions that insist on a standard file object. -\end{funcdesc} +\end{methoddesc} All methods raise \exception{IOError} when the request fails. diff --git a/Doc/lib/libposixpath.tex b/Doc/lib/libposixpath.tex index 0b2da66..7684fa0 100644 --- a/Doc/lib/libposixpath.tex +++ b/Doc/lib/libposixpath.tex @@ -58,18 +58,20 @@ Equivalent to \function{exists()} on platforms lacking \end{funcdesc} \begin{funcdesc}{expanduser}{path} -On \UNIX, return the argument with an initial component of \samp{\~} or -\samp{\~\var{user}} replaced by that \var{user}'s home directory. -An initial \samp{\~} is replaced by the environment variable +On \UNIX{} and Windows, return the argument with an initial component of +\samp{\~} or \samp{\~\var{user}} replaced by that \var{user}'s home directory. + +On \UNIX, an initial \samp{\~} is replaced by the environment variable \envvar{HOME} if it is set; otherwise the current user's home directory is looked up in the password directory through the built-in module \refmodule{pwd}\refbimodindex{pwd}. An initial \samp{\~\var{user}} is looked up directly in the password directory. -On Windows, only \samp{\~} is supported; it is replaced by the -environment variable \envvar{HOME} or by a combination of -\envvar{HOMEDRIVE} and \envvar{HOMEPATH}. +On Windows, \envvar{HOME} and \envvar{USERPROFILE} will be used if set, +otherwise a combination of \envvar{HOMEPATH} and \envvar{HOMEDRIVE} will be +used. An initial \samp{\~\var{user}} is handled by stripping the last +directory component from the created user path derived above. If the expansion fails or if the path does not begin with a tilde, the path is returned unchanged. @@ -81,6 +83,9 @@ of the form \samp{\$\var{name}} or \samp{\$\{\var{name}\}} are replaced by the value of environment variable \var{name}. Malformed variable names and references to non-existing variables are left unchanged. + +On Windows, \samp{\%\var{name}\%} expansions are supported in addition to +\samp{\$\var{name}} and \samp{\$\{\var{name}\}}. \end{funcdesc} \begin{funcdesc}{getatime}{path} @@ -184,6 +189,15 @@ operating system). \versionadded{2.2} \end{funcdesc} +\begin{funcdesc}{relpath}{path\optional{, start}} +Return a relative filepath to \var{path} either from the current +directory or from an optional \var{start} point. + +\var{start} defaults to \member{os.curdir}. +Availability: Windows, \UNIX. +\versionadded{2.6} +\end{funcdesc} + \begin{funcdesc}{samefile}{path1, path2} Return \code{True} if both pathname arguments refer to the same file or directory (as indicated by device number and i-node number). @@ -234,7 +248,12 @@ empty string. On systems which do not use drive specifications, Split the pathname \var{path} into a pair \code{(\var{root}, \var{ext})} such that \code{\var{root} + \var{ext} == \var{path}}, and \var{ext} is empty or begins with a period and contains -at most one period. +at most one period. Leading periods on the basename are +ignored; \code{\var{splitext}.('.cshrc')} returns +\code{('.cshrc', '')}. + +\versionchanged[Earlier versions could produce an empty root when +the only period was the first character]{2.6} \end{funcdesc} \begin{funcdesc}{splitunc}{path} diff --git a/Doc/lib/libpprint.tex b/Doc/lib/libpprint.tex index fd03038..9203b3a 100644 --- a/Doc/lib/libpprint.tex +++ b/Doc/lib/libpprint.tex @@ -158,12 +158,12 @@ l/lib/python1.5/test', '/usr/local/lib/python1.5/sunos5', '/usr/local/lib/python \class{PrettyPrinter} instances have the following methods: -\begin{methoddesc}{pformat}{object} +\begin{methoddesc}[PrettyPrinter]{pformat}{object} Return the formatted representation of \var{object}. This takes into account the options passed to the \class{PrettyPrinter} constructor. \end{methoddesc} -\begin{methoddesc}{pprint}{object} +\begin{methoddesc}[PrettyPrinter]{pprint}{object} Print the formatted representation of \var{object} on the configured stream, followed by a newline. \end{methoddesc} @@ -173,7 +173,7 @@ corresponding functions of the same names. Using these methods on an instance is slightly more efficient since new \class{PrettyPrinter} objects don't need to be created. -\begin{methoddesc}{isreadable}{object} +\begin{methoddesc}[PrettyPrinter]{isreadable}{object} Determine if the formatted representation of the object is ``readable,'' or can be used to reconstruct the value using \function{eval()}\bifuncindex{eval}. Note that this returns false for @@ -182,7 +182,7 @@ recursive objects. If the \var{depth} parameter of the this returns false. \end{methoddesc} -\begin{methoddesc}{isrecursive}{object} +\begin{methoddesc}[PrettyPrinter]{isrecursive}{object} Determine if the object requires a recursive representation. \end{methoddesc} @@ -190,7 +190,7 @@ This method is provided as a hook to allow subclasses to modify the way objects are converted to strings. The default implementation uses the internals of the \function{saferepr()} implementation. -\begin{methoddesc}{format}{object, context, maxlevels, level} +\begin{methoddesc}[PrettyPrinter]{format}{object, context, maxlevels, level} Returns three values: the formatted version of \var{object} as a string, a flag indicating whether the result is readable, and a flag indicating whether recursion was detected. The first argument is the diff --git a/Doc/lib/libqueue.tex b/Doc/lib/libqueue.tex index 95ad47f..591a910 100644 --- a/Doc/lib/libqueue.tex +++ b/Doc/lib/libqueue.tex @@ -45,22 +45,22 @@ other queue organizations (e.g. stack) but the inheritable interface is not described here. See the source code for details. The public methods are: -\begin{methoddesc}{qsize}{} +\begin{methoddesc}[Queue]{qsize}{} Return the approximate size of the queue. Because of multithreading semantics, this number is not reliable. \end{methoddesc} -\begin{methoddesc}{empty}{} +\begin{methoddesc}[Queue]{empty}{} Return \code{True} if the queue is empty, \code{False} otherwise. Because of multithreading semantics, this is not reliable. \end{methoddesc} -\begin{methoddesc}{full}{} +\begin{methoddesc}[Queue]{full}{} Return \code{True} if the queue is full, \code{False} otherwise. Because of multithreading semantics, this is not reliable. \end{methoddesc} -\begin{methoddesc}{put}{item\optional{, block\optional{, timeout}}} +\begin{methoddesc}[Queue]{put}{item\optional{, block\optional{, timeout}}} Put \var{item} into the queue. If optional args \var{block} is true and \var{timeout} is None (the default), block if necessary until a free slot is available. If \var{timeout} is a positive number, it @@ -74,11 +74,11 @@ exception (\var{timeout} is ignored in that case). \end{methoddesc} -\begin{methoddesc}{put_nowait}{item} +\begin{methoddesc}[Queue]{put_nowait}{item} Equivalent to \code{put(\var{item}, False)}. \end{methoddesc} -\begin{methoddesc}{get}{\optional{block\optional{, timeout}}} +\begin{methoddesc}[Queue]{get}{\optional{block\optional{, timeout}}} Remove and return an item from the queue. If optional args \var{block} is true and \var{timeout} is None (the default), block if necessary until an item is available. If \var{timeout} is @@ -92,14 +92,14 @@ immediately available, else raise the \exception{Empty} exception \end{methoddesc} -\begin{methoddesc}{get_nowait}{} +\begin{methoddesc}[Queue]{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}{} +\begin{methoddesc}[Queue]{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. @@ -113,7 +113,7 @@ placed in the queue. \versionadded{2.5} \end{methoddesc} -\begin{methoddesc}{join}{} +\begin{methoddesc}[Queue]{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 diff --git a/Doc/lib/libre.tex b/Doc/lib/libre.tex index 84e382d..a0b8b51 100644 --- a/Doc/lib/libre.tex +++ b/Doc/lib/libre.tex @@ -812,7 +812,7 @@ used for groups that did not participate in the match; it defaults to \end{methoddesc} \begin{methoddesc}[MatchObject]{start}{\optional{group}} -\methodline{end}{\optional{group}} +\methodline[MatchObject]{end}{\optional{group}} Return the indices of the start and end of the substring matched by \var{group}; \var{group} defaults to zero (meaning the whole matched substring). diff --git a/Doc/lib/librepr.tex b/Doc/lib/librepr.tex index 7905112..2876448 100644 --- a/Doc/lib/librepr.tex +++ b/Doc/lib/librepr.tex @@ -44,18 +44,18 @@ provide size limits for the representations of different object types, and methods which format specific object types. -\begin{memberdesc}{maxlevel} +\begin{memberdesc}[Repr]{maxlevel} Depth limit on the creation of recursive representations. The default is \code{6}. \end{memberdesc} -\begin{memberdesc}{maxdict} -\memberline{maxlist} -\memberline{maxtuple} -\memberline{maxset} -\memberline{maxfrozenset} -\memberline{maxdeque} -\memberline{maxarray} +\begin{memberdesc}[Repr]{maxdict} +\memberline[Repr]{maxlist} +\memberline[Repr]{maxtuple} +\memberline[Repr]{maxset} +\memberline[Repr]{maxfrozenset} +\memberline[Repr]{maxdeque} +\memberline[Repr]{maxarray} Limits on the number of entries represented for the named object type. The default is \code{4} for \member{maxdict}, \code{5} for \member{maxarray}, and \code{6} for the others. @@ -63,13 +63,13 @@ and methods which format specific object types. and \member{set}]{2.4}. \end{memberdesc} -\begin{memberdesc}{maxlong} +\begin{memberdesc}[Repr]{maxlong} Maximum number of characters in the representation for a long integer. Digits are dropped from the middle. The default is \code{40}. \end{memberdesc} -\begin{memberdesc}{maxstring} +\begin{memberdesc}[Repr]{maxstring} Limit on the number of characters in the representation of the string. Note that the ``normal'' representation of the string is used as the character source: if escape sequences are needed in the @@ -77,19 +77,19 @@ and methods which format specific object types. shortened. The default is \code{30}. \end{memberdesc} -\begin{memberdesc}{maxother} +\begin{memberdesc}[Repr]{maxother} This limit is used to control the size of object types for which no specific formatting method is available on the \class{Repr} object. It is applied in a similar manner as \member{maxstring}. The default is \code{20}. \end{memberdesc} -\begin{methoddesc}{repr}{obj} +\begin{methoddesc}[Repr]{repr}{obj} The equivalent to the built-in \function{repr()} that uses the formatting imposed by the instance. \end{methoddesc} -\begin{methoddesc}{repr1}{obj, level} +\begin{methoddesc}[Repr]{repr1}{obj, level} Recursive implementation used by \method{repr()}. This uses the type of \var{obj} to determine which formatting method to call, passing it \var{obj} and \var{level}. The type-specific methods @@ -98,7 +98,7 @@ and methods which format specific object types. call. \end{methoddesc} -\begin{methoddescni}{repr_\var{type}}{obj, level} +\begin{methoddescni}[Repr]{repr_\var{type}}{obj, level} Formatting methods for specific types are implemented as methods with a name based on the type name. In the method name, \var{type} is replaced by diff --git a/Doc/lib/librexec.tex b/Doc/lib/librexec.tex index 3e54102..3104004 100644 --- a/Doc/lib/librexec.tex +++ b/Doc/lib/librexec.tex @@ -89,20 +89,20 @@ makes use of this and would break were it not available. \class{RExec} instances support the following methods: -\begin{methoddesc}{r_eval}{code} +\begin{methoddesc}[RExec]{r_eval}{code} \var{code} must either be a string containing a Python expression, or a compiled code object, which will be evaluated in the restricted environment's \module{__main__} module. The value of the expression or code object will be returned. \end{methoddesc} -\begin{methoddesc}{r_exec}{code} +\begin{methoddesc}[RExec]{r_exec}{code} \var{code} must either be a string containing one or more lines of Python code, or a compiled code object, which will be executed in the restricted environment's \module{__main__} module. \end{methoddesc} -\begin{methoddesc}{r_execfile}{filename} +\begin{methoddesc}[RExec]{r_execfile}{filename} Execute the Python code contained in the file \var{filename} in the restricted environment's \module{__main__} module. \end{methoddesc} @@ -112,17 +112,17 @@ beginning with \samp{r_}, but the code will be granted access to restricted versions of the standard I/O streams \code{sys.stdin}, \code{sys.stderr}, and \code{sys.stdout}. -\begin{methoddesc}{s_eval}{code} +\begin{methoddesc}[RExec]{s_eval}{code} \var{code} must be a string containing a Python expression, which will be evaluated in the restricted environment. \end{methoddesc} -\begin{methoddesc}{s_exec}{code} +\begin{methoddesc}[RExec]{s_exec}{code} \var{code} must be a string containing one or more lines of Python code, which will be executed in the restricted environment. \end{methoddesc} -\begin{methoddesc}{s_execfile}{code} +\begin{methoddesc}[RExec]{s_execfile}{code} Execute the Python code contained in the file \var{filename} in the restricted environment. \end{methoddesc} @@ -132,13 +132,13 @@ implicitly called by code executing in the restricted environment. Overriding these methods in a subclass is used to change the policies enforced by a restricted environment. -\begin{methoddesc}{r_import}{modulename\optional{, globals\optional{, - locals\optional{, fromlist}}}} +\begin{methoddesc}[RExec]{r_import}{modulename\optional{, globals\optional{, + locals\optional{, fromlist}}}} Import the module \var{modulename}, raising an \exception{ImportError} exception if the module is considered unsafe. \end{methoddesc} -\begin{methoddesc}{r_open}{filename\optional{, mode\optional{, bufsize}}} +\begin{methoddesc}[RExec]{r_open}{filename\optional{, mode\optional{, bufsize}}} Method called when \function{open()} is called in the restricted environment. The arguments are identical to those of \function{open()}, and a file object (or a class instance compatible with file objects) @@ -148,28 +148,28 @@ the example below for an implementation of a less restrictive \method{r_open()}. \end{methoddesc} -\begin{methoddesc}{r_reload}{module} +\begin{methoddesc}[RExec]{r_reload}{module} Reload the module object \var{module}, re-parsing and re-initializing it. \end{methoddesc} -\begin{methoddesc}{r_unload}{module} +\begin{methoddesc}[RExec]{r_unload}{module} Unload the module object \var{module} (remove it from the restricted environment's \code{sys.modules} dictionary). \end{methoddesc} And their equivalents with access to restricted standard I/O streams: -\begin{methoddesc}{s_import}{modulename\optional{, globals\optional{, - locals\optional{, fromlist}}}} +\begin{methoddesc}[RExec]{s_import}{modulename\optional{, globals\optional{, + locals\optional{, fromlist}}}} Import the module \var{modulename}, raising an \exception{ImportError} exception if the module is considered unsafe. \end{methoddesc} -\begin{methoddesc}{s_reload}{module} +\begin{methoddesc}[RExec]{s_reload}{module} Reload the module object \var{module}, re-parsing and re-initializing it. \end{methoddesc} -\begin{methoddesc}{s_unload}{module} +\begin{methoddesc}[RExec]{s_unload}{module} Unload the module object \var{module}. % XXX what are the semantics of this? \end{methoddesc} @@ -184,7 +184,7 @@ instance won't have any effect; instead, create a subclass of Instances of the new class will then use those new values. All these attributes are tuples of strings. -\begin{memberdesc}{nok_builtin_names} +\begin{memberdesc}[RExec]{nok_builtin_names} Contains the names of built-in functions which will \emph{not} be available to programs running in the restricted environment. The value for \class{RExec} is \code{('open', 'reload', '__import__')}. @@ -196,7 +196,7 @@ built-in functions are added to Python, they will also be added to this module.) \end{memberdesc} -\begin{memberdesc}{ok_builtin_modules} +\begin{memberdesc}[RExec]{ok_builtin_modules} Contains the names of built-in modules which can be safely imported. The value for \class{RExec} is \code{('audioop', 'array', 'binascii', 'cmath', 'errno', 'imageop', 'marshal', 'math', 'md5', 'operator', @@ -205,14 +205,14 @@ The value for \class{RExec} is \code{('audioop', 'array', 'binascii', applies --- use the value from the base class as a starting point. \end{memberdesc} -\begin{memberdesc}{ok_path} +\begin{memberdesc}[RExec]{ok_path} Contains the directories which will be searched when an \keyword{import} is performed in the restricted environment. The value for \class{RExec} is the same as \code{sys.path} (at the time the module is loaded) for unrestricted code. \end{memberdesc} -\begin{memberdesc}{ok_posix_names} +\begin{memberdesc}[RExec]{ok_posix_names} % Should this be called ok_os_names? Contains the names of the functions in the \refmodule{os} module which will be available to programs running in the restricted environment. The @@ -221,14 +221,14 @@ value for \class{RExec} is \code{('error', 'fstat', 'listdir', 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')}. \end{memberdesc} -\begin{memberdesc}{ok_sys_names} +\begin{memberdesc}[RExec]{ok_sys_names} Contains the names of the functions and variables in the \refmodule{sys} module which will be available to programs running in the restricted environment. The value for \class{RExec} is \code{('ps1', 'ps2', 'copyright', 'version', 'platform', 'exit', 'maxint')}. \end{memberdesc} -\begin{memberdesc}{ok_file_types} +\begin{memberdesc}[RExec]{ok_file_types} Contains the file types from which modules are allowed to be loaded. Each file type is an integer constant defined in the \refmodule{imp} module. The meaningful values are \constant{PY_SOURCE}, \constant{PY_COMPILED}, and diff --git a/Doc/lib/librfc822.tex b/Doc/lib/librfc822.tex index 4ca3734..b59e6ad 100644 --- a/Doc/lib/librfc822.tex +++ b/Doc/lib/librfc822.tex @@ -100,7 +100,7 @@ however, some mailers don't follow that format as specified, so \code{'Mon, 20 Nov 1995 19:12:08 -0500'}. If it succeeds in parsing the date, \function{parsedate()} returns a 9-tuple that can be passed directly to \function{time.mktime()}; otherwise \code{None} will be -returned. Note that fields 6, 7, and 8 of the result tuple are not +returned. Note that indexes 6, 7, and 8 of the result tuple are not usable. \end{funcdesc} @@ -114,7 +114,7 @@ offset is the opposite of the sign of the \code{time.timezone} variable for the same timezone; the latter variable follows the \POSIX{} standard while this module follows \rfc{2822}.) If the input string has no timezone, the last element of the tuple returned is -\code{None}. Note that fields 6, 7, and 8 of the result tuple are not +\code{None}. Note that indexes 6, 7, and 8 of the result tuple are not usable. \end{funcdesc} @@ -142,12 +142,12 @@ switch dates. Not enough to worry about for common use. A \class{Message} instance has the following methods: -\begin{methoddesc}{rewindbody}{} +\begin{methoddesc}[Message]{rewindbody}{} Seek to the start of the message body. This only works if the file object is seekable. \end{methoddesc} -\begin{methoddesc}{isheader}{line} +\begin{methoddesc}[Message]{isheader}{line} Returns a line's canonicalized fieldname (the dictionary key that will be used to index it) if the line is a legal \rfc{2822} header; otherwise returns \code{None} (implying that parsing should stop here and the @@ -155,33 +155,33 @@ line be pushed back on the input stream). It is sometimes useful to override this method in a subclass. \end{methoddesc} -\begin{methoddesc}{islast}{line} +\begin{methoddesc}[Message]{islast}{line} Return true if the given line is a delimiter on which Message should stop. The delimiter line is consumed, and the file object's read location positioned immediately after it. By default this method just checks that the line is blank, but you can override it in a subclass. \end{methoddesc} -\begin{methoddesc}{iscomment}{line} +\begin{methoddesc}[Message]{iscomment}{line} Return \code{True} if the given line should be ignored entirely, just skipped. By default this is a stub that always returns \code{False}, but you can override it in a subclass. \end{methoddesc} -\begin{methoddesc}{getallmatchingheaders}{name} +\begin{methoddesc}[Message]{getallmatchingheaders}{name} Return a list of lines consisting of all headers matching \var{name}, if any. Each physical line, whether it is a continuation line or not, is a separate list item. Return the empty list if no header matches \var{name}. \end{methoddesc} -\begin{methoddesc}{getfirstmatchingheader}{name} +\begin{methoddesc}[Message]{getfirstmatchingheader}{name} Return a list of lines comprising the first header matching \var{name}, and its continuation line(s), if any. Return \code{None} if there is no header matching \var{name}. \end{methoddesc} -\begin{methoddesc}{getrawheader}{name} +\begin{methoddesc}[Message]{getrawheader}{name} Return a single string consisting of the text after the colon in the first header matching \var{name}. This includes leading whitespace, the trailing linefeed, and internal linefeeds and whitespace if there @@ -189,19 +189,19 @@ any continuation line(s) were present. Return \code{None} if there is no header matching \var{name}. \end{methoddesc} -\begin{methoddesc}{getheader}{name\optional{, default}} +\begin{methoddesc}[Message]{getheader}{name\optional{, default}} Like \code{getrawheader(\var{name})}, but strip leading and trailing whitespace. Internal whitespace is not stripped. The optional \var{default} argument can be used to specify a different default to be returned when there is no header matching \var{name}. \end{methoddesc} -\begin{methoddesc}{get}{name\optional{, default}} +\begin{methoddesc}[Message]{get}{name\optional{, default}} An alias for \method{getheader()}, to make the interface more compatible with regular dictionaries. \end{methoddesc} -\begin{methoddesc}{getaddr}{name} +\begin{methoddesc}[Message]{getaddr}{name} Return a pair \code{(\var{full name}, \var{email address})} parsed from the string returned by \code{getheader(\var{name})}. If no header matching \var{name} exists, return \code{(None, None)}; @@ -217,7 +217,7 @@ If the header contained exact same result. \end{methoddesc} -\begin{methoddesc}{getaddrlist}{name} +\begin{methoddesc}[Message]{getaddrlist}{name} This is similar to \code{getaddr(\var{list})}, but parses a header containing a list of email addresses (e.g.\ a \mailheader{To} header) and returns a list of \code{(\var{full name}, \var{email address})} pairs @@ -229,7 +229,7 @@ are several \mailheader{Cc} headers), all are parsed for addresses. Any continuation lines the named headers contain are also parsed. \end{methoddesc} -\begin{methoddesc}{getdate}{name} +\begin{methoddesc}[Message]{getdate}{name} Retrieve a header using \method{getheader()} and parse it into a 9-tuple compatible with \function{time.mktime()}; note that fields 6, 7, and 8 are not usable. If there is no header matching @@ -241,7 +241,7 @@ collection of email from many sources, it is still possible that this function may occasionally yield an incorrect result. \end{methoddesc} -\begin{methoddesc}{getdate_tz}{name} +\begin{methoddesc}[Message]{getdate_tz}{name} Retrieve a header using \method{getheader()} and parse it into a 10-tuple; the first 9 elements will make a tuple compatible with \function{time.mktime()}, and the 10th is a number giving the offset @@ -270,19 +270,19 @@ support the \method{clear()}, \method{copy()}, \method{popitem()}, or Finally, \class{Message} instances have some public instance variables: -\begin{memberdesc}{headers} +\begin{memberdesc}[Message]{headers} A list containing the entire set of header lines, in the order in which they were read (except that setitem calls may disturb this order). Each line contains a trailing newline. The blank line terminating the headers is not contained in the list. \end{memberdesc} -\begin{memberdesc}{fp} +\begin{memberdesc}[Message]{fp} The file or file-like object passed at instantiation time. This can be used to read the message content. \end{memberdesc} -\begin{memberdesc}{unixfrom} +\begin{memberdesc}[Message]{unixfrom} The \UNIX{} \samp{From~} line, if the message had one, or an empty string. This is needed to regenerate the message in some contexts, such as an \code{mbox}-style mailbox file. @@ -293,34 +293,34 @@ such as an \code{mbox}-style mailbox file. An \class{AddressList} instance has the following methods: -\begin{methoddesc}{__len__}{} +\begin{methoddesc}[AddressList]{__len__}{} Return the number of addresses in the address list. \end{methoddesc} -\begin{methoddesc}{__str__}{} +\begin{methoddesc}[AddressList]{__str__}{} Return a canonicalized string representation of the address list. Addresses are rendered in "name" <host@domain> form, comma-separated. \end{methoddesc} -\begin{methoddesc}{__add__}{alist} +\begin{methoddesc}[AddressList]{__add__}{alist} Return a new \class{AddressList} instance that contains all addresses in both \class{AddressList} operands, with duplicates removed (set union). \end{methoddesc} -\begin{methoddesc}{__iadd__}{alist} +\begin{methoddesc}[AddressList]{__iadd__}{alist} In-place version of \method{__add__()}; turns this \class{AddressList} instance into the union of itself and the right-hand instance, \var{alist}. \end{methoddesc} -\begin{methoddesc}{__sub__}{alist} +\begin{methoddesc}[AddressList]{__sub__}{alist} Return a new \class{AddressList} instance that contains every address in the left-hand \class{AddressList} operand that is not present in the right-hand address operand (set difference). \end{methoddesc} -\begin{methoddesc}{__isub__}{alist} +\begin{methoddesc}[AddressList]{__isub__}{alist} In-place version of \method{__sub__()}, removing addresses in this list which are also in \var{alist}. \end{methoddesc} @@ -328,7 +328,7 @@ list which are also in \var{alist}. Finally, \class{AddressList} instances have one public instance variable: -\begin{memberdesc}{addresslist} +\begin{memberdesc}[AddressList]{addresslist} A list of tuple string pairs, one per address. In each member, the first is the canonicalized name part, the second is the actual route-address (\character{@}-separated username-host.domain diff --git a/Doc/lib/libsched.tex b/Doc/lib/libsched.tex index 6b586a8..75bab7e 100644 --- a/Doc/lib/libsched.tex +++ b/Doc/lib/libsched.tex @@ -48,7 +48,7 @@ From print_time 930343700.273 \class{scheduler} instances have the following methods: -\begin{methoddesc}{enterabs}{time, priority, action, argument} +\begin{methoddesc}[scheduler]{enterabs}{time, priority, action, argument} Schedule a new event. The \var{time} argument should be a numeric type compatible with the return value of the \var{timefunc} function passed to the constructor. Events scheduled for @@ -63,23 +63,23 @@ Return value is an event which may be used for later cancellation of the event (see \method{cancel()}). \end{methoddesc} -\begin{methoddesc}{enter}{delay, priority, action, argument} +\begin{methoddesc}[scheduler]{enter}{delay, priority, action, argument} Schedule an event for \var{delay} more time units. Other then the relative time, the other arguments, the effect and the return value are the same as those for \method{enterabs()}. \end{methoddesc} -\begin{methoddesc}{cancel}{event} +\begin{methoddesc}[scheduler]{cancel}{event} Remove the event from the queue. If \var{event} is not an event currently in the queue, this method will raise a \exception{RuntimeError}. \end{methoddesc} -\begin{methoddesc}{empty}{} +\begin{methoddesc}[scheduler]{empty}{} Return true if the event queue is empty. \end{methoddesc} -\begin{methoddesc}{run}{} +\begin{methoddesc}[scheduler]{run}{} Run all scheduled events. This function will wait (using the \function{delayfunc} function passed to the constructor) for the next event, then execute it and so on until there are no more diff --git a/Doc/lib/libselect.tex b/Doc/lib/libselect.tex index e93f70f..69583d4 100644 --- a/Doc/lib/libselect.tex +++ b/Doc/lib/libselect.tex @@ -77,7 +77,7 @@ afterward the whole bitmap has to be linearly scanned again. \cfunction{select()} is O(highest file descriptor), while \cfunction{poll()} is O(number of file descriptors). -\begin{methoddesc}{register}{fd\optional{, eventmask}} +\begin{methoddesc}[poll]{register}{fd\optional{, eventmask}} Register a file descriptor with the polling object. Future calls to the \method{poll()} method will then check whether the file descriptor has any pending I/O events. \var{fd} can be either an integer, or an @@ -105,7 +105,7 @@ error, and has the same effect as registering the descriptor exactly once. \end{methoddesc} -\begin{methoddesc}{unregister}{fd} +\begin{methoddesc}[poll]{unregister}{fd} Remove a file descriptor being tracked by a polling object. Just like the \method{register()} method, \var{fd} can be an integer or an object with a \method{fileno()} method that returns an integer. @@ -114,7 +114,7 @@ Attempting to remove a file descriptor that was never registered causes a \exception{KeyError} exception to be raised. \end{methoddesc} -\begin{methoddesc}{poll}{\optional{timeout}} +\begin{methoddesc}[poll]{poll}{\optional{timeout}} Polls the set of registered file descriptors, and returns a possibly-empty list containing \code{(\var{fd}, \var{event})} 2-tuples for the descriptors that have events or errors to report. diff --git a/Doc/lib/libshlex.tex b/Doc/lib/libshlex.tex index 3a43648..451615f 100644 --- a/Doc/lib/libshlex.tex +++ b/Doc/lib/libshlex.tex @@ -58,7 +58,7 @@ parsing rules. See section~\ref{shlex-objects}. A \class{shlex} instance has the following methods: -\begin{methoddesc}{get_token}{} +\begin{methoddesc}[shlex]{get_token}{} Return a token. If tokens have been stacked using \method{push_token()}, pop a token off the stack. Otherwise, read one from the input stream. If reading encounters an immediate @@ -66,17 +66,17 @@ end-of-file, \member{self.eof} is returned (the empty string (\code{''}) in non-\POSIX{} mode, and \code{None} in \POSIX{} mode). \end{methoddesc} -\begin{methoddesc}{push_token}{str} +\begin{methoddesc}[shlex]{push_token}{str} Push the argument onto the token stack. \end{methoddesc} -\begin{methoddesc}{read_token}{} +\begin{methoddesc}[shlex]{read_token}{} Read a raw token. Ignore the pushback stack, and do not interpret source requests. (This is not ordinarily a useful entry point, and is documented here only for the sake of completeness.) \end{methoddesc} -\begin{methoddesc}{sourcehook}{filename} +\begin{methoddesc}[shlex]{sourcehook}{filename} When \class{shlex} detects a source request (see \member{source} below) this method is given the following token as argument, and expected to return a tuple consisting of a filename and @@ -106,7 +106,7 @@ For more explicit control of source stacking, use the \method{push_source()} and \method{pop_source()} methods. \end{methoddesc} -\begin{methoddesc}{push_source}{stream\optional{, filename}} +\begin{methoddesc}[shlex]{push_source}{stream\optional{, filename}} Push an input source stream onto the input stack. If the filename argument is specified it will later be available for use in error messages. This is the same method used internally by the @@ -114,14 +114,14 @@ messages. This is the same method used internally by the \versionadded{2.1} \end{methoddesc} -\begin{methoddesc}{pop_source}{} +\begin{methoddesc}[shlex]{pop_source}{} Pop the last-pushed input source from the input stack. This is the same method used internally when the lexer reaches \EOF{} on a stacked input stream. \versionadded{2.1} \end{methoddesc} -\begin{methoddesc}{error_leader}{\optional{file\optional{, line}}} +\begin{methoddesc}[shlex]{error_leader}{\optional{file\optional{, line}}} This method generates an error message leader in the format of a \UNIX{} C compiler error label; the format is \code{'"\%s", line \%d: '}, where the \samp{\%s} is replaced with the name of the current source @@ -137,63 +137,63 @@ Instances of \class{shlex} subclasses have some public instance variables which either control lexical analysis or can be used for debugging: -\begin{memberdesc}{commenters} +\begin{memberdesc}[shlex]{commenters} The string of characters that are recognized as comment beginners. All characters from the comment beginner to end of line are ignored. Includes just \character{\#} by default. \end{memberdesc} -\begin{memberdesc}{wordchars} +\begin{memberdesc}[shlex]{wordchars} The string of characters that will accumulate into multi-character tokens. By default, includes all \ASCII{} alphanumerics and underscore. \end{memberdesc} -\begin{memberdesc}{whitespace} +\begin{memberdesc}[shlex]{whitespace} Characters that will be considered whitespace and skipped. Whitespace bounds tokens. By default, includes space, tab, linefeed and carriage-return. \end{memberdesc} -\begin{memberdesc}{escape} +\begin{memberdesc}[shlex]{escape} Characters that will be considered as escape. This will be only used in \POSIX{} mode, and includes just \character{\textbackslash} by default. \versionadded{2.3} \end{memberdesc} -\begin{memberdesc}{quotes} +\begin{memberdesc}[shlex]{quotes} Characters that will be considered string quotes. The token accumulates until the same quote is encountered again (thus, different quote types protect each other as in the shell.) By default, includes \ASCII{} single and double quotes. \end{memberdesc} -\begin{memberdesc}{escapedquotes} +\begin{memberdesc}[shlex]{escapedquotes} Characters in \member{quotes} that will interpret escape characters defined in \member{escape}. This is only used in \POSIX{} mode, and includes just \character{"} by default. \versionadded{2.3} \end{memberdesc} -\begin{memberdesc}{whitespace_split} +\begin{memberdesc}[shlex]{whitespace_split} If \code{True}, tokens will only be split in whitespaces. This is useful, for example, for parsing command lines with \class{shlex}, getting tokens in a similar way to shell arguments. \versionadded{2.3} \end{memberdesc} -\begin{memberdesc}{infile} +\begin{memberdesc}[shlex]{infile} The name of the current input file, as initially set at class instantiation time or stacked by later source requests. It may be useful to examine this when constructing error messages. \end{memberdesc} -\begin{memberdesc}{instream} +\begin{memberdesc}[shlex]{instream} The input stream from which this \class{shlex} instance is reading characters. \end{memberdesc} -\begin{memberdesc}{source} +\begin{memberdesc}[shlex]{source} This member is \code{None} by default. If you assign a string to it, that string will be recognized as a lexical-level inclusion request similar to the \samp{source} keyword in various shells. That is, the @@ -204,23 +204,23 @@ become the original input stream. Source requests may be stacked any number of levels deep. \end{memberdesc} -\begin{memberdesc}{debug} +\begin{memberdesc}[shlex]{debug} If this member is numeric and \code{1} or more, a \class{shlex} instance will print verbose progress output on its behavior. If you need to use this, you can read the module source code to learn the details. \end{memberdesc} -\begin{memberdesc}{lineno} +\begin{memberdesc}[shlex]{lineno} Source line number (count of newlines seen so far plus one). \end{memberdesc} -\begin{memberdesc}{token} +\begin{memberdesc}[shlex]{token} The token buffer. It may be useful to examine this when catching exceptions. \end{memberdesc} -\begin{memberdesc}{eof} +\begin{memberdesc}[shlex]{eof} Token used to determine end of file. This will be set to the empty string (\code{''}), in non-\POSIX{} mode, and to \code{None} in \POSIX{} mode. diff --git a/Doc/lib/libshutil.tex b/Doc/lib/libshutil.tex index ee3b535..5201332 100644 --- a/Doc/lib/libshutil.tex +++ b/Doc/lib/libshutil.tex @@ -34,7 +34,9 @@ file type and creator codes will not be correct. is the buffer size. In particular, a negative \var{length} value means to copy the data without looping over the source data in chunks; by default the data is read in chunks to avoid uncontrolled - memory consumption. + memory consumption. Note that if the current file position of the + \var{fsrc} object is not 0, only the contents from the current file + position to the end of the file will be copied. \end{funcdesc} \begin{funcdesc}{copymode}{src, dst} diff --git a/Doc/lib/libsimplexmlrpc.tex b/Doc/lib/libsimplexmlrpc.tex index 6b45855..235905e 100644 --- a/Doc/lib/libsimplexmlrpc.tex +++ b/Doc/lib/libsimplexmlrpc.tex @@ -15,7 +15,9 @@ CGI environment, using \class{CGIXMLRPCRequestHandler}. \begin{classdesc}{SimpleXMLRPCServer}{addr\optional{, requestHandler\optional{, - logRequests\optional{, allow_none\optional{, encoding}}}}} + logRequests\optional{, + allow_none\optional{, + encoding}}}}} Create a new server instance. This class provides methods for registration of functions that can be called by @@ -28,8 +30,13 @@ CGI environment, using \class{CGIXMLRPCRequestHandler}. setting this parameter to false will turn off logging. The \var{allow_none} and \var{encoding} parameters are passed on to \module{xmlrpclib} and control the XML-RPC responses that will be returned - from the server. + from the server. The \var{bind_and_activate} parameter controls whether + \method{server_bind()} and \method{server_activate()} are called immediately + by the constructor; it defaults to true. Setting it to false allows code to + manipulate the \var{allow_reuse_address} class variable before the address + is bound. \versionchanged[The \var{allow_none} and \var{encoding} parameters were added]{2.5} + \versionchanged[The \var{bind_and_activate} parameter was added]{2.6} \end{classdesc} \begin{classdesc}{CGIXMLRPCRequestHandler}{\optional{allow_none\optional{, encoding}}} @@ -101,13 +108,13 @@ simple, stand alone XML-RPC servers. \end{methoddesc} -\begin{methoddesc}{register_introspection_functions}{} +\begin{methoddesc}[SimpleXMLRPCServer]{register_introspection_functions}{} Registers the XML-RPC introspection functions \code{system.listMethods}, \code{system.methodHelp} and \code{system.methodSignature}. \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{register_multicall_functions}{} +\begin{methoddesc}[SimpleXMLRPCServer]{register_multicall_functions}{} Registers the XML-RPC multicall function system.multicall. \end{methoddesc} @@ -171,7 +178,7 @@ print s.system.listMethods() The \class{CGIXMLRPCRequestHandler} class can be used to handle XML-RPC requests sent to Python CGI scripts. -\begin{methoddesc}{register_function}{function\optional{, name}} +\begin{methoddesc}[CGIXMLRPCRequestHandler]{register_function}{function\optional{, name}} Register a function that can respond to XML-RPC requests. If \var{name} is given, it will be the method name associated with function, otherwise \var{function.__name__} will be used. \var{name} @@ -180,7 +187,7 @@ characters not legal in Python identifiers, including the period character. \end{methoddesc} -\begin{methoddesc}{register_instance}{instance} +\begin{methoddesc}[CGIXMLRPCRequestHandler]{register_instance}{instance} Register an object which is used to expose method names which have not been registered using \method{register_function()}. If instance contains a \method{_dispatch()} method, it is called with the @@ -196,17 +203,17 @@ parameters from the request, and the return value is passed back to the client. \end{methoddesc} -\begin{methoddesc}{register_introspection_functions}{} +\begin{methoddesc}[CGIXMLRPCRequestHandler]{register_introspection_functions}{} Register the XML-RPC introspection functions \code{system.listMethods}, \code{system.methodHelp} and \code{system.methodSignature}. \end{methoddesc} -\begin{methoddesc}{register_multicall_functions}{} +\begin{methoddesc}[CGIXMLRPCRequestHandler]{register_multicall_functions}{} Register the XML-RPC multicall function \code{system.multicall}. \end{methoddesc} -\begin{methoddesc}{handle_request}{\optional{request_text = None}} +\begin{methoddesc}[CGIXMLRPCRequestHandler]{handle_request}{\optional{request_text = None}} Handle a XML-RPC request. If \var{request_text} is given, it should be the POST data provided by the HTTP server, otherwise the contents of stdin will be used. diff --git a/Doc/lib/libsite.tex b/Doc/lib/libsite.tex index c079790..11858d1 100644 --- a/Doc/lib/libsite.tex +++ b/Doc/lib/libsite.tex @@ -28,12 +28,17 @@ the newly added path for configuration files. A path configuration file is a file whose name has the form \file{\var{package}.pth} and exists in one of the four directories -mentioned above; its contents are additional items (one -per line) to be added to \code{sys.path}. Non-existing items are -never added to \code{sys.path}, but no check is made that the item -refers to a directory (rather than a file). No item is added to -\code{sys.path} more than once. Blank lines and lines beginning with -\code{\#} are skipped. Lines starting with \code{import} are executed. +mentioned above; its contents are additional items (one per line) to +be added to \code{sys.path}. Non-existing items are never added to +\code{sys.path}, but no check is made that the item refers to a +directory (rather than a file). No item is added to \code{sys.path} +more than once. Blank lines and lines beginning with \code{\#} are +skipped. Lines starting with \code{import} (followed by space or tab) +are executed. + +\versionchanged[A space or tab is now required after the import +keyword]{2.6} + \index{package} \indexiii{path}{configuration}{file} diff --git a/Doc/lib/libsmtplib.tex b/Doc/lib/libsmtplib.tex index a873a9d..26293d6 100644 --- a/Doc/lib/libsmtplib.tex +++ b/Doc/lib/libsmtplib.tex @@ -15,13 +15,16 @@ listener daemon. For details of SMTP and ESMTP operation, consult (\citetitle{SMTP Service Extensions}). \begin{classdesc}{SMTP}{\optional{host\optional{, port\optional{, - local_hostname}}}} + local_hostname\optional{, timeout}}}}} A \class{SMTP} instance encapsulates an SMTP connection. It has methods that support a full repertoire of SMTP and ESMTP operations. If the optional host and port parameters are given, the SMTP \method{connect()} method is called with those parameters during initialization. An \exception{SMTPConnectError} is raised if the specified host doesn't respond correctly. +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global +default timeout setting will be used). For normal use, you should only require the initialization/connect, \method{sendmail()}, and \method{quit()} methods. An example is @@ -31,7 +34,7 @@ included below. \begin{classdesc}{SMTP_SSL}{\optional{host\optional{, port\optional{, local_hostname\optional{, keyfile\optional{, - certfile}}}}}} + certfile\optional{, timeout}}}}}}} A \class{SMTP_SSL} instance behaves exactly the same as instances of \class{SMTP}. \class{SMTP_SSL} should be used for situations where SSL is required from the beginning of the connection and using \method{starttls()} is not appropriate. @@ -39,6 +42,26 @@ If \var{host} is not specified, the local host is used. If \var{port} is omitted, the standard SMTP-over-SSL port (465) is used. \var{keyfile} and \var{certfile} are also optional, and can contain a PEM formatted private key and certificate chain file for the SSL connection. +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global +default timeout setting will be used). +\end{classdesc} + +\begin{classdesc}{LMTP}{\optional{host\optional{, port\optional{, + local_hostname}}}} + +The LMTP protocol, which is very similar to ESMTP, is heavily based +on the standard SMTP client. It's common to use Unix sockets for LMTP, +so our connect() method must support that as well as a regular +host:port server. To specify a Unix socket, you must use an absolute +path for \var{host}, starting with a '/'. + +Authentication is supported, using the regular SMTP mechanism. When +using a Unix socket, LMTP generally don't support or require any +authentication, but your mileage might vary. + +\versionadded{2.6} + \end{classdesc} A nice selection of exceptions is defined as well: @@ -103,13 +126,13 @@ A nice selection of exceptions is defined as well: An \class{SMTP} instance has the following methods: -\begin{methoddesc}{set_debuglevel}{level} +\begin{methoddesc}[SMTP]{set_debuglevel}{level} Set the debug output level. A true value for \var{level} results in debug messages for connection and for all messages sent to and received from the server. \end{methoddesc} -\begin{methoddesc}{connect}{\optional{host\optional{, port}}} +\begin{methoddesc}[SMTP]{connect}{\optional{host\optional{, port}}} Connect to a host on a given port. The defaults are to connect to the local host at the standard SMTP port (25). If the hostname ends with a colon (\character{:}) followed by a @@ -119,7 +142,7 @@ This method is automatically invoked by the constructor if a host is specified during instantiation. \end{methoddesc} -\begin{methoddesc}{docmd}{cmd, \optional{, argstring}} +\begin{methoddesc}[SMTP]{docmd}{cmd, \optional{, argstring}} Send a command \var{cmd} to the server. The optional argument \var{argstring} is simply concatenated to the command, separated by a space. @@ -136,7 +159,7 @@ If the connection to the server is lost while waiting for the reply, \exception{SMTPServerDisconnected} will be raised. \end{methoddesc} -\begin{methoddesc}{helo}{\optional{hostname}} +\begin{methoddesc}[SMTP]{helo}{\optional{hostname}} Identify yourself to the SMTP server using \samp{HELO}. The hostname argument defaults to the fully qualified domain name of the local host. @@ -146,7 +169,7 @@ explicitly. It will be implicitly called by the \method{sendmail()} when necessary. \end{methoddesc} -\begin{methoddesc}{ehlo}{\optional{hostname}} +\begin{methoddesc}[SMTP]{ehlo}{\optional{hostname}} Identify yourself to an ESMTP server using \samp{EHLO}. The hostname argument defaults to the fully qualified domain name of the local host. Examine the response for ESMTP option and store them for use by @@ -157,13 +180,13 @@ mail, it should not be necessary to call this method explicitly. It will be implicitly called by \method{sendmail()} when necessary. \end{methoddesc} -\begin{methoddesc}{has_extn}{name} +\begin{methoddesc}[SMTP]{has_extn}{name} Return \constant{True} if \var{name} is in the set of SMTP service extensions returned by the server, \constant{False} otherwise. Case is ignored. \end{methoddesc} -\begin{methoddesc}{verify}{address} +\begin{methoddesc}[SMTP]{verify}{address} Check the validity of an address on this server using SMTP \samp{VRFY}. Returns a tuple consisting of code 250 and a full \rfc{822} address (including human name) if the user address is valid. Otherwise returns @@ -172,7 +195,7 @@ an SMTP error code of 400 or greater and an error string. \note{Many sites disable SMTP \samp{VRFY} in order to foil spammers.} \end{methoddesc} -\begin{methoddesc}{login}{user, password} +\begin{methoddesc}[SMTP]{login}{user, password} Log in on an SMTP server that requires authentication. The arguments are the username and the password to authenticate with. If there has been no previous \samp{EHLO} or \samp{HELO} command this @@ -190,7 +213,7 @@ or may raise the following exceptions: \end{description} \end{methoddesc} -\begin{methoddesc}{starttls}{\optional{keyfile\optional{, certfile}}} +\begin{methoddesc}[SMTP]{starttls}{\optional{keyfile\optional{, certfile}}} Put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP commands that follow will be encrypted. You should then call \method{ehlo()} again. @@ -199,8 +222,8 @@ If \var{keyfile} and \var{certfile} are provided, these are passed to the \refmodule{socket} module's \function{ssl()} function. \end{methoddesc} -\begin{methoddesc}{sendmail}{from_addr, to_addrs, msg\optional{, - mail_options, rcpt_options}} +\begin{methoddesc}[SMTP]{sendmail}{from_addr, to_addrs, msg\optional{, + mail_options, rcpt_options}} Send mail. The required arguments are an \rfc{822} from-address string, a list of \rfc{822} to-address strings (a bare string will be treated as a list with 1 address), and a message string. The caller @@ -256,7 +279,7 @@ an exception is raised. \end{methoddesc} -\begin{methoddesc}{quit}{} +\begin{methoddesc}[SMTP]{quit}{} Terminate the SMTP session and close the connection. \end{methoddesc} diff --git a/Doc/lib/libsocket.tex b/Doc/lib/libsocket.tex index 1a231d3..ff0fb87 100644 --- a/Doc/lib/libsocket.tex +++ b/Doc/lib/libsocket.tex @@ -14,7 +14,7 @@ For an introduction to socket programming (in C), see the following papers: \citetitle{An Introductory 4.3BSD Interprocess Communication Tutorial}, by Stuart Sechrest and \citetitle{An Advanced 4.3BSD Interprocess Communication Tutorial}, by Samuel J. Leffler et al, -both in the \citetitle{\UNIX{} Programmer's Manual, Supplementary Documents 1} +both in the \citetitle{UNIX Programmer's Manual, Supplementary Documents 1} (sections PS1:7 and PS1:8). The platform-specific reference material for the various socket-related system calls are also a valuable source of information on the details of socket semantics. For \UNIX, refer @@ -170,6 +170,15 @@ supported on this platform. \versionadded{2.3} \end{datadesc} +\begin{funcdesc}{create_connection}{address\optional{, timeout}} +Connects to the \var{address} received (as usual, a \code{(host, port)} +pair), with an optional timeout for the connection. Specially useful for +higher-level protocols, it is not normally used directly from +application-level code. Passing the optional \var{timeout} parameter +will set the timeout on the socket instance (if it is not given or +\code{None}, the global default timeout setting is used). +\end{funcdesc} + \begin{funcdesc}{getaddrinfo}{host, port\optional{, family\optional{, socktype\optional{, proto\optional{, flags}}}}} @@ -548,7 +557,7 @@ are described in \ref{bltin-file-objects}, ``File Objects.'') The file object references a \cfunction{dup()}ped version of the socket file descriptor, so the file object and socket object may be closed or garbage-collected independently. -The socket must be in blocking mode. +The socket must be in blocking mode (it can not have a timeout). \index{I/O control!buffering}The optional \var{mode} and \var{bufsize} arguments are interpreted the same way as by the built-in \function{file()} function; see ``Built-in Functions'' @@ -584,6 +593,7 @@ sending the data. See the \UNIX{} manual page \manpage{recv}{2} for the meaning of the optional argument \var{flags}; it defaults to zero. (The format of \var{address} depends on the address family --- see above.) +\versionadded{2.5} \end{methoddesc} \begin{methoddesc}[socket]{recv_into}{buffer\optional{, nbytes\optional{, flags}}} @@ -593,6 +603,7 @@ If \var{nbytes} is not specified (or 0), receive up to the size available in the given buffer. See the \UNIX{} manual page \manpage{recv}{2} for the meaning of the optional argument \var{flags}; it defaults to zero. +\versionadded{2.5} \end{methoddesc} \begin{methoddesc}[socket]{send}{string\optional{, flags}} @@ -722,23 +733,23 @@ The socket protocol. SSL objects have the following methods. -\begin{methoddesc}{write}{s} +\begin{methoddesc}[SSL]{write}{s} Writes the string \var{s} to the on the object's SSL connection. The return value is the number of bytes written. \end{methoddesc} -\begin{methoddesc}{read}{\optional{n}} +\begin{methoddesc}[SSL]{read}{\optional{n}} If \var{n} is provided, read \var{n} bytes from the SSL connection, otherwise read until EOF. The return value is a string of the bytes read. \end{methoddesc} -\begin{methoddesc}{server}{} +\begin{methoddesc}[SSL]{server}{} Returns a string describing the server's certificate. Useful for debugging purposes; do not parse the content of this string because its format can't be parsed unambiguously. \end{methoddesc} -\begin{methoddesc}{issuer}{} +\begin{methoddesc}[SSL]{issuer}{} Returns a string describing the issuer of the server's certificate. Useful for debugging purposes; do not parse the content of this string because its format can't be parsed unambiguously. diff --git a/Doc/lib/libsqlite3.tex b/Doc/lib/libsqlite3.tex index aeb60c1..19eed7e 100644 --- a/Doc/lib/libsqlite3.tex +++ b/Doc/lib/libsqlite3.tex @@ -210,37 +210,37 @@ the feature again. A \class{Connection} instance has the following attributes and methods: \label{sqlite3-Connection-IsolationLevel} -\begin{memberdesc}{isolation_level} +\begin{memberdesc}[Connection]{isolation_level} Get or set the current isolation level. None for autocommit mode or one of "DEFERRED", "IMMEDIATE" or "EXLUSIVE". See ``Controlling Transactions'', section~\ref{sqlite3-Controlling-Transactions}, for a more detailed explanation. \end{memberdesc} -\begin{methoddesc}{cursor}{\optional{cursorClass}} +\begin{methoddesc}[Connection]{cursor}{\optional{cursorClass}} The cursor method accepts a single optional parameter \var{cursorClass}. If supplied, this must be a custom cursor class that extends \class{sqlite3.Cursor}. \end{methoddesc} -\begin{methoddesc}{execute}{sql, \optional{parameters}} +\begin{methoddesc}[Connection]{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}} +\begin{methoddesc}[Connection]{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} +\begin{methoddesc}[Connection]{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} +\begin{methoddesc}[Connection]{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 @@ -255,7 +255,7 @@ Example: \verbatiminput{sqlite3/md5func.py} \end{methoddesc} -\begin{methoddesc}{create_aggregate}{name, num_params, aggregate_class} +\begin{methoddesc}[Connection]{create_aggregate}{name, num_params, aggregate_class} Creates a user-defined aggregate function. @@ -271,7 +271,7 @@ Example: \verbatiminput{sqlite3/mysumaggr.py} \end{methoddesc} -\begin{methoddesc}{create_collation}{name, callable} +\begin{methoddesc}[Connection]{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 @@ -293,14 +293,14 @@ To remove a collation, call \code{create_collation} with None as callable: \end{verbatim} \end{methoddesc} -\begin{methoddesc}{interrupt}{} +\begin{methoddesc}[Connection]{interrupt}{} You can call this method from a different thread to abort any queries that might be executing on the connection. The query will then abort and the caller will get an exception. \end{methoddesc} -\begin{methoddesc}{set_authorizer}{authorizer_callback} +\begin{methoddesc}[Connection]{set_authorizer}{authorizer_callback} This routine registers a callback. The callback is invoked for each attempt to access a column of a table in the database. The callback should return @@ -322,7 +322,7 @@ first one. All necessary constants are available in the \module{sqlite3} module. \end{methoddesc} -\begin{memberdesc}{row_factory} +\begin{memberdesc}[Connection]{row_factory} You can change this attribute to a callable that accepts the cursor and the original row as a tuple and will return the real result row. This way, you can implement more advanced ways of returning results, such @@ -341,7 +341,7 @@ module. % XXX what's a db_row-based solution? \end{memberdesc} -\begin{memberdesc}{text_factory} +\begin{memberdesc}[Connection]{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 @@ -359,7 +359,7 @@ module. \verbatiminput{sqlite3/text_factory.py} \end{memberdesc} -\begin{memberdesc}{total_changes} +\begin{memberdesc}[Connection]{total_changes} Returns the total number of database rows that have been modified, inserted, or deleted since the database connection was opened. \end{memberdesc} @@ -372,7 +372,7 @@ module. A \class{Cursor} instance has the following attributes and methods: -\begin{methoddesc}{execute}{sql, \optional{parameters}} +\begin{methoddesc}[Cursor]{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 @@ -394,7 +394,7 @@ This example shows how to use the named style: \end{methoddesc} -\begin{methoddesc}{executemany}{sql, seq_of_parameters} +\begin{methoddesc}[Cursor]{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 using an iterator yielding parameters instead of a sequence. @@ -406,7 +406,7 @@ Here's a shorter example using a generator: \verbatiminput{sqlite3/executemany_2.py} \end{methoddesc} -\begin{methoddesc}{executescript}{sql_script} +\begin{methoddesc}[Cursor]{executescript}{sql_script} This is a nonstandard convenience method for executing multiple SQL statements at once. It issues a COMMIT statement first, then executes the SQL script it @@ -419,7 +419,7 @@ Example: \verbatiminput{sqlite3/executescript.py} \end{methoddesc} -\begin{memberdesc}{rowcount} +\begin{memberdesc}[Cursor]{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. diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index 66cc476..0c45f18 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -559,25 +559,25 @@ linear concatenation performance across versions and implementations. These are the string methods which both 8-bit strings and Unicode objects support: -\begin{methoddesc}[string]{capitalize}{} +\begin{methoddesc}[str]{capitalize}{} Return a copy of the string with only its first character capitalized. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{center}{width\optional{, fillchar}} +\begin{methoddesc}[str]{center}{width\optional{, fillchar}} Return centered in a string of length \var{width}. Padding is done using the specified \var{fillchar} (default is a space). \versionchanged[Support for the \var{fillchar} argument]{2.4} \end{methoddesc} -\begin{methoddesc}[string]{count}{sub\optional{, start\optional{, end}}} +\begin{methoddesc}[str]{count}{sub\optional{, start\optional{, end}}} Return the number of occurrences of substring \var{sub} in string S\code{[\var{start}:\var{end}]}. Optional arguments \var{start} and \var{end} are interpreted as in slice notation. \end{methoddesc} -\begin{methoddesc}[string]{decode}{\optional{encoding\optional{, errors}}} +\begin{methoddesc}[str]{decode}{\optional{encoding\optional{, errors}}} Decodes the string using the codec registered for \var{encoding}. \var{encoding} defaults to the default string encoding. \var{errors} may be given to set a different error handling scheme. The default is @@ -589,7 +589,7 @@ may be given to set a different error handling scheme. The default is \versionchanged[Support for other error handling schemes added]{2.3} \end{methoddesc} -\begin{methoddesc}[string]{encode}{\optional{encoding\optional{,errors}}} +\begin{methoddesc}[str]{encode}{\optional{encoding\optional{,errors}}} Return an encoded version of the string. Default encoding is the current default string encoding. \var{errors} may be given to set a different error handling scheme. The default for \var{errors} is @@ -604,7 +604,7 @@ For a list of possible encodings, see section~\ref{standard-encodings}. \code{'backslashreplace'} and other error handling schemes added]{2.3} \end{methoddesc} -\begin{methoddesc}[string]{endswith}{suffix\optional{, start\optional{, end}}} +\begin{methoddesc}[str]{endswith}{suffix\optional{, start\optional{, end}}} Return \code{True} if the string ends with the specified \var{suffix}, otherwise return \code{False}. \var{suffix} can also be a tuple of suffixes to look for. With optional \var{start}, test beginning at @@ -613,13 +613,13 @@ that position. With optional \var{end}, stop comparing at that position. \versionchanged[Accept tuples as \var{suffix}]{2.5} \end{methoddesc} -\begin{methoddesc}[string]{expandtabs}{\optional{tabsize}} +\begin{methoddesc}[str]{expandtabs}{\optional{tabsize}} Return a copy of the string where all tab characters are expanded using spaces. If \var{tabsize} is not given, a tab size of \code{8} characters is assumed. \end{methoddesc} -\begin{methoddesc}[string]{find}{sub\optional{, start\optional{, end}}} +\begin{methoddesc}[str]{find}{sub\optional{, start\optional{, end}}} Return the lowest index in the string where substring \var{sub} is found, such that \var{sub} is contained in the range [\var{start}, \var{end}]. Optional arguments \var{start} and \var{end} are @@ -627,47 +627,47 @@ interpreted as in slice notation. Return \code{-1} if \var{sub} is not found. \end{methoddesc} -\begin{methoddesc}[string]{index}{sub\optional{, start\optional{, end}}} +\begin{methoddesc}[str]{index}{sub\optional{, start\optional{, end}}} Like \method{find()}, but raise \exception{ValueError} when the substring is not found. \end{methoddesc} -\begin{methoddesc}[string]{isalnum}{} +\begin{methoddesc}[str]{isalnum}{} Return true if all characters in the string are alphanumeric and there is at least one character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{isalpha}{} +\begin{methoddesc}[str]{isalpha}{} Return true if all characters in the string are alphabetic and there is at least one character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{isdigit}{} +\begin{methoddesc}[str]{isdigit}{} Return true if all characters in the string are digits and there is at least one character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{islower}{} +\begin{methoddesc}[str]{islower}{} Return true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{isspace}{} +\begin{methoddesc}[str]{isspace}{} Return true if there are only whitespace characters in the string and there is at least one character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{istitle}{} +\begin{methoddesc}[str]{istitle}{} Return true if the string is a titlecased string and there is at least one character, for example uppercase characters may only follow uncased characters and lowercase characters only cased ones. Return false @@ -676,20 +676,20 @@ otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{isupper}{} +\begin{methoddesc}[str]{isupper}{} Return true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{join}{seq} +\begin{methoddesc}[str]{join}{seq} Return a string which is the concatenation of the strings in the sequence \var{seq}. The separator between elements is the string providing this method. \end{methoddesc} -\begin{methoddesc}[string]{ljust}{width\optional{, fillchar}} +\begin{methoddesc}[str]{ljust}{width\optional{, fillchar}} Return the string left justified in a string of length \var{width}. Padding is done using the specified \var{fillchar} (default is a space). The original string is returned if @@ -697,13 +697,13 @@ space). The original string is returned if \versionchanged[Support for the \var{fillchar} argument]{2.4} \end{methoddesc} -\begin{methoddesc}[string]{lower}{} +\begin{methoddesc}[str]{lower}{} Return a copy of the string converted to lowercase. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{lstrip}{\optional{chars}} +\begin{methoddesc}[str]{lstrip}{\optional{chars}} Return a copy of the string with leading characters removed. The \var{chars} argument is a string specifying the set of characters to be removed. If omitted or \code{None}, the \var{chars} argument @@ -718,7 +718,7 @@ 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} +\begin{methoddesc}[str]{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 @@ -727,26 +727,26 @@ two empty strings. \versionadded{2.5} \end{methoddesc} -\begin{methoddesc}[string]{replace}{old, new\optional{, count}} +\begin{methoddesc}[str]{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 \var{count} is given, only the first \var{count} occurrences are replaced. \end{methoddesc} -\begin{methoddesc}[string]{rfind}{sub \optional{,start \optional{,end}}} +\begin{methoddesc}[str]{rfind}{sub \optional{,start \optional{,end}}} Return the highest index in the string where substring \var{sub} is found, such that \var{sub} is contained within s[start,end]. Optional arguments \var{start} and \var{end} are interpreted as in slice notation. Return \code{-1} on failure. \end{methoddesc} -\begin{methoddesc}[string]{rindex}{sub\optional{, start\optional{, end}}} +\begin{methoddesc}[str]{rindex}{sub\optional{, start\optional{, end}}} Like \method{rfind()} but raises \exception{ValueError} when the substring \var{sub} is not found. \end{methoddesc} -\begin{methoddesc}[string]{rjust}{width\optional{, fillchar}} +\begin{methoddesc}[str]{rjust}{width\optional{, fillchar}} Return the string right justified in a string of length \var{width}. Padding is done using the specified \var{fillchar} (default is a space). The original string is returned if @@ -754,7 +754,7 @@ The original string is returned if \versionchanged[Support for the \var{fillchar} argument]{2.4} \end{methoddesc} -\begin{methoddesc}[string]{rpartition}{sep} +\begin{methoddesc}[str]{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 @@ -763,7 +763,7 @@ the string itself. \versionadded{2.5} \end{methoddesc} -\begin{methoddesc}[string]{rsplit}{\optional{sep \optional{,maxsplit}}} +\begin{methoddesc}[str]{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} splits are done, the \emph{rightmost} ones. If \var{sep} is not specified @@ -773,7 +773,7 @@ is described in detail below. \versionadded{2.4} \end{methoddesc} -\begin{methoddesc}[string]{rstrip}{\optional{chars}} +\begin{methoddesc}[str]{rstrip}{\optional{chars}} Return a copy of the string with trailing characters removed. The \var{chars} argument is a string specifying the set of characters to be removed. If omitted or \code{None}, the \var{chars} argument @@ -788,7 +788,7 @@ a suffix; rather, all combinations of its values are stripped: \versionchanged[Support for the \var{chars} argument]{2.2.2} \end{methoddesc} -\begin{methoddesc}[string]{split}{\optional{sep \optional{,maxsplit}}} +\begin{methoddesc}[str]{split}{\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} splits are done. (thus, the list will have at most \code{\var{maxsplit}+1} @@ -811,13 +811,13 @@ Splitting an empty string or a string consisting of just whitespace returns an empty list. \end{methoddesc} -\begin{methoddesc}[string]{splitlines}{\optional{keepends}} +\begin{methoddesc}[str]{splitlines}{\optional{keepends}} Return a list of the lines in the string, breaking at line boundaries. Line breaks are not included in the resulting list unless \var{keepends} is given and true. \end{methoddesc} -\begin{methoddesc}[string]{startswith}{prefix\optional{, +\begin{methoddesc}[str]{startswith}{prefix\optional{, start\optional{, end}}} Return \code{True} if string starts with the \var{prefix}, otherwise return \code{False}. \var{prefix} can also be a tuple of @@ -828,7 +828,7 @@ position. \versionchanged[Accept tuples as \var{prefix}]{2.5} \end{methoddesc} -\begin{methoddesc}[string]{strip}{\optional{chars}} +\begin{methoddesc}[str]{strip}{\optional{chars}} Return a copy of the string with the leading and trailing characters removed. The \var{chars} argument is a string specifying the set of characters to be removed. If omitted or \code{None}, the \var{chars} @@ -843,21 +843,21 @@ a prefix or suffix; rather, all combinations of its values are stripped: \versionchanged[Support for the \var{chars} argument]{2.2.2} \end{methoddesc} -\begin{methoddesc}[string]{swapcase}{} +\begin{methoddesc}[str]{swapcase}{} Return a copy of the string with uppercase characters converted to lowercase and vice versa. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{title}{} +\begin{methoddesc}[str]{title}{} Return a titlecased version of the string: words start with uppercase characters, all remaining cased characters are lowercase. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{translate}{table\optional{, deletechars}} +\begin{methoddesc}[str]{translate}{table\optional{, deletechars}} Return a copy of the string where all characters occurring in the optional argument \var{deletechars} are removed, and the remaining characters have been mapped through the given translation table, which @@ -865,6 +865,13 @@ must be a string of length 256. You can use the \function{maketrans()} helper function in the \refmodule{string} module to create a translation table. +For string objects, set the \var{table} argument to \code{None} +for translations that only delete characters: +\begin{verbatim} + >>> 'read this short text'.translate(None, 'aeiou') + 'rd ths shrt txt' +\end{verbatim} +\versionadded[Support for a \code{None} \var{table} argument]{2.6} For Unicode objects, the \method{translate()} method does not accept the optional \var{deletechars} argument. Instead, it @@ -877,13 +884,13 @@ character mapping codec using the \refmodule{codecs} module (see \module{encodings.cp1251} for an example). \end{methoddesc} -\begin{methoddesc}[string]{upper}{} +\begin{methoddesc}[str]{upper}{} Return a copy of the string converted to uppercase. For 8-bit strings, this method is locale-dependent. \end{methoddesc} -\begin{methoddesc}[string]{zfill}{width} +\begin{methoddesc}[str]{zfill}{width} Return the numeric string left filled with zeros in a string of length \var{width}. The original string is returned if \var{width} is less than \code{len(\var{s})}. @@ -1592,14 +1599,15 @@ A file object is its own iterator, for example \code{iter(\var{f})} returns iterator, typically in a \keyword{for} loop (for example, \code{for line in f: print line}), the \method{__next__()} method is called repeatedly. This method returns the next input line, or raises -\exception{StopIteration} when \EOF{} is hit. In order to make a -\keyword{for} loop the most efficient way of looping over the lines of -a file (a very common operation), the \method{__next__()} method uses a -hidden read-ahead buffer. As a consequence of using a read-ahead -buffer, combining \method{__next__()} with other file methods (like -\method{readline()}) does not work right. However, using -\method{seek()} to reposition the file to an absolute position will -flush the read-ahead buffer. +\exception{StopIteration} when \EOF{} is hit when the file is open for +reading (behavior is undefined when the file is open for writing). In +order to make a \keyword{for} loop the most efficient way of looping +over the lines of a file (a very common operation), the +\method{__next__()} method uses a hidden read-ahead buffer. As a +consequence of using a read-ahead buffer, combining \method{__next__()} +with other file methods (like \method{readline()}) does not work +right. However, using \method{seek()} to reposition the file to an +absolute position will flush the read-ahead buffer. \versionadded{2.3} \end{methoddesc} diff --git a/Doc/lib/libstring.tex b/Doc/lib/libstring.tex index bc1649f..055ac0c 100644 --- a/Doc/lib/libstring.tex +++ b/Doc/lib/libstring.tex @@ -419,7 +419,8 @@ parameter cannot be passed in earlier 2.2 versions]{2.2.3} Delete all characters from \var{s} that are in \var{deletechars} (if present), and then translate the characters using \var{table}, which must be a 256-character string giving the translation for each - character value, indexed by its ordinal. + character value, indexed by its ordinal. If \var{table} is \code{None}, + then only the character deletion step is performed. \end{funcdesc} \begin{funcdesc}{upper}{s} diff --git a/Doc/lib/libsubprocess.tex b/Doc/lib/libsubprocess.tex index cb30974..4a57350 100644 --- a/Doc/lib/libsubprocess.tex +++ b/Doc/lib/libsubprocess.tex @@ -176,16 +176,16 @@ metacharacters, can safely be passed to child processes. Instances of the \class{Popen} class have the following methods: -\begin{methoddesc}{poll}{} +\begin{methoddesc}[Popen]{poll}{} Check if child process has terminated. Returns returncode attribute. \end{methoddesc} -\begin{methoddesc}{wait}{} +\begin{methoddesc}[Popen]{wait}{} Wait for child process to terminate. Returns returncode attribute. \end{methoddesc} -\begin{methoddesc}{communicate}{input=None} +\begin{methoddesc}[Popen]{communicate}{input=None} Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional \var{input} argument should be a string to be sent to the @@ -199,29 +199,29 @@ if the data size is large or unlimited.} The following attributes are also available: -\begin{memberdesc}{stdin} +\begin{memberdesc}[Popen]{stdin} If the \var{stdin} argument is \code{PIPE}, this attribute is a file object that provides input to the child process. Otherwise, it is \code{None}. \end{memberdesc} -\begin{memberdesc}{stdout} +\begin{memberdesc}[Popen]{stdout} If the \var{stdout} argument is \code{PIPE}, this attribute is a file object that provides output from the child process. Otherwise, it is \code{None}. \end{memberdesc} -\begin{memberdesc}{stderr} +\begin{memberdesc}[Popen]{stderr} If the \var{stderr} argument is \code{PIPE}, this attribute is file object that provides error output from the child process. Otherwise, it is \code{None}. \end{memberdesc} -\begin{memberdesc}{pid} +\begin{memberdesc}[Popen]{pid} The process ID of the child process. \end{memberdesc} -\begin{memberdesc}{returncode} +\begin{memberdesc}[Popen]{returncode} The child return code. A \code{None} value indicates that the process hasn't terminated yet. A negative value -N indicates that the child was terminated by signal N (\UNIX{} only). diff --git a/Doc/lib/libsun.tex b/Doc/lib/libsun.tex index 1472509..8fcfb6a 100644 --- a/Doc/lib/libsun.tex +++ b/Doc/lib/libsun.tex @@ -3,3 +3,5 @@ The modules described in this chapter provide interfaces to features that are unique to SunOS 5 (also known as Solaris version 2). + +\localmoduletable diff --git a/Doc/lib/libsys.tex b/Doc/lib/libsys.tex index ac161d9..1284668 100644 --- a/Doc/lib/libsys.tex +++ b/Doc/lib/libsys.tex @@ -15,8 +15,8 @@ It is always available. whether this is a full pathname or not). If the command was executed using the \programopt{-c} command line option to the interpreter, \code{argv[0]} is set to the string \code{'-c'}. If no - script name was passed to the Python interpreter, \code{argv} has - zero length. + script name was passed to the Python interpreter, \code{argv[0]} is + the empty string. \end{datadesc} \begin{datadesc}{byteorder} diff --git a/Doc/lib/libtarfile.tex b/Doc/lib/libtarfile.tex index 96f835f..73c35ed 100644 --- a/Doc/lib/libtarfile.tex +++ b/Doc/lib/libtarfile.tex @@ -12,21 +12,24 @@ Some facts and figures: \begin{itemize} \item reads and writes \module{gzip} and \module{bzip2} compressed archives. -\item creates \POSIX{} 1003.1-1990 compliant or GNU tar compatible archives. -\item reads GNU tar extensions \emph{longname}, \emph{longlink} and - \emph{sparse}. -\item stores pathnames of unlimited length using GNU tar extensions. +\item read/write support for the \POSIX{}.1-1988 (ustar) format. +\item read/write support for the GNU tar format including \emph{longname} and + \emph{longlink} extensions, read-only support for the \emph{sparse} + extension. +\item read/write support for the \POSIX{}.1-2001 (pax) format. + \versionadded{2.6} \item handles directories, regular files, hardlinks, symbolic links, fifos, character devices and block devices and is able to acquire and restore file information like timestamp, access permissions and owner. \item can handle tape devices. \end{itemize} -\begin{funcdesc}{open}{\optional{name\optional{, mode - \optional{, fileobj\optional{, bufsize}}}}} +\begin{funcdesc}{open}{name\optional{, mode\optional{, + fileobj\optional{, bufsize}}}, **kwargs} Return a \class{TarFile} object for the pathname \var{name}. - For detailed information on \class{TarFile} objects, - see \citetitle{TarFile Objects} (section \ref{tarfile-objects}). + For detailed information on \class{TarFile} objects and the keyword + arguments that are allowed, see \citetitle{TarFile Objects} + (section \ref{tarfile-objects}). \var{mode} has to be a string of the form \code{'filemode[:compression]'}, it defaults to \code{'r'}. Here is a full list of mode combinations: @@ -130,6 +133,31 @@ Some facts and figures: \versionadded{2.6} \end{excdesc} +\begin{datadesc}{USTAR_FORMAT} + \POSIX{}.1-1988 (ustar) format. It supports filenames up to a length of + at best 256 characters and linknames up to 100 characters. The maximum + file size is 8 gigabytes. This is an old and limited but widely + supported format. +\end{datadesc} + +\begin{datadesc}{GNU_FORMAT} + GNU tar format. It supports arbitrarily long filenames and linknames and + files bigger than 8 gigabytes. It is the defacto standard on GNU/Linux + systems. +\end{datadesc} + +\begin{datadesc}{PAX_FORMAT} + \POSIX{}.1-2001 (pax) format. It is the most flexible format with + virtually no limits. It supports long filenames and linknames, large files + and stores pathnames in a portable way. However, not all tar + implementations today are able to handle pax archives properly. +\end{datadesc} + +\begin{datadesc}{DEFAULT_FORMAT} + The default format for creating archives. This is currently + \constant{GNU_FORMAT}. +\end{datadesc} + \begin{seealso} \seemodule{zipfile}{Documentation of the \refmodule{zipfile} standard module.} @@ -152,12 +180,21 @@ tar archive several times. Each archive member is represented by a \class{TarInfo} object, see \citetitle{TarInfo Objects} (section \ref{tarinfo-objects}) for details. -\begin{classdesc}{TarFile}{\optional{name - \optional{, mode\optional{, fileobj}}}} - Open an \emph{(uncompressed)} tar archive \var{name}. +\begin{classdesc}{TarFile}{name=None, mode='r', fileobj=None, + format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, + ignore_zeros=False, encoding=None, pax_headers=None, debug=0, + errorlevel=0} + + All following arguments are optional and can be accessed as instance + attributes as well. + + \var{name} is the pathname of the archive. It can be omitted if + \var{fileobj} is given. In this case, the file object's \member{name} + attribute is used if it exists. + \var{mode} is either \code{'r'} to read from an existing archive, \code{'a'} to append data to an existing file or \code{'w'} to create a new - file overwriting an existing one. \var{mode} defaults to \code{'r'}. + file overwriting an existing one. If \var{fileobj} is given, it is used for reading or writing data. If it can be determined, \var{mode} is overridden by \var{fileobj}'s mode. @@ -165,6 +202,48 @@ tar archive several times. Each archive member is represented by a \begin{notice} \var{fileobj} is not closed, when \class{TarFile} is closed. \end{notice} + + \var{format} controls the archive format. It must be one of the constants + \constant{USTAR_FORMAT}, \constant{GNU_FORMAT} or \constant{PAX_FORMAT} + that are defined at module level. + \versionadded{2.6} + + The \var{tarinfo} argument can be used to replace the default + \class{TarInfo} class with a different one. + \versionadded{2.6} + + If \var{dereference} is \code{False}, add symbolic and hard links to the + archive. If it is \code{True}, add the content of the target files to the + archive. This has no effect on systems that do not support symbolic links. + + If \var{ignore_zeros} is \code{False}, treat an empty block as the end of + the archive. If it is \var{True}, skip empty (and invalid) blocks and try + to get as many members as possible. This is only useful for reading + concatenated or damaged archives. + + \var{debug} can be set from \code{0} (no debug messages) up to \code{3} + (all debug messages). The messages are written to \code{sys.stderr}. + + If \var{errorlevel} is \code{0}, all errors are ignored when using + \method{extract()}. Nevertheless, they appear as error messages in the + debug output, when debugging is enabled. If \code{1}, all \emph{fatal} + errors are raised as \exception{OSError} or \exception{IOError} exceptions. + If \code{2}, all \emph{non-fatal} errors are raised as \exception{TarError} + exceptions as well. + + The \var{encoding} argument defines the local character encoding. It + defaults to the value from \function{sys.getfilesystemencoding()} or if + that is \code{None} to \code{"ascii"}. \var{encoding} is used only in + connection with the pax format which stores text data in \emph{UTF-8}. If + it is not set correctly, character conversion will fail with a + \exception{UnicodeError}. + \versionadded{2.6} + + The \var{pax_headers} argument must be a dictionary whose elements are + either unicode objects, numbers or strings that can be decoded to unicode + using \var{encoding}. This information will be added to the archive as a + pax global header. + \versionadded{2.6} \end{classdesc} \begin{methoddesc}{open}{...} @@ -279,43 +358,11 @@ tar archive several times. Each archive member is represented by a \end{methoddesc} \begin{memberdesc}{posix} - If true, create a \POSIX{} 1003.1-1990 compliant archive. GNU - extensions are not used, because they are not part of the \POSIX{} - standard. This limits the length of filenames to at most 256, - link names to 100 characters and the maximum file size to 8 - gigabytes. A \exception{ValueError} is raised if a file exceeds - this limit. If false, create a GNU tar compatible archive. It - will not be \POSIX{} compliant, but can store files without any - of the above restrictions. + Setting this to \constant{True} is equivalent to setting the + \member{format} attribute to \constant{USTAR_FORMAT}, + \constant{False} is equivalent to \constant{GNU_FORMAT}. \versionchanged[\var{posix} defaults to \constant{False}]{2.4} -\end{memberdesc} - -\begin{memberdesc}{dereference} - If false, add symbolic and hard links to archive. If true, add the - content of the target files to the archive. This has no effect on - systems that do not support symbolic links. -\end{memberdesc} - -\begin{memberdesc}{ignore_zeros} - If false, treat an empty block as the end of the archive. If true, - skip empty (and invalid) blocks and try to get as many members as - possible. This is only useful for concatenated or damaged - archives. -\end{memberdesc} - -\begin{memberdesc}{debug=0} - To be set from \code{0} (no debug messages; the default) up to - \code{3} (all debug messages). The messages are written to - \code{sys.stderr}. -\end{memberdesc} - -\begin{memberdesc}{errorlevel} - If \code{0} (the default), all errors are ignored when using - \method{extract()}. Nevertheless, they appear as error messages - in the debug output, when debugging is enabled. If \code{1}, all - \emph{fatal} errors are raised as \exception{OSError} or - \exception{IOError} exceptions. If \code{2}, all \emph{non-fatal} - errors are raised as \exception{TarError} exceptions as well. + \deprecated{2.6}{Use the \member{format} attribute instead.} \end{memberdesc} %----------------- @@ -343,12 +390,16 @@ the file's data itself. invalid.]{2.6} \end{methoddesc} -\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}. +\begin{methoddesc}{fromtarfile}{tarfile} + Read the next member from the \class{TarFile} object \var{tarfile} and + return it as a \class{TarInfo} object. + \versionadded{2.6} +\end{methoddesc} - \versionadded[The \var{posix} parameter]{2.5} +\begin{methoddesc}{tobuf}{\optional{format}} + Create a string buffer from a \class{TarInfo} object. See + \class{TarFile}'s \member{format} argument for information. + \versionchanged[The \var{format} parameter]{2.6} \end{methoddesc} A \code{TarInfo} object has the following public data attributes: diff --git a/Doc/lib/libtelnetlib.tex b/Doc/lib/libtelnetlib.tex index b8dfeee..269ee9b 100644 --- a/Doc/lib/libtelnetlib.tex +++ b/Doc/lib/libtelnetlib.tex @@ -23,13 +23,16 @@ Mark), BRK (Break), IP (Interrupt process), AO (Abort output), AYT SB (Subnegotiation Begin). -\begin{classdesc}{Telnet}{\optional{host\optional{, port}}} +\begin{classdesc}{Telnet}{\optional{host\optional{, port\optional{, timeout}}}} \class{Telnet} represents a connection to a Telnet server. The instance is initially not connected by default; the \method{open()} method must be used to establish a connection. Alternatively, the host name and optional port number can be passed to the constructor, to, in which case the connection to the server will be established before the constructor returns. +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global default +timeout setting will be used). Do not reopen an already connected instance. @@ -52,7 +55,7 @@ individual descriptions below. \class{Telnet} instances have the following methods: -\begin{methoddesc}{read_until}{expected\optional{, timeout}} +\begin{methoddesc}[Telnet]{read_until}{expected\optional{, timeout}} Read until a given string, \var{expected}, is encountered or until \var{timeout} seconds have passed. @@ -61,17 +64,17 @@ possibly the empty string. Raise \exception{EOFError} if the connection is closed and no cooked data is available. \end{methoddesc} -\begin{methoddesc}{read_all}{} +\begin{methoddesc}[Telnet]{read_all}{} Read all data until \EOF; block until connection closed. \end{methoddesc} -\begin{methoddesc}{read_some}{} +\begin{methoddesc}[Telnet]{read_some}{} Read at least one byte of cooked data unless \EOF{} is hit. Return \code{''} if \EOF{} is hit. Block if no data is immediately available. \end{methoddesc} -\begin{methoddesc}{read_very_eager}{} +\begin{methoddesc}[Telnet]{read_very_eager}{} Read everything that can be without blocking in I/O (eager). Raise \exception{EOFError} if connection closed and no cooked data @@ -79,7 +82,7 @@ available. Return \code{''} if no cooked data available otherwise. Do not block unless in the midst of an IAC sequence. \end{methoddesc} -\begin{methoddesc}{read_eager}{} +\begin{methoddesc}[Telnet]{read_eager}{} Read readily available data. Raise \exception{EOFError} if connection closed and no cooked data @@ -87,7 +90,7 @@ available. Return \code{''} if no cooked data available otherwise. Do not block unless in the midst of an IAC sequence. \end{methoddesc} -\begin{methoddesc}{read_lazy}{} +\begin{methoddesc}[Telnet]{read_lazy}{} Process and return data already in the queues (lazy). Raise \exception{EOFError} if connection closed and no data available. @@ -95,7 +98,7 @@ Return \code{''} if no cooked data available otherwise. Do not block unless in the midst of an IAC sequence. \end{methoddesc} -\begin{methoddesc}{read_very_lazy}{} +\begin{methoddesc}[Telnet]{read_very_lazy}{} Return any data available in the cooked queue (very lazy). Raise \exception{EOFError} if connection closed and no data available. @@ -103,7 +106,7 @@ Return \code{''} if no cooked data available otherwise. This method never blocks. \end{methoddesc} -\begin{methoddesc}{read_sb_data}{} +\begin{methoddesc}[Telnet]{read_sb_data}{} Return the data collected between a SB/SE pair (suboption begin/end). The callback should access these data when it was invoked with a \code{SE} command. This method never blocks. @@ -111,52 +114,55 @@ The callback should access these data when it was invoked with a \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{open}{host\optional{, port}} +\begin{methoddesc}[Telnet]{open}{host\optional{, port\optional{, timeout}}} Connect to a host. The optional second argument is the port number, which defaults to the standard Telnet port (23). +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global default +timeout setting will be used). Do not try to reopen an already connected instance. \end{methoddesc} -\begin{methoddesc}{msg}{msg\optional{, *args}} +\begin{methoddesc}[Telnet]{msg}{msg\optional{, *args}} Print a debug message when the debug level is \code{>} 0. If extra arguments are present, they are substituted in the message using the standard string formatting operator. \end{methoddesc} -\begin{methoddesc}{set_debuglevel}{debuglevel} +\begin{methoddesc}[Telnet]{set_debuglevel}{debuglevel} Set the debug level. The higher the value of \var{debuglevel}, the more debug output you get (on \code{sys.stdout}). \end{methoddesc} -\begin{methoddesc}{close}{} +\begin{methoddesc}[Telnet]{close}{} Close the connection. \end{methoddesc} -\begin{methoddesc}{get_socket}{} +\begin{methoddesc}[Telnet]{get_socket}{} Return the socket object used internally. \end{methoddesc} -\begin{methoddesc}{fileno}{} +\begin{methoddesc}[Telnet]{fileno}{} Return the file descriptor of the socket object used internally. \end{methoddesc} -\begin{methoddesc}{write}{buffer} +\begin{methoddesc}[Telnet]{write}{buffer} Write a string to the socket, doubling any IAC characters. This can block if the connection is blocked. May raise \exception{socket.error} if the connection is closed. \end{methoddesc} -\begin{methoddesc}{interact}{} +\begin{methoddesc}[Telnet]{interact}{} Interaction function, emulates a very dumb Telnet client. \end{methoddesc} -\begin{methoddesc}{mt_interact}{} +\begin{methoddesc}[Telnet]{mt_interact}{} Multithreaded version of \method{interact()}. \end{methoddesc} -\begin{methoddesc}{expect}{list\optional{, timeout}} +\begin{methoddesc}[Telnet]{expect}{list\optional{, timeout}} Read until one from a list of a regular expressions matches. The first argument is a list of regular expressions, either @@ -178,7 +184,7 @@ or if more than one expression can match the same input, the results are indeterministic, and may depend on the I/O timing. \end{methoddesc} -\begin{methoddesc}{set_option_negotiation_callback}{callback} +\begin{methoddesc}[Telnet]{set_option_negotiation_callback}{callback} Each time a telnet option is read on the input flow, this \var{callback} (if set) is called with the following parameters : callback(telnet socket, command (DO/DONT/WILL/WONT), option). No other diff --git a/Doc/lib/libtempfile.tex b/Doc/lib/libtempfile.tex index 9b4d848..8bc559e 100644 --- a/Doc/lib/libtempfile.tex +++ b/Doc/lib/libtempfile.tex @@ -53,7 +53,7 @@ The \var{dir}, \var{prefix} and \var{suffix} parameters are passed to \begin{funcdesc}{NamedTemporaryFile}{\optional{mode=\code{'w+b'}\optional{, bufsize=\code{-1}\optional{, suffix\optional{, prefix\optional{, - dir}}}}}} + dir\optional{, delete}}}}}}} This function operates exactly as \function{TemporaryFile()} does, except that the file is guaranteed to have a visible name in the file system (on \UNIX, the directory entry is not unlinked). That name can @@ -61,7 +61,27 @@ be retrieved from the \member{name} member of the file object. Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on \UNIX; it cannot on Windows NT or later). +If \var{delete} is true (the default), the file is deleted as soon as +it is closed. \versionadded{2.3} +\versionadded[The \var{delete} parameter]{2.6} +\end{funcdesc} + +\begin{funcdesc}{SpooledTemporaryFile}{\optional{max\_size=\code{0}, + \optional{mode=\code{'w+b'}\optional{, + bufsize=\code{-1}\optional{, + suffix\optional{, prefix\optional{, + dir}}}}}}} +This function operates exactly as \function{TemporaryFile()} does, +except that data is spooled in memory until the file size exceeds +\var{max_size}, or until the file's \function{fileno()} method is +called, at which point the contents are written to disk and operation +proceeds as with \function{TemporaryFile()}. + +The resulting file has one additional method, \function{rollover()}, +which causes the file to roll over to an on-disk file regardless of +its size. +\versionadded{2.6} \end{funcdesc} \begin{funcdesc}{mkstemp}{\optional{suffix\optional{, diff --git a/Doc/lib/libtest.tex b/Doc/lib/libtest.tex index f30b49b..d258089 100644 --- a/Doc/lib/libtest.tex +++ b/Doc/lib/libtest.tex @@ -14,11 +14,11 @@ your tests while \module{test.regrtest} drives the testing suite. Each module in the \module{test} package whose name starts with \samp{test_} is a testing suite for a specific module or feature. -All new tests should be written using the \refmodule{unittest} module; -using \refmodule{unittest} is not required but makes the tests more -flexible and maintenance of the tests easier. Some older tests are -written to use \refmodule{doctest} and a ``traditional'' testing -style; these styles of tests will not be covered. +All new tests should be written using the \refmodule{unittest} or +\refmodule{doctest} module. Some older tests are +written using a ``traditional'' testing style that compares output +printed to \code{sys.stdout}; this style of test is considered +deprecated. \begin{seealso} \seemodule{unittest}{Writing PyUnit regression tests.} @@ -29,8 +29,8 @@ style; these styles of tests will not be covered. \subsection{Writing Unit Tests for the \module{test} package% \label{writing-tests}} -It is preferred that tests for the \module{test} package use the -\refmodule{unittest} module and follow a few guidelines. +It is preferred that tests that use the \refmodule{unittest} module +follow a few guidelines. One is to name the test module by starting it with \samp{test_} and end it with the name of the module being tested. The test methods in the test module should start with \samp{test_} and end with @@ -196,7 +196,9 @@ regression tests. This module defines the following exceptions: \begin{excdesc}{TestFailed} -Exception to be raised when a test fails. +Exception to be raised when a test fails. This is deprecated in favor +of \module{unittest}-based tests and \class{unittest.TestCase}'s +assertion methods. \end{excdesc} \begin{excdesc}{TestSkipped} @@ -273,18 +275,30 @@ filter settings. Execute \class{unittest.TestCase} subclasses passed to the function. The function scans the classes for methods starting with the prefix \samp{test_} and executes the tests individually. -This is the preferred way to execute tests. -\end{funcdesc} -\begin{funcdesc}{run_suite}{suite\optional{, testclass}} -Execute the \class{unittest.TestSuite} instance \var{suite}. -The optional argument \var{testclass} accepts one of the test classes in the -suite so as to print out more detailed information on where the testing suite -originated from. +It is also legal to pass strings as parameters; these should be keys in +\code{sys.modules}. Each associated module will be scanned by +\code{unittest.TestLoader.loadTestsFromModule()}. This is usually seen in +the following \function{test_main()} function: + +\begin{verbatim} +def test_main(): + test_support.run_unittest(__name__) +\end{verbatim} + +This will run all tests defined in the named module. \end{funcdesc} The \module{test.test_support} module defines the following classes: +\begin{classdesc}{TransientResource}{exc\optional{, **kwargs}} +Create a context manager that raises \class{ResourceDenied} if the specified +exception type is raised. Any keyword arguments are treated as name/value +pairs to be compared against any exception raised with the \code{with} +statement. Only if all pairs match is \class{ResourceDenied} raised. +\versionadded{2.6} +\end{classdesc} + \begin{classdesc}{EnvironmentVarGuard}{} Class used to temporarily set or unset environment variables. Instances can be used as a context manager. diff --git a/Doc/lib/libtextwrap.tex b/Doc/lib/libtextwrap.tex index 38f9b03..8ea25a8 100644 --- a/Doc/lib/libtextwrap.tex +++ b/Doc/lib/libtextwrap.tex @@ -54,7 +54,7 @@ edge of the display, while still presenting them in the source code in indented form. Note that tabs and spaces are both treated as whitespace, but they are -not equal: the lines \code{" {} hello"} and \code{"\textbackslash{}thello"} +not equal: the lines \code{" {} hello"} and \code{"\e thello"} are considered to have no common leading whitespace. (This behaviour is new in Python 2.5; older versions of this module incorrectly expanded tabs before searching for common leading whitespace.) @@ -115,6 +115,13 @@ replaced by a single space, which is \emph{not} the same as tab expansion.} \end{memberdesc} +\begin{memberdesc}{drop_whitespace} +(default: \code{True}) If true, whitespace that, after wrapping, happens +to end up at the beginning or end of a line is dropped (leading whitespace +in the first line is always preserved, though). +\versionadded[Whitespace was always dropped in earlier versions]{2.6} +\end{memberdesc} + \begin{memberdesc}{initial_indent} (default: \code{''}) String that will be prepended to the first line of wrapped output. Counts towards the length of the first line. diff --git a/Doc/lib/libthreading.tex b/Doc/lib/libthreading.tex index 0334750..522ea2f 100644 --- a/Doc/lib/libthreading.tex +++ b/Doc/lib/libthreading.tex @@ -15,17 +15,16 @@ situations where \module{threading} cannot be used because This module defines the following functions and objects: \begin{funcdesc}{activeCount}{} -Return the number of currently active \class{Thread} objects. -The returned count is equal to the length of the list returned by +Return the number of \class{Thread} objects currently alive. The +returned count is equal to the length of the list returned by \function{enumerate()}. -A function that returns the number of currently active threads. \end{funcdesc} -\begin{funcdesc}{Condition}{} +\begin{funcdescni}{Condition}{} A factory function that returns a new condition variable object. A condition variable allows one or more threads to wait until they are notified by another thread. -\end{funcdesc} +\end{funcdescni} \begin{funcdesc}{currentThread}{} Return the current \class{Thread} object, corresponding to the @@ -36,18 +35,18 @@ is returned. \end{funcdesc} \begin{funcdesc}{enumerate}{} -Return a list of all currently active \class{Thread} objects. -The list includes daemonic threads, dummy thread objects created -by \function{currentThread()}, and the main thread. It excludes terminated -threads and threads that have not yet been started. +Return a list of all \class{Thread} objects currently alive. The list +includes daemonic threads, dummy thread objects created by +\function{currentThread()}, and the main thread. It excludes +terminated threads and threads that have not yet been started. \end{funcdesc} -\begin{funcdesc}{Event}{} +\begin{funcdescni}{Event}{} A factory function that returns a new event object. An event manages a flag that can be set to true with the \method{set()} method and reset to false with the \method{clear()} method. The \method{wait()} method blocks until the flag is true. -\end{funcdesc} +\end{funcdescni} \begin{classdesc*}{local}{} A class that represents thread-local data. Thread-local data are data @@ -82,14 +81,14 @@ acquire it again without blocking; the thread must release it once for each time it has acquired it. \end{funcdesc} -\begin{funcdesc}{Semaphore}{\optional{value}} +\begin{funcdescni}{Semaphore}{\optional{value}} A factory function that returns a new semaphore object. A semaphore manages a counter representing the number of \method{release()} calls minus the number of \method{acquire()} calls, plus an initial value. The \method{acquire()} method blocks if necessary until it can return without making the counter negative. If not given, \var{value} defaults to 1. -\end{funcdesc} +\end{funcdescni} \begin{funcdesc}{BoundedSemaphore}{\optional{value}} A factory function that returns a new bounded semaphore object. A bounded @@ -100,12 +99,12 @@ semaphore is released too many times it's a sign of a bug. If not given, \var{value} defaults to 1. \end{funcdesc} -\begin{classdesc*}{Thread}{} +\begin{classdesc*}{Thread} A class that represents a thread of control. This class can be safely subclassed in a limited fashion. \end{classdesc*} -\begin{classdesc*}{Timer}{} +\begin{classdesc*}{Timer} A thread that executes a function after a specified interval has passed. \end{classdesc*} @@ -183,7 +182,7 @@ and may vary across implementations. All methods are executed atomically. -\begin{methoddesc}{acquire}{\optional{blocking\code{ = 1}}} +\begin{methoddesc}[Lock]{acquire}{\optional{blocking\code{ = 1}}} Acquire a lock, blocking or non-blocking. When invoked without arguments, block until the lock is @@ -198,7 +197,7 @@ immediately; otherwise, do the same thing as when called without arguments, and return true. \end{methoddesc} -\begin{methoddesc}{release}{} +\begin{methoddesc}[Lock]{release}{} Release a lock. When the lock is locked, reset it to unlocked, and return. If @@ -228,7 +227,7 @@ the final \method{release()} (the \method{release()} of the outermost pair) resets the lock to unlocked and allows another thread blocked in \method{acquire()} to proceed. -\begin{methoddesc}{acquire}{\optional{blocking\code{ = 1}}} +\begin{methoddesc}[RLock]{acquire}{\optional{blocking\code{ = 1}}} Acquire a lock, blocking or non-blocking. When invoked without arguments: if this thread already owns @@ -250,7 +249,7 @@ immediately; otherwise, do the same thing as when called without arguments, and return true. \end{methoddesc} -\begin{methoddesc}{release}{} +\begin{methoddesc}[RLock]{release}{} Release a lock, decrementing the recursion level. If after the decrement it is zero, reset the lock to unlocked (not owned by any thread), and if any other threads are blocked waiting for the lock to @@ -526,12 +525,9 @@ calling the thread's \method{start()} method. This invokes the \method{run()} method in a separate thread of control. Once the thread's activity is started, the thread is considered -'alive' and 'active' (these concepts are almost, but not quite -exactly, the same; their definition is intentionally somewhat -vague). It stops being alive and active when its \method{run()} -method terminates -- either normally, or by raising an unhandled -exception. The \method{isAlive()} method tests whether the thread is -alive. +'alive'. It stops being alive when its \method{run()} method terminates +-- either normally, or by raising an unhandled exception. The +\method{isAlive()} method tests whether the thread is alive. Other threads can call a thread's \method{join()} method. This blocks the calling thread until the thread whose \method{join()} method is @@ -551,14 +547,13 @@ There is a ``main thread'' object; this corresponds to the initial thread of control in the Python program. It is not a daemon thread. -There is the possibility that ``dummy thread objects'' are -created. These are thread objects corresponding to ``alien -threads''. These are threads of control started outside the -threading module, such as directly from C code. Dummy thread objects -have limited functionality; they are always considered alive, -active, and daemonic, and cannot be \method{join()}ed. They are never -deleted, since it is impossible to detect the termination of alien -threads. +There is the possibility that ``dummy thread objects'' are created. +These are thread objects corresponding to ``alien threads'', which +are threads of control started outside the threading module, such as +directly from C code. Dummy thread objects have limited +functionality; they are always considered alive and daemonic, and +cannot be \method{join()}ed. They are never deleted, since it is +impossible to detect the termination of alien threads. \begin{classdesc}{Thread}{group=None, target=None, name=None, @@ -646,7 +641,8 @@ name. The initial name is set by the constructor. Return whether the thread is alive. Roughly, a thread is alive from the moment the \method{start()} method -returns until its \method{run()} method terminates. +returns until its \method{run()} method terminates. The module +function \function{enumerate()} returns a list of all alive threads. \end{methoddesc} \begin{methoddesc}{isDaemon}{} @@ -659,8 +655,8 @@ This must be called before \method{start()} is called. The initial value is inherited from the creating thread. -The entire Python program exits when no active non-daemon -threads are left. +The entire Python program exits when no alive non-daemon threads are +left. \end{methoddesc} diff --git a/Doc/lib/libtimeit.tex b/Doc/lib/libtimeit.tex index 2629439..7f38a7e 100644 --- a/Doc/lib/libtimeit.tex +++ b/Doc/lib/libtimeit.tex @@ -31,6 +31,13 @@ To measure the execution time of the first statement, use the \method{timeit()} method. The \method{repeat()} method is a convenience to call \method{timeit()} multiple times and return a list of results. + +\versionchanged[The \var{stmt} and \var{setup} parameters can now also + take objects that are callable without arguments. This + will embed calls to them in a timer function that will + then be executed by \method{timeit()}. Note that the timing + overhead is a little larger in this case because of the + extra function calls]{2.6} \end{classdesc} \begin{methoddesc}{print_exc}{\optional{file=\constant{None}}} @@ -97,12 +104,30 @@ measured. If so, GC can be re-enabled as the first statement in the \end{methoddesc} +Starting with version 2.6, the module also defines two convenience functions: + +\begin{funcdesc}{repeat}{stmt\optional{, setup\optional{, timer\optional{, + repeat\code{=3} \optional{, number\code{=1000000}}}}}} +Create a \class{Timer} instance with the given statement, setup code and timer +function and run its \method{repeat} method with the given repeat count and +\var{number} executions. +\versionadded{2.6} +\end{funcdesc} + +\begin{funcdesc}{timeit}{stmt\optional{, setup\optional{, timer\optional{, + number\code{=1000000}}}}} +Create a \class{Timer} instance with the given statement, setup code and timer +function and run its \method{timeit} method with \var{number} executions. +\versionadded{2.6} +\end{funcdesc} + + \subsection{Command Line Interface} When called as a program from the command line, the following form is used: \begin{verbatim} -python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement ...] +python -m timeit [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement ...] \end{verbatim} where the following options are understood: diff --git a/Doc/lib/libturtle.tex b/Doc/lib/libturtle.tex index 6161cd9..fa8f0dd 100644 --- a/Doc/lib/libturtle.tex +++ b/Doc/lib/libturtle.tex @@ -261,7 +261,7 @@ The only method which is more powerful as a method is \function{degrees()}, which takes an optional argument letting you specify the number of units corresponding to a full circle: -\begin{methoddesc}{degrees}{\optional{fullcircle}} +\begin{methoddesc}[Turtle]{degrees}{\optional{fullcircle}} \var{fullcircle} is by default 360. This can cause the pen to have any angular units whatever: give \var{fullcircle} 2*$\pi$ for radians, or 400 for gradians. diff --git a/Doc/lib/libunittest.tex b/Doc/lib/libunittest.tex index 350abae..fa198c7 100644 --- a/Doc/lib/libunittest.tex +++ b/Doc/lib/libunittest.tex @@ -91,7 +91,7 @@ specific class. \end{seealso} -\subsection{Basic example \label{minimal-example}} +\subsection{Basic example \label{unittest-minimal-example}} The \module{unittest} module provides a rich set of tools for constructing and running tests. This section demonstrates that a @@ -290,6 +290,7 @@ Often, many small test cases will use the same fixture. In this case, we would end up subclassing \class{SimpleWidgetTestCase} into many small one-method classes such as \class{DefaultWidgetSizeTestCase}. This is time-consuming and + discouraging, so in the same vein as JUnit, \module{unittest} provides a simpler mechanism: @@ -540,7 +541,7 @@ easier.} \begin{funcdesc}{main}{\optional{module\optional{, defaultTest\optional{, argv\optional{, - testRunner\optional{, testRunner}}}}}} + testRunner\optional{, testLoader}}}}}} A command-line program that runs a set of tests; this is primarily for making test modules conveniently executable. The simplest use for this function is to include the following line at the end of a @@ -550,6 +551,9 @@ easier.} if __name__ == '__main__': unittest.main() \end{verbatim} + + The \var{testRunner} argument can either be a test runner class or + an already created instance of it. \end{funcdesc} In some cases, the existing tests may have been written using the @@ -615,14 +619,14 @@ The test code can use any of the following methods to check for and report failures. \begin{methoddesc}[TestCase]{assert_}{expr\optional{, msg}} -\methodline{failUnless}{expr\optional{, msg}} +\methodline[TestCase]{failUnless}{expr\optional{, msg}} Signal a test failure if \var{expr} is false; the explanation for the error will be \var{msg} if given, otherwise it will be \constant{None}. \end{methoddesc} \begin{methoddesc}[TestCase]{assertEqual}{first, second\optional{, msg}} -\methodline{failUnlessEqual}{first, second\optional{, msg}} +\methodline[TestCase]{failUnlessEqual}{first, second\optional{, msg}} Test that \var{first} and \var{second} are equal. If the values do not compare equal, the test will fail with the explanation given by \var{msg}, or \constant{None}. Note that using \method{failUnlessEqual()} @@ -633,7 +637,7 @@ report failures. \end{methoddesc} \begin{methoddesc}[TestCase]{assertNotEqual}{first, second\optional{, msg}} -\methodline{failIfEqual}{first, second\optional{, msg}} +\methodline[TestCase]{failIfEqual}{first, second\optional{, msg}} Test that \var{first} and \var{second} are not equal. If the values do compare equal, the test will fail with the explanation given by \var{msg}, or \constant{None}. Note that using \method{failIfEqual()} @@ -645,7 +649,7 @@ report failures. \begin{methoddesc}[TestCase]{assertAlmostEqual}{first, second\optional{, places\optional{, msg}}} -\methodline{failUnlessAlmostEqual}{first, second\optional{, +\methodline[TestCase]{failUnlessAlmostEqual}{first, second\optional{, places\optional{, msg}}} Test that \var{first} and \var{second} are approximately equal by computing the difference, rounding to the given number of \var{places}, @@ -657,7 +661,7 @@ report failures. \begin{methoddesc}[TestCase]{assertNotAlmostEqual}{first, second\optional{, places\optional{, msg}}} -\methodline{failIfAlmostEqual}{first, second\optional{, +\methodline[TestCase]{failIfAlmostEqual}{first, second\optional{, places\optional{, msg}}} Test that \var{first} and \var{second} are not approximately equal by computing the difference, rounding to the given number of \var{places}, @@ -668,7 +672,7 @@ report failures. \end{methoddesc} \begin{methoddesc}[TestCase]{assertRaises}{exception, callable, \moreargs} -\methodline{failUnlessRaises}{exception, callable, \moreargs} +\methodline[TestCase]{failUnlessRaises}{exception, callable, \moreargs} Test that an exception is raised when \var{callable} is called with any positional or keyword arguments that are also passed to \method{assertRaises()}. The test passes if \var{exception} is diff --git a/Doc/lib/liburllib2.tex b/Doc/lib/liburllib2.tex index 542a7b8..f6ff513 100644 --- a/Doc/lib/liburllib2.tex +++ b/Doc/lib/liburllib2.tex @@ -588,7 +588,7 @@ The same as \method{http_error_301()}, but called for the \class{HTTPCookieProcessor} instances have one attribute: -\begin{memberdesc}{cookiejar} +\begin{memberdesc}[HTTPCookieProcessor]{cookiejar} The \class{cookielib.CookieJar} in which cookies are stored. \end{memberdesc} @@ -817,7 +817,10 @@ Use of Basic HTTP Authentication: import urllib2 # Create an OpenerDirector with support for Basic HTTP Authentication... auth_handler = urllib2.HTTPBasicAuthHandler() -auth_handler.add_password('realm', 'host', 'username', 'password') +auth_handler.add_password(realm='PDQ Application', + uri='https://mahler:8092/site-updates.py', + user='klem', + passwd='kadidd!ehopper') opener = urllib2.build_opener(auth_handler) # ...and install it globally so it can be used with urlopen. urllib2.install_opener(opener) diff --git a/Doc/lib/liburlparse.tex b/Doc/lib/liburlparse.tex index 76622d5..16f38a0 100644 --- a/Doc/lib/liburlparse.tex +++ b/Doc/lib/liburlparse.tex @@ -157,7 +157,7 @@ The \var{allow_fragments} argument has the same meaning and default as for \function{urlparse()}. \note{If \var{url} is an absolute URL (that is, starting with \code{//} - or \code{scheme://}, the \var{url}'s host name and/or scheme + or \code{scheme://}), the \var{url}'s host name and/or scheme will be present in the result. For example:} \begin{verbatim} diff --git a/Doc/lib/libwebbrowser.tex b/Doc/lib/libwebbrowser.tex index 11d77a1..50366ba 100644 --- a/Doc/lib/libwebbrowser.tex +++ b/Doc/lib/libwebbrowser.tex @@ -154,20 +154,20 @@ webbrowser.open_new(url) Browser controllers provide two methods which parallel two of the module-level convenience functions: -\begin{funcdesc}{open}{url\optional{, new\optional{, autoraise=1}}} +\begin{methoddesc}[controller]{open}{url\optional{, new\optional{, autoraise=1}}} Display \var{url} using the browser handled by this controller. If \var{new} is 1, a new browser window is opened if possible. If \var{new} is 2, a new browser page ("tab") is opened if possible. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{open_new}{url} +\begin{methoddesc}[controller]{open_new}{url} Open \var{url} in a new window of the browser handled by this controller, if possible, otherwise, open \var{url} in the only browser window. Alias \function{open_new}. -\end{funcdesc} +\end{methoddesc} -\begin{funcdesc}{open_new_tab}{url} +\begin{methoddesc}[controller]{open_new_tab}{url} Open \var{url} in a new page ("tab") of the browser handled by this controller, if possible, otherwise equivalent to \function{open_new}. \versionadded{2.5} -\end{funcdesc} +\end{methoddesc} diff --git a/Doc/lib/libwinreg.tex b/Doc/lib/libwinreg.tex index 3853997..1a38964 100644 --- a/Doc/lib/libwinreg.tex +++ b/Doc/lib/libwinreg.tex @@ -151,7 +151,7 @@ This module offers the following functions: An application should only call \function{FlushKey()} if it requires absolute certainty that registry changes are on disk. - \emph{If you don't know whether a \function{FlushKey()} call is required, it + \note{If you don't know whether a \function{FlushKey()} call is required, it probably isn't.} \end{funcdesc} @@ -393,14 +393,14 @@ This module offers the following functions: \method{Detach()} method to return the integer handle, and also disconnect the Windows handle from the handle object. -\begin{methoddesc}{Close}{} +\begin{methoddesc}[PyHKEY]{Close}{} Closes the underlying Windows handle. If the handle is already closed, no error is raised. \end{methoddesc} -\begin{methoddesc}{Detach}{} +\begin{methoddesc}[PyHKEY]{Detach}{} Detaches the Windows handle from the handle object. The result is an integer (or long on 64 bit Windows) that holds diff --git a/Doc/lib/libxmlrpclib.tex b/Doc/lib/libxmlrpclib.tex index 1aa2dd5..2a6f3f6 100644 --- a/Doc/lib/libxmlrpclib.tex +++ b/Doc/lib/libxmlrpclib.tex @@ -134,12 +134,12 @@ may be either returned data in a conformant type or a \class{Fault} or Servers that support the XML introspection API support some common methods grouped under the reserved \member{system} member: -\begin{methoddesc}{system.listMethods}{} +\begin{methoddesc}[ServerProxy]{system.listMethods}{} This method returns a list of strings, one for each (non-system) method supported by the XML-RPC server. \end{methoddesc} -\begin{methoddesc}{system.methodSignature}{name} +\begin{methoddesc}[ServerProxy]{system.methodSignature}{name} This method takes one parameter, the name of a method implemented by the XML-RPC server.It returns an array of possible signatures for this method. A signature is an array of types. The first of these types is @@ -159,7 +159,7 @@ returned. In Python this means that the type of the returned value will be something other that list. \end{methoddesc} -\begin{methoddesc}{system.methodHelp}{name} +\begin{methoddesc}[ServerProxy]{system.methodHelp}{name} This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns a documentation string describing the use of that method. If no such string is available, an empty string is @@ -184,7 +184,7 @@ implemented in the obvious ways. It also has the following method, supported mainly for internal use by the unmarshalling code: -\begin{methoddesc}{encode}{out} +\begin{methoddesc}[Boolean]{encode}{out} Write the XML-RPC encoding of this Boolean item to the out stream object. \end{methoddesc} @@ -197,11 +197,11 @@ ISO 8601 time/date string, or a {}\class{\refmodule{datetime}.datetime}, instance. It has the following methods, supported mainly for internal use by the marshalling/unmarshalling code: -\begin{methoddesc}{decode}{string} +\begin{methoddesc}[DateTime]{decode}{string} Accept a string as the instance's new time value. \end{methoddesc} -\begin{methoddesc}{encode}{out} +\begin{methoddesc}[DateTime]{encode}{out} Write the XML-RPC encoding of this \class{DateTime} item to the \var{out} stream object. \end{methoddesc} @@ -242,11 +242,11 @@ It also supports certain of Python's built-in operators through a A \class{Fault} object encapsulates the content of an XML-RPC fault tag. Fault objects have the following members: -\begin{memberdesc}{faultCode} +\begin{memberdesc}[Fault]{faultCode} A string indicating the fault type. \end{memberdesc} -\begin{memberdesc}{faultString} +\begin{memberdesc}[Fault]{faultString} A string containing a diagnostic message associated with the fault. \end{memberdesc} @@ -258,19 +258,19 @@ underlying transport layer (such as a 404 `not found' error if the server named by the URI does not exist). It has the following members: -\begin{memberdesc}{url} +\begin{memberdesc}[ProtocolError]{url} The URI or URL that triggered the error. \end{memberdesc} -\begin{memberdesc}{errcode} +\begin{memberdesc}[ProtocolError]{errcode} The error code. \end{memberdesc} -\begin{memberdesc}{errmsg} +\begin{memberdesc}[ProtocolError]{errmsg} The error message or diagnostic string. \end{memberdesc} -\begin{memberdesc}{headers} +\begin{memberdesc}[ProtocolError]{headers} A string containing the headers of the HTTP/HTTPS request that triggered the error. \end{memberdesc} diff --git a/Doc/lib/libzipfile.tex b/Doc/lib/libzipfile.tex index 06a236c..6cdd1d3 100644 --- a/Doc/lib/libzipfile.tex +++ b/Doc/lib/libzipfile.tex @@ -141,6 +141,34 @@ cat myzip.zip >> python.exe Return a list of archive members by name. \end{methoddesc} +\begin{methoddesc}{open}{name\optional{, mode\optional{, pwd}}} + Extract a member from the archive as a file-like object (ZipExtFile). + \var{name} is the name of the file in the archive. The \var{mode} + parameter, if included, must be one of the following: \code{'r'} (the + default), \code{'U'}, or \code{'rU'}. Choosing \code{'U'} or + \code{'rU'} will enable universal newline support in the read-only + object. \var{pwd} is the password used for encrypted files. + \begin{notice} + The file-like object is read-only and provides the following methods: + \method{read()}, \method{readline()}, \method{readlines()}, + \method{__iter__()}, \method{next()}. + \end{notice} + \begin{notice} + If the ZipFile was created by passing in a file-like object as the + first argument to the constructor, then the object returned by + \method{open()} shares the ZipFile's file pointer. Under these + circumstances, the object returned by \method{open()} should not + be used after any additional operations are performed on the + ZipFile object. If the ZipFile was created by passing in a string + (the filename) as the first argument to the constructor, then + \method{open()} will create a new file object that will be held + by the ZipExtFile, allowing it to operate independently of the + ZipFile. + \end{notice} + + \versionadded{2.6} +\end{methoddesc} + \begin{methoddesc}{printdir}{} Print a table of contents for the archive to \code{sys.stdout}. \end{methoddesc} diff --git a/Doc/lib/libzlib.tex b/Doc/lib/libzlib.tex index 876f8c0..3cc7b3c 100644 --- a/Doc/lib/libzlib.tex +++ b/Doc/lib/libzlib.tex @@ -131,7 +131,7 @@ compress a set of data that share a common initial prefix. Decompression objects support the following methods, and two attributes: -\begin{memberdesc}{unused_data} +\begin{memberdesc}[Decompress]{unused_data} A string which contains any bytes past the end of the compressed data. That is, this remains \code{""} until the last byte that contains compression data is available. If the whole string turned out to @@ -145,7 +145,7 @@ decompression object's \method{decompress} method until the \member{unused_data} attribute is no longer the empty string. \end{memberdesc} -\begin{memberdesc}{unconsumed_tail} +\begin{memberdesc}[Decompress]{unconsumed_tail} A string that contains any data that was not consumed by the last \method{decompress} call because it exceeded the limit for the uncompressed data buffer. This data has not yet been seen by the zlib diff --git a/Doc/mac/libframework.tex b/Doc/mac/libframework.tex index 692c31f..edc76c1 100644 --- a/Doc/mac/libframework.tex +++ b/Doc/mac/libframework.tex @@ -189,8 +189,6 @@ null-event is passed (so you can look at mouse position, etc). Window objects have the following methods, among others: -\setindexsubitem{(Window method)} - \begin{methoddesc}[Window]{open}{} Override this method to open a window. Store the MacOS window-id in \member{self.wid} and call the \method{do_postopen()} method to @@ -218,7 +216,7 @@ event. An update event for the window was received. Redraw the window. \end{methoddesc} -\begin{methoddesc}{do_activate}{activate, event} +\begin{methoddesc}[Window]{do_activate}{activate, event} The window was activated (\code{\var{activate} == 1}) or deactivated (\code{\var{activate} == 0}). Handle things like focus highlighting, etc. diff --git a/Doc/mac/libmacic.tex b/Doc/mac/libmacic.tex index 6d3a0d7..f8006f3 100644 --- a/Doc/mac/libmacic.tex +++ b/Doc/mac/libmacic.tex @@ -68,14 +68,14 @@ Besides the dictionary interface, \class{IC} objects have the following methods: -\begin{methoddesc}{launchurl}{url\optional{, hint}} +\begin{methoddesc}[IC]{launchurl}{url\optional{, hint}} Parse the given URL, launch the correct application and pass it the URL. The optional \var{hint} can be a scheme name such as \code{'mailto:'}, in which case incomplete URLs are completed with this scheme. If \var{hint} is not provided, incomplete URLs are invalid. \end{methoddesc} -\begin{methoddesc}{parseurl}{data\optional{, start\optional{, end\optional{, hint}}}} +\begin{methoddesc}[IC]{parseurl}{data\optional{, start\optional{, end\optional{, hint}}}} Find an URL somewhere in \var{data} and return start position, end position and the URL. The optional \var{start} and \var{end} can be used to limit the search, so for instance if a user clicks in a long @@ -85,7 +85,7 @@ user clicked. As above, \var{hint} is an optional scheme used to complete incomplete URLs. \end{methoddesc} -\begin{methoddesc}{mapfile}{file} +\begin{methoddesc}[IC]{mapfile}{file} Return the mapping entry for the given \var{file}, which can be passed as either a filename or an \function{FSSpec()} result, and which need not exist. @@ -106,7 +106,7 @@ postprocessing application, \var{mimetype} is the MIME type of this file and \var{entryname} is the name of this entry. \end{methoddesc} -\begin{methoddesc}{maptypecreator}{type, creator\optional{, filename}} +\begin{methoddesc}[IC]{maptypecreator}{type, creator\optional{, filename}} Return the mapping entry for files with given 4-character \var{type} and \var{creator} codes. The optional \var{filename} may be specified to further help finding the correct entry (if the creator code is @@ -115,7 +115,7 @@ further help finding the correct entry (if the creator code is The mapping entry is returned in the same format as for \var{mapfile}. \end{methoddesc} -\begin{methoddesc}{settypecreator}{file} +\begin{methoddesc}[IC]{settypecreator}{file} Given an existing \var{file}, specified either as a filename or as an \function{FSSpec()} result, set its creator and type correctly based on its extension. The finder is told about the change, so the finder diff --git a/Doc/mac/undoc.tex b/Doc/mac/undoc.tex index 72abadf..96e4900 100644 --- a/Doc/mac/undoc.tex +++ b/Doc/mac/undoc.tex @@ -21,7 +21,7 @@ touch with \modulesynopsis{Helper module for BuildApplet, BuildApplication and macfreeze.} -\deprecated{2.4} +\deprecated{2.4}{} \section{\module{cfmfile} --- Code Fragment Resource module} \declaremodule{standard}{cfmfile} @@ -33,7 +33,7 @@ accompanying ``cfrg'' resources. It can parse them and merge them, and is used by BuildApplication to combine all plugin modules to a single executable. -\deprecated{2.4} +\deprecated{2.4}{} \section{\module{icopen} --- Internet Config replacement for \method{open()}} \declaremodule{standard}{icopen} diff --git a/Doc/mac/using.tex b/Doc/mac/using.tex index b21a98e..ca522c6 100644 --- a/Doc/mac/using.tex +++ b/Doc/mac/using.tex @@ -2,7 +2,7 @@ \sectionauthor{Bob Savage}{bobsavage@mac.com} Python on a Macintosh running Mac OS X is in principle very similar to -Python on any other \UNIX platform, but there are a number of additional +Python on any other \UNIX{} platform, but there are a number of additional features such as the IDE and the Package Manager that are worth pointing out. Python on Mac OS 9 or earlier can be quite different from Python on @@ -13,206 +13,166 @@ for the latest 2.3 release for Mac OS 9 and related documentation. \section{Getting and Installing MacPython \label{getting-OSX}} -Mac OS X 10.3 comes with Python 2.3 pre-installed by Apple. -This installation does not come with the IDE and other additions, however, -so to get these you need to install the \program{MacPython for Panther additions} -from the MacPython website, \url{http://www.cwi.nl/\textasciitilde jack/macpython}. - -For MacPython 2.4, or for any MacPython on earlier releases of Mac OS X, -you need to install a full distribution from the same website. +Mac OS X 10.4 comes with Python 2.3 pre-installed by Apple. However, you are +encouraged to install the most recent version of Python from the Python website +(\url{http://www.python.org}). A ``universal binary'' build of Python 2.5, which +runs natively on the Mac's new Intel and legacy PPC CPU's, is available there. What you get after installing is a number of things: \begin{itemize} - \item A \file{MacPython-2.3} folder in your \file{Applications} - folder. In here you find the PythonIDE Integrated Development Environment; - PythonLauncher, which handles double-clicking Python scripts from - the Finder; and the Package Manager. - - \item A fairly standard \UNIX{} commandline Python interpreter in - \file{/usr/local/bin/python}, but without the usual - \file{/usr/local/lib/python}. - - \item A framework \file{/Library/Frameworks/Python.framework}, where - all the action really is, but which you usually do not have to be aware of. +\item A \file{MacPython 2.5} folder in your \file{Applications} folder. In here + you find IDLE, the development environment that is a standard part of official + Python distributions; PythonLauncher, which handles double-clicking Python + scripts from the Finder; and the ``Build Applet'' tool, which allows you to + package Python scripts as standalone applications on your system. + +\item A framework \file{/Library/Frameworks/Python.framework}, which includes + the Python executable and libraries. The installer adds this location to your + shell path. To uninstall MacPython, you can simply remove these three + things. A symlink to the Python executable is placed in /usr/local/bin/. \end{itemize} -To uninstall MacPython you can simply remove these three things. +The Apple-provided build of Python is installed in +\file{/System/Library/Frameworks/Python.framework} and \file{/usr/bin/python}, +respectively. You should never modify or delete these, as they are +Apple-controlled and are used by Apple- or third-party software. -If you use the ``additions'' installer to install on top of an existing -Apple-Python you will not get the framework and the commandline interpreter, -as they have been installed by Apple already, in -\file{/System/Library/Frameworks/Python.framework} and -\file{/usr/bin/python}, respectively. You should in principle never modify -or delete these, as they are Apple-controlled and may be used by Apple- or -third-party software. +IDLE includes a help menu that allows you to access Python documentation. If you +are completely new to Python you should start reading the tutorial introduction +in that document. -PythonIDE contains an Apple Help Viewer book called "MacPython Help" -which you can access through its help menu. If you are completely new to -Python you should start reading the IDE introduction in that document. +If you are familiar with Python on other \UNIX{} platforms you should read the +section on running Python scripts from the \UNIX{} shell. -If you are familiar with Python on other \UNIX{} platforms you should -read the section on running Python scripts from the \UNIX{} shell. \subsection{How to run a Python script} -Your best way to get started with Python on Mac OS X is through the PythonIDE -integrated development environment, see section \ref{IDE} and use the Help -menu when the IDE is running. +Your best way to get started with Python on Mac OS X is through the IDLE +integrated development environment, see section \ref{IDE} and use the Help menu +when the IDE is running. -If you want to run Python scripts from the Terminal window command line -or from the Finder you first need an editor to create your script. -Mac OS X comes with a number of standard \UNIX{} command line editors, -\program{vim} and \program{emacs} among them. If you want a more Mac-like -editor \program{BBEdit} or \program{TextWrangler} from Bare Bones Software -(see \url{http://www.barebones.com/products/bbedit/index.shtml}) are -good choices. \program{AppleWorks} or any other -word processor that can save files in ASCII is also a possibility, including -\program{TextEdit} which is included with OS X. +If you want to run Python scripts from the Terminal window command line or from +the Finder you first need an editor to create your script. Mac OS X comes with a +number of standard \UNIX{} command line editors, \program{vim} and +\program{emacs} among them. If you want a more Mac-like editor, \program{BBEdit} +or \program{TextWrangler} from Bare Bones Software (see +\url{http://www.barebones.com/products/bbedit/index.shtml}) are good choices, as +is \program{TextMate} (see \url{http://macromates.com/}). Other editors include +\program{Gvim} (\url{http://macvim.org}) and \program{Aquamacs} +(\url{http://aquamacs.org}). To run your script from the Terminal window you must make sure that -\file{/usr/local/bin} is in your shell search path. +\file{/usr/local/bin} is in your shell search path. To run your script from the Finder you have two options: + \begin{itemize} - \item Drag it to \program{PythonLauncher} - \item Select \program{PythonLauncher} as the default application - to open your script (or any .py script) through the finder Info window - and double-click it. +\item Drag it to \program{PythonLauncher} +\item Select \program{PythonLauncher} as the default application to open your + script (or any .py script) through the finder Info window and double-click it. + \program{PythonLauncher} has various preferences to control how your script is + launched. Option-dragging allows you to change these for one invocation, or + use its Preferences menu to change things globally. \end{itemize} -PythonLauncher has various preferences to control how your script is launched. -Option-dragging allows you to change these for one invocation, or use its -Preferences menu to change things globally. \subsection{Running scripts with a GUI \label{osx-gui-scripts}} -There is one Mac OS X quirk that you need to be aware of: programs -that talk to the Aqua window manager (in other words, anything that has a GUI) -need to be run in a special way. Use \program{pythonw} instead of \program{python} -to start such scripts. +With older versions of Python, there is one Mac OS X quirk that you need to be +aware of: programs that talk to the Aqua window manager (in other words, +anything that has a GUI) need to be run in a special way. Use \program{pythonw} +instead of \program{python} to start such scripts. -\subsection{configuration} +With Python 2.5, you can use either \program{python} or \program{pythonw}. -MacPython honours all standard \UNIX{} environment variables such as -\envvar{PYTHONPATH}, but setting these variables for programs started -from the Finder is non-standard -as the Finder does not read your \file{.profile} or \file{.cshrc} at startup. -You need to create a file \file{\textasciitilde /.MacOSX/environment.plist}. -See Apple's Technical Document QA1067 for details. +\subsection{Configuration} -Installing additional Python packages is most easily done through the -Package Manager, see the MacPython Help Book for details. +Python on OS X honors all standard \UNIX{} environment variables such as +\envvar{PYTHONPATH}, but setting these variables for programs started from the +Finder is non-standard as the Finder does not read your \file{.profile} or +\file{.cshrc} at startup. You need to create a file \file{\textasciitilde + /.MacOSX/environment.plist}. See Apple's Technical Document QA1067 for +details. + +For more information on installation Python packages in MacPython, see section +\ref{mac-package-manager}, ``Installing Additional Python Packages.'' \section{The IDE\label{IDE}} -The \program{Python IDE} (Integrated Development Environment) is a -separate application that acts as a text editor for your Python code, -a class browser, a graphical debugger, and more. - -The online Python Help contains a quick walkthrough of the IDE that -shows the major features and how to use them. - -\subsection{Using the ``Python Interactive'' window} - -Use this window like you would use a normal \UNIX{} command line -interpreter. - -\subsection{Writing a Python Script \label{IDEwrite}} - -In addition to using the \program{Python IDE} interactively, you can -also type out a complete Python program, saving it incrementally, and -execute it or smaller selections of it. - -You can create a new script, open a previously saved script, and save -your currently open script by selecting the appropriate item in the -``File'' menu. Dropping a Python script onto the -\program{Python IDE} will open it for editing. - -When the \program{Python IDE} saves a script, it uses the creator code -settings which are available by clicking on the small black triangle -on the top right of the document window, and selecting ``save -options''. The default is to save the file with the \program{Python -IDE} as the creator, this means that you can open the file for editing -by simply double-clicking on its icon. You might want to change this -behaviour so that it will be opened by the -\program{PythonLauncher}, and run. To do this simply choose -``PythonLauncher'' from the ``save options''. Note that these -options are associated with the \emph{file} not the application. - - -\subsection{Executing a script from within the IDE - \label{IDEexecution}} - -You can run the script in the frontmost window of the \program{Python -IDE} by hitting the run all button. You should be aware, however that -if you use the Python convention \samp{if __name__ == "__main__":} the -script will \emph{not} be ``__main__'' by default. To get that -behaviour you must select the ``Run as __main__'' option from the -small black triangle on the top right of the document window. Note -that this option is associated with the \emph{file} not the -application. It \emph{will} stay active after a save, however; to shut -this feature off simply select it again. - - -\subsection{``Save as'' versus ``Save as Applet'' - \label{IDEapplet}} - -When you are done writing your Python script you have the option of -saving it as an ``applet'' (by selecting ``Save as applet'' from the -``File'' menu). This has a significant advantage in that you can drop -files or folders onto it, to pass them to the applet the way -command-line users would type them onto the command-line to pass them -as arguments to the script. However, you should make sure to save the -applet as a separate file, do not overwrite the script you are -writing, because you will not be able to edit it again. - -Accessing the items passed to the applet via ``drag-and-drop'' is done -using the standard \member{sys.argv} mechanism. See the general -documentation for more -% need to link to the appropriate place in non-Mac docs - -Note that saving a script as an applet will not make it runnable on a -system without a Python installation. - -%\subsection{Debugger} -% **NEED INFO HERE** - -%\subsection{Module Browser} -% **NEED INFO HERE** - -%\subsection{Profiler} -% **NEED INFO HERE** -% end IDE - -%\subsection{The ``Scripts'' menu} -% **NEED INFO HERE** - -\section{The Package Manager} - -Historically MacPython came with a number of useful extension packages -included, because most Macintosh users do not have access to a development -environment and C compiler. For Mac OS X that bundling is no longer done, -but a new mechanism has been made available to allow easy access to -extension packages. - -The Python Package Manager helps you installing additional packages -that enhance Python. It determines the exact MacOS version and Python -version you have and uses that information to download a database that -has packages that are tested and tried on that combination. In other -words: if something is in your Package Manager window but does not work -you are free to blame the database maintainer. - -PackageManager then checks which of the packages you have installed and -which ones are not. This should also work when you have installed packages -outside of PackageManager. You can select packages and install them, -and PackageManager will work out the requirements and install these too. - -Often PackageManager will list a package in two flavors: binary and -source. Binary should always work, source will only work if you have -installed the Apple Developer Tools. PackageManager will warn you about -this, and also about other external dependencies. - -PackageManager is available as a separate application and also as a -function of the IDE, through the File->Package Manager menu entry. +MacPython ships with the standard IDLE development environment. A good +introduction to using IDLE can be found at +\url{http://hkn.eecs.berkeley.edu/~dyoo/python/idle_intro/index.html}. + + +\section{Installing Additional Python Packages \label{mac-package-manager}} + +There are several methods to install additional Python packages: + +\begin{itemize} +\item \url{http://pythonmac.org/packages/} contains selected compiled packages + for Python 2.5, 2.4, and 2.3. +\item Packages can be installed via the standard Python distutils mode + (\samp{python setup.py install}). +\item Many packages can also be installed via the \program{setuptools} + extension. +\end{itemize} + + +\section{GUI Programming on the Mac} + +There are several options for building GUI applications on the Mac with Python. + +\emph{PyObjC} is a Python binding to Apple's Objective-C/Cocoa framework, which +is the foundation of most modern Mac development. Information on PyObjC is +available from \url{http://pybojc.sourceforge.net}. + +The standard Python GUI toolkit is \module{Tkinter}, based on the cross-platform +Tk toolkit (\url{http://www.tcl.tk}). An Aqua-native version of Tk is bundled +with OS X by Apple, and the latest version can be downloaded and installed from +\url{http://www.activestate.com}; it can also be built from source. + +\emph{wxPython} is another popular cross-platform GUI toolkit that runs natively +on Mac OS X. Packages and documentation are available from +\url{http://www.wxpython.org}. + +\emph{PyQt} is another popular cross-platform GUI toolkit that runs natively on +Mac OS X. More information can be found at +\url{http://www.riverbankcomputing.co.uk/pyqt/}. + + +\section{Distributing Python Applications on the Mac} + +The ``Build Applet'' tool that is placed in the MacPython 2.5 folder is fine for +packaging small Python scripts on your own machine to run as a standard Mac +application. This tool, however, is not robust enough to distribute Python +applications to other users. + +The standard tool for deploying standalone Python applications on the Mac is +\program{py2app}. More information on installing and using py2app can be found +at \url{http://undefined.org/python/\#py2app}. + +\section{Application Scripting} + +Python can also be used to script other Mac applications via Apple's Open +Scripting Architecture (OSA); see +\url{http://appscript.sourceforge.net}. Appscript is a high-level, user-friendly +Apple event bridge that allows you to control scriptable Mac OS X applications +using ordinary Python scripts. Appscript makes Python a serious alternative to +Apple's own \emph{AppleScript} language for automating your Mac. A related +package, \emph{PyOSA}, is an OSA language component for the Python scripting +language, allowing Python code to be executed by any OSA-enabled application +(Script Editor, Mail, iTunes, etc.). PyOSA makes Python a full peer to +AppleScript. + +\section{Other Resources} + +The MacPython mailing list is an excellent support resource for Python users and +developers on the Mac: + +\url{http://www.python.org/community/sigs/current/pythonmac-sig/} + +Another useful resource is the MacPython wiki: + +\url{http://wiki.python.org/moin/MacPython} diff --git a/Doc/ref/ref1.tex b/Doc/ref/ref1.tex index 15bcf36..6234716 100644 --- a/Doc/ref/ref1.tex +++ b/Doc/ref/ref1.tex @@ -93,7 +93,7 @@ grammar notation. This uses the following style of definition: \index{syntax} \index{notation} -\begin{productionlist} +\begin{productionlist}[*] \production{name}{\token{lc_letter} (\token{lc_letter} | "_")*} \production{lc_letter}{"a"..."z"} \end{productionlist} diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index 40b2ebd..8340e17 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -218,7 +218,7 @@ when using mixed operands. \indexii{integer}{representation} \item[Floating point numbers] -These represent machine-level double precision floating point numbers. +These represent machine-level double precision floating point numbers. You are at the mercy of the underlying machine architecture (and C or Java implementation) for the accepted range and handling of overflow. Python does not support single-precision floating point numbers; the @@ -470,7 +470,7 @@ parameter list. \obindex{function} \obindex{user-defined function} -Special attributes: +Special attributes: \begin{tableiii}{lll}{member}{Attribute}{Meaning}{} \lineiii{__doc__}{The function's documentation string, or @@ -860,12 +860,12 @@ but they are mentioned here for completeness. \begin{description} \item[Code objects] -Code objects represent \emph{byte-compiled} executable Python code, or +Code objects represent \emph{byte-compiled} executable Python code, or \emph{bytecode}. The difference between a code object and a function object is that the function object contains an explicit reference to the function's globals (the module in which it -was defined), while a code object contains no context; +was defined), while a code object contains no context; also the default argument values are stored in the function object, not in the code object (because they represent values calculated at run-time). Unlike function objects, code objects are immutable and @@ -1069,7 +1069,7 @@ by the built-in \function{classmethod()} constructor. %========================================================================= \section{New-style and classic classes} -Classes and instances come in two flavors: 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 @@ -1244,7 +1244,7 @@ description...}>} should be returned. The return value must be a string object. If a class defines \method{__repr__()} but not \method{__str__()}, then \method{__repr__()} is also used when an ``informal'' string -representation of instances of that class is required. +representation of instances of that class is required. This is typically used for debugging, so it is important that the representation is information-rich and unambiguous. @@ -1280,10 +1280,14 @@ follows: \code{\var{x}!=\var{y}} calls \code{\var{x}.__ne__(\var{y})}, \code{\var{x}>\var{y}} calls \code{\var{x}.__gt__(\var{y})}, and \code{\var{x}>=\var{y}} calls \code{\var{x}.__ge__(\var{y})}. -These methods can return any value, but if the comparison operator is -used in a Boolean context, the return value should be interpretable as -a Boolean value, else a \exception{TypeError} will be raised. -By convention, \code{False} is used for false and \code{True} for true. + +A rich comparison method may return the singleton \code{NotImplemented} if it +does not implement the operation for a given pair of arguments. +By convention, \code{False} and \code{True} are returned for a successful +comparison. However, these methods can return any value, so if the +comparison operator is used in a Boolean context (e.g., in the condition +of an \code{if} statement), Python will call \function{bool()} on the +value to determine if the result is true or false. There are no implied relationships among the comparison operators. The truth of \code{\var{x}==\var{y}} does not imply that \code{\var{x}!=\var{y}} @@ -1297,9 +1301,7 @@ the right argument does); rather, \method{__lt__()} and \method{__ge__()} are each other's reflection, and \method{__eq__()} and \method{__ne__()} are their own reflection. -Arguments to rich comparison methods are never coerced. A rich -comparison method may return \code{NotImplemented} if it does not -implement the operation for a given pair of arguments. +Arguments to rich comparison methods are never coerced. \end{methoddesc} \begin{methoddesc}[object]{__cmp__}{self, other} @@ -1399,7 +1401,7 @@ instead of the normal mechanism (i.e.\ store the value in the instance dictionary). \var{name} is the attribute name, \var{value} is the value to be assigned to it. -If \method{__setattr__()} wants to assign to an instance attribute, it +If \method{__setattr__()} wants to assign to an instance attribute, it should not simply execute \samp{self.\var{name} = value} --- this would cause a recursive call to itself. Instead, it should insert the value in the dictionary of instance attributes, e.g., @@ -1422,8 +1424,8 @@ The following methods only apply to new-style classes. \begin{methoddesc}[object]{__getattribute__}{self, name} Called unconditionally to implement attribute accesses for instances -of the class. If the class also defines \method{__getattr__()}, the latter -will not be called unless \method{__getattribute__()} either calls it +of the class. If the class also defines \method{__getattr__()}, the latter +will not be called unless \method{__getattribute__()} either calls it explicitly or raises an \exception{AttributeError}. This method should return the (computed) attribute value or raise an \exception{AttributeError} exception. @@ -1475,7 +1477,7 @@ descriptor. The default behavior for attribute access is to get, set, or delete the attribute from an object's dictionary. For instance, \code{a.x} has a lookup chain starting with \code{a.__dict__['x']}, then -\code{type(a).__dict__['x']}, and continuing +\code{type(a).__dict__['x']}, and continuing through the base classes of \code{type(a)} excluding metaclasses. However, if the looked-up value is an object defining one of the descriptor @@ -1489,14 +1491,14 @@ The starting point for descriptor invocation is a binding, \code{a.x}. How the arguments are assembled depends on \code{a}: \begin{itemize} - + \item[Direct Call] The simplest and least common call is when user code directly invokes a descriptor method: \code{x.__get__(a)}. \item[Instance Binding] If binding to a new-style object instance, \code{a.x} is transformed into the call: \code{type(a).__dict__['x'].__get__(a, type(a))}. - + \item[Class Binding] If binding to a new-style class, \code{A.x} is transformed into the call: \code{A.__dict__['x'].__get__(None, A)}. @@ -1505,7 +1507,7 @@ How the arguments are assembled depends on \code{a}: \code{obj.__class__.__mro__} for the base class \code{A} immediately preceding \code{B} and then invokes the descriptor with the call: \code{A.__dict__['m'].__get__(obj, A)}. - + \end{itemize} For instance bindings, the precedence of descriptor invocation depends @@ -1518,7 +1520,7 @@ descriptors can be overridden by instances. Python methods (including \function{staticmethod()} and \function{classmethod()}) are implemented as non-data descriptors. Accordingly, instances can redefine and override methods. This allows individual instances to acquire -behaviors that differ from other instances of the same class. +behaviors that differ from other instances of the same class. The \function{property()} function is implemented as a data descriptor. Accordingly, instances cannot override the behavior of a property. @@ -1536,14 +1538,14 @@ definition. The \var{__slots__} declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because \var{__dict__} is not created for each instance. - + \begin{datadesc}{__slots__} This class variable can be assigned a string, iterable, or sequence of strings with variable names used by instances. If defined in a new-style class, \var{__slots__} reserves space for the declared variables and prevents the automatic creation of \var{__dict__} and \var{__weakref__} for each instance. -\versionadded{2.2} +\versionadded{2.2} \end{datadesc} \noindent @@ -1555,23 +1557,23 @@ Notes on using \var{__slots__} variables not listed in the \var{__slots__} definition. Attempts to assign to an unlisted variable name raises \exception{AttributeError}. If dynamic assignment of new variables is desired, then add \code{'__dict__'} to the -sequence of strings in the \var{__slots__} declaration. +sequence of strings in the \var{__slots__} declaration. \versionchanged[Previously, adding \code{'__dict__'} to the \var{__slots__} declaration would not enable the assignment of new attributes not -specifically listed in the sequence of instance variable names]{2.3} +specifically listed in the sequence of instance variable names]{2.3} \item Without a \var{__weakref__} variable for each instance, classes defining \var{__slots__} do not support weak references to its instances. If weak reference support is needed, then add \code{'__weakref__'} to the -sequence of strings in the \var{__slots__} declaration. +sequence of strings in the \var{__slots__} declaration. \versionchanged[Previously, adding \code{'__weakref__'} to the \var{__slots__} -declaration would not enable support for weak references]{2.3} +declaration would not enable support for weak references]{2.3} \item \var{__slots__} are implemented at the class level by creating descriptors (\ref{descriptors}) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by \var{__slots__}; otherwise, the class attribute would -overwrite the descriptor assignment. +overwrite the descriptor assignment. \item If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving @@ -1580,14 +1582,19 @@ program undefined. In the future, a check may be added to prevent this. \item The action of a \var{__slots__} declaration is limited to the class where it is defined. As a result, subclasses will have a \var{__dict__} -unless they also define \var{__slots__}. +unless they also define \var{__slots__}. \item \var{__slots__} do not work for classes derived from ``variable-length'' -built-in types such as \class{long}, \class{str} and \class{tuple}. +built-in types such as \class{long}, \class{str} and \class{tuple}. \item Any non-string iterable may be assigned to \var{__slots__}. Mappings may also be used; however, in the future, special meaning may -be assigned to the values corresponding to each key. +be assigned to the values corresponding to each key. + +\item \var{__class__} assignment works only if both classes have the +same \var{__slots__}. +\versionchanged[Previously, \var{__class__} assignment raised an error +if either new or old class had \var{__slots__}]{2.6} \end{itemize} @@ -1613,7 +1620,7 @@ the role of a factory function. This variable can be any callable accepting arguments for \code{name}, \code{bases}, and \code{dict}. Upon class creation, the callable is used instead of the built-in \function{type()}. -\versionadded{2.2} +\versionadded{2.2} \end{datadesc} The appropriate metaclass is determined by the following precedence rules: @@ -1630,7 +1637,7 @@ type). \item Otherwise, the old-style, classic metaclass (types.ClassType) is used. -\end{itemize} +\end{itemize} The potential uses for metaclasses are boundless. Some ideas that have been explored including logging, interface checking, automatic delegation, @@ -1663,15 +1670,15 @@ defined to handle simple, but not extended slices.) It is also recommended that mappings provide the methods \method{keys()}, \method{values()}, \method{items()}, \method{has_key()}, \method{get()}, \method{clear()}, \method{setdefault()}, \method{iterkeys()}, \method{itervalues()}, -\method{iteritems()}, \method{pop()}, \method{popitem()}, +\method{iteritems()}, \method{pop()}, \method{popitem()}, \method{copy()}, and \method{update()} behaving similar to those for Python's standard dictionary objects. The \module{UserDict} module provides a \class{DictMixin} class to help create those methods from a base set of \method{__getitem__()}, \method{__setitem__()}, -\method{__delitem__()}, and \method{keys()}. +\method{__delitem__()}, and \method{keys()}. Mutable sequences should provide methods \method{append()}, \method{count()}, \method{index()}, -\method{extend()}, +\method{extend()}, \method{insert()}, \method{pop()}, \method{remove()}, \method{reverse()} and \method{sort()}, like Python standard list objects. Finally, sequence types should implement addition (meaning concatenation) and @@ -1694,12 +1701,12 @@ through the values. \ttindex{items()} \ttindex{iterkeys()} \ttindex{itervalues()} - \ttindex{iteritems()} + \ttindex{iteritems()} \ttindex{has_key()} \ttindex{get()} \ttindex{setdefault()} - \ttindex{pop()} - \ttindex{popitem()} + \ttindex{pop()} + \ttindex{popitem()} \ttindex{clear()} \ttindex{copy()} \ttindex{update()} @@ -1707,7 +1714,7 @@ through the values. \withsubitem{(sequence object method)}{ \ttindex{append()} \ttindex{count()} - \ttindex{extend()} + \ttindex{extend()} \ttindex{index()} \ttindex{insert()} \ttindex{pop()} @@ -1721,7 +1728,7 @@ through the values. \ttindex{__rmul__()} \ttindex{__imul__()} \ttindex{__contains__()} - \ttindex{__iter__()}} + \ttindex{__iter__()}} \withsubitem{(numeric object method)} \begin{methoddesc}[container object]{__len__}{self} @@ -1744,7 +1751,7 @@ raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), \exception{IndexError} should be raised. For mapping types, if \var{key} is missing (not in the container), -\exception{KeyError} should be raised. +\exception{KeyError} should be raised. \note{\keyword{for} loops expect that an \exception{IndexError} will be raised for illegal indexes to allow proper detection of the end of the sequence.} @@ -1943,7 +1950,7 @@ the alternate context; \exception{TypeError} will be raised instead. \methodline[numeric object]{__rmul__}{self, other} \methodline[numeric object]{__rdiv__}{self, other} \methodline[numeric object]{__rtruediv__}{self, other} -\methodline[numeric object]{__rfloordiv__}{self, other} +\methodline[numeric object]{__rfloordiv__}{self, other} \methodline[numeric object]{__rmod__}{self, other} \methodline[numeric object]{__rdivmod__}{self, other} \methodline[numeric object]{__rpow__}{self, other} @@ -1964,7 +1971,7 @@ operands are of different types.\footnote{ For operands of the same type, it is assumed that if the non-reflected method (such as \method{__add__()}) fails the operation is not supported, which is why the reflected method - is not called.} + is not called.} For instance, to evaluate the expression \var{x}\code{-}\var{y}, where \var{y} is an instance of a class that has an \method{__rsub__()} method, \code{\var{y}.__rsub__(\var{x})} @@ -1989,7 +1996,7 @@ complicated). \methodline[numeric object]{__idiv__}{self, other} \methodline[numeric object]{__itruediv__}{self, other} \methodline[numeric object]{__ifloordiv__}{self, other} -\methodline[numeric object]{__imod__}{self, other} +\methodline[numeric object]{__imod__}{self, other} \methodline[numeric object]{__ipow__}{self, other\optional{, modulo}} \methodline[numeric object]{__ilshift__}{self, other} \methodline[numeric object]{__irshift__}{self, other} diff --git a/Doc/ref/ref5.tex b/Doc/ref/ref5.tex index 0b4e978..9a4fe3a 100644 --- a/Doc/ref/ref5.tex +++ b/Doc/ref/ref5.tex @@ -56,7 +56,7 @@ categorized syntactically as atoms. The syntax for atoms is: \production{enclosure} {\token{parenth_form} | \token{list_display}} \productioncont{| \token{generator_expression} | \token{dict_display}} - \productioncont{| \token{string_conversion}} + \productioncont{| \token{string_conversion} | \token{yield_atom}} \end{productionlist} @@ -65,6 +65,7 @@ categorized syntactically as atoms. The syntax for atoms is: \index{identifier} An identifier occurring as an atom is a name. See +section \ref{identifiers} for lexical definition and section~\ref{naming} for documentation of naming and binding. When the name is bound to an object, evaluation of the atom yields @@ -154,22 +155,20 @@ A list display is a possibly empty series of expressions enclosed in square brackets: \begin{productionlist} - \production{test} - {\token{or_test} | \token{lambda_form}} - \production{testlist} - {\token{test} ( "," \token{test} )* [ "," ]} \production{list_display} - {"[" [\token{listmaker}] "]"} - \production{listmaker} - {\token{expression} ( \token{list_for} - | ( "," \token{expression} )* [","] )} - \production{list_iter} - {\token{list_for} | \token{list_if}} + {"[" [\token{expression_list} | \token{list_comprehension}] "]"} + \production{list_comprehension} + {\token{expression} \token{list_for}} \production{list_for} - {"for" \token{expression_list} "in" \token{testlist} + {"for" \token{target_list} "in" \token{old_expression_list} [\token{list_iter}]} + \production{old_expression_list} + {\token{old_expression} + [("," \token{old_expression})+ [","]]} + \production{list_iter} + {\token{list_for} | \token{list_if}} \production{list_if} - {"if" \token{test} [\token{list_iter}]} + {"if" \token{old_expression} [\token{list_iter}]} \end{productionlist} A list display yields a new list object. Its contents are specified @@ -200,19 +199,18 @@ A generator expression is a compact generator notation in parentheses: \begin{productionlist} \production{generator_expression} - {"(" \token{test} \token{genexpr_for} ")"} + {"(" \token{expression} \token{genexpr_for} ")"} \production{genexpr_for} - {"for" \token{expression_list} "in" \token{test} + {"for" \token{target_list} "in" \token{or_test} [\token{genexpr_iter}]} \production{genexpr_iter} {\token{genexpr_for} | \token{genexpr_if}} \production{genexpr_if} - {"if" \token{test} [\token{genexpr_iter}]} + {"if" \token{old_expression} [\token{genexpr_iter}]} \end{productionlist} A generator expression yields a new generator object. \obindex{generator} -\obindex{generator expression} It consists of a single expression followed by at least one \keyword{for} clause and zero or more \keyword{for} or \keyword{if} clauses. The iterating values of the new generator are those that @@ -268,6 +266,142 @@ stored for a given key value prevails. \indexii{immutable}{object} +\subsection{Yield expressions\label{yieldexpr}} +\kwindex{yield} +\indexii{yield}{expression} +\indexii{generator}{function} + +\begin{productionlist} + \production{yield_atom} + {"(" \token{yield_expression} ")"} + \production{yield_expression} + {"yield" [\token{expression_list}]} +\end{productionlist} + +\versionadded{2.5} + +The \keyword{yield} expression is only used when defining a generator +function, and can only be used in the body of a function definition. +Using a \keyword{yield} expression in a function definition is +sufficient to cause that definition to create a generator function +instead of a normal function. + +When a generator function is called, it returns an iterator known as a +generator. That generator then controls the execution of a generator +function. The execution starts when one of the generator's methods is +called. At that time, the execution proceeds to the first +\keyword{yield} expression, where it is suspended again, returning the +value of \grammartoken{expression_list} to generator's caller. By +suspended we mean that all local state is retained, including the +current bindings of local variables, the instruction pointer, and the +internal evaluation stack. When the execution is resumed by calling +one of the generator's methods, the function can proceed exactly as +if the \keyword{yield} expression was just another external call. +The value of the \keyword{yield} expression after resuming depends on +the method which resumed the execution. + +\index{coroutine} + +All of this makes generator functions quite similar to coroutines; they +yield multiple times, they have more than one entry point and their +execution can be suspended. The only difference is that a generator +function cannot control where should the execution continue after it +yields; the control is always transfered to the generator's caller. + +\obindex{generator} + +The following generator's methods can be used to control the execution +of a generator function: + +\exindex{StopIteration} + +\begin{methoddesc}[generator]{next}{} + Starts the execution of a generator function or resumes it at the + last executed \keyword{yield} expression. When a generator function + is resumed with a \method{next()} method, the current \keyword{yield} + expression always evaluates to \constant{None}. The execution then + continues to the next \keyword{yield} expression, where the generator + is suspended again, and the value of the + \grammartoken{expression_list} is returned to \method{next()}'s + caller. If the generator exits without yielding another value, a + \exception{StopIteration} exception is raised. +\end{methoddesc} + +\begin{methoddesc}[generator]{send}{value} + Resumes the execution and ``sends'' a value into the generator + function. The \code{value} argument becomes the result of the + current \keyword{yield} expression. The \method{send()} method + returns the next value yielded by the generator, or raises + \exception{StopIteration} if the generator exits without yielding + another value. + When \method{send()} is called to start the generator, it must be + called with \constant{None} as the argument, because there is no + \keyword{yield} expression that could receieve the value. +\end{methoddesc} + +\begin{methoddesc}[generator]{throw} + {type\optional{, value\optional{, traceback}}} + Raises an exception of type \code{type} at the point where generator + was paused, and returns the next value yielded by the generator + function. If the generator exits without yielding another value, a + \exception{StopIteration} exception is raised. If the generator + function does not catch the passed-in exception, or raises a + different exception, then that exception propagates to the caller. +\end{methoddesc} + +\exindex{GeneratorExit} + +\begin{methoddesc}[generator]{close}{} + Raises a \exception{GeneratorExit} at the point where the generator + function was paused. If the generator function then raises + \exception{StopIteration} (by exiting normally, or due to already + being closed) or \exception{GeneratorExit} (by not catching the + exception), close returns to its caller. If the generator yields a + value, a \exception{RuntimeError} is raised. If the generator raises + any other exception, it is propagated to the caller. \method{close} + does nothing if the generator has already exited due to an exception + or normal exit. +\end{methoddesc} + +Here is a simple example that demonstrates the behavior of generators +and generator functions: + +\begin{verbatim} +>>> def echo(value=None): +... print "Execution starts when 'next()' is called for the first time." +... try: +... while True: +... try: +... value = (yield value) +... except GeneratorExit: +... # never catch GeneratorExit +... raise +... except Exception, e: +... value = e +... finally: +... print "Don't forget to clean up when 'close()' is called." +... +>>> generator = echo(1) +>>> print generator.next() +Execution starts when 'next()' is called for the first time. +1 +>>> print generator.next() +None +>>> print generator.send(2) +2 +>>> generator.throw(TypeError, "spam") +TypeError('spam',) +>>> generator.close() +Don't forget to clean up when 'close()' is called. +\end{verbatim} + +\begin{seealso} + \seepep{0342}{Coroutines via Enhanced Generators} + {The proposal to enhance the API and syntax of generators, + making them usable as simple coroutines.} +\end{seealso} + + \section{Primaries\label{primaries}} \index{primary} @@ -430,9 +564,8 @@ series of arguments: \begin{productionlist} \production{call} - {\token{primary} "(" [\token{argument_list} [","]] ")"} - {\token{primary} "(" [\token{argument_list} [","] | - \token{test} \token{genexpr_for} ] ")"} + {\token{primary} "(" [\token{argument_list} [","]} + \productioncont{ | \token{expression} \token{genexpr_for}] ")"} \production{argument_list} {\token{positional_arguments} ["," \token{keyword_arguments}]} \productioncont{ ["," "*" \token{expression}]} @@ -765,10 +898,9 @@ The shifting operations have lower priority than the arithmetic operations: \begin{productionlist} - % The empty groups below prevent conversion to guillemets. \production{shift_expr} {\token{a_expr} - | \token{shift_expr} ( "<{}<" | ">{}>" ) \token{a_expr}} + | \token{shift_expr} ( "<<" | ">>" ) \token{a_expr}} \end{productionlist} These operators accept plain or long integers as arguments. The @@ -966,14 +1098,18 @@ truth value. \section{Boolean operations\label{Booleans}} +\indexii{Conditional}{expression} \indexii{Boolean}{operation} Boolean operations have the lowest priority of all Python operations: \begin{productionlist} \production{expression} - {\token{or_test} [\token{if} \token{or_test} \token{else} - \token{test}] | \token{lambda_form}} + {\token{conditional_expression} | \token{lambda_form}} + \production{old_expression} + {\token{or_test} | \token{old_lambda_form}} + \production{conditional_expression} + {\token{or_test} ["if" \token{or_test} "else" \token{expression}]} \production{or_test} {\token{and_test} | \token{or_test} "or" \token{and_test}} \production{and_test} @@ -1025,6 +1161,8 @@ not \code{''}.) \begin{productionlist} \production{lambda_form} {"lambda" [\token{parameter_list}]: \token{expression}} + \production{old_lambda_form} + {"lambda" [\token{parameter_list}]: \token{old_expression}} \end{productionlist} Lambda forms (lambda expressions) have the same syntactic position as diff --git a/Doc/ref/ref6.tex b/Doc/ref/ref6.tex index e92a63d..60e7b02 100644 --- a/Doc/ref/ref6.tex +++ b/Doc/ref/ref6.tex @@ -106,7 +106,8 @@ objects: \begin{productionlist} \production{assignment_stmt} - {(\token{target_list} "=")+ \token{expression_list}} + {(\token{target_list} "=")+ + (\token{expression_list} | \token{yield_expression})} \production{target_list} {\token{target} ("," \token{target})* [","]} \production{target} @@ -271,11 +272,11 @@ operation and an assignment statement: \begin{productionlist} \production{augmented_assignment_stmt} - {\token{target} \token{augop} \token{expression_list}} + {\token{target} \token{augop} + (\token{expression_list} | \token{yield_expression})} \production{augop} {"+=" | "-=" | "*=" | "/=" | "\%=" | "**="} - % The empty groups below prevent conversion to guillemets. - \productioncont{| ">{}>=" | "<{}<=" | "\&=" | "\textasciicircum=" | "|="} + \productioncont{| ">>=" | "<<=" | "\&=" | "\textasciicircum=" | "|="} \end{productionlist} (See section~\ref{primaries} for the syntax definitions for the last @@ -404,7 +405,7 @@ to include an \grammartoken{expression_list}. In that context, a bare \begin{productionlist} \production{yield_stmt} - {"yield" \token{expression_list}} + {\token{yield_expression}} \end{productionlist} \index{generator!function} @@ -573,15 +574,19 @@ It continues with the next cycle of the nearest enclosing loop. \production{import_stmt} {"import" \token{module} ["as" \token{name}] ( "," \token{module} ["as" \token{name}] )*} - \productioncont{| "from" \token{module} "import" \token{identifier} + \productioncont{| "from" \token{relative_module} "import" \token{identifier} ["as" \token{name}]} \productioncont{ ( "," \token{identifier} ["as" \token{name}] )*} - \productioncont{| "from" \token{module} "import" "(" \token{identifier} - ["as" \token{name}]} + \productioncont{| "from" \token{relative_module} "import" "(" + \token{identifier} ["as" \token{name}]} \productioncont{ ( "," \token{identifier} ["as" \token{name}] )* [","] ")"} \productioncont{| "from" \token{module} "import" "*"} \production{module} {(\token{identifier} ".")* \token{identifier}} + \production{relative_module} + {"."* \token{module} | "."+} + \production{name} + {\token{identifier}} \end{productionlist} Import statements are executed in two steps: (1) find a module, and @@ -700,8 +705,10 @@ before the release in which the feature becomes standard. \begin{productionlist}[*] \production{future_statement} - {"from" "__future__" "import" feature ["as" name] ("," feature ["as" name])*} - \productioncont{| "from" "__future__" "import" "(" feature ["as" name] ("," feature ["as" name])* [","] ")"} + {"from" "__future__" "import" feature ["as" name]} + \productioncont{ ("," feature ["as" name])*} + \productioncont{| "from" "__future__" "import" "(" feature ["as" name]} + \productioncont{ ("," feature ["as" name])* [","] ")"} \production{feature}{identifier} \production{name}{identifier} \end{productionlist} @@ -718,9 +725,10 @@ lines that can appear before a future statement are: \end{itemize} -The features recognized by Python 2.3 are \samp{generators}, -\samp{division} and \samp{nested_scopes}. \samp{generators} and -\samp{nested_scopes} are redundant in 2.3 because they are always +The features recognized by Python 2.5 are \samp{absolute_import}, +\samp{division}, \samp{generators}, \samp{nested_scopes} and +\samp{with_statement}. \samp{generators} and \samp{nested_scopes} +are redundant in Python version 2.3 and above because they are always enabled. A future statement is recognized and treated specially at compile diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index 6bc0b08..02f96a4 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -313,7 +313,7 @@ be encapsulated for convenient reuse. \begin{productionlist} \production{with_stmt} - {"with" \token{expression} ["as" target] ":" \token{suite}} + {"with" \token{expression} ["as" \token{target}] ":" \token{suite}} \end{productionlist} The execution of the \keyword{with} statement proceeds as follows: diff --git a/Doc/texinputs/python.sty b/Doc/texinputs/python.sty index 3ce62f4..494323e 100644 --- a/Doc/texinputs/python.sty +++ b/Doc/texinputs/python.sty @@ -612,7 +612,7 @@ \newenvironment{cfuncdesc}[4][\py@badkey]{ \begin{fulllineitems} \cfuncline{#2}{#3}{#4} - \ifx#1\@undefined\else% + \ifx\@undefined#1\relax\else% \emph{Return value: \textbf{#1}.}\\ \fi }{\end{fulllineitems}} @@ -629,7 +629,7 @@ \newenvironment{ctypedesc}[2][\py@badkey]{ \begin{fulllineitems} \item[\bfcode{#2}% - \ifx#1\@undefined% + \ifx\@undefined#1\relax% \index{#2@{\py@idxcode{#2}} (C type)} \else% \index{#2@{\py@idxcode{#1}} (C type)} @@ -712,7 +712,7 @@ % \begin{methoddesc}[classname]{methodname}{args} \newcommand{\methodline}[3][\@undefined]{ \methodlineni{#2}{#3} - \ifx#1\@undefined + \ifx\@undefined#1\relax \index{#2@{\py@idxcode{#2()}} (\py@thisclass\ method)} \else \index{#2@{\py@idxcode{#2()}} (#1 method)} @@ -720,7 +720,7 @@ } \newenvironment{methoddesc}[3][\@undefined]{ \begin{fulllineitems} - \ifx#1\@undefined + \ifx\@undefined#1\relax \methodline{#2}{#3} \else \def\py@thisclass{#1} @@ -740,7 +740,7 @@ % object data attribute -------------------------------------------------- % \begin{memberdesc}[classname]{membername} \newcommand{\memberline}[2][\py@classbadkey]{% - \ifx#1\@undefined + \ifx\@undefined#1\relax \memberlineni{#2} \index{#2@{\py@idxcode{#2}} (\py@thisclass\ attribute)} \else @@ -750,7 +750,7 @@ } \newenvironment{memberdesc}[2][\py@classbadkey]{ \begin{fulllineitems} - \ifx#1\@undefined + \ifx\@undefined#1\relax \memberline{#2} \else \def\py@thisclass{#1} @@ -1046,14 +1046,14 @@ % \versionchanged[short explanation]{2.0} % \newcommand{\versionadded}[2][\py@badkey]{% - \ifx#1\@undefined% + \ifx\@undefined#1\relax% { New in version #2. }% \else% { New in version #2:\ #1. }% \fi% } \newcommand{\versionchanged}[2][\py@badkey]{% - \ifx#1\@undefined% + \ifx\@undefined#1\relax% { Changed in version #2. }% \else% { Changed in version #2:\ #1. }% diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex index 4abd0bd..a5e535d 100644 --- a/Doc/tut/tut.tex +++ b/Doc/tut/tut.tex @@ -813,7 +813,7 @@ Traceback (most recent call last): IndexError: string index out of range \end{verbatim} -The best way to remember how slices work is to think of the indices as +One way to remember how slices work is to think of the indices as pointing \emph{between} characters, with the left edge of the first character numbered 0. Then the right edge of the last character of a string of \var{n} characters has index \var{n}, for example: @@ -4312,8 +4312,7 @@ class DerivedClassName(Base1, Base2, Base3): <statement-N> \end{verbatim} -The only rule necessary to explain the semantics is the resolution -rule used for class attribute references. This is depth-first, +For old-style classes, the only rule is depth-first, left-to-right. Thus, if an attribute is not found in \class{DerivedClassName}, it is searched in \class{Base1}, then (recursively) in the base classes of \class{Base1}, and only if it is @@ -4328,16 +4327,26 @@ a name conflict with an attribute of \class{Base2}. The depth-first rule makes no differences between direct and inherited attributes of \class{Base1}.) -It is clear that indiscriminate use of multiple inheritance is a -maintenance nightmare, given the reliance in Python on conventions to -avoid accidental name conflicts. A well-known problem with multiple -inheritance is a class derived from two classes that happen to have a -common base class. While it is easy enough to figure out what happens -in this case (the instance will have a single copy of ``instance -variables'' or data attributes used by the common base class), it is -not clear that these semantics are in any way useful. +For new-style classes, the method resolution order changes dynamically +to support cooperative calls to \function{super()}. This approach +is known in some other multiple-inheritance languages as call-next-method +and is more powerful than the super call found in single-inheritance languages. + +With new-style classes, dynamic ordering is necessary because all +cases of multiple inheritance exhibit one or more diamond relationships +(where one at least one of the parent classes can be accessed through +multiple paths from the bottommost class). For example, all new-style +classes inherit from \class{object}, so any case of multiple inheritance +provides more than one path to reach \class{object}. To keep the +base classes from being accessed more than once, the dynamic algorithm +linearizes the search order in a way that preserves the left-to-right +ordering specified in each class, that calls each parent only once, and +that is monotonic (meaning that a class can be subclassed without affecting +the precedence order of its parents). Taken together, these properties +make it possible to design reliable and extensible classes with +multiple inheritance. For more detail, see +\url{http://www.python.org/download/releases/2.3/mro/}. -%% XXX Add rules for new-style MRO? \section{Private Variables \label{private}} diff --git a/Doc/whatsnew/whatsnew23.tex b/Doc/whatsnew/whatsnew23.tex index 72fd306..7c92be2 100644 --- a/Doc/whatsnew/whatsnew23.tex +++ b/Doc/whatsnew/whatsnew23.tex @@ -896,7 +896,7 @@ by Kevin Altis, Dave Cole, Andrew McNamara, Skip Montanaro, Cliff Wells. \end{seealso} %====================================================================== -\section{PEP 307: Pickle Enhancements \label{section-pep305}} +\section{PEP 307: Pickle Enhancements \label{section-pep307}} The \module{pickle} and \module{cPickle} modules received some attention during the 2.3 development cycle. In 2.2, new-style classes diff --git a/Doc/whatsnew/whatsnew24.tex b/Doc/whatsnew/whatsnew24.tex index 096b1ec..399bc0e 100644 --- a/Doc/whatsnew/whatsnew24.tex +++ b/Doc/whatsnew/whatsnew24.tex @@ -1291,7 +1291,7 @@ default is 2. [1, 2, 3] >>> list(i2) # Run the second iterator to exhaustion [1, 2, 3] ->\end{verbatim} +\end{verbatim} Note that \function{tee()} has to keep copies of the values returned by the iterator; in the worst case, it may need to keep all of them. diff --git a/Doc/whatsnew/whatsnew25.tex b/Doc/whatsnew/whatsnew25.tex index fce3927..b2f7380 100644 --- a/Doc/whatsnew/whatsnew25.tex +++ b/Doc/whatsnew/whatsnew25.tex @@ -1294,6 +1294,17 @@ the function is 6 times faster. (Contributed by Alan McIntyre and committed at the NeedForSpeed sprint.) % Patch 1442927 +\item It's now illegal to mix iterating over a file +with \code{for line in \var{file}} and calling +the file object's \method{read()}/\method{readline()}/\method{readlines()} +methods. Iteration uses an internal buffer and the +\method{read*()} methods don't use that buffer. +Instead they would return the data following the buffer, causing the +data to appear out of order. Mixing iteration and these methods will +now trigger a \exception{ValueError} from the \method{read*()} method. +(Implemented by Thomas Wouters.) +% Patch 1397960 + \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 @@ -1704,8 +1715,8 @@ 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})}. -Two new methods on socket objects, \method{recv_buf(\var{buffer})} and -\method{recvfrom_buf(\var{buffer})}, store the received data in an object +Two new methods on socket objects, \method{recv_into(\var{buffer})} and +\method{recvfrom_into(\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. diff --git a/Doc/whatsnew/whatsnew26.tex b/Doc/whatsnew/whatsnew26.tex index 3fa9b69..3543d9d 100644 --- a/Doc/whatsnew/whatsnew26.tex +++ b/Doc/whatsnew/whatsnew26.tex @@ -2,6 +2,46 @@ \usepackage{distutils} % $Id$ +% Rules for maintenance: +% +% * Anyone can add text to this document. Do not spend very much time +% on the wording of your changes, because your text will probably +% get rewritten to some degree. +% +% * The maintainer will go through Misc/NEWS periodically and add +% changes; it's therefore more important to add your changes to +% Misc/NEWS than to this file. +% +% * This is not a complete list of every single change; completeness +% is the purpose of Misc/NEWS. Some changes I consider too small +% or esoteric to include. If such a change is added to the text, +% I'll just remove it. (This is another reason you shouldn't spend +% too much time on writing your addition.) +% +% * If you want to draw your new text to the attention of the +% maintainer, add 'XXX' to the beginning of the paragraph or +% section. +% +% * It's OK to just add a fragmentary note about a change. For +% example: "XXX Describe the transmogrify() function added to the +% socket module." The maintainer will research the change and +% write the necessary text. +% +% * You can comment out your additions if you like, but it's not +% necessary (especially when a final release is some months away). +% +% * Credit the author of a patch or bugfix. Just the name is +% sufficient; the e-mail address isn't necessary. +% +% * It's helpful to add the bug/patch number as a comment: +% +% % Patch 12345 +% XXX Describe the transmogrify() function added to the socket +% module. +% (Contributed by P.Y. Developer.) +% +% This saves the maintainer the effort of going through the SVN log +% when researching a change. \title{What's New in Python 2.6} \release{0.0} @@ -29,6 +69,9 @@ rationale, refer to the PEP for a particular new feature. % Large, PEP-level features and changes should be described here. +% Should there be a new section here for 3k migration? +% Or perhaps a more general section describing module changes/deprecation? +% sets module deprecated %====================================================================== \section{Other Language Changes} @@ -37,7 +80,13 @@ Here are all of the changes that Python 2.6 makes to the core Python language. \begin{itemize} -\item TBD + +% Bug 1569356 +\item An obscure change: when you use the the \function{locals()} +function inside a \keyword{class} statement, the resulting dictionary +no longer returns free variables. (Free variables, in this case, are +variables referred to in the \keyword{class} statement +that aren't attributes of the class.) \end{itemize} @@ -47,7 +96,10 @@ language. \begin{itemize} -\item Optimizations should be described here. +% Patch 1624059 +\item Internally, a bit is now set in type objects to indicate some of +the standard built-in types. This speeds up checking if an object is +a subclass of one of these types. (Contributed by Neal Norwitz.) \end{itemize} @@ -67,6 +119,71 @@ details. \begin{itemize} +\item New data type in the \module{collections} module: +\class{NamedTuple(\var{typename}, \var{fieldnames})} is a factory function that +creates subclasses of the standard tuple whose fields are accessible +by name as well as index. For example: + +\begin{verbatim} +var_type = collections.NamedTuple('variable', + 'id name type size') +var = var_type(1, 'frequency', 'int', 4) + +print var[0], var.id # Equivalent +print var[2], var.type # Equivalent +\end{verbatim} + +(Contributed by Raymond Hettinger.) + +\item New method in the \module{curses} module: +for a window, \method{chgat()} changes the display characters for a +certain number of characters on a single line. + +\begin{verbatim} +# Boldface text starting at y=0,x=21 +# and affecting the rest of the line. +stdscr.chgat(0,21, curses.A_BOLD) +\end{verbatim} + +(Contributed by Fabian Kreutz.) + +\item New function in the \module{heapq} module: +\function{merge(iter1, iter2, ...)} +takes any number of iterables that return data +\emph{in sorted order}, +and +returns a new iterator that returns the contents of +all the iterators, also in sorted order. For example: + +\begin{verbatim} +heapq.merge([1, 3, 5, 9], [2, 8, 16]) -> + [1, 2, 3, 5, 8, 9, 16] +\end{verbatim} + +(Contributed by Raymond Hettinger.) + +\item New function in the \module{itertools} module: +\function{izip_longest(iter1, iter2, ...\optional{, fillvalue})} +makes tuples from each of the elements; if some of the iterables +are shorter than others, the missing values +are set to \var{fillvalue}. For example: + +\begin{verbatim} +itertools.izip_longest([1,2,3], [1,2,3,4,5]) -> + [(1, 1), (2, 2), (3, 3), (None, 4), (None, 5)] +\end{verbatim} + +(Contributed by Raymond Hettinger.) + +% Patch #1490190 +\item New functions in the \module{posix} module: \function{chflags()} +and \function{lchflags()} are wrappers for the corresponding system +calls (where they're available). Constants for the flag values are +defined in the \module{stat} module; some possible values include +\constant{UF_IMMUTABLE} to signal the file may not be changed and +\constant{UF_APPEND} to indicate that data can only be appended to the +file. (Contributed by M. Levinson.) + \item The \module{smtplib} module now supports SMTP over SSL thanks to the addition of the \class{SMTP_SSL} class. This class supports an interface identical to the existing \class{SMTP} diff --git a/Include/fileobject.h b/Include/fileobject.h index ee7c4ee..8fed9a3 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -55,6 +55,11 @@ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; char *Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); size_t Py_UniversalNewlineFread(char *, size_t, FILE *, PyObject *); +/* A routine to do sanity checking on the file mode string. returns + non-zero on if an exception occurred +*/ +int _PyFile_SanitizeMode(char *mode); + #ifdef __cplusplus } #endif diff --git a/Include/pydebug.h b/Include/pydebug.h index f6c7718..3c0fbc2 100644 --- a/Include/pydebug.h +++ b/Include/pydebug.h @@ -8,6 +8,7 @@ extern "C" { PyAPI_DATA(int) Py_DebugFlag; PyAPI_DATA(int) Py_VerboseFlag; PyAPI_DATA(int) Py_InteractiveFlag; +PyAPI_DATA(int) Py_InspectFlag; PyAPI_DATA(int) Py_OptimizeFlag; PyAPI_DATA(int) Py_NoSiteFlag; PyAPI_DATA(int) Py_UseClassExceptionsFlag; diff --git a/Include/pystate.h b/Include/pystate.h index cf29695..4919d99 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -21,6 +21,7 @@ typedef struct _is { PyObject *modules; PyObject *sysdict; PyObject *builtins; + PyObject *modules_reloading; PyObject *codec_search_path; PyObject *codec_search_cache; diff --git a/Include/setobject.h b/Include/setobject.h index a16c2f7..750a2a8 100644 --- a/Include/setobject.h +++ b/Include/setobject.h @@ -82,7 +82,8 @@ 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(int) _PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **key); +PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, long *hash); PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); @@ -52,7 +52,9 @@ the various releases. 2.4.1 2.4 2005 PSF yes 2.4.2 2.4.1 2005 PSF yes 2.4.3 2.4.2 2006 PSF yes + 2.4.4 2.4.3 2006 PSF yes 2.5 2.4 2006 PSF yes + 2.5.1 2.5 2007 PSF yes Footnotes: diff --git a/Lib/Bastion.py b/Lib/Bastion.py index d83ea3e..5331ba9 100644 --- a/Lib/Bastion.py +++ b/Lib/Bastion.py @@ -97,7 +97,7 @@ def Bastion(object, filter = lambda name: name[:1] != '_', """ - raise RuntimeError, "This code is not secure in Python 2.2 and 2.3" + raise RuntimeError, "This code is not secure in Python 2.2 and later" # Note: we define *two* ad-hoc functions here, get1 and get2. # Both are intended to be called in the same way: get(name). diff --git a/Lib/CGIHTTPServer.py b/Lib/CGIHTTPServer.py index f2d10e9..5017eec 100644 --- a/Lib/CGIHTTPServer.py +++ b/Lib/CGIHTTPServer.py @@ -197,6 +197,9 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): length = self.headers.getheader('content-length') if length: env['CONTENT_LENGTH'] = length + referer = self.headers.getheader('referer') + if referer: + env['HTTP_REFERER'] = referer accept = [] for line in self.headers.getallmatchingheaders('accept'): if line[:1] in "\t\n\r ": @@ -214,7 +217,7 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): # Since we're setting the env in the parent, provide empty # values to override previously set values for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', - 'HTTP_USER_AGENT', 'HTTP_COOKIE'): + 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): env.setdefault(k, "") os.environ.update(env) diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py index 8e644e1..6457e0f 100644 --- a/Lib/ConfigParser.py +++ b/Lib/ConfigParser.py @@ -594,7 +594,8 @@ class SafeConfigParser(ConfigParser): self._interpolate_some(option, L, rawval, section, vars, 1) return ''.join(L) - _interpvar_match = re.compile(r"%\(([^)]+)\)s").match + _interpvar_re = re.compile(r"%\(([^)]+)\)s") + _badpercent_re = re.compile(r"%[^%]|%$") def _interpolate_some(self, option, accum, rest, section, map, depth): if depth > MAX_INTERPOLATION_DEPTH: @@ -613,7 +614,7 @@ class SafeConfigParser(ConfigParser): accum.append("%") rest = rest[2:] elif c == "(": - m = self._interpvar_match(rest) + m = self._interpvar_re.match(rest) if m is None: raise InterpolationSyntaxError(option, section, "bad interpolation variable reference %r" % rest) @@ -638,4 +639,12 @@ class SafeConfigParser(ConfigParser): """Set an option. Extend ConfigParser.set: check for string values.""" if not isinstance(value, basestring): raise TypeError("option values must be strings") + # check for bad percent signs: + # first, replace all "good" interpolations + tmp_value = self._interpvar_re.sub('', value) + # then, check if there's a lone percent sign left + m = self._badpercent_re.search(tmp_value) + if m: + raise ValueError("invalid interpolation syntax in %r at " + "position %d" % (value, m.start())) ConfigParser.set(self, section, option, value) diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py index fd3b2c9..111e5f6 100644 --- a/Lib/DocXMLRPCServer.py +++ b/Lib/DocXMLRPCServer.py @@ -252,8 +252,10 @@ class DocXMLRPCServer( SimpleXMLRPCServer, """ def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, - logRequests=1): - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests) + logRequests=1, allow_none=False, encoding=None, + bind_and_activate=True): + SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, + allow_none, encoding, bind_and_activate) XMLRPCDocGenerator.__init__(self) class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, diff --git a/Lib/HTMLParser.py b/Lib/HTMLParser.py index 8380466..52f8c57 100644 --- a/Lib/HTMLParser.py +++ b/Lib/HTMLParser.py @@ -358,12 +358,30 @@ class HTMLParser(markupbase.ParserBase): self.error("unknown declaration: %r" % (data,)) # Internal -- helper to remove special character quoting + entitydefs = None def unescape(self, s): if '&' not in s: return s - s = s.replace("<", "<") - s = s.replace(">", ">") - s = s.replace("'", "'") - s = s.replace(""", '"') - s = s.replace("&", "&") # Must be last - return s + def replaceEntities(s): + s = s.groups()[0] + if s[0] == "#": + s = s[1:] + if s[0] in ['x','X']: + c = int(s[1:], 16) + else: + c = int(s) + return unichr(c) + else: + # Cannot use name2codepoint directly, because HTMLParser supports apos, + # which is not part of HTML 4 + import htmlentitydefs + if HTMLParser.entitydefs is None: + entitydefs = HTMLParser.entitydefs = {'apos':u"'"} + for k, v in htmlentitydefs.name2codepoint.items(): + entitydefs[k] = unichr(v) + try: + return self.entitydefs[s] + except KeyError: + return '&'+s+';' + + return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));", replaceEntities, s) diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/SimpleXMLRPCServer.py index 7065cc0..4aadffa 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/SimpleXMLRPCServer.py @@ -517,11 +517,11 @@ class SimpleXMLRPCServer(SocketServer.TCPServer, allow_reuse_address = True def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None): + logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): self.logRequests = logRequests SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) - SocketServer.TCPServer.__init__(self, addr, requestHandler) + SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) # [Bug #1222790] If possible, set close-on-exec flag; if a # method spawns a subprocess, the subprocess shouldn't have diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py index eedb251..84bbcf6 100644 --- a/Lib/SocketServer.py +++ b/Lib/SocketServer.py @@ -279,7 +279,7 @@ class TCPServer(BaseServer): Methods for the caller: - - __init__(server_address, RequestHandlerClass) + - __init__(server_address, RequestHandlerClass, bind_and_activate=True) - serve_forever() - handle_request() # if you don't use serve_forever() - fileno() -> int # for select() @@ -322,13 +322,14 @@ class TCPServer(BaseServer): allow_reuse_address = False - def __init__(self, server_address, RequestHandlerClass): + def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): """Constructor. May be extended, do not override.""" BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type) - self.server_bind() - self.server_activate() + if bind_and_activate: + self.server_bind() + self.server_activate() def server_bind(self): """Called by constructor to bind the socket. @@ -339,6 +340,7 @@ class TCPServer(BaseServer): if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) + self.server_address = self.socket.getsockname() def server_activate(self): """Called by constructor to activate the server. diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 10b0083..e5d2721 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -295,17 +295,16 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): """Return a time struct based on the input string and the format string.""" global _TimeRE_cache, _regex_cache with _cache_lock: - time_re = _TimeRE_cache - locale_time = time_re.locale_time - if _getlang() != locale_time.lang: + if _getlang() != _TimeRE_cache.locale_time.lang: _TimeRE_cache = TimeRE() - _regex_cache = {} + _regex_cache.clear() if len(_regex_cache) > _CACHE_MAX_SIZE: _regex_cache.clear() + locale_time = _TimeRE_cache.locale_time format_regex = _regex_cache.get(format) if not format_regex: try: - format_regex = time_re.compile(format) + format_regex = _TimeRE_cache.compile(format) # KeyError raised when a bad format is found; can be specified as # \\, in which case it was a stray % but with a space after it except KeyError as err: diff --git a/Lib/bisect.py b/Lib/bisect.py index 152f6c7..e4a2133 100644 --- a/Lib/bisect.py +++ b/Lib/bisect.py @@ -23,8 +23,8 @@ def bisect_right(a, x, lo=0, hi=None): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e <= x, and all e in - a[i:] have e > x. So if x already appears in the list, i points just - beyond the rightmost x already there. + a[i:] have e > x. So if x already appears in the list, a.insert(x) will + insert just after the rightmost x already there. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. @@ -62,8 +62,8 @@ def bisect_left(a, x, lo=0, hi=None): """Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e < x, and all e in - a[i:] have e >= x. So if x already appears in the list, i points just - before the leftmost x already there. + a[i:] have e >= x. So if x already appears in the list, a.insert(x) will + insert just before the leftmost x already there. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. @@ -49,11 +49,6 @@ __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. @@ -129,7 +124,7 @@ class Cmd: else: if self.use_rawinput: try: - line = raw_input(self.prompt) + line = input(self.prompt) except EOFError: line = 'EOF' else: diff --git a/Lib/collections.py b/Lib/collections.py new file mode 100644 index 0000000..dba9c7d --- /dev/null +++ b/Lib/collections.py @@ -0,0 +1,62 @@ +__all__ = ['deque', 'defaultdict', 'NamedTuple'] + +from _collections import deque, defaultdict +from operator import itemgetter as _itemgetter +import sys as _sys + +def NamedTuple(typename, s): + """Returns a new subclass of tuple with named fields. + + >>> Point = NamedTuple('Point', 'x y') + >>> Point.__doc__ # docstring for the new class + 'Point(x, y)' + >>> p = Point(11, y=22) # instantiate with positional args or keywords + >>> p[0] + p[1] # works just like the tuple (11, 22) + 33 + >>> x, y = p # unpacks just like a tuple + >>> x, y + (11, 22) + >>> p.x + p.y # fields also accessable by name + 33 + >>> p # readable __repr__ with name=value style + Point(x=11, y=22) + + """ + + field_names = s.split() + nargs = len(field_names) + + def __new__(cls, *args, **kwds): + if kwds: + try: + args += tuple(kwds[name] for name in field_names[len(args):]) + except KeyError as name: + raise TypeError('%s missing required argument: %s' % (typename, name)) + if len(args) != nargs: + raise TypeError('%s takes exactly %d arguments (%d given)' % (typename, nargs, len(args))) + return tuple.__new__(cls, args) + + repr_template = '%s(%s)' % (typename, ', '.join('%s=%%r' % name for name in field_names)) + + m = dict(vars(tuple)) # pre-lookup superclass methods (for faster lookup) + m.update(__doc__= '%s(%s)' % (typename, ', '.join(field_names)), + __slots__ = (), # no per-instance dict (so instances are same size as tuples) + __new__ = __new__, + __repr__ = lambda self, _format=repr_template.__mod__: _format(self), + __module__ = _sys._getframe(1).f_globals['__name__'], + ) + m.update((name, property(_itemgetter(index))) for index, name in enumerate(field_names)) + + return type(typename, (tuple,), m) + + +if __name__ == '__main__': + # verify that instances are pickable + from cPickle import loads, dumps + Point = NamedTuple('Point', 'x y') + p = Point(x=10, y=20) + assert p == loads(dumps(p)) + + import doctest + TestResults = NamedTuple('TestResults', 'failed attempted') + print(TestResults(*doctest.testmod())) diff --git a/Lib/commands.py b/Lib/commands.py index cfbb541..d19aa1a 100644 --- a/Lib/commands.py +++ b/Lib/commands.py @@ -32,6 +32,8 @@ __all__ = ["getstatusoutput","getoutput","getstatus"] # def getstatus(file): """Return output of "ls -ld <file>" in a string.""" + import warnings + warnings.warn("commands.getstatus() is deprecated", DeprecationWarning) return getoutput('ls -ld' + mkarg(file)) diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py index f4661ed..58d462b 100644 --- a/Lib/copy_reg.py +++ b/Lib/copy_reg.py @@ -43,7 +43,8 @@ def _reconstructor(cls, base, state): obj = object.__new__(cls) else: obj = base.__new__(cls, state) - base.__init__(obj, state) + if base.__init__ != object.__init__: + base.__init__(obj, state) return obj _HEAPTYPE = 1<<9 @@ -115,9 +115,10 @@ class DictWriter: def _dict_to_list(self, rowdict): if self.extrasaction == "raise": - for k in rowdict.keys(): - if k not in self.fieldnames: - raise ValueError, "dict contains fields not in fieldnames" + wrong_fields = [k for k in rowdict if k not in self.fieldnames] + if wrong_fields: + raise ValueError("dict contains fields not in fieldnames: " + + ", ".join(wrong_fields)) return [rowdict.get(key, self.restval) for key in self.fieldnames] def writerow(self, rowdict): diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index e2ea426..bd9b66e 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -233,6 +233,9 @@ class c_void_p(_SimpleCData): c_voidp = c_void_p # backwards compatibility (to a bug) _check_size(c_void_p) +class c_bool(_SimpleCData): + _type_ = "t" + # This cache maps types to pointers to them. _pointer_type_cache = {} @@ -480,7 +483,7 @@ def cast(obj, typ): return _cast(obj, obj, typ) _string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) -def string_at(ptr, size=0): +def string_at(ptr, size=-1): """string_at(addr[, size]) -> string Return the string at addr.""" @@ -492,7 +495,7 @@ except ImportError: pass else: _wstring_at = CFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) - def wstring_at(ptr, size=0): + def wstring_at(ptr, size=-1): """wstring_at(addr[, size]) -> string Return the string at addr.""" diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index e907e21..40892b9 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -21,7 +21,9 @@ if sys.platform == "win32": class POINT(Structure): _fields_ = [("x", c_int), ("y", c_int)] - +class RECT(Structure): + _fields_ = [("left", c_int), ("top", c_int), + ("right", c_int), ("bottom", c_int)] class FunctionTestCase(unittest.TestCase): def test_mro(self): @@ -379,5 +381,15 @@ class FunctionTestCase(unittest.TestCase): self.failUnlessEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h), (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9)) + def test_sf1651235(self): + # see http://www.python.org/sf/1651235 + + proto = CFUNCTYPE(c_int, RECT, POINT) + def callback(*args): + return 0 + + callback = proto(callback) + self.failUnlessRaises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT())) + if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_memfunctions.py b/Lib/ctypes/test/test_memfunctions.py index fbae2ce..aef7a73 100644 --- a/Lib/ctypes/test/test_memfunctions.py +++ b/Lib/ctypes/test/test_memfunctions.py @@ -14,6 +14,7 @@ class MemFunctionsTest(unittest.TestCase): self.failUnlessEqual(string_at(result), "Hello, World") self.failUnlessEqual(string_at(result, 5), "Hello") self.failUnlessEqual(string_at(result, 16), "Hello, World\0\0\0\0") + self.failUnlessEqual(string_at(result, 0), "") def test_memset(self): a = create_string_buffer(1000000) @@ -54,6 +55,7 @@ class MemFunctionsTest(unittest.TestCase): self.failUnlessEqual(wstring_at(a), "Hello, World") self.failUnlessEqual(wstring_at(a, 5), "Hello") self.failUnlessEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") + self.failUnlessEqual(wstring_at(a, 0), "") if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py index 2c5a990..eaabc7c 100644 --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -24,6 +24,8 @@ ArgType = type(byref(c_int(0))) unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong] signed_types = [c_byte, c_short, c_int, c_long, c_longlong] +bool_types = [] + float_types = [c_double, c_float] try: @@ -35,8 +37,16 @@ else: unsigned_types.append(c_ulonglong) signed_types.append(c_longlong) +try: + c_bool +except NameError: + pass +else: + bool_types.append(c_bool) + unsigned_ranges = valid_ranges(*unsigned_types) signed_ranges = valid_ranges(*signed_types) +bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]] ################################################################ @@ -60,6 +70,11 @@ class NumberTestCase(unittest.TestCase): self.failUnlessEqual(t(l).value, l) self.failUnlessEqual(t(h).value, h) + def test_bool_values(self): + from operator import truth + for t, v in zip(bool_types, bool_values): + self.failUnlessEqual(t(v).value, truth(v)) + def test_typeerror(self): # Only numbers are allowed in the contructor, # otherwise TypeError is raised @@ -82,7 +97,7 @@ class NumberTestCase(unittest.TestCase): def test_byref(self): # calling byref returns also a PyCArgObject instance - for t in signed_types + unsigned_types + float_types: + for t in signed_types + unsigned_types + float_types + bool_types: parm = byref(t()) self.failUnlessEqual(ArgType, type(parm)) @@ -101,7 +116,7 @@ class NumberTestCase(unittest.TestCase): self.assertRaises(TypeError, t, 3.14) def test_sizes(self): - for t in signed_types + unsigned_types + float_types: + for t in signed_types + unsigned_types + float_types + bool_types: size = struct.calcsize(t._type_) # sizeof of the type... self.failUnlessEqual(sizeof(t), size) @@ -164,6 +179,18 @@ class NumberTestCase(unittest.TestCase): a[0] = '?' self.failUnlessEqual(v.value, a[0]) + # array does not support c_bool / 't' + # def test_bool_from_address(self): + # from ctypes import c_bool + # from array import array + # a = array(c_bool._type_, [True]) + # v = t.from_address(a.buffer_info()[0]) + # self.failUnlessEqual(v.value, a[0]) + # self.failUnlessEqual(type(v) is t) + # a[0] = False + # self.failUnlessEqual(v.value, a[0]) + # self.failUnlessEqual(type(v) is t) + def test_init(self): # c_int() can be initialized from Python's int, and c_int. # Not from c_long or so, which seems strange, abd should diff --git a/Lib/ctypes/test/test_repr.py b/Lib/ctypes/test/test_repr.py index 1044f67..f6f9366 100644 --- a/Lib/ctypes/test/test_repr.py +++ b/Lib/ctypes/test/test_repr.py @@ -4,7 +4,7 @@ import unittest subclasses = [] for base in [c_byte, c_short, c_int, c_long, c_longlong, c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong, - c_float, c_double]: + c_float, c_double, c_bool]: class X(base): pass subclasses.append(X) diff --git a/Lib/decimal.py b/Lib/decimal.py index 148b626..a7238e1 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -29,8 +29,8 @@ and IEEE standard 854-1987: Decimal floating point has finite precision with arbitrarily large bounds. -The purpose of the module is to support arithmetic using familiar -"schoolhouse" rules and to avoid the some of tricky representation +The purpose of this module is to support arithmetic using familiar +"schoolhouse" rules and to avoid some of the tricky representation issues associated with binary floating point. The package is especially useful for financial applications or for contexts where users have expectations that are at odds with binary floating point (for instance, @@ -136,7 +136,7 @@ __all__ = [ import copy as _copy -#Rounding +# Rounding ROUND_DOWN = 'ROUND_DOWN' ROUND_HALF_UP = 'ROUND_HALF_UP' ROUND_HALF_EVEN = 'ROUND_HALF_EVEN' @@ -145,11 +145,11 @@ ROUND_FLOOR = 'ROUND_FLOOR' ROUND_UP = 'ROUND_UP' ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' -#Rounding decision (not part of the public API) +# Rounding decision (not part of the public API) NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. -#Errors +# Errors class DecimalException(ArithmeticError): """Base exception class. @@ -179,9 +179,9 @@ class Clamped(DecimalException): This occurs and signals clamped if the exponent of a result has been altered in order to fit the constraints of a specific concrete - representation. This may occur when the exponent of a zero result would - be outside the bounds of a representation, or when a large normal - number would have an encoded exponent that cannot be represented. In + representation. This may occur when the exponent of a zero result would + be outside the bounds of a representation, or when a large normal + number would have an encoded exponent that cannot be represented. In this latter case, the exponent is reduced to fit and the corresponding number of zero digits are appended to the coefficient ("fold-down"). """ @@ -194,8 +194,8 @@ class InvalidOperation(DecimalException): Something creates a signaling NaN -INF + INF - 0 * (+-)INF - (+-)INF / (+-)INF + 0 * (+-)INF + (+-)INF / (+-)INF x % 0 (+-)INF % x x._rescale( non-integer ) @@ -207,7 +207,7 @@ class InvalidOperation(DecimalException): """ def handle(self, context, *args): if args: - if args[0] == 1: #sNaN, must drop 's' but keep diagnostics + if args[0] == 1: # sNaN, must drop 's' but keep diagnostics return Decimal( (args[1]._sign, args[1]._int, 'n') ) return NaN @@ -216,11 +216,11 @@ class ConversionSyntax(InvalidOperation): This occurs and signals invalid-operation if an string is being converted to a number and it does not conform to the numeric string - syntax. The result is [0,qNaN]. + syntax. The result is [0,qNaN]. """ def handle(self, context, *args): - return (0, (0,), 'n') #Passed to something which uses a tuple. + return (0, (0,), 'n') # Passed to something which uses a tuple. class DivisionByZero(DecimalException, ZeroDivisionError): """Division by 0. @@ -245,7 +245,7 @@ class DivisionImpossible(InvalidOperation): This occurs and signals invalid-operation if the integer result of a divide-integer or remainder operation had too many digits (would be - longer than precision). The result is [0,qNaN]. + longer than precision). The result is [0,qNaN]. """ def handle(self, context, *args): @@ -256,12 +256,12 @@ class DivisionUndefined(InvalidOperation, ZeroDivisionError): This occurs and signals invalid-operation if division by zero was attempted (during a divide-integer, divide, or remainder operation), and - the dividend is also zero. The result is [0,qNaN]. + the dividend is also zero. The result is [0,qNaN]. """ def handle(self, context, tup=None, *args): if tup is not None: - return (NaN, NaN) #for 0 %0, 0 // 0 + return (NaN, NaN) # for 0 %0, 0 // 0 return NaN class Inexact(DecimalException): @@ -269,7 +269,7 @@ class Inexact(DecimalException): This occurs and signals inexact whenever the result of an operation is not exact (that is, it needed to be rounded and any discarded digits - were non-zero), or if an overflow or underflow condition occurs. The + were non-zero), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The inexact signal may be tested (or trapped) to determine if a given @@ -281,11 +281,11 @@ class InvalidContext(InvalidOperation): """Invalid context. Unknown rounding, for example. This occurs and signals invalid-operation if an invalid context was - detected during an operation. This can occur if contexts are not checked + detected during an operation. This can occur if contexts are not checked on creation and either the precision exceeds the capability of the underlying concrete representation or an unknown or unsupported rounding - was specified. These aspects of the context need only be checked when - the values are required to be used. The result is [0,qNaN]. + was specified. These aspects of the context need only be checked when + the values are required to be used. The result is [0,qNaN]. """ def handle(self, context, *args): @@ -296,7 +296,7 @@ class Rounded(DecimalException): This occurs and signals rounded whenever the result of an operation is rounded (that is, some zero or non-zero digits were discarded from the - coefficient), or if an overflow or underflow condition occurs. The + coefficient), or if an overflow or underflow condition occurs. The result in all cases is unchanged. The rounded signal may be tested (or trapped) to determine if a given @@ -309,7 +309,7 @@ class Subnormal(DecimalException): This occurs and signals subnormal whenever the result of a conversion or operation is subnormal (that is, its adjusted exponent is less than - Emin, before any rounding). The result in all cases is unchanged. + Emin, before any rounding). The result in all cases is unchanged. The subnormal signal may be tested (or trapped) to determine if a given or operation (or sequence of operations) yielded a subnormal result. @@ -328,13 +328,13 @@ class Overflow(Inexact, Rounded): For round-half-up and round-half-even (and for round-half-down and round-up, if implemented), the result of the operation is [sign,inf], - where sign is the sign of the intermediate result. For round-down, the + where sign is the sign of the intermediate result. For round-down, the result is the largest finite number that can be represented in the - current precision, with the sign of the intermediate result. For + current precision, with the sign of the intermediate result. For round-ceiling, the result is the same as for round-down if the sign of - the intermediate result is 1, or is [0,inf] otherwise. For round-floor, + the intermediate result is 1, or is [0,inf] otherwise. For round-floor, the result is the same as for round-down if the sign of the intermediate - result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded + result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded will also be raised. """ @@ -360,10 +360,10 @@ class Underflow(Inexact, Rounded, Subnormal): This occurs and signals underflow if a result is inexact and the adjusted exponent of the result would be smaller (more negative) than the smallest value that can be handled by the implementation (the value - Emin). That is, the result is both inexact and subnormal. + Emin). That is, the result is both inexact and subnormal. The result after an underflow will be a subnormal number rounded, if - necessary, so that its exponent is not less than Etiny. This may result + necessary, so that its exponent is not less than Etiny. This may result in 0 with the sign of the intermediate result and an exponent of Etiny. In all cases, Inexact, Rounded, and Subnormal will also be raised. @@ -379,7 +379,7 @@ _condition_map = {ConversionSyntax:InvalidOperation, DivisionUndefined:InvalidOperation, InvalidContext:InvalidOperation} -##### Context Functions ####################################### +##### Context Functions ################################################## # The getcontext() and setcontext() function manage access to a thread-local # current context. Py2.4 offers direct support for thread locals. If that @@ -392,7 +392,7 @@ try: except ImportError: # Python was compiled without threads; create a mock object instead import sys - class MockThreading: + class MockThreading(object): def local(self, sys=sys): return sys.modules[__name__] threading = MockThreading() @@ -403,8 +403,8 @@ try: except AttributeError: - #To fix reloading, force it to create a new context - #Old contexts have different exceptions in their dicts, making problems. + # To fix reloading, force it to create a new context + # Old contexts have different exceptions in their dicts, making problems. if hasattr(threading.currentThread(), '__decimal_context__'): del threading.currentThread().__decimal_context__ @@ -469,14 +469,14 @@ def localcontext(ctx=None): ctx.prec += 2 # Rest of sin calculation algorithm # uses a precision 2 greater than normal - return +s # Convert result to normal precision + return +s # Convert result to normal precision def sin(x): with localcontext(ExtendedContext): # Rest of sin calculation algorithm # uses the Extended Context from the # General Decimal Arithmetic Specification - return +s # Convert result to normal context + return +s # Convert result to normal context """ # The string below can't be included in the docstring until Python 2.6 @@ -489,11 +489,11 @@ def localcontext(ctx=None): ... ctx = getcontext() ... ctx.prec += 2 ... print(ctx.prec) - ... + ... 30 >>> with localcontext(ExtendedContext): ... print(getcontext().prec) - ... + ... 9 >>> print(getcontext().prec) 28 @@ -502,7 +502,7 @@ def localcontext(ctx=None): return _ContextManager(ctx) -##### Decimal class ########################################### +##### Decimal class ####################################################### class Decimal(object): """Floating point class for decimal arithmetic.""" @@ -518,7 +518,7 @@ class Decimal(object): >>> Decimal('3.14') # string input Decimal("3.14") - >>> Decimal((0, (3, 1, 4), -2)) # tuple input (sign, digit_tuple, exponent) + >>> Decimal((0, (3, 1, 4), -2)) # tuple (sign, digit_tuple, exponent) Decimal("3.14") >>> Decimal(314) # int or long Decimal("314") @@ -557,13 +557,13 @@ class Decimal(object): # tuple/list conversion (possibly from as_tuple()) if isinstance(value, (list,tuple)): if len(value) != 3: - raise ValueError, 'Invalid arguments' + raise ValueError('Invalid arguments') if value[0] not in (0,1): - raise ValueError, 'Invalid sign' + raise ValueError('Invalid sign') for digit in value[1]: if not isinstance(digit, (int,int)) or digit < 0: - raise ValueError, "The second value in the tuple must be composed of non negative integer elements." - + raise ValueError("The second value in the tuple must be" + "composed of non negative integer elements.") self._sign = value[0] self._int = tuple(value[1]) if value[2] in ('F','n','N'): @@ -596,22 +596,23 @@ class Decimal(object): if _isnan(value): sig, sign, diag = _isnan(value) self._is_special = True - if len(diag) > context.prec: #Diagnostic info too long + if len(diag) > context.prec: # Diagnostic info too long self._sign, self._int, self._exp = \ context._raise_error(ConversionSyntax) return self if sig == 1: - self._exp = 'n' #qNaN - else: #sig == 2 - self._exp = 'N' #sNaN + self._exp = 'n' # qNaN + else: # sig == 2 + self._exp = 'N' # sNaN self._sign = sign - self._int = tuple(map(int, diag)) #Diagnostic info + self._int = tuple(map(int, diag)) # Diagnostic info return self try: self._sign, self._int, self._exp = _string2exact(value) except ValueError: self._is_special = True - self._sign, self._int, self._exp = context._raise_error(ConversionSyntax) + self._sign, self._int, self._exp = \ + context._raise_error(ConversionSyntax) return self raise TypeError("Cannot convert %r to Decimal" % value) @@ -694,15 +695,15 @@ class Decimal(object): if self._is_special or other._is_special: ans = self._check_nans(other, context) if ans: - return 1 # Comparison involving NaN's always reports self > other + return 1 # Comparison involving NaN's always reports self > other # INF = INF return cmp(self._isinfinity(), other._isinfinity()) if not self and not other: - return 0 #If both 0, sign comparison isn't certain. + return 0 # If both 0, sign comparison isn't certain. - #If different signs, neg one is less + # If different signs, neg one is less if other._sign < self._sign: return -1 if self._sign < other._sign: @@ -713,7 +714,7 @@ class Decimal(object): if self_adjusted == other_adjusted and \ self._int + (0,)*(self._exp - other._exp) == \ other._int + (0,)*(other._exp - self._exp): - return 0 #equal, except in precision. ([0]*(-x) = []) + return 0 # equal, except in precision. ([0]*(-x) = []) elif self_adjusted > other_adjusted and self._int[0] != 0: return (-1)**self._sign elif self_adjusted < other_adjusted and other._int[0] != 0: @@ -724,7 +725,7 @@ class Decimal(object): context = getcontext() context = context._shallow_copy() - rounding = context._set_rounding(ROUND_UP) #round away from 0 + rounding = context._set_rounding(ROUND_UP) # round away from 0 flags = context._ignore_all_flags() res = self.__sub__(other, context=context) @@ -782,7 +783,7 @@ class Decimal(object): if other is NotImplemented: return other - #compare(NaN, NaN) = NaN + # Compare(NaN, NaN) = NaN if (self._is_special or other and other._is_special): ans = self._check_nans(other, context) if ans: @@ -843,11 +844,11 @@ class Decimal(object): tmp = map(str, self._int) numdigits = len(self._int) leftdigits = self._exp + numdigits - if eng and not self: #self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY - if self._exp < 0 and self._exp >= -6: #short, no need for e/E + if eng and not self: # self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY + if self._exp < 0 and self._exp >= -6: # short, no need for e/E s = '-'*self._sign + '0.' + '0'*(abs(self._exp)) return s - #exp is closest mult. of 3 >= self._exp + # exp is closest mult. of 3 >= self._exp exp = ((self._exp - 1)// 3 + 1) * 3 if exp != self._exp: s = '0.'+'0'*(exp - self._exp) @@ -859,7 +860,7 @@ class Decimal(object): else: s += 'e' if exp > 0: - s += '+' #0.0e+3, not 0.0e3 + s += '+' # 0.0e+3, not 0.0e3 s += str(exp) s = '-'*self._sign + s return s @@ -999,19 +1000,19 @@ class Decimal(object): return ans if self._isinfinity(): - #If both INF, same sign => same as both, opposite => error. + # If both INF, same sign => same as both, opposite => error. if self._sign != other._sign and other._isinfinity(): return context._raise_error(InvalidOperation, '-INF + INF') return Decimal(self) if other._isinfinity(): - return Decimal(other) #Can't both be infinity here + return Decimal(other) # Can't both be infinity here shouldround = context._rounding_decision == ALWAYS_ROUND exp = min(self._exp, other._exp) negativezero = 0 if context.rounding == ROUND_FLOOR and self._sign != other._sign: - #If the answer is 0, the sign should be negative, in this case. + # If the answer is 0, the sign should be negative, in this case. negativezero = 1 if not self and not other: @@ -1046,19 +1047,19 @@ class Decimal(object): return Decimal((negativezero, (0,), exp)) if op1.int < op2.int: op1, op2 = op2, op1 - #OK, now abs(op1) > abs(op2) + # OK, now abs(op1) > abs(op2) if op1.sign == 1: result.sign = 1 op1.sign, op2.sign = op2.sign, op1.sign else: result.sign = 0 - #So we know the sign, and op1 > 0. + # So we know the sign, and op1 > 0. elif op1.sign == 1: result.sign = 1 op1.sign, op2.sign = (0, 0) else: result.sign = 0 - #Now, op1 > abs(op2) > 0 + # Now, op1 > abs(op2) > 0 if op2.sign == 0: result.int = op1.int + op2.int @@ -1116,7 +1117,8 @@ class Decimal(object): if ans: return ans - return Decimal(self) # Must be infinite, and incrementing makes no difference + # Must be infinite, and incrementing makes no difference + return Decimal(self) L = list(self._int) L[-1] += 1 @@ -1172,7 +1174,7 @@ class Decimal(object): if not self or not other: ans = Decimal((resultsign, (0,), resultexp)) if shouldround: - #Fixing in case the exponent is out of bounds + # Fixing in case the exponent is out of bounds ans = ans._fix(context) return ans @@ -1191,7 +1193,7 @@ class Decimal(object): op1 = _WorkRep(self) op2 = _WorkRep(other) - ans = Decimal( (resultsign, map(int, str(op1.int * op2.int)), resultexp)) + ans = Decimal((resultsign, map(int, str(op1.int * op2.int)), resultexp)) if shouldround: ans = ans._fix(context) @@ -1283,12 +1285,11 @@ class Decimal(object): sign, 1) return context._raise_error(DivisionByZero, 'x / 0', sign) - #OK, so neither = 0, INF or NaN - + # OK, so neither = 0, INF or NaN shouldround = context._rounding_decision == ALWAYS_ROUND - #If we're dividing into ints, and self < other, stop. - #self.__abs__(0) does not round. + # If we're dividing into ints, and self < other, stop. + # self.__abs__(0) does not round. if divmod and (self.__abs__(0, context) < other.__abs__(0, context)): if divmod == 1 or divmod == 3: @@ -1300,7 +1301,7 @@ class Decimal(object): ans2) elif divmod == 2: - #Don't round the mod part, if we don't need it. + # Don't round the mod part, if we don't need it. return (Decimal( (sign, (0,), 0) ), Decimal(self)) op1 = _WorkRep(self) @@ -1349,7 +1350,7 @@ class Decimal(object): op1.exp -= 1 if res.exp == 0 and divmod and op2.int > op1.int: - #Solves an error in precision. Same as a previous block. + # Solves an error in precision. Same as a previous block. if res.int >= prec_limit and shouldround: return context._raise_error(DivisionImpossible) @@ -1434,7 +1435,7 @@ class Decimal(object): # ignored in the calling function. context = context._shallow_copy() flags = context._ignore_flags(Rounded, Inexact) - #keep DivisionImpossible flags + # Keep DivisionImpossible flags (side, r) = self.__divmod__(other, context=context) if r._isnan(): @@ -1457,7 +1458,7 @@ class Decimal(object): if r < comparison: r._sign, comparison._sign = s1, s2 - #Get flags now + # Get flags now self.__divmod__(other, context=context) return r._fix(context) r._sign, comparison._sign = s1, s2 @@ -1479,7 +1480,8 @@ class Decimal(object): if r > comparison or decrease and r == comparison: r._sign, comparison._sign = s1, s2 context.prec += 1 - if len(side.__add__(Decimal(1), context=context)._int) >= context.prec: + numbsquant = len(side.__add__(Decimal(1), context=context)._int) + if numbsquant >= context.prec: context.prec -= 1 return context._raise_error(DivisionImpossible)[1] context.prec -= 1 @@ -1514,7 +1516,7 @@ class Decimal(object): context = getcontext() return context._raise_error(InvalidContext) elif self._isinfinity(): - raise OverflowError, "Cannot convert infinity to long" + raise OverflowError("Cannot convert infinity to long") if self._exp >= 0: s = ''.join(map(str, self._int)) + '0'*self._exp else: @@ -1568,13 +1570,13 @@ class Decimal(object): context._raise_error(Clamped) return ans ans = ans._rescale(Etiny, context=context) - #It isn't zero, and exp < Emin => subnormal + # It isn't zero, and exp < Emin => subnormal context._raise_error(Subnormal) if context.flags[Inexact]: context._raise_error(Underflow) else: if ans: - #Only raise subnormal if non-zero. + # Only raise subnormal if non-zero. context._raise_error(Subnormal) else: Etop = context.Etop() @@ -1591,7 +1593,8 @@ class Decimal(object): return ans context._raise_error(Inexact) context._raise_error(Rounded) - return context._raise_error(Overflow, 'above Emax', ans._sign) + c = context._raise_error(Overflow, 'above Emax', ans._sign) + return c return ans def _round(self, prec=None, rounding=None, context=None): @@ -1651,18 +1654,18 @@ class Decimal(object): ans = Decimal( (temp._sign, tmp, temp._exp - expdiff)) return ans - #OK, but maybe all the lost digits are 0. + # OK, but maybe all the lost digits are 0. lostdigits = self._int[expdiff:] if lostdigits == (0,) * len(lostdigits): ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff)) - #Rounded, but not Inexact + # Rounded, but not Inexact context._raise_error(Rounded) return ans # Okay, let's round and lose data this_function = getattr(temp, self._pick_rounding_function[rounding]) - #Now we've got the rounding function + # Now we've got the rounding function if prec != context.prec: context = context._shallow_copy() @@ -1758,7 +1761,7 @@ class Decimal(object): context = getcontext() if self._is_special or n._is_special or n.adjusted() > 8: - #Because the spot << doesn't work with really big exponents + # Because the spot << doesn't work with really big exponents if n._isinfinity() or n.adjusted() > 8: return context._raise_error(InvalidOperation, 'x ** INF') @@ -1788,9 +1791,10 @@ class Decimal(object): return Infsign[sign] return Decimal( (sign, (0,), 0) ) - #with ludicrously large exponent, just raise an overflow and return inf. - if not modulo and n > 0 and (self._exp + len(self._int) - 1) * n > context.Emax \ - and self: + # With ludicrously large exponent, just raise an overflow + # and return inf. + if not modulo and n > 0 and \ + (self._exp + len(self._int) - 1) * n > context.Emax and self: tmp = Decimal('inf') tmp._sign = sign @@ -1810,7 +1814,7 @@ class Decimal(object): context = context._shallow_copy() context.prec = firstprec + elength + 1 if n < 0: - #n is a long now, not Decimal instance + # n is a long now, not Decimal instance n = -n mul = Decimal(1).__truediv__(mul, context=context) @@ -1819,7 +1823,7 @@ class Decimal(object): spot <<= 1 spot >>= 1 - #Spot is the highest power of 2 less than n + # spot is the highest power of 2 less than n while spot: val = val.__mul__(val, context=context) if val._isinfinity(): @@ -1877,7 +1881,7 @@ class Decimal(object): if exp._isinfinity() or self._isinfinity(): if exp._isinfinity() and self._isinfinity(): - return self #if both are inf, it is OK + return self # if both are inf, it is OK if context is None: context = getcontext() return context._raise_error(InvalidOperation, @@ -1982,13 +1986,13 @@ class Decimal(object): return Decimal(self) if not self: - #exponent = self._exp / 2, using round_down. - #if self._exp < 0: + # exponent = self._exp / 2, using round_down. + # if self._exp < 0: # exp = (self._exp+1) // 2 - #else: + # else: exp = (self._exp) // 2 if self._sign == 1: - #sqrt(-0) = -0 + # sqrt(-0) = -0 return Decimal( (1, (0,), exp)) else: return Decimal( (0, (0,), exp)) @@ -2023,8 +2027,7 @@ class Decimal(object): context=context), context=context) ans._exp -= 1 + tmp.adjusted() // 2 - #ans is now a linear approximation. - + # ans is now a linear approximation. Emax, Emin = context.Emax, context.Emin context.Emax, context.Emin = DefaultContext.Emax, DefaultContext.Emin @@ -2039,12 +2042,12 @@ class Decimal(object): if context.prec == maxp: break - #round to the answer's precision-- the only error can be 1 ulp. + # Round to the answer's precision-- the only error can be 1 ulp. context.prec = firstprec prevexp = ans.adjusted() ans = ans._round(context=context) - #Now, check if the other last digits are better. + # Now, check if the other last digits are better. context.prec = firstprec + 1 # In case we rounded up another digit and we should actually go lower. if prevexp != ans.adjusted(): @@ -2076,10 +2079,10 @@ class Decimal(object): context._raise_error(Rounded) context._raise_error(Inexact) else: - #Exact answer, so let's set the exponent right. - #if self._exp < 0: + # Exact answer, so let's set the exponent right. + # if self._exp < 0: # exp = (self._exp +1)// 2 - #else: + # else: exp = self._exp // 2 context.prec += ans._exp - exp ans = ans._rescale(exp, context=context) @@ -2100,7 +2103,7 @@ class Decimal(object): return other if self._is_special or other._is_special: - # if one operand is a quiet NaN and the other is number, then the + # If one operand is a quiet NaN and the other is number, then the # number is always returned sn = self._isnan() on = other._isnan() @@ -2114,13 +2117,13 @@ class Decimal(object): ans = self c = self.__cmp__(other) if c == 0: - # if both operands are finite and equal in numerical value + # If both operands are finite and equal in numerical value # then an ordering is applied: # - # if the signs differ then max returns the operand with the + # If the signs differ then max returns the operand with the # positive sign and min returns the operand with the negative sign # - # if the signs are the same then the exponent is used to select + # If the signs are the same then the exponent is used to select # the result. if self._sign != other._sign: if self._sign: @@ -2141,7 +2144,7 @@ class Decimal(object): def min(self, other, context=None): """Returns the smaller value. - like min(self, other) except if one is not a number, returns + Like min(self, other) except if one is not a number, returns NaN (and signals if one is sNaN). Also rounds. """ other = _convert_other(other) @@ -2149,7 +2152,7 @@ class Decimal(object): return other if self._is_special or other._is_special: - # if one operand is a quiet NaN and the other is number, then the + # If one operand is a quiet NaN and the other is number, then the # number is always returned sn = self._isnan() on = other._isnan() @@ -2163,13 +2166,13 @@ class Decimal(object): ans = self c = self.__cmp__(other) if c == 0: - # if both operands are finite and equal in numerical value + # If both operands are finite and equal in numerical value # then an ordering is applied: # - # if the signs differ then max returns the operand with the + # If the signs differ then max returns the operand with the # positive sign and min returns the operand with the negative sign # - # if the signs are the same then the exponent is used to select + # If the signs are the same then the exponent is used to select # the result. if self._sign != other._sign: if other._sign: @@ -2204,11 +2207,11 @@ class Decimal(object): """Return the adjusted exponent of self""" try: return self._exp + len(self._int) - 1 - #If NaN or Infinity, self._exp is string + # If NaN or Infinity, self._exp is string except TypeError: return 0 - # support for pickling, copy, and deepcopy + # Support for pickling, copy, and deepcopy def __reduce__(self): return (self.__class__, (str(self),)) @@ -2222,13 +2225,14 @@ class Decimal(object): return self # My components are also immutable return self.__class__(str(self)) -##### Context class ########################################### +##### Context class ####################################################### # get rounding method function: -rounding_functions = [name for name in Decimal.__dict__.keys() if name.startswith('_round_')] +rounding_functions = [name for name in Decimal.__dict__.keys() + if name.startswith('_round_')] for name in rounding_functions: - #name is like _round_half_even, goes to the global ROUND_HALF_EVEN value. + # name is like _round_half_even, goes to the global ROUND_HALF_EVEN value. globalname = name[1:].upper() val = globals()[globalname] Decimal._pick_rounding_function[val] = name @@ -2255,7 +2259,7 @@ class Context(object): Contains: prec - precision (for use in rounding, division, square roots..) - rounding - rounding type. (how you round) + rounding - rounding type (how you round) _rounding_decision - ALWAYS_ROUND, NEVER_ROUND -- do you round? traps - If traps[exception] = 1, then the exception is raised when it is caused. Otherwise, a value is @@ -2294,9 +2298,13 @@ class Context(object): def __repr__(self): """Show the current context.""" s = [] - s.append('Context(prec=%(prec)d, rounding=%(rounding)s, Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d' % vars(self)) - s.append('flags=[' + ', '.join([f.__name__ for f, v in self.flags.items() if v]) + ']') - s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']') + s.append('Context(prec=%(prec)d, rounding=%(rounding)s, ' + 'Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d' + % vars(self)) + names = [f.__name__ for f, v in self.flags.items() if v] + s.append('flags=[' + ', '.join(names) + ']') + names = [t.__name__ for t, v in self.traps.items() if v] + s.append('traps=[' + ', '.join(names) + ']') return ', '.join(s) + ')' def clear_flags(self): @@ -2313,9 +2321,9 @@ class Context(object): def copy(self): """Returns a deep copy from self.""" - nc = Context(self.prec, self.rounding, self.traps.copy(), self.flags.copy(), - self._rounding_decision, self.Emin, self.Emax, - self.capitals, self._clamp, self._ignored_flags) + nc = Context(self.prec, self.rounding, self.traps.copy(), + self.flags.copy(), self._rounding_decision, self.Emin, + self.Emax, self.capitals, self._clamp, self._ignored_flags) return nc __copy__ = copy @@ -2329,16 +2337,16 @@ class Context(object): """ error = _condition_map.get(condition, condition) if error in self._ignored_flags: - #Don't touch the flag + # Don't touch the flag return error().handle(self, *args) self.flags[error] += 1 if not self.traps[error]: - #The errors define how to handle themselves. + # The errors define how to handle themselves. return condition().handle(self, *args) # Errors should only be risked on copies of the context - #self._ignored_flags = [] + # self._ignored_flags = [] raise error, explanation def _ignore_all_flags(self): @@ -2362,7 +2370,7 @@ class Context(object): def __hash__(self): """A Context cannot be hashed.""" # We inherit object.__hash__, so we must deny this explicitly - raise TypeError, "Cannot hash a Context." + raise TypeError("Cannot hash a Context.") def Etiny(self): """Returns Etiny (= Emin - prec + 1)""" @@ -2417,12 +2425,12 @@ class Context(object): d = Decimal(num, context=self) return d._fix(self) - #Methods + # Methods def abs(self, a): """Returns the absolute value of the operand. If the operand is negative, the result is the same as using the minus - operation on the operand. Otherwise, the result is the same as using + operation on the operand. Otherwise, the result is the same as using the plus operation on the operand. >>> ExtendedContext.abs(Decimal('2.1')) @@ -2524,8 +2532,8 @@ class Context(object): If either operand is a NaN then the general rules apply. Otherwise, the operands are compared as as though by the compare - operation. If they are numerically equal then the left-hand operand - is chosen as the result. Otherwise the maximum (closer to positive + operation. If they are numerically equal then the left-hand operand + is chosen as the result. Otherwise the maximum (closer to positive infinity) of the two operands is chosen as the result. >>> ExtendedContext.max(Decimal('3'), Decimal('2')) @@ -2544,8 +2552,8 @@ class Context(object): If either operand is a NaN then the general rules apply. Otherwise, the operands are compared as as though by the compare - operation. If they are numerically equal then the left-hand operand - is chosen as the result. Otherwise the minimum (closer to negative + operation. If they are numerically equal then the left-hand operand + is chosen as the result. Otherwise the minimum (closer to negative infinity) of the two operands is chosen as the result. >>> ExtendedContext.min(Decimal('3'), Decimal('2')) @@ -2634,14 +2642,14 @@ class Context(object): The right-hand operand must be a whole number whose integer part (after any exponent has been applied) has no more than 9 digits and whose - fractional part (if any) is all zeros before any rounding. The operand + fractional part (if any) is all zeros before any rounding. The operand may be positive, negative, or zero; if negative, the absolute value of the power is used, and the left-hand operand is inverted (divided into 1) before use. If the increased precision needed for the intermediate calculations - exceeds the capabilities of the implementation then an Invalid operation - condition is raised. + exceeds the capabilities of the implementation then an Invalid + operation condition is raised. If, when raising to a negative power, an underflow occurs during the division into 1, the operation is not halted at that point but @@ -2679,18 +2687,18 @@ class Context(object): return a.__pow__(b, modulo, context=self) def quantize(self, a, b): - """Returns a value equal to 'a' (rounded) and having the exponent of 'b'. + """Returns a value equal to 'a' (rounded), having the exponent of 'b'. The coefficient of the result is derived from that of the left-hand - operand. It may be rounded using the current rounding setting (if the + operand. It may be rounded using the current rounding setting (if the exponent is being increased), multiplied by a positive power of ten (if the exponent is being decreased), or is unchanged (if the exponent is already equal to that of the right-hand operand). Unlike other operations, if the length of the coefficient after the quantize operation would be greater than precision then an Invalid - operation condition is raised. This guarantees that, unless there is an - error condition, the exponent of the result of a quantize is always + operation condition is raised. This guarantees that, unless there is + an error condition, the exponent of the result of a quantize is always equal to that of the right-hand operand. Also unlike other operations, quantize will never raise Underflow, even @@ -2733,9 +2741,9 @@ class Context(object): """Returns the remainder from integer division. The result is the residue of the dividend after the operation of - calculating integer division as described for divide-integer, rounded to - precision digits if necessary. The sign of the result, if non-zero, is - the same as that of the original dividend. + calculating integer division as described for divide-integer, rounded + to precision digits if necessary. The sign of the result, if + non-zero, is the same as that of the original dividend. This operation will fail under the same conditions as integer division (that is, if integer division on the same two operands would fail, the @@ -2759,7 +2767,7 @@ class Context(object): def remainder_near(self, a, b): """Returns to be "a - b * n", where n is the integer nearest the exact value of "x / b" (if two integers are equally near then the even one - is chosen). If the result is equal to 0 then its sign will be the + is chosen). If the result is equal to 0 then its sign will be the sign of a. This operation will fail under the same conditions as integer division @@ -2801,7 +2809,7 @@ class Context(object): return a.same_quantum(b) def sqrt(self, a): - """Returns the square root of a non-negative number to context precision. + """Square root of a non-negative number to context precision. If the result must be inexact, it is rounded using the round-half-even algorithm. @@ -2862,7 +2870,7 @@ class Context(object): as using the quantize() operation using the given operand as the left-hand-operand, 1E+0 as the right-hand-operand, and the precision of the operand as the precision setting, except that no flags will - be set. The rounding mode is taken from the context. + be set. The rounding mode is taken from the context. >>> ExtendedContext.to_integral(Decimal('2.1')) Decimal("2") @@ -2937,8 +2945,9 @@ def _normalize(op1, op2, shouldround = 0, prec = 0): other_len = len(str(other.int)) if numdigits > (other_len + prec + 1 - tmp_len): # If the difference in adjusted exps is > prec+1, we know - # other is insignificant, so might as well put a 1 after the precision. - # (since this is only for addition.) Also stops use of massive longs. + # other is insignificant, so might as well put a 1 after the + # precision (since this is only for addition). Also stops + # use of massive longs. extend = prec + 2 - tmp_len if extend <= 0: @@ -2961,13 +2970,13 @@ def _adjust_coefficients(op1, op2): Used on _WorkRep instances during division. """ adjust = 0 - #If op1 is smaller, make it larger + # If op1 is smaller, make it larger while op2.int > op1.int: op1.int *= 10 op1.exp -= 1 adjust += 1 - #If op2 is too small, make it larger + # If op2 is too small, make it larger while op1.int >= (10 * op2.int): op2.int *= 10 op2.exp -= 1 @@ -2975,7 +2984,7 @@ def _adjust_coefficients(op1, op2): return op1, op2, adjust -##### Helper Functions ######################################## +##### Helper Functions #################################################### def _convert_other(other): """Convert other to Decimal. @@ -3016,16 +3025,16 @@ def _isnan(num): if not num: return 0 - #get the sign, get rid of trailing [+-] + # Get the sign, get rid of trailing [+-] sign = 0 if num[0] == '+': num = num[1:] - elif num[0] == '-': #elif avoids '+-nan' + elif num[0] == '-': # elif avoids '+-nan' num = num[1:] sign = 1 if num.startswith('nan'): - if len(num) > 3 and not num[3:].isdigit(): #diagnostic info + if len(num) > 3 and not num[3:].isdigit(): # diagnostic info return 0 return (1, sign, num[3:].lstrip('0')) if num.startswith('snan'): @@ -3035,7 +3044,7 @@ def _isnan(num): return 0 -##### Setup Specific Contexts ################################ +##### Setup Specific Contexts ############################################ # The default context prototype used by Context() # Is mutable, so that new contexts can have different default values @@ -3068,19 +3077,19 @@ ExtendedContext = Context( ) -##### Useful Constants (internal use only) #################### +##### Useful Constants (internal use only) ################################ -#Reusable defaults +# Reusable defaults Inf = Decimal('Inf') negInf = Decimal('-Inf') -#Infsign[sign] is infinity w/ that sign +# Infsign[sign] is infinity w/ that sign Infsign = (Inf, negInf) NaN = Decimal('NaN') -##### crud for parsing strings ################################# +##### crud for parsing strings ############################################# import re # There's an optional sign at the start, and an optional exponent @@ -3100,13 +3109,15 @@ _parser = re.compile(r""" ([eE](?P<exp>[-+]? \d+))? # \s* $ -""", re.VERBOSE).match #Uncomment the \s* to allow leading or trailing spaces. +""", re.VERBOSE).match # Uncomment the \s* to allow leading or trailing spaces. del re -# return sign, n, p s.t. float string value == -1**sign * n * 10**p exactly - def _string2exact(s): + """Return sign, n, p s.t. + + Float string value == -1**sign * n * 10**p exactly + """ m = _parser(s) if m is None: raise ValueError("invalid literal for Decimal: %r" % s) diff --git a/Lib/difflib.py b/Lib/difflib.py index 2a057d9..5b42d07 100644 --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -1946,8 +1946,7 @@ class HtmlDiff(object): fromlist,tolist,flaglist,next_href,next_id = self._convert_flags( fromlist,tolist,flaglist,context,numlines) - import cStringIO - s = cStringIO.StringIO() + s = [] fmt = ' <tr><td class="diff_next"%s>%s</td>%s' + \ '<td class="diff_next">%s</td>%s</tr>\n' for i in range(len(flaglist)): @@ -1955,9 +1954,9 @@ class HtmlDiff(object): # mdiff yields None on separator lines skip the bogus ones # generated for the first line if i > 0: - s.write(' </tbody> \n <tbody>\n') + s.append(' </tbody> \n <tbody>\n') else: - s.write( fmt % (next_id[i],next_href[i],fromlist[i], + s.append( fmt % (next_id[i],next_href[i],fromlist[i], next_href[i],tolist[i])) if fromdesc or todesc: header_row = '<thead><tr>%s%s%s%s</tr></thead>' % ( @@ -1969,7 +1968,7 @@ class HtmlDiff(object): header_row = '' table = self._table_template % dict( - data_rows=s.getvalue(), + data_rows=''.join(s), header_row=header_row, prefix=self._prefix[1]) diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index 21d34c7..86ad44f 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -20,4 +20,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.0" +__version__ = "2.5.1" diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index d0cd162..2832d57 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py @@ -186,7 +186,7 @@ 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.executable.find(sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -199,7 +199,7 @@ class build_ext (Command): # Python's library directory must be appended to library_dirs if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ and sysconfig.get_config_var('Py_ENABLE_SHARED'): - if sys.executable.find(sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: @@ -533,7 +533,8 @@ class build_ext (Command): if self.swig_cpp: log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - if self.swig_cpp or ('-c++' in self.swig_opts): + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): target_ext = '.cpp' else: target_ext = '.c' diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py index ca1feaa..07c76f1 100644 --- a/Lib/distutils/msvccompiler.py +++ b/Lib/distutils/msvccompiler.py @@ -187,6 +187,19 @@ def get_build_architecture(): j = sys.version.find(")", i) return sys.version[i+len(prefix):j] +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths class MSVCCompiler (CCompiler) : @@ -270,7 +283,8 @@ class MSVCCompiler (CCompiler) : self.__paths.append(p) except KeyError: pass - os.environ['path'] = ';'.join(self.__paths) + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) self.preprocess_options = None if self.__arch == "Intel": diff --git a/Lib/doctest.py b/Lib/doctest.py index 5ee4d85..2671cc6 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -2625,8 +2625,23 @@ __test__ = {"_TestClass": _TestClass, } def _test(): - r = unittest.TextTestRunner() - r.run(DocTestSuite()) + testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-'] + if testfiles: + for filename in testfiles: + if filename.endswith(".py"): + # It is a module -- insert its dir into sys.path and try to + # import it. If it is part of a package, that possibly won't work + # because of package imports. + dirname, filename = os.path.split(filename) + sys.path.insert(0, dirname) + m = __import__(filename[:-3]) + del sys.path[0] + testmod(m) + else: + testfile(filename, module_relative=False) + else: + r = unittest.TextTestRunner() + r.run(DocTestSuite()) if __name__ == "__main__": _test() diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 8047df2..81913a3 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2006 Python Software Foundation +# Copyright (C) 2002-2007 Python Software Foundation # Contact: email-sig@python.org """Email address parsing code. @@ -172,6 +172,7 @@ class AddrlistClass: self.pos = 0 self.LWS = ' \t' self.CR = '\r\n' + self.FWS = self.LWS + self.CR self.atomends = self.specials + self.LWS + self.CR # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it # is obsolete syntax. RFC 2822 requires that we recognize obsolete @@ -418,7 +419,7 @@ class AddrlistClass: plist = [] while self.pos < len(self.field): - if self.field[self.pos] in self.LWS: + if self.field[self.pos] in self.FWS: self.pos += 1 elif self.field[self.pos] == '"': plist.append(self.getquote()) diff --git a/Lib/email/header.py b/Lib/email/header.py index 3de44f9..ab0d3fc 100644 --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -39,7 +39,8 @@ ecre = re.compile(r''' \? # literal ? (?P<encoded>.*?) # non-greedy up to the next ?= is the encoded string \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) + (?=[ \t]|$) # whitespace or the end of the string + ''', re.VERBOSE | re.IGNORECASE | re.MULTILINE) # Field name regexp, including trailing colon, but not separating whitespace, # according to RFC 2822. Character range is from tilde to exclamation mark. diff --git a/Lib/email/message.py b/Lib/email/message.py index 9d25cb0..6fc3af1 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -238,7 +238,7 @@ class Message: self.del_param('charset') self._charset = None return - if isinstance(charset, str): + if isinstance(charset, basestring): charset = email.charset.Charset(charset) if not isinstance(charset, email.charset.Charset): raise TypeError(charset) @@ -756,7 +756,9 @@ class Message: charset = charset[2] # charset character must be in us-ascii range try: - charset = unicode(charset, 'us-ascii').encode('us-ascii') + if isinstance(charset, str): + charset = unicode(charset, 'us-ascii') + charset = charset.encode('us-ascii') except UnicodeError: return failobj # RFC 2046, $4.1.2 says charsets are not case sensitive diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py index c3269d7..a2e09fa 100644 --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001-2007 Python Software Foundation # Contact: email-sig@python.org # email package unit tests @@ -501,6 +501,13 @@ class TestMessageAPI(TestEmailBase): msg.set_payload(x) self.assertEqual(msg.get_payload(decode=True), x) + def test_get_content_charset(self): + msg = Message() + msg.set_charset('us-ascii') + self.assertEqual('us-ascii', msg.get_content_charset()) + msg.set_charset(u'us-ascii') + self.assertEqual('us-ascii', msg.get_content_charset()) + # Test the email.Encoders module @@ -1519,6 +1526,18 @@ class TestRFC2047(unittest.TestCase): hu = make_header(dh).__unicode__() eq(hu, u'The quick brown fox jumped over the lazy dog') + def test_rfc2047_without_whitespace(self): + s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' + dh = decode_header(s) + self.assertEqual(dh, [(s, None)]) + + def test_rfc2047_with_whitespace(self): + s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' + dh = decode_header(s) + self.assertEqual(dh, [('Sm', None), ('\xf6', 'iso-8859-1'), + ('rg', None), ('\xe5', 'iso-8859-1'), + ('sbord', None)]) + # Test the MIMEMessage class @@ -2164,6 +2183,12 @@ class TestMiscellaneous(TestEmailBase): # formataddr() quotes the name if there's a dot in it self.assertEqual(Utils.formataddr((a, b)), y) + def test_multiline_from_comment(self): + x = """\ +Foo +\tBar <foo@example.com>""" + self.assertEqual(Utils.parseaddr(x), ('Foo Bar', 'foo@example.com')) + def test_quote_dump(self): self.assertEqual( Utils.formataddr(('A Silly; Person', 'person@dom.ain')), diff --git a/Lib/email/test/test_email_renamed.py b/Lib/email/test/test_email_renamed.py index 21061b0..7f72270 100644 --- a/Lib/email/test/test_email_renamed.py +++ b/Lib/email/test/test_email_renamed.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2006 Python Software Foundation +# Copyright (C) 2001-2007 Python Software Foundation # Contact: email-sig@python.org # email package unit tests @@ -1524,6 +1524,18 @@ class TestRFC2047(unittest.TestCase): hu = make_header(dh).__unicode__() eq(hu, u'The quick brown fox jumped over the lazy dog') + def test_rfc2047_missing_whitespace(self): + s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' + dh = decode_header(s) + self.assertEqual(dh, [(s, None)]) + + def test_rfc2047_with_whitespace(self): + s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' + dh = decode_header(s) + self.assertEqual(dh, [('Sm', None), ('\xf6', 'iso-8859-1'), + ('rg', None), ('\xe5', 'iso-8859-1'), + ('sbord', None)]) + # Test the MIMEMessage class @@ -2170,6 +2182,12 @@ class TestMiscellaneous(TestEmailBase): # formataddr() quotes the name if there's a dot in it self.assertEqual(utils.formataddr((a, b)), y) + def test_multiline_from_comment(self): + x = """\ +Foo +\tBar <foo@example.com>""" + self.assertEqual(utils.parseaddr(x), ('Foo Bar', 'foo@example.com')) + def test_quote_dump(self): self.assertEqual( utils.formataddr(('A Silly; Person', 'person@dom.ain')), diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 85e3cc9..cdc893b 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -76,9 +76,15 @@ class FTP: '''An FTP client class. - To create a connection, call the class using these argument: - host, user, passwd, acct - These are all strings, and have default value ''. + To create a connection, call the class using these arguments: + host, user, passwd, acct, timeout + + The first four arguments are all strings, and have default value ''. + timeout must be numeric and defaults to None if not passed, + meaning that no timeout will be set on any ftp socket(s) + If a timeout is passed, then this is now the default timeout for all ftp + socket operations for this instance. + Then use self.connect() with optional host and port argument. To download a file, use ftp.retrlines('RETR ' + filename), @@ -102,33 +108,26 @@ class FTP: # Initialize host to localhost, port to standard ftp port # Optional arguments are host (for connect()), # and user, passwd, acct (for login()) - def __init__(self, host='', user='', passwd='', acct=''): + def __init__(self, host='', user='', passwd='', acct='', timeout=None): + self.timeout = timeout if host: self.connect(host) - if user: self.login(user, passwd, acct) + if user: + self.login(user, passwd, acct) - def connect(self, host = '', port = 0): + def connect(self, host='', port=0, timeout=None): '''Connect to host. Arguments are: - - host: hostname to connect to (string, default previous host) - - port: port to connect to (integer, default previous port)''' - if host: self.host = host - if port: self.port = port - msg = "getaddrinfo returns an empty list" - for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) - except socket.error as err: - msg = err - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg - self.af = af + - host: hostname to connect to (string, default previous host) + - port: port to connect to (integer, default previous port) + ''' + if host != '': + self.host = host + if port > 0: + self.port = port + if timeout is not None: + self.timeout = timeout + self.sock = socket.create_connection((self.host, self.port), self.timeout) + self.af = self.sock.family self.file = self.sock.makefile('rb') self.welcome = self.getresp() return self.welcome diff --git a/Lib/genericpath.py b/Lib/genericpath.py index 1574cef..e2bc7da 100644 --- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -75,3 +75,32 @@ def commonprefix(m): if s1[i] != s2[i]: return s1[:i] return s1[:n] + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +# Generic implementation of splitext, to be parametrized with +# the separators +def _splitext(p, sep, altsep, extsep): + """Split the extension from a pathname. + + Extension is everything from the last dot to the end, ignoring + leading dots. Returns "(root, ext)"; ext may be empty.""" + + sepIndex = p.rfind(sep) + if altsep: + altsepIndex = p.rfind(altsep) + sepIndex = max(sepIndex, altsepIndex) + + dotIndex = p.rfind(extsep) + if dotIndex > sepIndex: + # skip all leading dots + filenameIndex = sepIndex + 1 + while filenameIndex < dotIndex: + if p[filenameIndex] != extsep: + return p[:dotIndex], p[dotIndex:] + filenameIndex += 1 + + return p, '' diff --git a/Lib/glob.py b/Lib/glob.py index 95656cc..75d7bf9 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -1,8 +1,9 @@ """Filename globbing utility.""" +import sys import os -import fnmatch import re +import fnmatch __all__ = ["glob", "iglob"] @@ -48,13 +49,16 @@ def iglob(pathname): def glob1(dirname, pattern): if not dirname: dirname = os.curdir + if isinstance(pattern, unicode) and not isinstance(dirname, unicode): + dirname = unicode(dirname, sys.getfilesystemencoding() or + sys.getdefaultencoding()) try: names = os.listdir(dirname) except os.error: return [] - if pattern[0]!='.': - names=filter(lambda x: x[0]!='.',names) - return fnmatch.filter(names,pattern) + if pattern[0] != '.': + names = filter(lambda x: x[0] != '.', names) + return fnmatch.filter(names, pattern) def glob0(dirname, basename): if basename == '': diff --git a/Lib/heapq.py b/Lib/heapq.py index 6ee26d1..d34ea3b 100644 --- a/Lib/heapq.py +++ b/Lib/heapq.py @@ -311,7 +311,7 @@ except ImportError: def merge(*iterables): '''Merge multiple sorted inputs into a single sorted output. - Similar to sorted(itertools.chain(*iterables)) but returns an iterable, + Similar to sorted(itertools.chain(*iterables)) but returns a generator, does not pull the data into memory all at once, and assumes that each of the input streams is already sorted (smallest to largest). diff --git a/Lib/httplib.py b/Lib/httplib.py index 89d5392..84401ac 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -625,7 +625,8 @@ class HTTPConnection: debuglevel = 0 strict = 0 - def __init__(self, host, port=None, strict=None): + def __init__(self, host, port=None, strict=None, timeout=None): + self.timeout = timeout self.sock = None self._buffer = [] self.__response = None @@ -658,25 +659,8 @@ class HTTPConnection: def connect(self): """Connect to the host and port specified in __init__.""" - msg = "getaddrinfo returns an empty list" - for res in socket.getaddrinfo(self.host, self.port, 0, - socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - if self.debuglevel > 0: - print("connect: (%s, %s)" % (self.host, self.port)) - self.sock.connect(sa) - except socket.error as msg: - if self.debuglevel > 0: - print('connect fail:', (self.host, self.port)) - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg + self.sock = socket.create_connection((self.host,self.port), + self.timeout) def close(self): """Close the connection to the HTTP server.""" @@ -948,8 +932,8 @@ class HTTPConnection: self.__state = _CS_IDLE if response.will_close: - # Pass the socket to the response - self.sock = None + # this effectively passes the connection to the response + self.close() else: # remember this, so we can tell when it is complete self.__response = response diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py index 61730b8..4311999 100644 --- a/Lib/idlelib/MultiCall.py +++ b/Lib/idlelib/MultiCall.py @@ -350,6 +350,8 @@ def MultiCallCreator(widget): triplets.append(triplet) def event_delete(self, virtual, *sequences): + if virtual not in self.__eventinfo: + return func, triplets = self.__eventinfo[virtual] for seq in sequences: triplet = _parse_sequence(seq) diff --git a/Lib/imaplib.py b/Lib/imaplib.py index fcf68ef..2df533f 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -746,8 +746,10 @@ class IMAP4: if not command in Commands: raise self.error("Unknown IMAP4 UID command: %s" % command) if self.state not in Commands[command]: - raise self.error('command %s illegal in state %s' - % (command, self.state)) + raise self.error("command %s illegal in state %s, " + "only allowed in states %s" % + (command, self.state, + ', '.join(Commands[command]))) name = 'UID' typ, dat = self._simple_command(name, command, *args) if command in ('SEARCH', 'SORT'): @@ -811,8 +813,10 @@ class IMAP4: if self.state not in Commands[name]: self.literal = None - raise self.error( - 'command %s illegal in state %s' % (name, self.state)) + raise self.error("command %s illegal in state %s, " + "only allowed in states %s" % + (name, self.state, + ', '.join(Commands[name]))) for typ in ('OK', 'NO', 'BAD'): if typ in self.untagged_responses: diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 83bf3e3..71ec9c3 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -365,12 +365,14 @@ class SocketHandler(logging.Handler): self.retryMax = 30.0 self.retryFactor = 2.0 - def makeSocket(self): + def makeSocket(self, timeout=1): """ A factory method which allows subclasses to define the precise type of socket they want. """ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if hasattr(s, 'settimeout'): + s.settimeout(timeout) s.connect((self.host, self.port)) return s diff --git a/Lib/macpath.py b/Lib/macpath.py index d389d70..f54ffa0 100644 --- a/Lib/macpath.py +++ b/Lib/macpath.py @@ -2,6 +2,7 @@ import os from stat import * +import genericpath from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", @@ -69,17 +70,8 @@ def split(s): def splitext(p): - """Split a path into root and extension. - The extension is everything starting at the last dot in the last - pathname component; the root is everything before that. - It is always true that root + ext == p.""" - - i = p.rfind('.') - if i<=p.rfind(':'): - return p, '' - else: - return p[:i], p[i:] - + return genericpath._splitext(p, sep, altsep, extsep) +splitext.__doc__ = genericpath._splitext.__doc__ def splitdrive(p): """Split a pathname into a drive specification and the rest of the diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 23d5127..99d7a4a 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -8,6 +8,7 @@ module as os.path. import os import stat import sys +import genericpath from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", @@ -15,7 +16,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "getatime","getctime", "islink","exists","lexists","isdir","isfile", "ismount","walk","expanduser","expandvars","normpath","abspath", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", - "extsep","devnull","realpath","supports_unicode_filenames"] + "extsep","devnull","realpath","supports_unicode_filenames","relpath"] # strings representing various path-related bits and pieces curdir = '.' @@ -182,16 +183,8 @@ def split(p): # It is always true that root + ext == p. def splitext(p): - """Split the extension from a pathname. - - Extension is everything from the last dot to the end. - Return (root, ext), either part may be empty.""" - - i = p.rfind('.') - if i<=max(p.rfind('/'), p.rfind('\\')): - return p, '' - else: - return p[:i], p[i:] + return genericpath._splitext(p, sep, altsep, extsep) +splitext.__doc__ = genericpath._splitext.__doc__ # Return the tail (basename) part of a path. @@ -285,36 +278,44 @@ def expanduser(path): i, n = 1, len(path) while i < n and path[i] not in '/\\': i = i + 1 - if i == 1: - if 'HOME' in os.environ: - userhome = os.environ['HOME'] - elif not 'HOMEPATH' in os.environ: - return path - else: - try: - drive = os.environ['HOMEDRIVE'] - except KeyError: - drive = '' - userhome = join(drive, os.environ['HOMEPATH']) - else: + + if 'HOME' in os.environ: + userhome = os.environ['HOME'] + elif 'USERPROFILE' in os.environ: + userhome = os.environ['USERPROFILE'] + elif not 'HOMEPATH' in os.environ: return path + else: + try: + drive = os.environ['HOMEDRIVE'] + except KeyError: + drive = '' + userhome = join(drive, os.environ['HOMEPATH']) + + if i != 1: #~user + userhome = join(dirname(userhome), path[1:i]) + return userhome + path[i:] # Expand paths containing shell variable substitutions. # The following rules apply: # - no expansion within single quotes -# - no escape character, except for '$$' which is translated into '$' +# - '$$' is translated into '$' +# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2% # - ${varname} is accepted. -# - varnames can be made out of letters, digits and the character '_' +# - $varname is accepted. +# - %varname% is accepted. +# - varnames can be made out of letters, digits and the characters '_-' +# (though is not verifed in the ${varname} and %varname% cases) # XXX With COMMAND.COM you can use any characters in a variable name, # XXX except '^|<>='. def expandvars(path): - """Expand shell variables of form $var and ${var}. + """Expand shell variables of the forms $var, ${var} and %var%. Unknown variables are left unchanged.""" - if '$' not in path: + if '$' not in path and '%' not in path: return path import string varchars = string.ascii_letters + string.digits + '_-' @@ -332,6 +333,24 @@ def expandvars(path): except ValueError: res = res + path index = pathlen - 1 + elif c == '%': # variable or '%' + if path[index + 1:index + 2] == '%': + res = res + c + index = index + 1 + else: + path = path[index+1:] + pathlen = len(path) + try: + index = path.index('%') + except ValueError: + res = res + '%' + path + index = pathlen - 1 + else: + var = path[:index] + if var in os.environ: + res = res + os.environ[var] + else: + res = res + '%' + var + '%' elif c == '$': # variable or '$$' if path[index + 1:index + 2] == '$': res = res + c @@ -446,3 +465,29 @@ realpath = abspath # Win9x family and earlier have no Unicode filename support. supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and sys.getwindowsversion()[3] >= 2) + +def relpath(path, start=curdir): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + if start_list[0].lower() != path_list[0].lower(): + unc_path, rest = splitunc(path) + unc_start, rest = splitunc(start) + if bool(unc_path) ^ bool(unc_start): + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + else: + raise ValueError("path is on drive %s, start on drive %s" + % (path_list[0], start_list[0])) + # Work out how much of the filepath is shared by start and path. + for i in range(min(len(start_list), len(path_list))): + if start_list[i].lower() != path_list[i].lower(): + break + else: + i += 1 + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + return join(*rel_list) @@ -221,7 +221,7 @@ def renames(old, new): __all__.extend(["makedirs", "removedirs", "renames"]) -def walk(top, topdown=True, onerror=None): +def walk(top, topdown=True, onerror=None, followlinks=False): """Directory tree generator. For each directory in the directory tree rooted at top (including top @@ -257,6 +257,10 @@ def walk(top, topdown=True, onerror=None): to abort the walk. Note that the filename is available as the filename attribute of the exception object. + By default, os.walk does not follow symbolic links to subdirectories on + systems that support them. In order to get this functionality, set the + optional argument 'followlinks' to true. + Caution: if you pass a relative pathname for top, don't change the current working directory between resumptions of walk. walk never changes the current directory, and assumes that the client doesn't @@ -300,8 +304,8 @@ def walk(top, topdown=True, onerror=None): yield top, dirs, nondirs for name in dirs: path = join(top, name) - if not islink(path): - for x in walk(path, topdown, onerror): + if followlinks or not islink(path): + for x in walk(path, topdown, onerror, followlinks): yield x if not topdown: yield top, dirs, nondirs diff --git a/Lib/pdb.doc b/Lib/pdb.doc index 81df323..c513954 100644 --- a/Lib/pdb.doc +++ b/Lib/pdb.doc @@ -131,6 +131,12 @@ n(ext) r(eturn) Continue execution until the current function returns. +run [args...] + Restart the debugged python program. If a string is supplied it is + splitted with "shlex", and the result is used as the new sys.argv. + History, breakpoints, actions and debugger options are preserved. + "restart" is an alias for "run". + c(ont(inue)) Continue execution, only stop when a breakpoint is encountered. @@ -13,6 +13,12 @@ import os import re import pprint import traceback + + +class Restart(Exception): + """Causes a debugger to be restarted for the debugged python program.""" + pass + # Create a custom safe Repr instance and increase its maxstring. # The default of 30 truncates error messages too easily. _repr = Repr() @@ -484,11 +490,16 @@ class Pdb(bdb.Bdb, cmd.Cmd): except ValueError: # something went wrong print('Breakpoint index %r is not a number' % args[0], file=self.stdout) + return try: cond = args[1] except: cond = None - bp = bdb.Breakpoint.bpbynumber[bpnum] + try: + bp = bdb.Breakpoint.bpbynumber[bpnum] + except IndexError: + print >>self.stdout, 'Breakpoint index %r is not valid' % args[0] + return if bp: bp.cond = cond if not cond: @@ -503,11 +514,16 @@ class Pdb(bdb.Bdb, cmd.Cmd): except ValueError: # something went wrong print('Breakpoint index %r is not a number' % args[0], file=self.stdout) + return try: count = int(args[1].strip()) except: count = 0 - bp = bdb.Breakpoint.bpbynumber[bpnum] + try: + bp = bdb.Breakpoint.bpbynumber[bpnum] + except IndexError: + print >>self.stdout, 'Breakpoint index %r is not valid' % args[0] + return if bp: bp.ignore = count if count > 0: @@ -601,6 +617,18 @@ class Pdb(bdb.Bdb, cmd.Cmd): return 1 do_n = do_next + def do_run(self, arg): + """Restart program by raising an exception to be caught in the main debugger + loop. If arguments were given, set them in sys.argv.""" + if arg: + import shlex + argv0 = sys.argv[0:1] + sys.argv = shlex.split(arg) + sys.argv[:0] = argv0 + raise Restart + + do_restart = do_run + def do_return(self, arg): self.set_return(self.curframe) return 1 @@ -1005,6 +1033,15 @@ command with a 'global' command, e.g.: (Pdb) global list_options; list_options = ['-l'] (Pdb)""", file=self.stdout) + def help_run(self): + print("""run [args...] +Restart the debugged python program. If a string is supplied, it is +splitted with "shlex" and the result is used as the new sys.argv. +History, breakpoints, actions and debugger options are preserved. +"restart" is an alias for "run".""") + + help_restart = help_run + def help_quit(self): self.help_q() @@ -1113,11 +1150,17 @@ see no sign that the breakpoint was reached. return None def _runscript(self, filename): - # Start with fresh empty copy of globals and locals and tell the script - # that it's being run as __main__ to avoid scripts being able to access - # the pdb.py namespace. - globals_ = {"__name__" : "__main__"} - locals_ = globals_ + # The script has to run in __main__ namespace (or imports from + # __main__ will break). + # + # So we clear up the __main__ and set several special variables + # (this gets rid of pdb's globals and cleans old variables on restarts). + import __main__ + __main__.__dict__.clear() + __main__.__dict__.update({"__name__" : "__main__", + "__file__" : filename, + "__builtins__": __builtins__, + }) # When bdb sets tracing, a number of call and line events happens # BEFORE debugger even reaches user's code (and the exact sequence of @@ -1128,7 +1171,7 @@ see no sign that the breakpoint was reached. self.mainpyfile = self.canonic(filename) self._user_requested_quit = 0 statement = 'execfile( "%s")' % filename - self.run(statement, globals=globals_, locals=locals_) + self.run(statement) # Simplified interface @@ -1197,9 +1240,8 @@ def main(): # Note on saving/restoring sys.argv: it's a good idea when sys.argv was # modified by the script being debugged. It's a bad idea when it was - # changed by the user from the command line. The best approach would be to - # have a "restart" command which would allow explicit specification of - # command line arguments. + # changed by the user from the command line. There is a "restart" command which + # allows explicit specification of command line arguments. pdb = Pdb() while 1: try: @@ -1207,6 +1249,9 @@ def main(): if pdb._user_requested_quit: break print("The program finished and will be restarted") + except Restart: + print("Restarting", mainpyfile, "with arguments:") + print("\t" + " ".join(sys.argv[1:])) except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. print("The program exited via sys.exit(). Exit status: ", end=' ') @@ -1223,5 +1268,6 @@ def main(): # When invoked as main program, invoke the debugger on a script -if __name__=='__main__': - main() +if __name__ == '__main__': + import pdb + pdb.main() diff --git a/Lib/popen2.py b/Lib/popen2.py index 3618487..ab30463 100644 --- a/Lib/popen2.py +++ b/Lib/popen2.py @@ -200,45 +200,3 @@ else: return inst.fromchild, inst.tochild __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": - cmd = "more" - # "more" doesn't act the same way across Windows flavors, - # sometimes adding an extra newline at the start or the - # end. So we strip whitespace off both ends for comparison. - expected = teststr.strip() - print("testing popen2...") - r, w = popen2(cmd) - w.write(teststr) - w.close() - got = r.read() - if got.strip() != expected: - raise ValueError("wrote %r read %r" % (teststr, got)) - print("testing popen3...") - try: - r, w, e = popen3([cmd]) - except: - r, w, e = popen3(cmd) - w.write(teststr) - w.close() - got = r.read() - if got.strip() != expected: - raise ValueError("wrote %r read %r" % (teststr, got)) - got = e.read() - if got: - raise ValueError("unexpected %r on stderr" % (got,)) - for inst in _active[:]: - inst.wait() - _cleanup() - if _active: - raise ValueError("_active not empty") - print("All OK") - -if __name__ == '__main__': - _test() diff --git a/Lib/poplib.py b/Lib/poplib.py index adf784f..0caed18 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -76,24 +76,10 @@ class POP3: """ - def __init__(self, host, port = POP3_PORT): + def __init__(self, host, port=POP3_PORT, timeout=None): self.host = host self.port = port - msg = "getaddrinfo returns an empty list" - self.sock = None - for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) - except socket.error as msg: - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg + self.sock = socket.create_connection((host, port), timeout) self.file = self.sock.makefile('rb') self._debugging = 0 self.welcome = self._getresp() diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 1521236..6f15d48 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -12,6 +12,7 @@ for manipulation of the pathname component of URLs. import os import stat +import genericpath from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", @@ -20,7 +21,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "ismount","walk","expanduser","expandvars","normpath","abspath", "samefile","sameopenfile","samestat", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames"] + "devnull","realpath","supports_unicode_filenames","relpath"] # strings representing various path-related bits and pieces curdir = '.' @@ -88,14 +89,8 @@ def split(p): # It is always true that root + ext == p. def splitext(p): - """Split the extension from a pathname. Extension is everything from the - last dot to the end. Returns "(root, ext)", either part may be empty.""" - i = p.rfind('.') - if i<=p.rfind('/'): - return p, '' - else: - return p[:i], p[i:] - + return genericpath._splitext(p, sep, altsep, extsep) +splitext.__doc__ = genericpath._splitext.__doc__ # Split a pathname into a drive specification and the rest of the # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. @@ -387,3 +382,18 @@ def _resolve_link(path): return path supports_unicode_filenames = False + +def relpath(path, start=curdir): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + return join(*rel_list) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 59c4593..6a272ce 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -854,7 +854,7 @@ class HTMLDoc(Doc): if imclass is not cl: note = ' from ' + self.classlink(imclass, mod) else: - if object.im_self: + if object.im_self is not None: note = ' method of %s instance' % self.classlink( object.im_self.__class__, mod) else: @@ -1232,7 +1232,7 @@ class TextDoc(Doc): if imclass is not cl: note = ' from ' + classname(imclass, mod) else: - if object.im_self: + if object.im_self is not None: note = ' method of %s instance' % classname( object.im_self.__class__, mod) else: @@ -1468,6 +1468,27 @@ def resolve(thing, forceload=0): else: return thing, getattr(thing, '__name__', None) +def render_doc(thing, title='Python Library Documentation: %s', forceload=0): + """Render text documentation, given an object or a path to an object.""" + object, name = resolve(thing, forceload) + desc = describe(object) + module = inspect.getmodule(object) + if name and '.' in name: + desc += ' in ' + name[:name.rfind('.')] + elif module and module is not object: + desc += ' in module ' + module.__name__ + elif not (inspect.ismodule(object) or + inspect.isclass(object) or + inspect.isroutine(object) or + inspect.isgetsetdescriptor(object) or + inspect.ismemberdescriptor(object) or + isinstance(object, property)): + # If the passed object is a piece of data or an instance, + # document its available methods instead of its value. + object = type(object) + desc += ' object' + return title % desc + '\n\n' + text.document(object, name) + def doc(thing, title='Python Library Documentation: %s', forceload=0): """Display text documentation, given an object or a path to an object.""" try: @@ -1488,7 +1509,7 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0): # document its available methods instead of its value. object = type(object) desc += ' object' - pager(title % desc + '\n\n' + text.document(object, name)) + pager(render_doc(thing, title, forceload)) except (ImportError, ErrorDuringImport) as value: print(value) @@ -1519,6 +1540,7 @@ def raw_input(prompt): class Helper: keywords = { 'and': 'BOOLEAN', + 'as': 'with', 'assert': ('ref/assert', ''), 'break': ('ref/break', 'while for'), 'class': ('ref/class', 'CLASSES SPECIALMETHODS'), @@ -1546,6 +1568,7 @@ class Helper: 'return': ('ref/return', 'FUNCTIONS'), 'try': ('ref/try', 'EXCEPTIONS'), 'while': ('ref/while', 'break continue if TRUTHVALUE'), + 'with': ('ref/with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 'yield': ('ref/yield', ''), } @@ -1626,6 +1649,7 @@ class Helper: 'LOOPING': ('ref/compound', 'for while break continue'), 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'), 'DEBUGGING': ('lib/module-pdb', 'pdb'), + 'CONTEXTMANAGERS': ('ref/context-managers', 'with'), } def __init__(self, input, output): @@ -1634,16 +1658,21 @@ class Helper: self.docdir = None execdir = os.path.dirname(sys.executable) homedir = os.environ.get('PYTHONHOME') + join = os.path.join for dir in [os.environ.get('PYTHONDOCS'), homedir and os.path.join(homedir, 'doc'), - os.path.join(execdir, 'doc'), - '/usr/doc/python-docs-' + sys.version.split()[0], - '/usr/doc/python-' + sys.version.split()[0], - '/usr/doc/python-docs-' + sys.version[:3], - '/usr/doc/python-' + sys.version[:3], - os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]: - if dir and os.path.isdir(os.path.join(dir, 'lib')): + join(execdir, 'doc'), # for Windows + join(sys.prefix, 'doc/python-docs-' + sys.version.split()[0]), + join(sys.prefix, 'doc/python-' + sys.version.split()[0]), + join(sys.prefix, 'doc/python-docs-' + sys.version[:3]), + join(sys.prefix, 'doc/python-' + sys.version[:3]), + join(sys.prefix, 'Resources/English.lproj/Documentation')]: + if dir and os.path.isdir(join(dir, 'lib')): self.docdir = dir + break + if dir and os.path.isdir(join(dir, 'html', 'lib')): + self.docdir = join(dir, 'html') + break def __repr__(self): if inspect.stack()[1][3] == '?': diff --git a/Lib/rexec.py b/Lib/rexec.py index e5ceb72..c4ce1d0 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', + 'readlines', 'seek', 'tell', 'write', 'writelines', '__iter__') @@ -181,7 +181,7 @@ class RExec(ihooks._Verbose): """ - raise RuntimeError, "This code is not secure in Python 2.2 and 2.3" + raise RuntimeError, "This code is not secure in Python 2.2 and later" ihooks._Verbose.__init__(self, verbose) # XXX There's a circular reference here: diff --git a/Lib/robotparser.py b/Lib/robotparser.py index 0edb55f..32aba46 100644 --- a/Lib/robotparser.py +++ b/Lib/robotparser.py @@ -65,7 +65,7 @@ class RobotFileParser: lines.append(line.strip()) line = f.readline() self.errcode = opener.errcode - if self.errcode == 401 or self.errcode == 403: + if self.errcode in (401, 403): self.disallow_all = True _debug("disallow all") elif self.errcode >= 400: @@ -168,10 +168,7 @@ class RobotFileParser: def __str__(self): - ret = "" - for entry in self.entries: - ret = ret + str(entry) + "\n" - return ret + return ''.join([str(entry) + "\n" for entry in self.entries]) class RuleLine: @@ -198,12 +195,12 @@ class Entry: self.rulelines = [] def __str__(self): - ret = "" + ret = [] for agent in self.useragents: - ret = ret + "User-agent: "+agent+"\n" + ret.extend(["User-agent: ", agent, "\n"]) for line in self.rulelines: - ret = ret + str(line) + "\n" - return ret + ret.extend([str(line), "\n"]) + return ''.join(ret) def applies_to(self, useragent): """check if this entry applies to the specified agent""" diff --git a/Lib/sched.py b/Lib/sched.py index 2f8df05..7c3235e 100644 --- a/Lib/sched.py +++ b/Lib/sched.py @@ -72,7 +72,7 @@ class scheduler: def empty(self): """Check whether the queue is empty.""" - return not not self.queue + return not self.queue def run(self): """Execute events until the queue is empty. diff --git a/Lib/site.py b/Lib/site.py index 48cf385..c4e0d51 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -134,7 +134,7 @@ def addpackage(sitedir, name, known_paths): for line in f: if line.startswith("#"): continue - if line.startswith("import"): + if line.startswith("import ") or line.startswith("import\t"): exec(line) continue line = line.rstrip() diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 9851d08..299a70d 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -226,10 +226,11 @@ class SMTP: debuglevel = 0 file = None helo_resp = None + ehlo_msg = "ehlo" ehlo_resp = None does_esmtp = 0 - def __init__(self, host = '', port = 0, local_hostname = None): + def __init__(self, host='', port=0, local_hostname=None, timeout=None): """Initialize a new instance. If specified, `host' is the name of the remote host to which to @@ -240,6 +241,7 @@ class SMTP: the local hostname is found using socket.getfqdn(). """ + self.timeout = timeout self.esmtp_features = {} self.default_port = SMTP_PORT if host: @@ -273,12 +275,11 @@ class SMTP: """ self.debuglevel = debuglevel - def _get_socket(self,af, socktype, proto,sa): + def _get_socket(self, port, host, timeout): # This makes it simpler for SMTP_SSL to use the SMTP connect code # and just alter the socket connection bit. - self.sock = socket.socket(af, socktype, proto) if self.debuglevel > 0: print('connect:', (host, port), file=stderr) - self.sock.connect(sa) + return socket.create_connection((port, host), timeout) def connect(self, host='localhost', port = 0): """Connect to a host on a given port. @@ -297,24 +298,10 @@ class SMTP: host, port = host[:i], host[i+1:] try: port = int(port) except ValueError: - raise socket.error, "nonnumeric port" + raise socket.error("nonnumeric port") if not port: port = self.default_port if self.debuglevel > 0: print('connect:', (host, port), file=stderr) - msg = "getaddrinfo returns an empty list" - self.sock = None - for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self._get_socket(af,socktype,proto,sa) - except socket.error as msg: - if self.debuglevel > 0: print('connect fail:', msg, file=stderr) - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg + self.sock = self._get_socket(host, port, self.timeout) (code, msg) = self.getreply() if self.debuglevel > 0: print("connect:", msg, file=stderr) return (code, msg) @@ -401,7 +388,7 @@ class SMTP: host. """ self.esmtp_features = {} - self.putcmd("ehlo", name or self.local_hostname) + self.putcmd(self.ehlo_msg, name or self.local_hostname) (code,msg)=self.getreply() # According to RFC1869 some (badly written) # MTA's will disconnect on an ehlo. Toss an exception if @@ -731,21 +718,64 @@ class SMTP_SSL(SMTP): are also optional - they can contain a PEM formatted private key and certificate chain file for the SSL connection. """ - def __init__(self, host = '', port = 0, local_hostname = None, - keyfile = None, certfile = None): + def __init__(self, host='', port=0, local_hostname=None, + keyfile=None, certfile=None, timeout=None): self.keyfile = keyfile self.certfile = certfile - SMTP.__init__(self,host,port,local_hostname) + SMTP.__init__(self, host, port, local_hostname, timeout) self.default_port = SMTP_SSL_PORT - def _get_socket(self,af, socktype, proto,sa): - self.sock = socket.socket(af, socktype, proto) + def _get_socket(self, host, port, timeout): if self.debuglevel > 0: print('connect:', (host, port), file=stderr) - self.sock.connect(sa) + self.sock = socket.create_connection((host, port), timeout) sslobj = socket.ssl(self.sock, self.keyfile, self.certfile) self.sock = SSLFakeSocket(self.sock, sslobj) self.file = SSLFakeFile(sslobj) +# +# LMTP extension +# +LMTP_PORT = 2003 + +class LMTP(SMTP): + """LMTP - Local Mail Transfer Protocol + + The LMTP protocol, which is very similar to ESMTP, is heavily based + on the standard SMTP client. It's common to use Unix sockets for LMTP, + so our connect() method must support that as well as a regular + host:port server. To specify a Unix socket, you must use an absolute + path as the host, starting with a '/'. + + Authentication is supported, using the regular SMTP mechanism. When + using a Unix socket, LMTP generally don't support or require any + authentication, but your mileage might vary.""" + + ehlo_msg = "lhlo" + + def __init__(self, host = '', port = LMTP_PORT, local_hostname = None): + """Initialize a new instance.""" + SMTP.__init__(self, host, port, local_hostname) + + def connect(self, host = 'localhost', port = 0): + """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" + if host[0] != '/': + return SMTP.connect(self, host, port) + + # Handle Unix-domain sockets. + try: + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.connect(host) + except socket.error as msg: + if self.debuglevel > 0: print>>stderr, 'connect fail:', host + if self.sock: + self.sock.close() + self.sock = None + raise socket.error(msg) + (code, msg) = self.getreply() + if self.debuglevel > 0: print>>stderr, "connect:", msg + return (code, msg) + + # Test the sendmail method, which tests most of the others. # Note: This always sends to localhost. if __name__ == '__main__': diff --git a/Lib/socket.py b/Lib/socket.py index 3fe6ec5..8dd2383 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -24,6 +24,7 @@ inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89) ssl() -- secure socket layer support (only available if configured) socket.getdefaulttimeout() -- get the default timeout value socket.setdefaulttimeout() -- set the default timeout value +create_connection() -- connects to an address, with an optional timeout [*] not available on all platforms! @@ -139,8 +140,6 @@ class _closedsocket(object): __slots__ = [] def _dummy(*args): raise error(EBADF, 'Bad file descriptor') - def close(self): - pass # All _delegate_methods must also be initialized here. send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy __getattr__ = _dummy @@ -159,7 +158,6 @@ class _socketobject(object): setattr(self, method, getattr(_sock, method)) def close(self): - self._sock.close() self._sock = _closedsocket() dummy = self._sock._dummy for method in _delegate_methods: @@ -414,3 +412,32 @@ class _fileobject(object): if not line: raise StopIteration return line + + +def create_connection(address, timeout=None): + """Connect to address (host, port) with an optional timeout. + + Provides access to socketobject timeout for higher-level + protocols. Passing a timeout will set the timeout on the + socket instance (if not present, or passed as None, the + default global timeout setting will be used). + """ + + msg = "getaddrinfo returns an empty list" + host, port = address + for res in getaddrinfo(host, port, 0, SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket(af, socktype, proto) + if timeout is not None: + sock.settimeout(timeout) + sock.connect(sa) + return sock + + except error as err: + msg = err + if sock is not None: + sock.close() + + raise error(msg) @@ -8,3 +8,6 @@ warnings.warn("The sre module is deprecated, please import re.", from re import * from re import __all__ + +# old pickles expect the _compile() reconstructor in this module +from re import _compile diff --git a/Lib/subprocess.py b/Lib/subprocess.py index e9c9a0e..2aa02ae 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -597,7 +597,7 @@ class Popen(object): # either have to redirect all three or none. If the subprocess # user has only redirected one or two handles, we are # automatically creating PIPEs for the rest. We should close - # these after the process is started. See bug #1124861. + # these after the process is started. See bug #1124861. if mswindows: if stdin is None and p2cwrite is not None: os.close(p2cwrite) @@ -629,7 +629,7 @@ class Popen(object): return data - def __del__(self): + def __del__(self, sys=sys): if not self._child_created: # We didn't get to successfully create a child process. return diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 963127c..efade27 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -33,7 +33,7 @@ __version__ = "$Revision$" # $Source$ -version = "0.8.0" +version = "0.9.0" __author__ = "Lars Gustäbel (lars@gustaebel.de)" __date__ = "$Date$" __cvsid__ = "$Id$" @@ -50,6 +50,7 @@ import errno import time import struct import copy +import re if sys.platform == 'mac': # This module needs work for MacOS9, especially in the area of pathname @@ -71,42 +72,60 @@ from __builtin__ import open as _open # Since 'open' is TarFile.open #--------------------------------------------------------- # tar constants #--------------------------------------------------------- -NUL = "\0" # the null character -BLOCKSIZE = 512 # length of processing blocks +NUL = "\0" # the null character +BLOCKSIZE = 512 # length of processing blocks RECORDSIZE = BLOCKSIZE * 20 # length of records -MAGIC = "ustar" # magic tar string -VERSION = "00" # version number +GNU_MAGIC = "ustar \0" # magic gnu tar string +POSIX_MAGIC = "ustar\x0000" # magic posix tar string -LENGTH_NAME = 100 # maximum length of a filename -LENGTH_LINK = 100 # maximum length of a linkname -LENGTH_PREFIX = 155 # maximum length of the prefix field -MAXSIZE_MEMBER = 077777777777 # maximum size of a file (11 octal digits) +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field -REGTYPE = "0" # regular file +REGTYPE = "0" # regular file AREGTYPE = "\0" # regular file -LNKTYPE = "1" # link (inside tarfile) -SYMTYPE = "2" # symbolic link -CHRTYPE = "3" # character special device -BLKTYPE = "4" # block special device -DIRTYPE = "5" # directory +LNKTYPE = "1" # link (inside tarfile) +SYMTYPE = "2" # symbolic link +CHRTYPE = "3" # character special device +BLKTYPE = "4" # block special device +DIRTYPE = "5" # directory FIFOTYPE = "6" # fifo special device CONTTYPE = "7" # contiguous file -GNUTYPE_LONGNAME = "L" # GNU tar extension for longnames -GNUTYPE_LONGLINK = "K" # GNU tar extension for longlink -GNUTYPE_SPARSE = "S" # GNU tar extension for sparse file +GNUTYPE_LONGNAME = "L" # GNU tar longname +GNUTYPE_LONGLINK = "K" # GNU tar longlink +GNUTYPE_SPARSE = "S" # GNU tar sparse file + +XHDTYPE = "x" # POSIX.1-2001 extended header +XGLTYPE = "g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = "X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT #--------------------------------------------------------- # tarfile constants #--------------------------------------------------------- -SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, # file types that tarfile - SYMTYPE, DIRTYPE, FIFOTYPE, # can cope with. +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, GNUTYPE_SPARSE) -REGULAR_TYPES = (REGTYPE, AREGTYPE, # file types that somehow - CONTTYPE, GNUTYPE_SPARSE) # represent regular files +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") #--------------------------------------------------------- # Bits used in the mode field, values in octal. @@ -133,6 +152,13 @@ TOWRITE = 0002 # write by other TOEXEC = 0001 # execute/search by other #--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +ENCODING = sys.getfilesystemencoding() +if ENCODING is None: + ENCODING = "ascii" + +#--------------------------------------------------------- # Some useful functions #--------------------------------------------------------- @@ -141,6 +167,15 @@ def stn(s, length): """ return s[:length] + (length - len(s)) * NUL +def nts(s): + """Convert a null-terminated string field to a python string. + """ + # Use the string up to the first null char. + p = s.find("\0") + if p == -1: + return s + return s[:p] + def nti(s): """Convert a number field to a python number. """ @@ -148,7 +183,7 @@ def nti(s): # itn() below. if s[0] != chr(0200): try: - n = int(s.rstrip(NUL + " ") or "0", 8) + n = int(nts(s) or "0", 8) except ValueError: raise HeaderError("invalid header") else: @@ -158,7 +193,7 @@ def nti(s): n += ord(s[i + 1]) return n -def itn(n, digits=8, posix=False): +def itn(n, digits=8, format=DEFAULT_FORMAT): """Convert a python number to a number field. """ # POSIX 1003.1-1988 requires numbers to be encoded as a string of @@ -170,7 +205,7 @@ def itn(n, digits=8, posix=False): if 0 <= n < 8 ** (digits - 1): s = "%0*o" % (digits - 1, n) + NUL else: - if posix: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): raise ValueError("overflow in number field") if n < 0: @@ -516,7 +551,10 @@ class _Stream: buf = self.__read(self.bufsize) if not buf: break - buf = self.cmp.decompress(buf) + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") t.append(buf) c += len(buf) t = "".join(t) @@ -577,6 +615,7 @@ class _BZ2Proxy(object): def __init__(self, fileobj, mode): self.fileobj = fileobj self.mode = mode + self.name = getattr(self.fileobj, "name", None) self.init() def init(self): @@ -849,8 +888,8 @@ class TarInfo(object): """Construct a TarInfo object. name is the optional name of the member. """ - self.name = name # member name (dirnames must end with '/') - self.mode = 0666 # file permissions + self.name = name # member name + self.mode = 0644 # file permissions self.uid = 0 # user id self.gid = 0 # group id self.size = 0 # file size @@ -858,17 +897,274 @@ class TarInfo(object): 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.uname = "root" # user name + self.gname = "root" # group name self.devmajor = 0 # device major number self.devminor = 0 # device minor number self.offset = 0 # the tar header starts here self.offset_data = 0 # the file's data starts here + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + def __repr__(self): return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": normpath(self.name), + "mode": self.mode & 07777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": normpath(self.linkname) if self.linkname else "", + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING): + """Return a tar header as a string of 512 byte blocks. + """ + if format == USTAR_FORMAT: + return self.create_ustar_header() + elif format == GNU_FORMAT: + return self.create_gnu_header() + elif format == PAX_FORMAT: + return self.create_pax_header(encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self): + """Return the object as a ustar header block. + """ + info = self.get_info() + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT) + + def create_gnu_header(self): + """Return the object as a GNU header block sequence. + """ + info = self.get_info() + info["magic"] = GNU_MAGIC + + buf = "" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME) + + return buf + self._create_header(info, GNU_FORMAT) + + def create_pax_header(self, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info = self.get_info() + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + val = info[name].decode(encoding) + + # Try to encode the string as ASCII. + try: + val.encode("ascii") + except UnicodeEncodeError: + pax_headers[hname] = val + continue + + if len(val) > length: + if name == "name": + # Try to squeeze a longname in the prefix and name fields as in + # ustar format. + try: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + except ValueError: + pax_headers[hname] = val + else: + continue + else: + pax_headers[hname] = val + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = unicode(val) + info[name] = 0 + + if pax_headers: + buf = self._create_pax_generic_header(pax_headers) + else: + buf = "" + + return buf + self._create_header(info, USTAR_FORMAT) + + @classmethod + def create_pax_global_header(cls, pax_headers, encoding): + """Return the object as a pax global header block sequence. + """ + new_headers = {} + for key, val in pax_headers.items(): + key = cls._to_unicode(key, encoding) + val = cls._to_unicode(val, encoding) + new_headers[key] = val + return cls._create_pax_generic_header(new_headers, type=XGLTYPE) + + @staticmethod + def _to_unicode(value, encoding): + if isinstance(value, unicode): + return value + elif isinstance(value, (int, float)): + return unicode(value) + elif isinstance(value, str): + return unicode(value, encoding) + else: + raise ValueError("unable to convert to unicode: %r" % value) + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100), + itn(info.get("mode", 0) & 07777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + " ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100), + stn(info.get("magic", ""), 8), + stn(info.get("uname", ""), 32), + stn(info.get("gname", ""), 32), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, "".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + "%06o\0" % chksum + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name += NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type=XHDTYPE): + """Return a POSIX.1-2001 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be unicode objects. + """ + records = [] + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + value = value.encode("utf8") + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records.append("%d %s=%s\n" % (p, keyword, value)) + records = "".join(records) + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT) + \ + cls._create_payload(records) + @classmethod def frombuf(cls, buf): """Construct a TarInfo object from a 512 byte string buffer. @@ -882,125 +1178,251 @@ class TarInfo(object): if chksum not in calc_chksums(buf): raise HeaderError("bad checksum") - tarinfo = cls() - 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 = chksum - 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]) - prefix = buf[345:500].rstrip(NUL) - - if prefix and not tarinfo.issparse(): - tarinfo.name = prefix + "/" + tarinfo.name + obj = cls() + obj.buf = buf + obj.name = nts(buf[0:100]) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257]) + obj.uname = nts(buf[265:297]) + obj.gname = nts(buf[297:329]) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500]) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE - return tarinfo + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") - def tobuf(self, posix=False): - """Return a tar header as a string of 512 byte blocks. - """ - buf = "" - type = self.type - prefix = "" + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj - if self.name.endswith("/"): - type = DIRTYPE + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + if not buf: + return + obj = cls.frombuf(buf) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) - if type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): - # Prevent "././@LongLink" from being normalized. - name = self.name + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) else: - name = normpath(self.name) + return self._proc_builtin(tarfile) - if type == DIRTYPE: - # directories should end with '/' - name += "/" + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset - linkname = self.linkname - if linkname: - # if linkname is empty we end up with a '.' - linkname = normpath(linkname) + # Patch the TarInfo object with saved extended + # header information. + for keyword, value in tarfile.pax_headers.items(): + if keyword in PAX_FIELDS: + setattr(self, keyword, value) + self.pax_headers[keyword] = value - if posix: - if self.size > MAXSIZE_MEMBER: - raise ValueError("file is too large (>= 8 GB)") + return self - if len(self.linkname) > LENGTH_LINK: - raise ValueError("linkname is too long (>%d)" % (LENGTH_LINK)) + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) - if len(name) > LENGTH_NAME: - prefix = name[:LENGTH_PREFIX + 1] - while prefix and prefix[-1] != "/": - prefix = prefix[:-1] + # Fetch the next header and process it. + b = tarfile.fileobj.read(BLOCKSIZE) + t = self.frombuf(b) + t.offset = self.offset + next = t._proc_member(tarfile) - name = name[len(prefix):] - prefix = prefix[:-1] + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = buf.rstrip(NUL) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = buf.rstrip(NUL) - if not prefix or len(name) > LENGTH_NAME: - raise ValueError("name is too long") + return next - else: - if len(self.linkname) > LENGTH_LINK: - buf += self._create_gnulong(self.linkname, GNUTYPE_LONGLINK) + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + buf = self.buf + sp = _ringbuffer() + pos = 386 + lastpos = 0 + realpos = 0 + # There are 4 possible sparse structs in the + # first header. + for i in xrange(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset > lastpos: + sp.append(_hole(lastpos, offset - lastpos)) + sp.append(_data(offset, numbytes, realpos)) + realpos += numbytes + lastpos = offset + numbytes + pos += 24 - if len(name) > LENGTH_NAME: - buf += self._create_gnulong(name, GNUTYPE_LONGNAME) + isextended = ord(buf[482]) + origsize = nti(buf[483:495]) - parts = [ - stn(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 - 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(prefix, 155) - ] + # If the isextended flag is given, + # there are extra headers to process. + while isextended == 1: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in xrange(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset > lastpos: + sp.append(_hole(lastpos, offset - lastpos)) + sp.append(_data(offset, numbytes, realpos)) + realpos += numbytes + lastpos = offset + numbytes + pos += 24 + isextended = ord(buf[504]) - buf += struct.pack("%ds" % BLOCKSIZE, "".join(parts)) - chksum = calc_chksums(buf[-BLOCKSIZE:])[0] - buf = buf[:-364] + "%06o\0" % chksum + buf[-357:] - self.buf = buf - return buf + if lastpos < origsize: + sp.append(_hole(lastpos, origsize - lastpos)) + + self.sparse = sp - def _create_gnulong(self, name, type): - """Create a GNU longname/longlink header from name. - It consists of an extended tar header, with the length - of the longname as size, followed by data blocks, - which contain the longname as a null terminated string. + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2001. """ - name += NUL + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) - tarinfo = self.__class__() - tarinfo.name = "././@LongLink" - tarinfo.type = type - tarinfo.mode = 0 - tarinfo.size = len(name) - - # create extended header - buf = tarinfo.tobuf() - # create name blocks - buf += name - blocks, remainder = divmod(len(name), BLOCKSIZE) - if remainder > 0: - buf += (BLOCKSIZE - remainder) * NUL - return buf + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Fields in POSIX.1-2001 that are numbers, all other fields + # are treated as UTF-8 strings. + type_mapping = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int + } + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. + regex = re.compile(r"(\d+) ([^=]+)=", re.U) + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + keyword = keyword.decode("utf8") + keyword = keyword.encode(tarfile.encoding) + + value = value.decode("utf8") + if keyword in type_mapping: + try: + value = type_mapping[keyword](value) + except ValueError: + value = 0 + else: + value = value.encode(tarfile.encoding) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header that will be patched with the + # supplement information from the pax header (extended + # only). + t = self.fromtarfile(tarfile) + + if self.type != XGLTYPE and t is not None: + # Patch the TarInfo object from the next header with + # the pax header's information. + for keyword, value in pax_headers.items(): + if keyword in PAX_FIELDS: + setattr(t, keyword, value) + pax_headers[keyword] = value + t.pax_headers = pax_headers.copy() + + return t + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE def isreg(self): return self.type in REGULAR_TYPES @@ -1040,12 +1462,18 @@ class TarFile(object): # messages (if debug >= 0). If > 0, errors # are passed to the caller as exceptions. - posix = False # If True, generates POSIX.1-1990-compliant - # archives (no GNU extensions!) + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Transfer UTF-8 strings from POSIX.1-2001 + # headers to this encoding. + + tarinfo = TarInfo # The default TarInfo class to use. - fileobject = ExFileObject + fileobject = ExFileObject # The default ExFileObject class to use. - def __init__(self, name=None, mode="r", fileobj=None): + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + pax_headers=None, debug=None, errorlevel=None): """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to read from an existing archive, 'a' to append data to an existing file or 'w' to create a new file overwriting an existing one. `mode' @@ -1054,58 +1482,86 @@ class TarFile(object): can be determined, `mode' is overridden by `fileobj's mode. `fileobj' is not closed, when TarFile is closed. """ - self.name = os.path.abspath(name) - if len(mode) > 1 or mode not in "raw": raise ValueError("mode must be 'r', 'a' or 'w'") - self._mode = mode - self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] if not fileobj: - if self._mode == "a" and not os.path.exists(self.name): + if self.mode == "a" and not os.path.exists(name): # Create nonexistent files in append mode. - self._mode = "w" - self.mode = "wb" - fileobj = _open(self.name, self.mode) + self.mode = "w" + self._mode = "wb" + fileobj = _open(name, self._mode) self._extfileobj = False else: - if self.name is None and hasattr(fileobj, "name"): - self.name = os.path.abspath(fileobj.name) + if name is None and hasattr(fileobj, "name"): + name = fileobj.name if hasattr(fileobj, "mode"): - self.mode = fileobj.mode + self._mode = fileobj.mode self._extfileobj = True + self.name = os.path.abspath(name) self.fileobj = fileobj - # Init datastructures + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # 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 = 0 # current position in the archive file self.inodes = {} # dictionary caching the inodes of # archive members already added + self.pax_headers = {} # save contents of global pax headers - if self._mode == "r": + if self.mode == "r": self.firstmember = None self.firstmember = self.next() - if self._mode == "a": + if self.mode == "a": # Move to the end of the archive, # before the first empty block. self.firstmember = None while True: - try: - tarinfo = self.next() - except ReadError: - self.fileobj.seek(0) - break - if tarinfo is None: + if self.next() is None: if self.offset > 0: self.fileobj.seek(- BLOCKSIZE, 1) break - if self._mode in "aw": + if self.mode in "aw": self._loaded = True + if pax_headers: + buf = self.tarinfo.create_pax_global_header( + pax_headers.copy(), self.encoding) + self.fileobj.write(buf) + self.offset += len(buf) + + def _getposix(self): + return self.format == USTAR_FORMAT + def _setposix(self, value): + import warnings + warnings.warn("use the format attribute instead", DeprecationWarning) + if value: + self.format = USTAR_FORMAT + else: + self.format = GNU_FORMAT + posix = property(_getposix, _setposix) + #-------------------------------------------------------------------------- # Below are the classmethods which act as alternate constructors to the # TarFile class. The open() method is the only one that is needed for @@ -1118,7 +1574,7 @@ class TarFile(object): # by adding it to the mapping in OPEN_METH. @classmethod - def open(cls, name=None, mode="r", fileobj=None, bufsize=20*512): + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): """Open a tar archive for reading, writing or appending. Return an appropriate TarFile class. @@ -1151,8 +1607,8 @@ class TarFile(object): if fileobj is not None: saved_pos = fileobj.tell() try: - return func(name, "r", fileobj) - except (ReadError, CompressionError): + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: if fileobj is not None: fileobj.seek(saved_pos) continue @@ -1169,7 +1625,7 @@ class TarFile(object): func = getattr(cls, cls.OPEN_METH[comptype]) else: raise CompressionError("unknown compression type %r" % comptype) - return func(name, filemode, fileobj) + return func(name, filemode, fileobj, **kwargs) elif "|" in mode: filemode, comptype = mode.split("|", 1) @@ -1180,25 +1636,26 @@ class TarFile(object): raise ValueError("mode must be 'r' or 'w'") t = cls(name, filemode, - _Stream(name, filemode, comptype, fileobj, bufsize)) + _Stream(name, filemode, comptype, fileobj, bufsize), + **kwargs) t._extfileobj = False return t elif mode in "aw": - return cls.taropen(name, mode, fileobj) + return cls.taropen(name, mode, fileobj, **kwargs) raise ValueError("undiscernible mode") @classmethod - def taropen(cls, name, mode="r", fileobj=None): + def taropen(cls, name, mode="r", fileobj=None, **kwargs): """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'") - return cls(name, mode, fileobj) + return cls(name, mode, fileobj, **kwargs) @classmethod - def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9): + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): """Open gzip compressed tar archive name for reading or writing. Appending is not allowed. """ @@ -1216,14 +1673,15 @@ class TarFile(object): try: t = cls.taropen(name, mode, - gzip.GzipFile(name, mode, compresslevel, fileobj)) + gzip.GzipFile(name, mode, compresslevel, fileobj), + **kwargs) except IOError: raise ReadError("not a gzip file") t._extfileobj = False return t @classmethod - def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9): + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): """Open bzip2 compressed tar archive name for reading or writing. Appending is not allowed. """ @@ -1241,7 +1699,7 @@ class TarFile(object): fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) try: - t = cls.taropen(name, mode, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) except IOError: raise ReadError("not a bzip2 file") t._extfileobj = False @@ -1264,7 +1722,7 @@ class TarFile(object): if self.closed: return - if self._mode in "aw": + if self.mode in "aw": self.fileobj.write(NUL * (BLOCKSIZE * 2)) self.offset += (BLOCKSIZE * 2) # fill up the end with zero-blocks @@ -1330,7 +1788,8 @@ class TarFile(object): # Now, fill the TarInfo object with # information specific for the file. - tarinfo = TarInfo() + tarinfo = self.tarinfo() + tarinfo.tarfile = self # Use os.stat or os.lstat, depending on platform # and if symlinks shall be resolved. @@ -1346,8 +1805,8 @@ class TarFile(object): stmd = statres.st_mode if stat.S_ISREG(stmd): inode = (statres.st_ino, statres.st_dev) - if not self.dereference and \ - statres.st_nlink > 1 and inode in self.inodes: + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: # Is it a hardlink to an already # archived file? type = LNKTYPE @@ -1424,7 +1883,7 @@ class TarFile(object): print("%d-%02d-%02d %02d:%02d:%02d" \ % time.localtime(tarinfo.mtime)[:6], end=' ') - print(tarinfo.name, end=' ') + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') if verbose: if tarinfo.issym(): @@ -1456,7 +1915,7 @@ class TarFile(object): if recursive: if arcname == ".": arcname = "" - for f in os.listdir("."): + for f in os.listdir(name): self.add(f, os.path.join(arcname, f)) return @@ -1495,7 +1954,7 @@ class TarFile(object): tarinfo = copy.copy(tarinfo) - buf = tarinfo.tobuf(self.posix) + buf = tarinfo.tobuf(self.format, self.encoding) self.fileobj.write(buf) self.offset += len(buf) @@ -1527,7 +1986,7 @@ class TarFile(object): # Extract directory with a safe mode, so that # all files below can be extracted as well. try: - os.makedirs(os.path.join(path, tarinfo.name), 0777) + os.makedirs(os.path.join(path, tarinfo.name), 0700) except EnvironmentError: pass directories.append(tarinfo) @@ -1559,10 +2018,10 @@ class TarFile(object): """ self._check("r") - if isinstance(member, TarInfo): - tarinfo = member - else: + if isinstance(member, basestring): tarinfo = self.getmember(member) + else: + tarinfo = member # Prepare the link target for makelink(). if tarinfo.islnk(): @@ -1595,10 +2054,10 @@ class TarFile(object): """ self._check("r") - if isinstance(member, TarInfo): - tarinfo = member - else: + if isinstance(member, basestring): tarinfo = self.getmember(member) + else: + tarinfo = member if tarinfo.isreg(): return self.fileobject(self, tarinfo) @@ -1811,20 +2270,11 @@ class TarFile(object): # Read the next block. self.fileobj.seek(self.offset) while True: - buf = self.fileobj.read(BLOCKSIZE) - if not buf: - return None - try: - tarinfo = TarInfo.frombuf(buf) - - # 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) + tarinfo = self.tarinfo.fromtarfile(self) + if tarinfo is None: + return + self.members.append(tarinfo) except HeaderError as e: if self.ignore_zeros: @@ -1837,149 +2287,11 @@ class TarFile(object): return None break - # 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 - - # Directory names should have a '/' at the end. - if tarinfo.isdir(): - tarinfo.name += "/" - - self.members.append(tarinfo) - return tarinfo - - #-------------------------------------------------------------------------- - # 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. 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): - """Process the blocks that hold a GNU longname - or longlink member. - """ - buf = "" - count = tarinfo.size - while count > 0: - block = self.fileobj.read(BLOCKSIZE) - buf += block - self.offset += BLOCKSIZE - count -= BLOCKSIZE - - # 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 = buf.rstrip(NUL) - elif tarinfo.type == GNUTYPE_LONGLINK: - next.linkname = buf.rstrip(NUL) - - return next - - def proc_sparse(self, tarinfo): - """Process a GNU sparse header plus extra headers. - """ - buf = tarinfo.buf - sp = _ringbuffer() - pos = 386 - lastpos = 0 - realpos = 0 - # There are 4 possible sparse structs in the - # first header. - for i in xrange(4): - try: - offset = nti(buf[pos:pos + 12]) - numbytes = nti(buf[pos + 12:pos + 24]) - except ValueError: - break - if offset > lastpos: - sp.append(_hole(lastpos, offset - lastpos)) - sp.append(_data(offset, numbytes, realpos)) - realpos += numbytes - lastpos = offset + numbytes - pos += 24 - - isextended = ord(buf[482]) - origsize = nti(buf[483:495]) - - # If the isextended flag is given, - # there are extra headers to process. - while isextended == 1: - buf = self.fileobj.read(BLOCKSIZE) - self.offset += BLOCKSIZE - pos = 0 - for i in xrange(21): - try: - offset = nti(buf[pos:pos + 12]) - numbytes = nti(buf[pos + 12:pos + 24]) - except ValueError: - break - if offset > lastpos: - sp.append(_hole(lastpos, offset - lastpos)) - sp.append(_data(offset, numbytes, realpos)) - realpos += numbytes - lastpos = offset + numbytes - pos += 24 - isextended = ord(buf[504]) - - if lastpos < origsize: - sp.append(_hole(lastpos, origsize - lastpos)) - - tarinfo.sparse = sp - - tarinfo.offset_data = self.offset - self.offset += self._block(tarinfo.size) - tarinfo.size = origsize - return tarinfo #-------------------------------------------------------------------------- # Little helper methods: - def _block(self, count): - """Round up a byte count by BLOCKSIZE and return it, - e.g. _block(834) => 1024. - """ - blocks, remainder = divmod(count, BLOCKSIZE) - if remainder: - blocks += 1 - return blocks * BLOCKSIZE - def _getmember(self, name, tarinfo=None): """Find an archive member by name from bottom to top. If tarinfo is given, it is used as the starting point. @@ -2012,8 +2324,8 @@ class TarFile(object): """ if self.closed: 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) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) def __iter__(self): """Provide an iterator object. diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index dd263ae..1040e3c 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -184,7 +184,7 @@ class Telnet: """ - def __init__(self, host=None, port=0): + def __init__(self, host=None, port=0, timeout=None): """Constructor. When called without arguments, create an unconnected instance. @@ -195,6 +195,7 @@ class Telnet: self.debuglevel = DEBUGLEVEL self.host = host self.port = port + self.timeout = timeout self.sock = None self.rawq = '' self.irawq = 0 @@ -205,9 +206,9 @@ class Telnet: self.sbdataq = '' self.option_callback = None if host is not None: - self.open(host, port) + self.open(host, port, timeout) - def open(self, host, port=0): + def open(self, host, port=0, timeout=None): """Connect to a host. The optional second argument is the port number, which @@ -221,20 +222,9 @@ class Telnet: port = TELNET_PORT self.host = host self.port = port - msg = "getaddrinfo returns an empty list" - for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) - except socket.error as msg: - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg + if timeout is not None: + self.timeout = timeout + self.sock = socket.create_connection((host, port), self.timeout) def __del__(self): """Destructor -- close the connection.""" @@ -661,7 +651,7 @@ def test(): port = socket.getservbyname(portstr, 'tcp') tn = Telnet() tn.set_debuglevel(debuglevel) - tn.open(host, port) + tn.open(host, port, timeout=0.5) tn.interact() tn.close() diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 0ebf6b4..b63a46a 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -19,6 +19,7 @@ This module also provides some data items to the user: __all__ = [ "NamedTemporaryFile", "TemporaryFile", # high level safe interfaces + "SpooledTemporaryFile", "mkstemp", "mkdtemp", # low level safe interfaces "mktemp", # deprecated unsafe interface "TMP_MAX", "gettempprefix", # constants @@ -37,6 +38,11 @@ if _os.name == 'mac': import Carbon.Folders as _Folders try: + from cStringIO import StringIO as _StringIO +except: + from StringIO import StringIO as _StringIO + +try: import fcntl as _fcntl except ImportError: def _set_cloexec(fd): @@ -114,7 +120,7 @@ class _RandomNameSequence: characters = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "0123456789-_") + "0123456789_") def __init__(self): self.mutex = _allocate_lock() @@ -372,10 +378,11 @@ class _TemporaryFileWrapper: remove the file when it is no longer needed. """ - def __init__(self, file, name): + def __init__(self, file, name, delete=True): self.file = file self.name = name self.close_called = False + self.delete = delete def __getattr__(self, name): file = self.__dict__['file'] @@ -400,23 +407,25 @@ class _TemporaryFileWrapper: if not self.close_called: self.close_called = True self.file.close() - self.unlink(self.name) + if self.delete: + self.unlink(self.name) def __del__(self): self.close() def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="", - prefix=template, dir=None): + prefix=template, dir=None, delete=True): """Create and return a temporary file. Arguments: 'prefix', 'suffix', 'dir' -- as for mkstemp. 'mode' -- the mode argument to os.fdopen (default "w+b"). 'bufsize' -- the buffer size argument to os.fdopen (default -1). + 'delete' -- whether the file is deleted on close (default True). The file is created as mkstemp() would do it. Returns an object with a file-like interface; the name of the file is accessible as file.name. The file will be automatically deleted - when it is closed. + when it is closed unless the 'delete' argument is set to False. """ if dir is None: @@ -429,12 +438,12 @@ def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="", # Setting O_TEMPORARY in the flags causes the OS to delete # the file when it is closed. This is only supported by Windows. - if _os.name == 'nt': + if _os.name == 'nt' and delete: flags |= _os.O_TEMPORARY (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) file = _os.fdopen(fd, mode, bufsize) - return _TemporaryFileWrapper(file, name) + return _TemporaryFileWrapper(file, name, delete) if _os.name != 'posix' or _os.sys.platform == 'cygwin': # On non-POSIX and Cygwin systems, assume that we cannot unlink a file @@ -470,3 +479,111 @@ else: except: _os.close(fd) raise + +class SpooledTemporaryFile: + """Temporary file wrapper, specialized to switch from + StringIO to a real file when it exceeds a certain size or + when a fileno is needed. + """ + _rolled = False + + def __init__(self, max_size=0, mode='w+b', bufsize=-1, + suffix="", prefix=template, dir=None): + self._file = _StringIO() + self._max_size = max_size + self._rolled = False + self._TemporaryFileArgs = (mode, bufsize, suffix, prefix, dir) + + def _check(self, file): + if self._rolled: return + max_size = self._max_size + if max_size and file.tell() > max_size: + self.rollover() + + def rollover(self): + if self._rolled: return + file = self._file + newfile = self._file = TemporaryFile(*self._TemporaryFileArgs) + del self._TemporaryFileArgs + + newfile.write(file.getvalue()) + newfile.seek(file.tell(), 0) + + self._rolled = True + + # file protocol + def __iter__(self): + return self._file.__iter__() + + def close(self): + self._file.close() + + @property + def closed(self): + return self._file.closed + + @property + def encoding(self): + return self._file.encoding + + def fileno(self): + self.rollover() + return self._file.fileno() + + def flush(self): + self._file.flush() + + def isatty(self): + return self._file.isatty() + + @property + def mode(self): + return self._file.mode + + @property + def name(self): + return self._file.name + + @property + def newlines(self): + return self._file.newlines + + def next(self): + return self._file.next + + def read(self, *args): + return self._file.read(*args) + + def readline(self, *args): + return self._file.readline(*args) + + def readlines(self, *args): + return self._file.readlines(*args) + + def seek(self, *args): + self._file.seek(*args) + + @property + def softspace(self): + return self._file.softspace + + def tell(self): + return self._file.tell() + + def truncate(self): + self._file.truncate() + + def write(self, s): + file = self._file + rv = file.write(s) + self._check(file) + return rv + + def writelines(self, iterable): + file = self._file + rv = file.writelines(iterable) + self._check(file) + return rv + + def xreadlines(self, *args): + return self._file.xreadlines(*args) diff --git a/Lib/test/README b/Lib/test/README index 27f696c..747d842 100644 --- a/Lib/test/README +++ b/Lib/test/README @@ -15,7 +15,7 @@ testing facility provided with Python; any particular test should use only one of these options. Each option requires writing a test module using the conventions of the selected option: - - PyUnit_ based tests + - unittest_ based tests - doctest_ based tests - "traditional" Python test modules @@ -28,31 +28,34 @@ your test cases to exercise it more completely. In particular, you will be able to refer to the C and Python code in the CVS repository when writing your regression test cases. -.. _PyUnit: .. _unittest: http://www.python.org/doc/current/lib/module-unittest.html .. _doctest: http://www.python.org/doc/current/lib/module-doctest.html -PyUnit based tests +unittest-based tests ------------------ -The PyUnit_ framework is based on the ideas of unit testing as espoused +The unittest_ framework is based on the ideas of unit testing as espoused by Kent Beck and the `Extreme Programming`_ (XP) movement. The specific interface provided by the framework is tightly based on the JUnit_ Java implementation of Beck's original SmallTalk test framework. Please see the documentation of the unittest_ module for detailed information on -the interface and general guidelines on writing PyUnit based tests. - -The test_support helper module provides two functions for use by -PyUnit based tests in the Python regression testing framework: - -- ``run_unittest()`` takes a ``unittest.TestCase`` derived class as a - parameter and runs the tests defined in that class +the interface and general guidelines on writing unittest-based tests. + +The test_support helper module provides a function for use by +unittest-based tests in the Python regression testing framework, +``run_unittest()``. This is the primary way of running tests in the +standard library. You can pass it any number of the following: + +- classes derived from or instances of ``unittest.TestCase`` or + ``unittest.TestSuite``. These will be handed off to unittest for + converting into a proper TestSuite instance. + +- a string; this must be a key in sys.modules. The module associated with + that string will be scanned by ``unittest.TestLoader.loadTestsFromModule``. + This is usually seen as ``test_support.run_unittest(__name__)`` in a test + module's ``test_main()`` function. This has the advantage of picking up + new tests automatically, without you having to add each new test case + manually. -- ``run_suite()`` takes a populated ``TestSuite`` instance and runs the - tests - -``run_suite()`` is preferred because unittest files typically grow multiple -test classes, and you might as well be prepared. - All test methods in the Python regression framework have names that start with "``test_``" and use lower-case names with words separated with underscores. @@ -63,7 +66,7 @@ and the full class name. When there's a problem with a test, the latter information makes it easier to find the source for the test than the docstring. -All PyUnit-based tests in the Python test suite use boilerplate that +All unittest-based tests in the Python test suite use boilerplate that looks like this (with minor variations):: import unittest @@ -97,11 +100,7 @@ looks like this (with minor variations):: ...etc... def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(MyTestCase1)) - suite.addTest(unittest.makeSuite(MyTestCase2)) - ...add more suites... - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() @@ -415,7 +414,7 @@ Some Non-Obvious regrtest Features This is rarely required with the "traditional" Python tests, and you shouldn't create a module global with name test_main unless you're specifically exploiting this gimmick. This usage does - prove useful with PyUnit-based tests as well, however; defining + prove useful with unittest-based tests as well, however; defining a ``test_main()`` which is run by regrtest and a script-stub in the test module ("``if __name__ == '__main__': test_main()``") allows the test to be used like any other Python test and also work diff --git a/Lib/test/crashers/modify_dict_attr.py b/Lib/test/crashers/modify_dict_attr.py index ac1f0a8..be675c1 100644 --- a/Lib/test/crashers/modify_dict_attr.py +++ b/Lib/test/crashers/modify_dict_attr.py @@ -4,15 +4,16 @@ class Y(object): pass -class type_with_modifiable_dict(Y, type): +class type_with_modifiable_dict(type, Y): pass class MyClass(object, metaclass=type_with_modifiable_dict): - """This class has its __dict__ attribute completely exposed: - user code can read, reassign and even delete it. + """This class has its __dict__ attribute indirectly + exposed via the __dict__ getter/setter of Y. """ if __name__ == '__main__': - del MyClass.__dict__ # if we set tp_dict to NULL, + dictattr = Y.__dict__['__dict__'] + dictattr.__delete__(MyClass) # if we set tp_dict to NULL, print(MyClass) # doing anything with MyClass segfaults diff --git a/Lib/test/infinite_reload.py b/Lib/test/infinite_reload.py new file mode 100644 index 0000000..bfbec91 --- /dev/null +++ b/Lib/test/infinite_reload.py @@ -0,0 +1,7 @@ +# For testing http://python.org/sf/742342, which reports that Python +# segfaults (infinite recursion in C) in the presence of infinite +# reload()ing. This module is imported by test_import.py:test_infinite_reload +# to make sure this doesn't happen any more. + +import infinite_reload +reload(infinite_reload) diff --git a/Lib/test/output/test_operations b/Lib/test/output/test_operations deleted file mode 100644 index 309cd5b..0000000 --- a/Lib/test/output/test_operations +++ /dev/null @@ -1,19 +0,0 @@ -test_operations -3. Operations -XXX Mostly not yet implemented -3.1 Dictionary lookups fail if __cmp__() raises an exception -raising error -d[x2] = 2: caught the RuntimeError outside -raising error -z = d[x2]: caught the RuntimeError outside -raising error -x2 in d: caught the RuntimeError outside -raising error -d.get(x2): caught the RuntimeError outside -raising error -d.setdefault(x2, 42): caught the RuntimeError outside -raising error -d.pop(x2): caught the RuntimeError outside -raising error -d.update({x2: 2}): caught the RuntimeError outside -resize bugs not triggered. diff --git a/Lib/test/output/test_popen2 b/Lib/test/output/test_popen2 deleted file mode 100644 index a66cde9..0000000 --- a/Lib/test/output/test_popen2 +++ /dev/null @@ -1,9 +0,0 @@ -test_popen2 -Test popen2 module: -testing popen2... -testing popen3... -All OK -Testing os module: -testing popen2... -testing popen3... -All OK diff --git a/Lib/test/output/test_pty b/Lib/test/output/test_pty deleted file mode 100644 index b6e0e32..0000000 --- a/Lib/test/output/test_pty +++ /dev/null @@ -1,3 +0,0 @@ -test_pty -I wish to buy a fish license. -For my pet fish, Eric. diff --git a/Lib/test/output/test_pyexpat b/Lib/test/output/test_pyexpat deleted file mode 100644 index 61fe81d..0000000 --- a/Lib/test/output/test_pyexpat +++ /dev/null @@ -1,110 +0,0 @@ -test_pyexpat -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -PI: - 'xml-stylesheet' 'href="stylesheet.css"' -Comment: - ' comment data ' -Notation declared: ('notation', None, 'notation.jpeg', None) -Unparsed entity decl: - ('unparsed_entity', None, 'entity.file', None, 'notation') -Start element: - 'root' {'attr1': 'value1', 'attr2': 'value2\xe1\xbd\x80'} -NS decl: - 'myns' 'http://www.python.org/namespace' -Start element: - 'http://www.python.org/namespace!subelement' {} -Character data: - 'Contents of subelements' -End element: - 'http://www.python.org/namespace!subelement' -End of NS decl: - 'myns' -Start element: - 'sub2' {} -Start of CDATA section -Character data: - 'contents of CDATA section' -End of CDATA section -End element: - 'sub2' -External entity ref: (None, 'entity.file', None) -End element: - 'root' -PI: - u'xml-stylesheet' u'href="stylesheet.css"' -Comment: - u' comment data ' -Notation declared: (u'notation', None, u'notation.jpeg', None) -Unparsed entity decl: - (u'unparsed_entity', None, u'entity.file', None, u'notation') -Start element: - u'root' {u'attr1': u'value1', u'attr2': u'value2\u1f40'} -NS decl: - u'myns' u'http://www.python.org/namespace' -Start element: - u'http://www.python.org/namespace!subelement' {} -Character data: - u'Contents of subelements' -End element: - u'http://www.python.org/namespace!subelement' -End of NS decl: - u'myns' -Start element: - u'sub2' {} -Start of CDATA section -Character data: - u'contents of CDATA section' -End of CDATA section -End element: - u'sub2' -External entity ref: (None, u'entity.file', None) -End element: - u'root' -PI: - u'xml-stylesheet' u'href="stylesheet.css"' -Comment: - u' comment data ' -Notation declared: (u'notation', None, u'notation.jpeg', None) -Unparsed entity decl: - (u'unparsed_entity', None, u'entity.file', None, u'notation') -Start element: - u'root' {u'attr1': u'value1', u'attr2': u'value2\u1f40'} -NS decl: - u'myns' u'http://www.python.org/namespace' -Start element: - u'http://www.python.org/namespace!subelement' {} -Character data: - u'Contents of subelements' -End element: - u'http://www.python.org/namespace!subelement' -End of NS decl: - u'myns' -Start element: - u'sub2' {} -Start of CDATA section -Character data: - u'contents of CDATA section' -End of CDATA section -End element: - u'sub2' -External entity ref: (None, u'entity.file', None) -End element: - u'root' - -Testing constructor for proper handling of namespace_separator values: -Legal values tested o.k. -Caught expected TypeError: -ParserCreate() argument 2 must be string or None, not int -Caught expected ValueError: -namespace_separator must be at most one character, omitted, or None diff --git a/Lib/test/output/test_threadedtempfile b/Lib/test/output/test_threadedtempfile deleted file mode 100644 index 2552877..0000000 --- a/Lib/test/output/test_threadedtempfile +++ /dev/null @@ -1,5 +0,0 @@ -test_threadedtempfile -Creating -Starting -Reaping -Done: errors 0 ok 1000 diff --git a/Lib/test/output/xmltests b/Lib/test/output/xmltests deleted file mode 100644 index c798f6e..0000000 --- a/Lib/test/output/xmltests +++ /dev/null @@ -1,364 +0,0 @@ -xmltests -Passed testAAA -Passed setAttribute() sets ownerDocument -Passed setAttribute() sets ownerElement -Test Succeeded testAAA -Passed assertion: len(Node.allnodes) == 0 -Passed testAAB -Test Succeeded testAAB -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Passed Test -Passed Test -Passed Test -Passed Test -Passed Test -Passed Test -Test Succeeded testAddAttr -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testAppendChild -Passed assertion: len(Node.allnodes) == 0 -Passed appendChild(<fragment>) -Test Succeeded testAppendChildFragment -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListItem -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListItemNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListItems -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListKeys -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListKeysNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListLength -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrListValues -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrList__getitem__ -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testAttrList__setitem__ -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testAttributeRepr -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Passed Test -Passed Test -Passed Test -Test Succeeded testChangeAttr -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testChildNodes -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCloneAttributeDeep -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCloneAttributeShallow -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCloneDocumentDeep -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCloneDocumentShallow -Passed assertion: len(Node.allnodes) == 0 -Passed clone of element has same attribute keys -Passed clone of attribute node has proper attribute values -Passed clone of attribute node correctly owned -Passed testCloneElementDeep -Test Succeeded testCloneElementDeep -Passed assertion: len(Node.allnodes) == 0 -Passed clone of element has same attribute keys -Passed clone of attribute node has proper attribute values -Passed clone of attribute node correctly owned -Passed testCloneElementShallow -Test Succeeded testCloneElementShallow -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testClonePIDeep -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testClonePIShallow -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testComment -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCreateAttributeNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testCreateElementNS -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Passed Test -Test Succeeded testDeleteAttr -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testDocumentElement -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Test Succeeded testElement -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Test Succeeded testElementReprAndStr -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testFirstChild -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttrLength -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttrList -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttrValues -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttribute -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttributeNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetAttributeNode -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Test Succeeded testGetElementsByTagName -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Test Succeeded testGetElementsByTagNameNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testGetEmptyNodeListFromElementsByTagNameNS -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testHasChildNodes -Passed assertion: len(Node.allnodes) == 0 -Passed testInsertBefore -- node properly placed in tree -Passed testInsertBefore -- node properly placed in tree -Passed testInsertBefore -- node properly placed in tree -Test Succeeded testInsertBefore -Passed assertion: len(Node.allnodes) == 0 -Passed insertBefore(<fragment>, None) -Passed insertBefore(<fragment>, orig) -Test Succeeded testInsertBeforeFragment -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testLegalChildren -Passed assertion: len(Node.allnodes) == 0 -Passed NamedNodeMap.__setitem__() sets ownerDocument -Passed NamedNodeMap.__setitem__() sets ownerElement -Passed NamedNodeMap.__setitem__() sets value -Passed NamedNodeMap.__setitem__() sets nodeValue -Test Succeeded testNamedNodeMapSetItem -Passed assertion: len(Node.allnodes) == 0 -Passed test NodeList.item() -Test Succeeded testNodeListItem -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testNonZero -Passed assertion: len(Node.allnodes) == 0 -Passed testNormalize -- preparation -Passed testNormalize -- result -Passed testNormalize -- single empty node removed -Test Succeeded testNormalize -Passed assertion: len(Node.allnodes) == 0 -Passed testParents -Test Succeeded testParents -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParse -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseAttributeNamespaces -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseAttributes -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseElement -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseElementNamespaces -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Test Succeeded testParseFromFile -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseProcessingInstructions -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testParseString -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testProcessingInstruction -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testProcessingInstructionRepr -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testRemoveAttr -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testRemoveAttrNS -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testRemoveAttributeNode -Passed assertion: len(Node.allnodes) == 0 -Passed replaceChild(<fragment>) -Test Succeeded testReplaceChildFragment -Passed assertion: len(Node.allnodes) == 0 -Passed testSAX2DOM - siblings -Passed testSAX2DOM - parents -Test Succeeded testSAX2DOM -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testSetAttrValueandNodeValue -Passed assertion: len(Node.allnodes) == 0 -Passed testSiblings -Test Succeeded testSiblings -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testTextNodeRepr -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testTextRepr -Passed assertion: len(Node.allnodes) == 0 -Caught expected exception when adding extra document element. -Test Succeeded testTooManyDocumentElements -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testUnlink -Passed assertion: len(Node.allnodes) == 0 -Test Succeeded testWriteText -Passed assertion: len(Node.allnodes) == 0 -Passed Test -Passed Test -Test Succeeded testWriteXML -Passed assertion: len(Node.allnodes) == 0 -All tests succeeded -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -OK. -PI: - 'xml-stylesheet' 'href="stylesheet.css"' -Comment: - ' comment data ' -Notation declared: ('notation', None, 'notation.jpeg', None) -Unparsed entity decl: - ('unparsed_entity', None, 'entity.file', None, 'notation') -Start element: - 'root' {'attr1': 'value1', 'attr2': 'value2\xe1\xbd\x80'} -NS decl: - 'myns' 'http://www.python.org/namespace' -Start element: - 'http://www.python.org/namespace!subelement' {} -Character data: - 'Contents of subelements' -End element: - 'http://www.python.org/namespace!subelement' -End of NS decl: - 'myns' -Start element: - 'sub2' {} -Start of CDATA section -Character data: - 'contents of CDATA section' -End of CDATA section -End element: - 'sub2' -External entity ref: (None, 'entity.file', None) -End element: - 'root' -PI: - u'xml-stylesheet' u'href="stylesheet.css"' -Comment: - u' comment data ' -Notation declared: (u'notation', None, u'notation.jpeg', None) -Unparsed entity decl: - (u'unparsed_entity', None, u'entity.file', None, u'notation') -Start element: - u'root' {u'attr1': u'value1', u'attr2': u'value2\u1f40'} -NS decl: - u'myns' u'http://www.python.org/namespace' -Start element: - u'http://www.python.org/namespace!subelement' {} -Character data: - u'Contents of subelements' -End element: - u'http://www.python.org/namespace!subelement' -End of NS decl: - u'myns' -Start element: - u'sub2' {} -Start of CDATA section -Character data: - u'contents of CDATA section' -End of CDATA section -End element: - u'sub2' -External entity ref: (None, u'entity.file', None) -End element: - u'root' -PI: - u'xml-stylesheet' u'href="stylesheet.css"' -Comment: - u' comment data ' -Notation declared: (u'notation', None, u'notation.jpeg', None) -Unparsed entity decl: - (u'unparsed_entity', None, u'entity.file', None, u'notation') -Start element: - u'root' {u'attr1': u'value1', u'attr2': u'value2\u1f40'} -NS decl: - u'myns' u'http://www.python.org/namespace' -Start element: - u'http://www.python.org/namespace!subelement' {} -Character data: - u'Contents of subelements' -End element: - u'http://www.python.org/namespace!subelement' -End of NS decl: - u'myns' -Start element: - u'sub2' {} -Start of CDATA section -Character data: - u'contents of CDATA section' -End of CDATA section -End element: - u'sub2' -External entity ref: (None, u'entity.file', None) -End element: - u'root' - -Testing constructor for proper handling of namespace_separator values: -Legal values tested o.k. -Caught expected TypeError: -ParserCreate() argument 2 must be string or None, not int -Caught expected ValueError: -namespace_separator must be at most one character, omitted, or None -Passed test_attrs_empty -Passed test_attrs_wattr -Passed test_double_quoteattr -Passed test_escape_all -Passed test_escape_basic -Passed test_escape_extra -Passed test_expat_attrs_empty -Passed test_expat_attrs_wattr -Passed test_expat_dtdhandler -Passed test_expat_entityresolver -Passed test_expat_file -Passed test_expat_incomplete -Passed test_expat_incremental -Passed test_expat_incremental_reset -Passed test_expat_inpsource_filename -Passed test_expat_inpsource_location -Passed test_expat_inpsource_stream -Passed test_expat_inpsource_sysid -Passed test_expat_locator_noinfo -Passed test_expat_locator_withinfo -Passed test_expat_nsattrs_empty -Passed test_expat_nsattrs_wattr -Passed test_filter_basic -Passed test_make_parser -Passed test_make_parser2 -Passed test_nsattrs_empty -Passed test_nsattrs_wattr -Passed test_quoteattr_basic -Passed test_single_double_quoteattr -Passed test_single_quoteattr -Passed test_xmlgen_attr_escape -Passed test_xmlgen_basic -Passed test_xmlgen_content -Passed test_xmlgen_content_escape -Passed test_xmlgen_ignorable -Passed test_xmlgen_ns -Passed test_xmlgen_pi -37 tests, 0 failures diff --git a/Lib/test/outstanding_bugs.py b/Lib/test/outstanding_bugs.py index 04afcbd..7c6cd9e 100644 --- a/Lib/test/outstanding_bugs.py +++ b/Lib/test/outstanding_bugs.py @@ -10,13 +10,44 @@ import unittest from test import test_support # -# No test cases for outstanding bugs at the moment. +# One test case for outstanding bugs at the moment: # +class TestDifflibLongestMatch(unittest.TestCase): + # From Patch #1678339: + # The find_longest_match method in the difflib's SequenceMatcher has a bug. + + # The bug is in turn caused by a problem with creating a b2j mapping which + # should contain a list of indices for each of the list elements in b. + # However, when the b2j mapping is being created (this is being done in + # __chain_b method in the SequenceMatcher) the mapping becomes broken. The + # cause of this is that for the frequently used elements the list of indices + # is removed and the element is being enlisted in the populardict mapping. + + # The test case tries to match two strings like: + # abbbbbb.... and ...bbbbbbc + + # The number of b is equal and the find_longest_match should have returned + # the proper amount. However, in case the number of "b"s is large enough, the + # method reports that the length of the longest common substring is 0. It + # simply can't find it. + + # A bug was raised some time ago on this matter. It's ID is 1528074. + + def test_find_longest_match(self): + import difflib + for i in (190, 200, 210): + text1 = "a" + "b"*i + text2 = "b"*i + "c" + m = difflib.SequenceMatcher(None, text1, text2) + (aptr, bptr, l) = m.find_longest_match(0, len(text1), 0, len(text2)) + self.assertEquals(i, l) + self.assertEquals(aptr, 1) + self.assertEquals(bptr, 0) + def test_main(): - #test_support.run_unittest() - pass + test_support.run_unittest(TestDifflibLongestMatch) if __name__ == "__main__": test_main() diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index b10c57d..4691e13 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -835,6 +835,24 @@ class AbstractPickleTests(unittest.TestCase): y = self.loads(s) self.assertEqual(y._proto, None) + def test_reduce_ex_calls_base(self): + for proto in 0, 1, 2: + x = REX_four() + self.assertEqual(x._proto, None) + s = self.dumps(x, proto) + self.assertEqual(x._proto, proto) + y = self.loads(s) + self.assertEqual(y._proto, proto) + + def test_reduce_calls_base(self): + for proto in 0, 1, 2: + x = REX_five() + self.assertEqual(x._reduce_called, 0) + s = self.dumps(x, proto) + self.assertEqual(x._reduce_called, 1) + y = self.loads(s) + self.assertEqual(y._reduce_called, 1) + # Test classes for reduce_ex class REX_one(object): @@ -859,6 +877,20 @@ class REX_three(object): def __reduce__(self): raise TestFailed, "This __reduce__ shouldn't be called" +class REX_four(object): + _proto = None + def __reduce_ex__(self, proto): + self._proto = proto + return object.__reduce_ex__(self, proto) + # Calling base class method should succeed + +class REX_five(object): + _reduce_called = 0 + def __reduce__(self): + self._reduce_called = 1 + return object.__reduce__(self) + # This one used to fail with infinite recursion + # Test classes for newobj class MyInt(int): diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 6ef6663..87d9dc9 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -474,7 +474,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, generate=False, STDTESTS = [ 'test_grammar', 'test_opcodes', - 'test_operations', + 'test_dict', 'test_builtin', 'test_exceptions', 'test_types', diff --git a/Lib/test/ssl_cert.pem b/Lib/test/ssl_cert.pem new file mode 100644 index 0000000..9d7ac23 --- /dev/null +++ b/Lib/test/ssl_cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD +VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv +bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy +dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X +DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw +EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l +dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT +EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw +L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN +BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX +9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4= +-----END CERTIFICATE----- diff --git a/Lib/test/ssl_key.pem b/Lib/test/ssl_key.pem new file mode 100644 index 0000000..239ad66 --- /dev/null +++ b/Lib/test/ssl_key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNwL4lYKbpzzlmC5beaQXeQ +2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAAQJBALjkK+jc2+iihI98riEF +oudmkNziSRTYjnwjx8mCoAjPWviB3c742eO3FG4/soi1jD9A5alihEOXfUzloenr +8IECIQD3B5+0l+68BA/6d76iUNqAAV8djGTzvxnCxycnxPQydQIhAMXt4trUI3nc +a+U8YL2HPFA3gmhBsSICbq2OptOCnM7hAiEA6Xi3JIQECob8YwkRj29DU3/4WYD7 +WLPgsQpwo1GuSpECICGsnWH5oaeD9t9jbFoSfhJvv0IZmxdcLpRcpslpeWBBAiEA +6/5B8J0GHdJq89FHwEG/H2eVVUYu5y/aD6sgcm+0Avg= +-----END RSA PRIVATE KEY----- diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index b0852ee..2431262 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -1104,6 +1104,9 @@ class MixinStrStringUserStringTest: self.checkequal('Abc', 'abc', 'translate', table) self.checkequal('xyz', 'xyz', 'translate', table) self.checkequal('yz', 'xyz', 'translate', table, 'x') + self.checkequal('yx', 'zyzzx', 'translate', None, 'z') + self.checkequal('zyzzx', 'zyzzx', 'translate', None, '') + self.checkequal('zyzzx', 'zyzzx', 'translate', None) self.checkraises(ValueError, 'xyz', 'translate', 'too short', 'strip') self.checkraises(ValueError, 'xyz', 'translate', 'too short') diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index bb1fd8d..6003733 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -1,7 +1,5 @@ import unittest -from test import test_support - -from test.test_support import verify, verbose +from test.test_support import verbose, run_unittest import sys import warnings @@ -20,15 +18,15 @@ class AllTest(unittest.TestCase): # Silent fail here seems the best route since some modules # may not be available in all environments. return - verify(hasattr(sys.modules[modname], "__all__"), - "%s has no __all__ attribute" % modname) + self.failUnless(hasattr(sys.modules[modname], "__all__"), + "%s has no __all__ attribute" % modname) names = {} exec("from %s import *" % modname, names) if "__builtins__" in names: del names["__builtins__"] keys = set(names) all = set(sys.modules[modname].__all__) - verify(keys==all, "%s != %s" % (keys, all)) + self.assertEqual(keys, all) def test_all(self): if not sys.platform.startswith('java'): @@ -177,7 +175,7 @@ class AllTest(unittest.TestCase): def test_main(): - test_support.run_unittest(AllTest) + run_unittest(AllTest) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index ae7156b..34b573f 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -111,6 +111,21 @@ class BaseTest(unittest.TestCase): self.assertEqual(a.x, b.x) self.assertEqual(type(a), type(b)) + def test_pickle_for_empty_array(self): + for protocol in (0, 1, 2): + a = array.array(self.typecode) + b = loads(dumps(a, protocol)) + self.assertNotEqual(id(a), id(b)) + self.assertEqual(a, b) + + a = ArraySubclass(self.typecode) + a.x = 10 + b = loads(dumps(a, protocol)) + self.assertNotEqual(id(a), id(b)) + self.assertEqual(a, b) + self.assertEqual(a.x, b.x) + self.assertEqual(type(a), type(b)) + def test_insert(self): a = array.array(self.typecode, self.example) a.insert(0, self.example[0]) @@ -713,7 +728,6 @@ class CharacterTest(StringTest): return array.array.__new__(cls, 'c', s) def __init__(self, s, color='blue'): - array.array.__init__(self, 'c', s) self.color = color def strip(self): diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index 56077e7..142e543 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -28,7 +28,7 @@ class TestCase(unittest.TestCase): self.stream = StringIO.StringIO() sys.stdout = sys.stderr = self.stream atexit._clear() - + def tearDown(self): sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ @@ -50,63 +50,63 @@ class TestCase(unittest.TestCase): atexit.register(h2) atexit.register(h3) atexit._run_exitfuncs() - + self.assertEqual(self.stream.getvalue(), "h3\nh2\nh1\n") def test_raise(self): # be sure raises are handled properly atexit.register(raise1) atexit.register(raise2) - + self.assertRaises(TypeError, atexit._run_exitfuncs) - + def test_stress(self): a = [0] def inc(): a[0] += 1 - + for i in range(128): atexit.register(inc) atexit._run_exitfuncs() - + self.assertEqual(a[0], 128) - + def test_clear(self): a = [0] def inc(): a[0] += 1 - + atexit.register(inc) atexit._clear() atexit._run_exitfuncs() - + self.assertEqual(a[0], 0) - + def test_unregister(self): a = [0] def inc(): a[0] += 1 def dec(): a[0] -= 1 - - for i in range(4): + + for i in range(4): atexit.register(inc) atexit.register(dec) atexit.unregister(inc) atexit._run_exitfuncs() - + self.assertEqual(a[0], -1) - + def test_bound_methods(self): l = [] atexit.register(l.append, 5) atexit._run_exitfuncs() self.assertEqual(l, [5]) - + atexit.unregister(l.append) atexit._run_exitfuncs() self.assertEqual(l, [5]) - + def test_main(): test_support.run_unittest(TestCase) diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index 997a413..ff2c370 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -183,16 +183,8 @@ class BaseXYTestCase(unittest.TestCase): -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(LegacyBase64TestCase)) - suite.addTest(unittest.makeSuite(BaseXYTestCase)) - return suite - - def test_main(): - test_support.run_suite(suite()) - + test_support.run_unittest(__name__) if __name__ == '__main__': - unittest.main(defaultTest='suite') + test_main() diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 8272ad9..ea8be31 100755 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -148,6 +148,15 @@ class BinASCIITest(unittest.TestCase): "0"*75+"=\r\n=FF\r\n=FF\r\n=FF" ) + self.assertEqual(binascii.b2a_qp('\0\n'), '=00\n') + self.assertEqual(binascii.b2a_qp('\0\n', quotetabs=True), '=00\n') + self.assertEqual(binascii.b2a_qp('foo\tbar\t\n'), 'foo\tbar=09\n') + self.assertEqual(binascii.b2a_qp('foo\tbar\t\n', quotetabs=True), 'foo=09bar=09\n') + + self.assertEqual(binascii.b2a_qp('.'), '=2E') + self.assertEqual(binascii.b2a_qp('.\n'), '=2E\n') + self.assertEqual(binascii.b2a_qp('a.\n'), 'a.\n') + def test_empty_string(self): # A test for SF bug #1022953. Make sure SystemError is not raised. for n in ['b2a_qp', 'a2b_hex', 'b2a_base64', 'a2b_uu', 'a2b_qp', diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 1e19cf5..dd04b27 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -321,7 +321,7 @@ class BoolTest(unittest.TestCase): self.assertEqual(pickle.dumps(False), "I00\n.") self.assertEqual(pickle.dumps(True, True), "I01\n.") self.assertEqual(pickle.dumps(False, True), "I00\n.") - + try: import cPickle except ImportError: diff --git a/Lib/test/test_bsddb3.py b/Lib/test/test_bsddb3.py index 69e99c0..fe0469c 100644 --- a/Lib/test/test_bsddb3.py +++ b/Lib/test/test_bsddb3.py @@ -4,7 +4,7 @@ Run all test cases. """ import sys import unittest -from test.test_support import requires, verbose, run_suite, unlink +from test.test_support import requires, verbose, run_unittest, unlink # When running as a script instead of within the regrtest framework, skip the # requires test, since it's obvious we want to run them. @@ -58,9 +58,7 @@ def suite(): # For invocation through regrtest def test_main(): - tests = suite() - run_suite(tests) - + run_unittest(suite()) # For invocation as a script if __name__ == '__main__': @@ -73,4 +71,4 @@ if __name__ == '__main__': print('python version: %s' % sys.version) print('-=' * 38) - unittest.main(defaultTest='suite') + test_main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 500516c..ffa74af 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -107,9 +107,12 @@ class BuiltinTest(unittest.TestCase): __import__('sys') __import__('time') __import__('string') + __import__(name='sys') + __import__(name='time', level=0) self.assertRaises(ImportError, __import__, 'spamspam') self.assertRaises(TypeError, __import__, 1, 2, 3, 4) self.assertRaises(ValueError, __import__, '') + self.assertRaises(TypeError, __import__, 'sys', name='sys') def test_abs(self): # int @@ -207,15 +210,21 @@ class BuiltinTest(unittest.TestCase): compile('print(1)\n', '', 'exec') bom = '\xef\xbb\xbf' compile(bom + 'print(1)\n', '', 'exec') + compile(source='pass', filename='?', mode='exec') + compile(dont_inherit=0, filename='tmp', source='0', mode='eval') + compile('pass', '?', dont_inherit=1, mode='exec') self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'badmode') self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'single', 0xff) self.assertRaises(TypeError, compile, chr(0), 'f', 'exec') + self.assertRaises(TypeError, compile, 'pass', '?', 'exec', + mode='eval', source='0', filename='tmp') if have_unicode: compile(unicode('print(u"\xc3\xa5")\n', 'utf8'), '', 'exec') self.assertRaises(TypeError, compile, unichr(0), 'f', 'exec') self.assertRaises(ValueError, compile, unicode('a = 1'), 'f', 'bad') + def test_delattr(self): import sys sys.spam = 1 @@ -1035,6 +1044,11 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(ValueError, int, '53', 40) self.assertRaises(TypeError, int, 1, 12) + # SF patch #1638879: embedded NULs were not detected with + # explicit base + self.assertRaises(ValueError, int, '123\0', 10) + self.assertRaises(ValueError, int, '123\x00 245', 20) + self.assertEqual(int('100000000000000000000000000000000', 2), 4294967296) self.assertEqual(int('102002022201221111211', 3), 4294967296) @@ -1138,10 +1152,10 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(int(Foo0()), 42) self.assertEqual(int(Foo1()), 42) - # XXX invokes __int__ now + # XXX invokes __int__ now # self.assertEqual(long(Foo2()), 42L) self.assertEqual(int(Foo3()), 0) - # XXX likewise + # XXX likewise # self.assertEqual(long(Foo4()), 42) # self.assertRaises(TypeError, long, Foo5()) diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py index 2295772..85dfa32 100644 --- a/Lib/test/test_cfgparser.py +++ b/Lib/test/test_cfgparser.py @@ -417,6 +417,18 @@ class SafeConfigParserTestCase(ConfigParserTestCase): self.assertEqual(cf.get("section", "ok"), "xxx/%s") self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s") + def test_set_malformatted_interpolation(self): + cf = self.fromstring("[sect]\n" + "option1=foo\n") + + self.assertEqual(cf.get('sect', "option1"), "foo") + + self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo") + self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%") + self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo") + + self.assertEqual(cf.get('sect', "option1"), "foo") + def test_set_nonstring_types(self): cf = self.fromstring("[sect]\n" "option1=foo\n") diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index fd3a6bf..e091bd6 100755 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -1,52 +1,196 @@ -#! /usr/bin/env python -""" Simple test script for cmathmodule.c - Roger E. Masse -""" +from test.test_support import run_unittest +import unittest import cmath, math -from test.test_support import verbose, verify, TestFailed - -verify(abs(cmath.log(10) - math.log(10)) < 1e-9) -verify(abs(cmath.log(10,2) - math.log(10,2)) < 1e-9) -try: - cmath.log('a') -except TypeError: - pass -else: - raise TestFailed - -try: - cmath.log(10, 'a') -except TypeError: - pass -else: - raise TestFailed - - -testdict = {'acos' : 1.0, - 'acosh' : 1.0, - 'asin' : 1.0, - 'asinh' : 1.0, - 'atan' : 0.2, - 'atanh' : 0.2, - 'cos' : 1.0, - 'cosh' : 1.0, - 'exp' : 1.0, - 'log' : 1.0, - 'log10' : 1.0, - 'sin' : 1.0, - 'sinh' : 1.0, - 'sqrt' : 1.0, - 'tan' : 1.0, - 'tanh' : 1.0} - -for func in testdict.keys(): - f = getattr(cmath, func) - r = f(testdict[func]) - if verbose: - print('Calling %s(%f) = %f' % (func, testdict[func], abs(r))) - -p = cmath.pi -e = cmath.e -if verbose: - print('PI = ', abs(p)) - print('E = ', abs(e)) + +class CMathTests(unittest.TestCase): + # list of all functions in cmath + test_functions = [getattr(cmath, fname) for fname in [ + 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', + 'cos', 'cosh', 'exp', 'log', 'log10', 'sin', 'sinh', + 'sqrt', 'tan', 'tanh']] + # test first and second arguments independently for 2-argument log + test_functions.append(lambda x : cmath.log(x, 1729. + 0j)) + test_functions.append(lambda x : cmath.log(14.-27j, x)) + + def cAssertAlmostEqual(self, a, b, rel_eps = 1e-10, abs_eps = 1e-100): + """Check that two complex numbers are almost equal.""" + # the two complex numbers are considered almost equal if + # either the relative error is <= rel_eps or the absolute error + # is tiny, <= abs_eps. + if a == b == 0: + return + absolute_error = abs(a-b) + relative_error = absolute_error/max(abs(a), abs(b)) + if relative_error > rel_eps and absolute_error > abs_eps: + self.fail("%s and %s are not almost equal" % (a, b)) + + def test_constants(self): + e_expected = 2.71828182845904523536 + pi_expected = 3.14159265358979323846 + self.assertAlmostEqual(cmath.pi, pi_expected, 9, + "cmath.pi is %s; should be %s" % (cmath.pi, pi_expected)) + self.assertAlmostEqual(cmath.e, e_expected, 9, + "cmath.e is %s; should be %s" % (cmath.e, e_expected)) + + def test_user_object(self): + # Test automatic calling of __complex__ and __float__ by cmath + # functions + + # some random values to use as test values; we avoid values + # for which any of the functions in cmath is undefined + # (i.e. 0., 1., -1., 1j, -1j) or would cause overflow + cx_arg = 4.419414439 + 1.497100113j + flt_arg = -6.131677725 + + # a variety of non-complex numbers, used to check that + # non-complex return values from __complex__ give an error + non_complexes = ["not complex", 1, 5, 2., None, + object(), NotImplemented] + + # Now we introduce a variety of classes whose instances might + # end up being passed to the cmath functions + + # usual case: new-style class implementing __complex__ + class MyComplex(object): + def __init__(self, value): + self.value = value + def __complex__(self): + return self.value + + # old-style class implementing __complex__ + class MyComplexOS: + def __init__(self, value): + self.value = value + def __complex__(self): + return self.value + + # classes for which __complex__ raises an exception + class SomeException(Exception): + pass + class MyComplexException(object): + def __complex__(self): + raise SomeException + class MyComplexExceptionOS: + def __complex__(self): + raise SomeException + + # some classes not providing __float__ or __complex__ + class NeitherComplexNorFloat(object): + pass + class NeitherComplexNorFloatOS: + pass + class MyInt(object): + def __int__(self): return 2 + def __long__(self): return 2 + def __index__(self): return 2 + class MyIntOS: + def __int__(self): return 2 + def __long__(self): return 2 + def __index__(self): return 2 + + # other possible combinations of __float__ and __complex__ + # that should work + class FloatAndComplex(object): + def __float__(self): + return flt_arg + def __complex__(self): + return cx_arg + class FloatAndComplexOS: + def __float__(self): + return flt_arg + def __complex__(self): + return cx_arg + class JustFloat(object): + def __float__(self): + return flt_arg + class JustFloatOS: + def __float__(self): + return flt_arg + + for f in self.test_functions: + # usual usage + self.cAssertAlmostEqual(f(MyComplex(cx_arg)), f(cx_arg)) + self.cAssertAlmostEqual(f(MyComplexOS(cx_arg)), f(cx_arg)) + # other combinations of __float__ and __complex__ + self.cAssertAlmostEqual(f(FloatAndComplex()), f(cx_arg)) + self.cAssertAlmostEqual(f(FloatAndComplexOS()), f(cx_arg)) + self.cAssertAlmostEqual(f(JustFloat()), f(flt_arg)) + self.cAssertAlmostEqual(f(JustFloatOS()), f(flt_arg)) + # TypeError should be raised for classes not providing + # either __complex__ or __float__, even if they provide + # __int__, __long__ or __index__. An old-style class + # currently raises AttributeError instead of a TypeError; + # this could be considered a bug. + self.assertRaises(TypeError, f, NeitherComplexNorFloat()) + self.assertRaises(TypeError, f, MyInt()) + self.assertRaises(Exception, f, NeitherComplexNorFloatOS()) + self.assertRaises(Exception, f, MyIntOS()) + # non-complex return value from __complex__ -> TypeError + for bad_complex in non_complexes: + self.assertRaises(TypeError, f, MyComplex(bad_complex)) + self.assertRaises(TypeError, f, MyComplexOS(bad_complex)) + # exceptions in __complex__ should be propagated correctly + self.assertRaises(SomeException, f, MyComplexException()) + self.assertRaises(SomeException, f, MyComplexExceptionOS()) + + def test_input_type(self): + # ints and longs should be acceptable inputs to all cmath + # functions, by virtue of providing a __float__ method + for f in self.test_functions: + for arg in [2, 2.]: + self.cAssertAlmostEqual(f(arg), f(arg.__float__())) + + # but strings should give a TypeError + for f in self.test_functions: + for arg in ["a", "long_string", "0", "1j", ""]: + self.assertRaises(TypeError, f, arg) + + def test_cmath_matches_math(self): + # check that corresponding cmath and math functions are equal + # for floats in the appropriate range + + # test_values in (0, 1) + test_values = [0.01, 0.1, 0.2, 0.5, 0.9, 0.99] + + # test_values for functions defined on [-1., 1.] + unit_interval = test_values + [-x for x in test_values] + \ + [0., 1., -1.] + + # test_values for log, log10, sqrt + positive = test_values + [1.] + [1./x for x in test_values] + nonnegative = [0.] + positive + + # test_values for functions defined on the whole real line + real_line = [0.] + positive + [-x for x in positive] + + test_functions = { + 'acos' : unit_interval, + 'asin' : unit_interval, + 'atan' : real_line, + 'cos' : real_line, + 'cosh' : real_line, + 'exp' : real_line, + 'log' : positive, + 'log10' : positive, + 'sin' : real_line, + 'sinh' : real_line, + 'sqrt' : nonnegative, + 'tan' : real_line, + 'tanh' : real_line} + + for fn, values in test_functions.items(): + float_fn = getattr(math, fn) + complex_fn = getattr(cmath, fn) + for v in values: + self.cAssertAlmostEqual(float_fn(v), complex_fn(v)) + + # test two-argument version of log with various bases + for base in [0.5, 2., 10.]: + for v in positive: + self.cAssertAlmostEqual(cmath.log(v, base), math.log(v, base)) + +def test_main(): + run_unittest(CMathTests) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 5e89863..cacae7a 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -6,7 +6,7 @@ import subprocess class CmdLineTest(unittest.TestCase): def start_python(self, cmd_line): - outfp, infp = popen2.popen4('%s %s' % (sys.executable, cmd_line)) + outfp, infp = popen2.popen4('"%s" %s' % (sys.executable, cmd_line)) infp.close() data = outfp.read() outfp.close() diff --git a/Lib/test/test_codecencodings_cn.py b/Lib/test/test_codecencodings_cn.py index c558f1b..96b0d77 100644 --- a/Lib/test/test_codecencodings_cn.py +++ b/Lib/test/test_codecencodings_cn.py @@ -51,11 +51,7 @@ class Test_GB18030(test_multibytecodec_support.TestBase, unittest.TestCase): has_iso10646 = True def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(Test_GB2312)) - suite.addTest(unittest.makeSuite(Test_GBK)) - suite.addTest(unittest.makeSuite(Test_GB18030)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecencodings_hk.py b/Lib/test/test_codecencodings_hk.py index 1cd020f..b1c2606 100644 --- a/Lib/test/test_codecencodings_hk.py +++ b/Lib/test/test_codecencodings_hk.py @@ -21,9 +21,7 @@ class Test_Big5HKSCS(test_multibytecodec_support.TestBase, unittest.TestCase): ) def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(Test_Big5HKSCS)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecencodings_jp.py b/Lib/test/test_codecencodings_jp.py index 558598a..5f81f41 100644 --- a/Lib/test/test_codecencodings_jp.py +++ b/Lib/test/test_codecencodings_jp.py @@ -99,13 +99,7 @@ class Test_SJISX0213(test_multibytecodec_support.TestBase, unittest.TestCase): ) def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(Test_CP932)) - suite.addTest(unittest.makeSuite(Test_EUC_JISX0213)) - suite.addTest(unittest.makeSuite(Test_EUC_JP_COMPAT)) - suite.addTest(unittest.makeSuite(Test_SJIS_COMPAT)) - suite.addTest(unittest.makeSuite(Test_SJISX0213)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecencodings_kr.py b/Lib/test/test_codecencodings_kr.py index 8139f76..a30eaf9 100644 --- a/Lib/test/test_codecencodings_kr.py +++ b/Lib/test/test_codecencodings_kr.py @@ -45,11 +45,7 @@ class Test_JOHAB(test_multibytecodec_support.TestBase, unittest.TestCase): ) def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(Test_CP949)) - suite.addTest(unittest.makeSuite(Test_EUCKR)) - suite.addTest(unittest.makeSuite(Test_JOHAB)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecencodings_tw.py b/Lib/test/test_codecencodings_tw.py index 7c59478..983d06f 100644 --- a/Lib/test/test_codecencodings_tw.py +++ b/Lib/test/test_codecencodings_tw.py @@ -21,9 +21,7 @@ class Test_Big5(test_multibytecodec_support.TestBase, unittest.TestCase): ) def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(Test_Big5)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecmaps_cn.py b/Lib/test/test_codecmaps_cn.py index 8cbee76..75541ac 100644 --- a/Lib/test/test_codecmaps_cn.py +++ b/Lib/test/test_codecmaps_cn.py @@ -20,10 +20,7 @@ class TestGBKMap(test_multibytecodec_support.TestBase_Mapping, 'MICSFT/WINDOWS/CP936.TXT' def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestGB2312Map)) - suite.addTest(unittest.makeSuite(TestGBKMap)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py index e7f7b96..1068d0b 100644 --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -14,9 +14,7 @@ class TestBig5HKSCSMap(test_multibytecodec_support.TestBase_Mapping, mapfileurl = 'http://people.freebsd.org/~perky/i18n/BIG5HKSCS.TXT' def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestBig5HKSCSMap)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecmaps_jp.py b/Lib/test/test_codecmaps_jp.py index 08052d4..5466a98 100644 --- a/Lib/test/test_codecmaps_jp.py +++ b/Lib/test/test_codecmaps_jp.py @@ -61,13 +61,7 @@ class TestSJISX0213Map(test_multibytecodec_support.TestBase_Mapping, def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestCP932Map)) - suite.addTest(unittest.makeSuite(TestEUCJPCOMPATMap)) - suite.addTest(unittest.makeSuite(TestSJISCOMPATMap)) - suite.addTest(unittest.makeSuite(TestEUCJISX0213Map)) - suite.addTest(unittest.makeSuite(TestSJISX0213Map)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecmaps_kr.py b/Lib/test/test_codecmaps_kr.py index 7484a66..1b350b9 100644 --- a/Lib/test/test_codecmaps_kr.py +++ b/Lib/test/test_codecmaps_kr.py @@ -34,11 +34,7 @@ class TestJOHABMap(test_multibytecodec_support.TestBase_Mapping, pass_dectest = [('\\', u'\u20a9')] def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestCP949Map)) - suite.addTest(unittest.makeSuite(TestEUCKRMap)) - suite.addTest(unittest.makeSuite(TestJOHABMap)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_codecmaps_tw.py b/Lib/test/test_codecmaps_tw.py index 0b195f4..143ae23 100644 --- a/Lib/test/test_codecmaps_tw.py +++ b/Lib/test/test_codecmaps_tw.py @@ -25,10 +25,7 @@ class TestCP950Map(test_multibytecodec_support.TestBase_Mapping, ] def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestBIG5Map)) - suite.addTest(unittest.makeSuite(TestCP950Map)) - test_support.run_suite(suite) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py new file mode 100644 index 0000000..a139129 --- /dev/null +++ b/Lib/test/test_collections.py @@ -0,0 +1,57 @@ +import unittest +from test import test_support +from collections import NamedTuple + +class TestNamedTuple(unittest.TestCase): + + def test_factory(self): + Point = NamedTuple('Point', 'x y') + self.assertEqual(Point.__name__, 'Point') + self.assertEqual(Point.__doc__, 'Point(x, y)') + self.assertEqual(Point.__slots__, ()) + self.assertEqual(Point.__module__, __name__) + self.assertEqual(Point.__getitem__, tuple.__getitem__) + self.assert_('__getitem__' in Point.__dict__) # superclass methods localized + + def test_instance(self): + Point = NamedTuple('Point', 'x y') + p = Point(11, 22) + self.assertEqual(p, Point(x=11, y=22)) + self.assertEqual(p, Point(11, y=22)) + self.assertEqual(p, Point(y=22, x=11)) + self.assertEqual(p, Point(*(11, 22))) + self.assertEqual(p, Point(**dict(x=11, y=22))) + self.assertRaises(TypeError, Point, 1) # too few args + self.assertRaises(TypeError, Point, 1, 2, 3) # too many args + self.assertRaises(TypeError, eval, 'Point(XXX=1, y=2)', locals()) # wrong keyword argument + self.assertRaises(TypeError, eval, 'Point(x=1)', locals()) # missing keyword argument + self.assertEqual(repr(p), 'Point(x=11, y=22)') + self.assert_('__dict__' not in dir(p)) # verify instance has no dict + self.assert_('__weakref__' not in dir(p)) + + def test_tupleness(self): + Point = NamedTuple('Point', 'x y') + p = Point(11, 22) + + self.assert_(isinstance(p, tuple)) + self.assertEqual(p, (11, 22)) # matches a real tuple + self.assertEqual(tuple(p), (11, 22)) # coercable to a real tuple + self.assertEqual(list(p), [11, 22]) # coercable to a list + self.assertEqual(max(p), 22) # iterable + self.assertEqual(max(*p), 22) # star-able + x, y = p + self.assertEqual(p, (x, y)) # unpacks like a tuple + self.assertEqual((p[0], p[1]), (11, 22)) # indexable like a tuple + self.assertRaises(IndexError, p.__getitem__, 3) + + self.assertEqual(p.x, x) + self.assertEqual(p.y, y) + self.assertRaises(AttributeError, eval, 'p.z', locals()) + + +def test_main(verbose=None): + test_classes = [TestNamedTuple] + test_support.run_unittest(*test_classes) + +if __name__ == "__main__": + test_main(verbose=True) diff --git a/Lib/test/test_commands.py b/Lib/test/test_commands.py index b72a1b9..d899d66 100644 --- a/Lib/test/test_commands.py +++ b/Lib/test/test_commands.py @@ -4,6 +4,10 @@ ''' import unittest import os, tempfile, re +import warnings + +warnings.filterwarnings('ignore', r".*commands.getstatus.. is deprecated", + DeprecationWarning) from test.test_support import TestSkipped, run_unittest, reap_children from commands import * diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index d5fda13..ae55485 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -399,13 +399,26 @@ if 1: # is the max. Ensure the result of too many annotations is a # SyntaxError. s = "def f((%s)): pass" - s %= ', '.join('a%d:%d' % (i,i) for i in xrange(65535)) + s %= ', '.join('a%d:%d' % (i,i) for i in xrange(65535)) self.assertRaises(SyntaxError, compile, s, '?', 'exec') # Test that the max # of annotations compiles. s = "def f((%s)): pass" s %= ', '.join('a%d:%d' % (i,i) for i in xrange(65534)) compile(s, '?', 'exec') - + + def test_mangling(self): + class A: + def f(): + __mangled = 1 + __not_mangled__ = 2 + import __mangled_mod + import __package__.module + + self.assert_("_A__mangled" in A.f.__code__.co_varnames) + self.assert_("__not_mangled__" in A.f.__code__.co_varnames) + self.assert_("_A__mangled_mod" in A.f.__code__.co_varnames) + self.assert_("__package__" in A.f.__code__.co_varnames) + def test_main(): test_support.run_unittest(TestSpecifics) diff --git a/Lib/test/test_compiler.py b/Lib/test/test_compiler.py index 4fb6cc1..c55dc0e 100644 --- a/Lib/test/test_compiler.py +++ b/Lib/test/test_compiler.py @@ -190,7 +190,7 @@ class CompilerTest(unittest.TestCase): def testBytesLiteral(self): c = compiler.compile("b'foo'", '<string>', 'eval') b = eval(c) - + c = compiler.compile('def f(b=b"foo"):\n' ' b[0] += 1\n' ' return b\n' @@ -200,7 +200,7 @@ class CompilerTest(unittest.TestCase): dct = {} exec(c, dct) self.assertEquals(dct.get('result'), b"ioo") - + c = compiler.compile('def f():\n' ' b = b"foo"\n' ' b[0] += 1\n' diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 91f074b..0d034f5 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -208,6 +208,8 @@ class ComplexTest(unittest.TestCase): self.assertAlmostEqual(complex(), 0) self.assertAlmostEqual(complex("-1"), -1) self.assertAlmostEqual(complex("+1"), +1) + self.assertAlmostEqual(complex("(1+2j)"), 1+2j) + self.assertAlmostEqual(complex("(1.3+2.2j)"), 1.3+2.2j) class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -237,12 +239,17 @@ class ComplexTest(unittest.TestCase): self.assertRaises(ValueError, complex, "") self.assertRaises(TypeError, complex, None) self.assertRaises(ValueError, complex, "\0") + self.assertRaises(ValueError, complex, "3\09") self.assertRaises(TypeError, complex, "1", "2") self.assertRaises(TypeError, complex, "1", 42) self.assertRaises(TypeError, complex, 1, "2") self.assertRaises(ValueError, complex, "1+") self.assertRaises(ValueError, complex, "1+1j+1j") self.assertRaises(ValueError, complex, "--") + self.assertRaises(ValueError, complex, "(1+2j") + self.assertRaises(ValueError, complex, "1+2j)") + self.assertRaises(ValueError, complex, "1+(2j)") + self.assertRaises(ValueError, complex, "(1+2j)123") if test_support.have_unicode: self.assertRaises(ValueError, complex, unicode("1"*500)) self.assertRaises(ValueError, complex, unicode("x")) @@ -305,6 +312,11 @@ class ComplexTest(unittest.TestCase): self.assertNotEqual(repr(-(1+0j)), '(-1+-0j)') + self.assertEqual(1-6j,complex(repr(1-6j))) + self.assertEqual(1+6j,complex(repr(1+6j))) + self.assertEqual(-6j,complex(repr(-6j))) + self.assertEqual(6j,complex(repr(6j))) + def test_neg(self): self.assertEqual(-(1+6j), -1-6j) diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 9142428..a3a9a5b 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -9,7 +9,7 @@ import tempfile import unittest import threading from contextlib import * # Tests __all__ -from test.test_support import run_suite +from test import test_support class ContextManagerTestCase(unittest.TestCase): @@ -332,9 +332,7 @@ class LockContextTestCase(unittest.TestCase): # This is needed to make the test actually run under regrtest.py! def test_main(): - run_suite( - unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]) - ) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_crypt.py b/Lib/test/test_crypt.py index 7336711..a9c28cd 100755 --- a/Lib/test/test_crypt.py +++ b/Lib/test/test_crypt.py @@ -3,7 +3,7 @@ Roger E. Masse """ -from test.test_support import verify, verbose +from test.test_support import verbose import crypt c = crypt.crypt('mypassword', 'ab') diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 980f3fc..0ca6b78 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -484,12 +484,16 @@ class TestDialectExcel(TestCsvBase): self.readerAssertEqual('a"b"c', [['a"b"c']]) def test_quotes_and_more(self): + # Excel would never write a field containing '"a"b', but when + # reading one, it will return 'ab'. self.readerAssertEqual('"a"b', [['ab']]) def test_lone_quote(self): self.readerAssertEqual('a"b', [['a"b']]) def test_quote_and_quote(self): + # Excel would never write a field containing '"a" "b"', but when + # reading one, it will return 'a "b"'. self.readerAssertEqual('"a" "b"', [['a "b"']]) def test_space_and_quote(self): diff --git a/Lib/test/test_ctypes.py b/Lib/test/test_ctypes.py index fd2032b..7a81ab4 100644 --- a/Lib/test/test_ctypes.py +++ b/Lib/test/test_ctypes.py @@ -1,12 +1,12 @@ import unittest -from test.test_support import run_suite +from test.test_support import run_unittest import ctypes.test def test_main(): skipped, testcases = ctypes.test.get_tests(ctypes.test, "test_*.py", verbosity=0) suites = [unittest.makeSuite(t) for t in testcases] - run_suite(unittest.TestSuite(suites)) + run_unittest(unittest.TestSuite(suites)) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index fd8e6ed..ee679e7 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -129,6 +129,12 @@ def window_funcs(stdscr): stdscr.touchline(5,5,0) stdscr.vline('a', 3) stdscr.vline('a', 3, curses.A_STANDOUT) + stdscr.chgat(5, 2, 3, curses.A_BLINK) + stdscr.chgat(3, curses.A_BOLD) + stdscr.chgat(5, 8, curses.A_UNDERLINE) + stdscr.chgat(curses.A_BLINK) + stdscr.refresh() + stdscr.vline(1,1, 'a', 3) stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT) @@ -241,12 +247,21 @@ def test_userptr_without_set(stdscr): except curses.panel.error: pass +def test_resize_term(stdscr): + if hasattr(curses, 'resizeterm'): + lines, cols = curses.LINES, curses.COLS + curses.resizeterm(lines - 1, cols + 1) + + if curses.LINES != lines - 1 or curses.COLS != cols + 1: + raise RuntimeError, "Expected resizeterm to update LINES and COLS" + def main(stdscr): curses.savetty() try: module_funcs(stdscr) window_funcs(stdscr) test_userptr_without_set(stdscr) + test_resize_term(stdscr) finally: curses.resetty() diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 8a7c1b6..287585e 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -3,6 +3,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +import os import sys import pickle import unittest @@ -135,7 +136,7 @@ class TestTZInfo(unittest.TestCase): # Base clase for testing a particular aspect of timedelta, time, date and # datetime comparisons. -class HarmlessMixedComparison(unittest.TestCase): +class HarmlessMixedComparison: # Test that __eq__ and __ne__ don't complain for mixed-type comparisons. # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a @@ -174,7 +175,7 @@ class HarmlessMixedComparison(unittest.TestCase): ############################################################################# # timedelta tests -class TestTimeDelta(HarmlessMixedComparison): +class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): theclass = timedelta @@ -521,7 +522,7 @@ class TestDateOnly(unittest.TestCase): class SubclassDate(date): sub_var = 1 -class TestDate(HarmlessMixedComparison): +class TestDate(HarmlessMixedComparison, unittest.TestCase): # Tests here should pass for both dates and datetimes, except for a # few tests that TestDateTime overrides. @@ -1452,6 +1453,21 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, self.theclass.utcfromtimestamp, insane) + def test_negative_float_fromtimestamp(self): + # Windows doesn't accept negative timestamps + if os.name == "nt": + return + # The result is tz-dependent; at least test that this doesn't + # fail (like it did before bug 1646728 was fixed). + self.theclass.fromtimestamp(-1.05) + + def test_negative_float_utcfromtimestamp(self): + # Windows doesn't accept negative timestamps + if os.name == "nt": + return + d = self.theclass.utcfromtimestamp(-1.05) + self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) + def test_utcnow(self): import time @@ -1607,7 +1623,7 @@ class TestDateTime(TestDate): class SubclassTime(time): sub_var = 1 -class TestTime(HarmlessMixedComparison): +class TestTime(HarmlessMixedComparison, unittest.TestCase): theclass = time @@ -1890,7 +1906,7 @@ class TestTime(HarmlessMixedComparison): # A mixin for classes with a tzinfo= argument. Subclasses must define # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever) # must be legit (which is true for time and datetime). -class TZInfoBase(unittest.TestCase): +class TZInfoBase: def test_argument_passing(self): cls = self.theclass @@ -2050,7 +2066,7 @@ class TZInfoBase(unittest.TestCase): # Testing time objects with a non-None tzinfo. -class TestTimeTZ(TestTime, TZInfoBase): +class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): theclass = time def test_empty(self): @@ -2298,7 +2314,7 @@ class TestTimeTZ(TestTime, TZInfoBase): # Testing datetime objects with a non-None tzinfo. -class TestDateTimeTZ(TestDateTime, TZInfoBase): +class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase): theclass = datetime def test_trivial(self): @@ -3259,45 +3275,8 @@ class Oddballs(unittest.TestCase): self.assertEqual(as_datetime, datetime_sc) self.assertEqual(datetime_sc, as_datetime) -def test_suite(): - allsuites = [unittest.makeSuite(klass, 'test') - for klass in (TestModule, - TestTZInfo, - TestTimeDelta, - TestDateOnly, - TestDate, - TestDateTime, - TestTime, - TestTimeTZ, - TestDateTimeTZ, - TestTimezoneConversions, - Oddballs, - ) - ] - return unittest.TestSuite(allsuites) - def test_main(): - import gc - import sys - - thesuite = test_suite() - lastrc = None - while True: - test_support.run_suite(thesuite) - if 1: # change to 0, under a debug build, for some leak detection - break - gc.collect() - if gc.garbage: - raise SystemError("gc.garbage not empty after test run: %r" % - gc.garbage) - if hasattr(sys, 'gettotalrefcount'): - thisrc = sys.gettotalrefcount() - print('*' * 10, 'total refs:', thisrc, end=' ', file=sys.stderr) - if lastrc: - print('delta:', thisrc - lastrc, file=sys.stderr) - else: - print(file=sys.stderr) - lastrc = thisrc + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index c8ae2be..a8dfdb1 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -132,6 +132,15 @@ class TestDefaultDict(unittest.TestCase): self.assertEqual(d2.default_factory, list) self.assertEqual(d2, d1) + def test_keyerror_without_factory(self): + d1 = defaultdict() + try: + d1[(1,)] + except KeyError as err: + self.assertEqual(err.message, (1,)) + else: + self.fail("expected KeyError") + def test_main(): test_support.run_unittest(TestDefaultDict) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 7df31b7..4f7e60c 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -575,7 +575,7 @@ deque(['a', 'b', 'd', 'e', 'f']) >>> for value in roundrobin('abc', 'd', 'efgh'): ... print(value) -... +... a d e diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index eec4e40..aba1c74 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -773,6 +773,22 @@ def metaclass(): except TypeError: pass else: raise TestFailed, "calling object w/o call method should raise TypeError" + # Testing code to find most derived baseclass + class A(type): + def __new__(*args, **kwargs): + return type.__new__(*args, **kwargs) + + class B(object): + pass + + class C(object, metaclass=A): + pass + + # The most derived metaclass of D is A rather than type. + class D(B, C): + pass + + def pymods(): if verbose: print("Testing Python subclass of module...") log = [] @@ -1072,6 +1088,45 @@ def slots(): raise TestFailed, "[''] slots not caught" class C(object): __slots__ = ["a", "a_b", "_a", "A0123456789Z"] + # XXX(nnorwitz): was there supposed to be something tested + # from the class above? + + # Test a single string is not expanded as a sequence. + class C(object): + __slots__ = "abc" + c = C() + c.abc = 5 + vereq(c.abc, 5) + + # Test unicode slot names + try: + unicode + except NameError: + pass + else: + # Test a single unicode string is not expanded as a sequence. + class C(object): + __slots__ = unicode("abc") + c = C() + c.abc = 5 + vereq(c.abc, 5) + + # _unicode_to_string used to modify slots in certain circumstances + slots = (unicode("foo"), unicode("bar")) + class C(object): + __slots__ = slots + x = C() + x.foo = 5 + vereq(x.foo, 5) + veris(type(slots[0]), unicode) + # this used to leak references + try: + class C(object): + __slots__ = [unichr(128)] + except (TypeError, UnicodeEncodeError): + pass + else: + raise TestFailed, "[unichr(128)] slots not caught" # Test leaks class Counted(object): @@ -1318,6 +1373,22 @@ def errors(): else: verify(0, "__slots__ = [1] should be illegal") + class M1(type): + pass + class M2(type): + pass + class A1(object, metaclass=M1): + pass + class A2(object, metaclass=M2): + pass + try: + class B(A1, A2): + pass + except TypeError: + pass + else: + verify(0, "finding the most derived metaclass should have failed") + def classmethods(): if verbose: print("Testing class methods...") class C(object): @@ -2092,7 +2163,6 @@ def inherits(): __slots__ = ['prec'] def __init__(self, value=0.0, prec=12): self.prec = int(prec) - float.__init__(self, value) def __repr__(self): return "%.*g" % (self.prec, self) vereq(repr(precfloat(1.1)), "1.1") @@ -2644,6 +2714,51 @@ def setclass(): cant(o, type(1)) cant(o, type(None)) del o + class G(object): + __slots__ = ["a", "b"] + class H(object): + __slots__ = ["b", "a"] + try: + unicode + except NameError: + class I(object): + __slots__ = ["a", "b"] + else: + class I(object): + __slots__ = [unicode("a"), unicode("b")] + class J(object): + __slots__ = ["c", "b"] + class K(object): + __slots__ = ["a", "b", "d"] + class L(H): + __slots__ = ["e"] + class M(I): + __slots__ = ["e"] + class N(J): + __slots__ = ["__weakref__"] + class P(J): + __slots__ = ["__dict__"] + class Q(J): + pass + class R(J): + __slots__ = ["__dict__", "__weakref__"] + + for cls, cls2 in ((G, H), (G, I), (I, H), (Q, R), (R, Q)): + x = cls() + x.a = 1 + x.__class__ = cls2 + verify(x.__class__ is cls2, + "assigning %r as __class__ for %r silently failed" % (cls2, x)) + vereq(x.a, 1) + x.__class__ = cls + verify(x.__class__ is cls, + "assigning %r as __class__ for %r silently failed" % (cls, x)) + vereq(x.a, 1) + for cls in G, J, K, L, M, N, P, R, list, Int: + for cls2 in G, J, K, L, M, N, P, R, list, Int: + if cls is cls2: + continue + cant(cls(), cls2) def setdict(): if verbose: print("Testing __dict__ assignment...") @@ -3999,6 +4114,19 @@ def notimplemented(): check(iexpr, c, N1) check(iexpr, c, N2) +def test_assign_slice(): + # ceval.c's assign_slice used to check for + # tp->tp_as_sequence->sq_slice instead of + # tp->tp_as_sequence->sq_ass_slice + + class C(object): + def __setslice__(self, start, stop, value): + self.value = value + + c = C() + c[1:2] = 3 + vereq(c.value, 3) + def test_main(): weakref_segfault() # Must be first, somehow wrapper_segfault() @@ -4094,6 +4222,7 @@ def test_main(): test_init() methodwrapper() notimplemented() + test_assign_slice() if verbose: print("All OK") diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 001aa49..5b11666 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -243,7 +243,7 @@ methods. Static methods are easy to describe: they behave pretty much like static methods in C++ or Java. Here's an example: >>> class C: - ... + ... ... @staticmethod ... def foo(x, y): ... print("staticmethod", x, y) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index d8dfd7e..d98c607 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -425,7 +425,7 @@ class DictTest(unittest.TestCase): except RuntimeError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("e[42] didn't raise RuntimeError") + self.fail("e[42] didn't raise RuntimeError") class F(dict): def __init__(self): # An instance variable __missing__ should have no effect @@ -436,7 +436,7 @@ class DictTest(unittest.TestCase): except KeyError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("f[42] didn't raise KeyError") + self.fail("f[42] didn't raise KeyError") class G(dict): pass g = G() @@ -445,7 +445,7 @@ class DictTest(unittest.TestCase): except KeyError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("g[42] didn't raise KeyError") + self.fail("g[42] didn't raise KeyError") def test_tuple_keyerror(self): # SF #1576657 @@ -457,6 +457,76 @@ class DictTest(unittest.TestCase): else: self.fail("missing KeyError") + def test_bad_key(self): + # Dictionary lookups should fail if __cmp__() raises an exception. + class CustomException(Exception): + pass + + class BadDictKey: + def __hash__(self): + return hash(self.__class__) + + def __eq__(self, other): + if isinstance(other, self.__class__): + raise CustomException + return other + + d = {} + x1 = BadDictKey() + x2 = BadDictKey() + d[x1] = 1 + for stmt in ['d[x2] = 2', + 'z = d[x2]', + 'x2 in d', + 'd.get(x2)', + 'd.setdefault(x2, 42)', + 'd.pop(x2)', + 'd.update({x2: 2})']: + try: + exec(stmt, locals()) + except CustomException: + pass + else: + self.fail("Statement %r didn't raise exception" % stmt) + + def test_resize1(self): + # Dict resizing bug, found by Jack Jansen in 2.2 CVS development. + # This version got an assert failure in debug build, infinite loop in + # release build. Unfortunately, provoking this kind of stuff requires + # a mix of inserts and deletes hitting exactly the right hash codes in + # exactly the right order, and I can't think of a randomized approach + # that would be *likely* to hit a failing case in reasonable time. + + d = {} + for i in range(5): + d[i] = i + for i in range(5): + del d[i] + for i in range(5, 9): # i==8 was the problem + d[i] = i + + def test_resize2(self): + # Another dict resizing bug (SF bug #1456209). + # This caused Segmentation faults or Illegal instructions. + + 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 + from test import mapping_tests diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 8e97a6a..5d82cd7 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1,11 +1,11 @@ -from test.test_support import verify, verbose, TestFailed, run_unittest +# Minimal tests for dis module + +from test.test_support import verbose, run_unittest +import unittest import sys import dis import StringIO -# Minimal tests for dis module - -import unittest def _f(a): print(a) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 087b340..60079a6 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -35,7 +35,7 @@ class SampleClass: >>> for i in range(10): ... sc = sc.double() ... print(sc.get(), end=' ') - 6 12 24 48 96 192 384 768 1536 3072 + 6 12 24 48 96 192 384 768 1536 3072 """ def __init__(self, val): """ @@ -571,7 +571,7 @@ DocTestFinder finds the line number of each example: ... ... >>> for x in range(10): ... ... print(x, end=' ') - ... 0 1 2 3 4 5 6 7 8 9 + ... 0 1 2 3 4 5 6 7 8 9 ... >>> x//2 ... 6 ... ''' @@ -1461,11 +1461,11 @@ at the end of any line: >>> def f(x): r''' ... >>> for x in range(10): # doctest: +ELLIPSIS ... ... print(x, end=' ') - ... 0 1 2 ... 9 + ... 0 1 2 ... 9 ... ... >>> for x in range(10): ... ... print(x, end=' ') # doctest: +ELLIPSIS - ... 0 1 2 ... 9 + ... 0 1 2 ... 9 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) @@ -1478,7 +1478,7 @@ option directive, then they are combined: ... Should fail (option directive not on the last line): ... >>> for x in range(10): # doctest: +ELLIPSIS ... ... print(x, end=' ') # doctest: +NORMALIZE_WHITESPACE - ... 0 1 2...9 + ... 0 1 2...9 ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) diff --git a/Lib/test/test_email.py b/Lib/test/test_email.py index de0eee3..f609968 100644 --- a/Lib/test/test_email.py +++ b/Lib/test/test_email.py @@ -4,10 +4,10 @@ import unittest # The specific tests now live in Lib/email/test from email.test.test_email import suite -from test.test_support import run_suite +from test import test_support def test_main(): - run_suite(suite()) + test_support.run_unittest(suite()) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_email_codecs.py b/Lib/test/test_email_codecs.py index c550a6f..8951f81 100644 --- a/Lib/test/test_email_codecs.py +++ b/Lib/test/test_email_codecs.py @@ -9,7 +9,7 @@ from test import test_support def test_main(): suite = test_email_codecs.suite() suite.addTest(test_email_codecs_renamed.suite()) - test_support.run_suite(suite) + test_support.run_unittest(suite) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_email_renamed.py b/Lib/test/test_email_renamed.py index c3af598..163e791 100644 --- a/Lib/test/test_email_renamed.py +++ b/Lib/test/test_email_renamed.py @@ -4,10 +4,10 @@ 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 +from test import test_support def test_main(): - run_suite(suite()) + test_support.run_unittest(suite()) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index c0a5094..5a22297 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -229,6 +229,9 @@ class ExceptionTests(unittest.TestCase): (EnvironmentError, (1, 'strErrorStr', 'filenameStr'), {'message' : '', 'args' : (1, 'strErrorStr'), 'errno' : 1, 'strerror' : 'strErrorStr', 'filename' : 'filenameStr'}), + (SyntaxError, (), {'message' : '', 'msg' : None, 'text' : None, + 'filename' : None, 'lineno' : None, 'offset' : None, + 'print_file_and_line' : None}), (SyntaxError, ('msgStr',), {'message' : 'msgStr', 'args' : ('msgStr',), 'text' : None, 'print_file_and_line' : None, 'msg' : 'msgStr', @@ -337,7 +340,7 @@ class ExceptionTests(unittest.TestCase): def testExceptionCleanup(self): # Make sure "except V as N" exceptions are cleaned up properly - + try: raise Exception() except Exception as e: diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py index 17ca944..10d3cfc 100644 --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -3,7 +3,9 @@ Tests for fileinput module. Nick Mathewson ''' -from test.test_support import verify, verbose, TESTFN, TestFailed +import unittest +from test.test_support import verbose, TESTFN, run_unittest +from test.test_support import unlink as safe_unlink import sys, os, re from StringIO import StringIO from fileinput import FileInput, hook_encoded @@ -18,211 +20,206 @@ from fileinput import FileInput, hook_encoded def writeTmp(i, lines, mode='w'): # opening in text mode is the default name = TESTFN + str(i) f = open(name, mode) - for line in lines: - f.write(line) + f.writelines(lines) f.close() return name -pat = re.compile(r'LINE (\d+) OF FILE (\d+)') - def remove_tempfiles(*names): for name in names: - try: - os.unlink(name) - except: - pass - -def runTests(t1, t2, t3, t4, bs=0, round=0): - start = 1 + round*6 - if verbose: - print('%s. Simple iteration (bs=%s)' % (start+0, bs)) - fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) - lines = list(fi) - fi.close() - verify(len(lines) == 31) - verify(lines[4] == 'Line 5 of file 1\n') - verify(lines[30] == 'Line 1 of file 4\n') - verify(fi.lineno() == 31) - verify(fi.filename() == t4) - - if verbose: - print('%s. Status variables (bs=%s)' % (start+1, bs)) - fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) - s = "x" - while s and s != 'Line 6 of file 2\n': - s = fi.readline() - verify(fi.filename() == t2) - verify(fi.lineno() == 21) - verify(fi.filelineno() == 6) - verify(not fi.isfirstline()) - verify(not fi.isstdin()) - - if verbose: - print('%s. Nextfile (bs=%s)' % (start+2, bs)) - fi.nextfile() - verify(fi.readline() == 'Line 1 of file 3\n') - verify(fi.lineno() == 22) - fi.close() - - if verbose: - print('%s. Stdin (bs=%s)' % (start+3, bs)) - fi = FileInput(files=(t1, t2, t3, t4, '-'), bufsize=bs) - savestdin = sys.stdin - try: - sys.stdin = StringIO("Line 1 of stdin\nLine 2 of stdin\n") + safe_unlink(name) + +class BufferSizesTests(unittest.TestCase): + def test_buffer_sizes(self): + # First, run the tests with default and teeny buffer size. + for round, bs in (0, 0), (1, 30): + try: + t1 = writeTmp(1, ["Line %s of file 1\n" % (i+1) for i in range(15)]) + t2 = writeTmp(2, ["Line %s of file 2\n" % (i+1) for i in range(10)]) + t3 = writeTmp(3, ["Line %s of file 3\n" % (i+1) for i in range(5)]) + t4 = writeTmp(4, ["Line %s of file 4\n" % (i+1) for i in range(1)]) + self.buffer_size_test(t1, t2, t3, t4, bs, round) + finally: + remove_tempfiles(t1, t2, t3, t4) + + def buffer_size_test(self, t1, t2, t3, t4, bs=0, round=0): + pat = re.compile(r'LINE (\d+) OF FILE (\d+)') + + start = 1 + round*6 + if verbose: + print('%s. Simple iteration (bs=%s)' % (start+0, bs)) + fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) lines = list(fi) - verify(len(lines) == 33) - verify(lines[32] == 'Line 2 of stdin\n') - verify(fi.filename() == '<stdin>') + fi.close() + self.assertEqual(len(lines), 31) + self.assertEqual(lines[4], 'Line 5 of file 1\n') + self.assertEqual(lines[30], 'Line 1 of file 4\n') + self.assertEqual(fi.lineno(), 31) + self.assertEqual(fi.filename(), t4) + + if verbose: + print('%s. Status variables (bs=%s)' % (start+1, bs)) + fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) + s = "x" + while s and s != 'Line 6 of file 2\n': + s = fi.readline() + self.assertEqual(fi.filename(), t2) + self.assertEqual(fi.lineno(), 21) + self.assertEqual(fi.filelineno(), 6) + self.failIf(fi.isfirstline()) + self.failIf(fi.isstdin()) + + if verbose: + print('%s. Nextfile (bs=%s)' % (start+2, bs)) + fi.nextfile() + self.assertEqual(fi.readline(), 'Line 1 of file 3\n') + self.assertEqual(fi.lineno(), 22) + fi.close() + + if verbose: + print('%s. Stdin (bs=%s)' % (start+3, bs)) + fi = FileInput(files=(t1, t2, t3, t4, '-'), bufsize=bs) + savestdin = sys.stdin + try: + sys.stdin = StringIO("Line 1 of stdin\nLine 2 of stdin\n") + lines = list(fi) + self.assertEqual(len(lines), 33) + self.assertEqual(lines[32], 'Line 2 of stdin\n') + self.assertEqual(fi.filename(), '<stdin>') + fi.nextfile() + finally: + sys.stdin = savestdin + + if verbose: + print('%s. Boundary conditions (bs=%s)' % (start+4, bs)) + fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) + self.assertEqual(fi.lineno(), 0) + self.assertEqual(fi.filename(), None) fi.nextfile() - finally: - sys.stdin = savestdin - - if verbose: - print('%s. Boundary conditions (bs=%s)' % (start+4, bs)) - fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) - verify(fi.lineno() == 0) - verify(fi.filename() == None) - fi.nextfile() - verify(fi.lineno() == 0) - verify(fi.filename() == None) - - if verbose: - print('%s. Inplace (bs=%s)' % (start+5, bs)) - savestdout = sys.stdout - try: - fi = FileInput(files=(t1, t2, t3, t4), inplace=1, bufsize=bs) + self.assertEqual(fi.lineno(), 0) + self.assertEqual(fi.filename(), None) + + if verbose: + print('%s. Inplace (bs=%s)' % (start+5, bs)) + savestdout = sys.stdout + try: + fi = FileInput(files=(t1, t2, t3, t4), inplace=1, bufsize=bs) + for line in fi: + line = line[:-1].upper() + print(line) + fi.close() + finally: + sys.stdout = savestdout + + fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) for line in fi: - line = line[:-1].upper() - print(line) + self.assertEqual(line[-1], '\n') + m = pat.match(line[:-1]) + self.assertNotEqual(m, None) + self.assertEqual(int(m.group(1)), fi.filelineno()) fi.close() - finally: - sys.stdout = savestdout - - fi = FileInput(files=(t1, t2, t3, t4), bufsize=bs) - for line in fi: - verify(line[-1] == '\n') - m = pat.match(line[:-1]) - verify(m != None) - verify(int(m.group(1)) == fi.filelineno()) - fi.close() - - -def writeFiles(): - global t1, t2, t3, t4 - t1 = writeTmp(1, ["Line %s of file 1\n" % (i+1) for i in range(15)]) - t2 = writeTmp(2, ["Line %s of file 2\n" % (i+1) for i in range(10)]) - t3 = writeTmp(3, ["Line %s of file 3\n" % (i+1) for i in range(5)]) - t4 = writeTmp(4, ["Line %s of file 4\n" % (i+1) for i in range(1)]) - -# First, run the tests with default and teeny buffer size. -for round, bs in (0, 0), (1, 30): - try: - writeFiles() - runTests(t1, t2, t3, t4, bs, round) - finally: - remove_tempfiles(t1, t2, t3, t4) - -# Next, check for proper behavior with 0-byte files. -if verbose: - print("13. 0-byte files") -try: - t1 = writeTmp(1, [""]) - t2 = writeTmp(2, [""]) - t3 = writeTmp(3, ["The only line there is.\n"]) - t4 = writeTmp(4, [""]) - fi = FileInput(files=(t1, t2, t3, t4)) - line = fi.readline() - verify(line == 'The only line there is.\n') - verify(fi.lineno() == 1) - verify(fi.filelineno() == 1) - verify(fi.filename() == t3) - line = fi.readline() - verify(not line) - verify(fi.lineno() == 1) - verify(fi.filelineno() == 0) - verify(fi.filename() == t4) - fi.close() -finally: - remove_tempfiles(t1, t2, t3, t4) - -if verbose: - print("14. Files that don't end with newline") -try: - t1 = writeTmp(1, ["A\nB\nC"]) - t2 = writeTmp(2, ["D\nE\nF"]) - fi = FileInput(files=(t1, t2)) - lines = list(fi) - verify(lines == ["A\n", "B\n", "C", "D\n", "E\n", "F"]) - verify(fi.filelineno() == 3) - verify(fi.lineno() == 6) -finally: - remove_tempfiles(t1, t2) - -if verbose: - print("15. Unicode filenames") -try: - t1 = writeTmp(1, ["A\nB"]) - encoding = sys.getfilesystemencoding() - if encoding is None: - encoding = 'ascii' - fi = FileInput(files=unicode(t1, encoding)) - lines = list(fi) - verify(lines == ["A\n", "B"]) -finally: - remove_tempfiles(t1) - -if verbose: - print("16. fileno()") -try: - t1 = writeTmp(1, ["A\nB"]) - t2 = writeTmp(2, ["C\nD"]) - fi = FileInput(files=(t1, t2)) - verify(fi.fileno() == -1) - line = next(fi) - verify(fi.fileno() != -1) - fi.nextfile() - verify(fi.fileno() == -1) - line = list(fi) - verify(fi.fileno() == -1) -finally: - remove_tempfiles(t1, t2) - -if verbose: - print("17. Specify opening mode") -try: - # invalid mode, should raise ValueError - fi = FileInput(mode="w") - raise TestFailed("FileInput should reject invalid mode argument") -except ValueError: - pass -try: - # try opening in universal newline mode - t1 = writeTmp(1, ["A\nB\r\nC\rD"], mode="wb") - fi = FileInput(files=t1, mode="U") - lines = list(fi) - verify(lines == ["A\n", "B\n", "C\n", "D"]) -finally: - remove_tempfiles(t1) - -if verbose: - print("18. Test file opening hook") -try: - # cannot use openhook and inplace mode - fi = FileInput(inplace=1, openhook=lambda f,m: None) - raise TestFailed("FileInput should raise if both inplace " - "and openhook arguments are given") -except ValueError: - pass -try: - fi = FileInput(openhook=1) - raise TestFailed("FileInput should check openhook for being callable") -except ValueError: - pass -try: - t1 = writeTmp(1, ["A\nB"], mode="wb") - fi = FileInput(files=t1, openhook=hook_encoded("rot13")) - lines = list(fi) - verify(lines == ["N\n", "O"]) -finally: - remove_tempfiles(t1) + +class FileInputTests(unittest.TestCase): + def test_zero_byte_files(self): + try: + t1 = writeTmp(1, [""]) + t2 = writeTmp(2, [""]) + t3 = writeTmp(3, ["The only line there is.\n"]) + t4 = writeTmp(4, [""]) + fi = FileInput(files=(t1, t2, t3, t4)) + + line = fi.readline() + self.assertEqual(line, 'The only line there is.\n') + self.assertEqual(fi.lineno(), 1) + self.assertEqual(fi.filelineno(), 1) + self.assertEqual(fi.filename(), t3) + + line = fi.readline() + self.failIf(line) + self.assertEqual(fi.lineno(), 1) + self.assertEqual(fi.filelineno(), 0) + self.assertEqual(fi.filename(), t4) + fi.close() + finally: + remove_tempfiles(t1, t2, t3, t4) + + def test_files_that_dont_end_with_newline(self): + try: + t1 = writeTmp(1, ["A\nB\nC"]) + t2 = writeTmp(2, ["D\nE\nF"]) + fi = FileInput(files=(t1, t2)) + lines = list(fi) + self.assertEqual(lines, ["A\n", "B\n", "C", "D\n", "E\n", "F"]) + self.assertEqual(fi.filelineno(), 3) + self.assertEqual(fi.lineno(), 6) + finally: + remove_tempfiles(t1, t2) + + def test_unicode_filenames(self): + try: + t1 = writeTmp(1, ["A\nB"]) + encoding = sys.getfilesystemencoding() + if encoding is None: + encoding = 'ascii' + fi = FileInput(files=unicode(t1, encoding)) + lines = list(fi) + self.assertEqual(lines, ["A\n", "B"]) + finally: + remove_tempfiles(t1) + + def test_fileno(self): + try: + t1 = writeTmp(1, ["A\nB"]) + t2 = writeTmp(2, ["C\nD"]) + fi = FileInput(files=(t1, t2)) + self.assertEqual(fi.fileno(), -1) + line =next( fi) + self.assertNotEqual(fi.fileno(), -1) + fi.nextfile() + self.assertEqual(fi.fileno(), -1) + line = list(fi) + self.assertEqual(fi.fileno(), -1) + finally: + remove_tempfiles(t1, t2) + + def test_opening_mode(self): + try: + # invalid mode, should raise ValueError + fi = FileInput(mode="w") + self.fail("FileInput should reject invalid mode argument") + except ValueError: + pass + try: + # try opening in universal newline mode + t1 = writeTmp(1, ["A\nB\r\nC\rD"], mode="wb") + fi = FileInput(files=t1, mode="U") + lines = list(fi) + self.assertEqual(lines, ["A\n", "B\n", "C\n", "D"]) + finally: + remove_tempfiles(t1) + + def test_file_opening_hook(self): + try: + # cannot use openhook and inplace mode + fi = FileInput(inplace=1, openhook=lambda f, m: None) + self.fail("FileInput should raise if both inplace " + "and openhook arguments are given") + except ValueError: + pass + try: + fi = FileInput(openhook=1) + self.fail("FileInput should check openhook for being callable") + except ValueError: + pass + try: + t1 = writeTmp(1, ["A\nB"], mode="wb") + fi = FileInput(files=t1, openhook=hook_encoded("rot13")) + lines = list(fi) + self.assertEqual(lines, ["N\n", "O"]) + finally: + remove_tempfiles(t1) + +def test_main(): + run_unittest(BufferSizesTests, FileInputTests) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 6a78154..4d969f5 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -110,13 +110,13 @@ class OtherFileTests(unittest.TestCase): self.assertEquals(f.writable(), True) self.assertEquals(f.seekable(), True) f.close() - + f = _fileio._FileIO(TESTFN, "r") self.assertEquals(f.readable(), True) self.assertEquals(f.writable(), False) self.assertEquals(f.seekable(), True) f.close() - + f = _fileio._FileIO(TESTFN, "a+") self.assertEquals(f.readable(), True) self.assertEquals(f.writable(), True) diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py new file mode 100644 index 0000000..9298bf4 --- /dev/null +++ b/Lib/test/test_ftplib.py @@ -0,0 +1,93 @@ +import socket +import threading +import ftplib +import time + +from unittest import TestCase +from test import test_support + +def server(evt): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.settimeout(3) + serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + serv.bind(("", 9091)) + serv.listen(5) + try: + conn, addr = serv.accept() + except socket.timeout: + pass + else: + conn.send("1 Hola mundo\n") + conn.close() + finally: + serv.close() + evt.set() + +class GeneralTests(TestCase): + + def setUp(self): + ftplib.FTP.port = 9091 + self.evt = threading.Event() + threading.Thread(target=server, args=(self.evt,)).start() + time.sleep(.1) + + def tearDown(self): + self.evt.wait() + + def testBasic(self): + # do nothing + ftplib.FTP() + + # connects + ftp = ftplib.FTP("localhost") + ftp.sock.close() + + def testTimeoutDefault(self): + # default + ftp = ftplib.FTP("localhost") + self.assertTrue(ftp.sock.gettimeout() is None) + ftp.sock.close() + + def testTimeoutValue(self): + # a value + ftp = ftplib.FTP("localhost", timeout=30) + self.assertEqual(ftp.sock.gettimeout(), 30) + ftp.sock.close() + + def testTimeoutConnect(self): + ftp = ftplib.FTP() + ftp.connect("localhost", timeout=30) + self.assertEqual(ftp.sock.gettimeout(), 30) + ftp.sock.close() + + def testTimeoutDifferentOrder(self): + ftp = ftplib.FTP(timeout=30) + ftp.connect("localhost") + self.assertEqual(ftp.sock.gettimeout(), 30) + ftp.sock.close() + + def testTimeoutDirectAccess(self): + ftp = ftplib.FTP() + ftp.timeout = 30 + ftp.connect("localhost") + self.assertEqual(ftp.sock.gettimeout(), 30) + ftp.sock.close() + + def testTimeoutNone(self): + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + ftp = ftplib.FTP("localhost", timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(ftp.sock.gettimeout(), 30) + ftp.close() + + + +def test_main(verbose=None): + test_support.run_unittest(GeneralTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 2d5e33c..a2df21c 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -285,7 +285,7 @@ class TestReduce(unittest.TestCase): self.sofar.append(n*n) n += 1 return self.sofar[i] - + self.assertEqual(self.func(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc') self.assertEqual( self.func(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []), @@ -321,7 +321,7 @@ class TestReduce(unittest.TestCase): return i else: raise IndexError - + from operator import add self.assertEqual(self.func(add, SequenceClass(5)), 10) self.assertEqual(self.func(add, SequenceClass(5), 42), 52) @@ -333,7 +333,7 @@ class TestReduce(unittest.TestCase): d = {"one": 1, "two": 2, "three": 3} self.assertEqual(self.func(add, d), "".join(d.keys())) - + def test_main(verbose=None): diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 8068b35..10b02da 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1,390 +1,11 @@ -from test.test_support import verify, verbose, TestFailed, vereq +import unittest +from test.test_support import verbose, run_unittest import sys import gc import weakref -def expect(actual, expected, name): - if actual != expected: - raise TestFailed, "test_%s: actual %r, expected %r" % ( - name, actual, expected) - -def expect_nonzero(actual, name): - if actual == 0: - raise TestFailed, "test_%s: unexpected zero" % name - -def run_test(name, thunk): - if verbose: - print("testing %s..." % name, end=' ') - thunk() - if verbose: - print("ok") - -def test_list(): - l = [] - l.append(l) - gc.collect() - del l - expect(gc.collect(), 1, "list") - -def test_dict(): - d = {} - d[1] = d - gc.collect() - del d - expect(gc.collect(), 1, "dict") - -def test_tuple(): - # since tuples are immutable we close the loop with a list - l = [] - t = (l,) - l.append(t) - gc.collect() - del t - del l - expect(gc.collect(), 2, "tuple") - -def test_class(): - class A: - pass - A.a = A - gc.collect() - del A - expect_nonzero(gc.collect(), "class") - -def test_newstyleclass(): - class A(object): - pass - gc.collect() - del A - expect_nonzero(gc.collect(), "staticclass") - -def test_instance(): - class A: - pass - a = A() - a.a = a - gc.collect() - del a - expect_nonzero(gc.collect(), "instance") - -def test_newinstance(): - class A(object): - pass - a = A() - a.a = a - gc.collect() - del a - expect_nonzero(gc.collect(), "newinstance") - class B(list): - pass - class C(B, A): - pass - a = C() - a.a = a - gc.collect() - del a - expect_nonzero(gc.collect(), "newinstance(2)") - del B, C - expect_nonzero(gc.collect(), "newinstance(3)") - A.a = A() - del A - expect_nonzero(gc.collect(), "newinstance(4)") - expect(gc.collect(), 0, "newinstance(5)") - -def test_method(): - # Tricky: self.__init__ is a bound method, it references the instance. - class A: - def __init__(self): - self.init = self.__init__ - a = A() - gc.collect() - del a - expect_nonzero(gc.collect(), "method") - -def test_finalizer(): - # A() is uncollectable if it is part of a cycle, make sure it shows up - # in gc.garbage. - class A: - def __del__(self): pass - class B: - pass - a = A() - a.a = a - id_a = id(a) - b = B() - b.b = b - gc.collect() - del a - del b - expect_nonzero(gc.collect(), "finalizer") - for obj in gc.garbage: - if id(obj) == id_a: - del obj.a - break - else: - raise TestFailed, "didn't find obj in garbage (finalizer)" - gc.garbage.remove(obj) - -def test_finalizer_newclass(): - # A() is uncollectable if it is part of a cycle, make sure it shows up - # in gc.garbage. - class A(object): - def __del__(self): pass - class B(object): - pass - a = A() - a.a = a - id_a = id(a) - b = B() - b.b = b - gc.collect() - del a - del b - expect_nonzero(gc.collect(), "finalizer") - for obj in gc.garbage: - if id(obj) == id_a: - del obj.a - break - else: - raise TestFailed, "didn't find obj in garbage (finalizer)" - gc.garbage.remove(obj) - -def test_function(): - # Tricky: f -> d -> f, code should call d.clear() after the exec to - # break the cycle. - d = {} - exec("def f(): pass\n", d) - gc.collect() - del d - expect(gc.collect(), 2, "function") - -def test_frame(): - def f(): - frame = sys._getframe() - gc.collect() - f() - expect(gc.collect(), 1, "frame") - - -def test_saveall(): - # Verify that cyclic garbage like lists show up in gc.garbage if the - # SAVEALL option is enabled. - - # First make sure we don't save away other stuff that just happens to - # be waiting for collection. - gc.collect() - vereq(gc.garbage, []) # if this fails, someone else created immortal trash - - L = [] - L.append(L) - id_L = id(L) - - debug = gc.get_debug() - gc.set_debug(debug | gc.DEBUG_SAVEALL) - del L - gc.collect() - gc.set_debug(debug) - - vereq(len(gc.garbage), 1) - obj = gc.garbage.pop() - vereq(id(obj), id_L) - -def test_del(): - # __del__ methods can trigger collection, make this to happen - thresholds = gc.get_threshold() - gc.enable() - gc.set_threshold(1) - - class A: - def __del__(self): - dir(self) - a = A() - del a - - gc.disable() - gc.set_threshold(*thresholds) - -def test_del_newclass(): - # __del__ methods can trigger collection, make this to happen - thresholds = gc.get_threshold() - gc.enable() - gc.set_threshold(1) - - class A(object): - def __del__(self): - dir(self) - a = A() - del a - - gc.disable() - gc.set_threshold(*thresholds) - -def test_get_count(): - gc.collect() - expect(gc.get_count(), (0, 0, 0), "get_count()") - a = dict() - expect(gc.get_count(), (1, 0, 0), "get_count()") - -def test_collect_generations(): - gc.collect() - a = dict() - gc.collect(0) - expect(gc.get_count(), (0, 1, 0), "collect(0)") - gc.collect(1) - expect(gc.get_count(), (0, 0, 1), "collect(1)") - gc.collect(2) - expect(gc.get_count(), (0, 0, 0), "collect(1)") - -class Ouch: - n = 0 - def __del__(self): - Ouch.n = Ouch.n + 1 - if Ouch.n % 17 == 0: - gc.collect() - -def test_trashcan(): - # "trashcan" is a hack to prevent stack overflow when deallocating - # very deeply nested tuples etc. It works in part by abusing the - # type pointer and refcount fields, and that can yield horrible - # problems when gc tries to traverse the structures. - # If this test fails (as it does in 2.0, 2.1 and 2.2), it will - # most likely die via segfault. - - # Note: In 2.3 the possibility for compiling without cyclic gc was - # removed, and that in turn allows the trashcan mechanism to work - # via much simpler means (e.g., it never abuses the type pointer or - # refcount fields anymore). Since it's much less likely to cause a - # problem now, the various constants in this expensive (we force a lot - # of full collections) test are cut back from the 2.2 version. - gc.enable() - N = 150 - for count in range(2): - t = [] - for i in range(N): - t = [t, Ouch()] - u = [] - for i in range(N): - u = [u, Ouch()] - v = {} - for i in range(N): - v = {1: v, 2: Ouch()} - gc.disable() - -class Boom: - def __getattr__(self, someattribute): - del self.attr - raise AttributeError - -def test_boom(): - a = Boom() - b = Boom() - a.attr = b - b.attr = a - - gc.collect() - garbagelen = len(gc.garbage) - del a, b - # a<->b are in a trash cycle now. Collection will invoke Boom.__getattr__ - # (to see whether a and b have __del__ methods), and __getattr__ deletes - # the internal "attr" attributes as a side effect. That causes the - # trash cycle to get reclaimed via refcounts falling to 0, thus mutating - # the trash graph as a side effect of merely asking whether __del__ - # exists. This used to (before 2.3b1) crash Python. Now __getattr__ - # isn't called. - expect(gc.collect(), 4, "boom") - expect(len(gc.garbage), garbagelen, "boom") - -class Boom2: - def __init__(self): - self.x = 0 - - def __getattr__(self, someattribute): - self.x += 1 - if self.x > 1: - del self.attr - raise AttributeError - -def test_boom2(): - a = Boom2() - b = Boom2() - a.attr = b - b.attr = a - - gc.collect() - garbagelen = len(gc.garbage) - del a, b - # Much like test_boom(), except that __getattr__ doesn't break the - # cycle until the second time gc checks for __del__. As of 2.3b1, - # there isn't a second time, so this simply cleans up the trash cycle. - # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get reclaimed - # this way. - expect(gc.collect(), 4, "boom2") - expect(len(gc.garbage), garbagelen, "boom2") - -# boom__new and boom2_new are exactly like boom and boom2, except use -# new-style classes. - -class Boom_New(object): - def __getattr__(self, someattribute): - del self.attr - raise AttributeError - -def test_boom_new(): - a = Boom_New() - b = Boom_New() - a.attr = b - b.attr = a - - gc.collect() - garbagelen = len(gc.garbage) - del a, b - expect(gc.collect(), 4, "boom_new") - expect(len(gc.garbage), garbagelen, "boom_new") - -class Boom2_New(object): - def __init__(self): - self.x = 0 - - def __getattr__(self, someattribute): - self.x += 1 - if self.x > 1: - del self.attr - raise AttributeError - -def test_boom2_new(): - a = Boom2_New() - b = Boom2_New() - a.attr = b - b.attr = a - - gc.collect() - garbagelen = len(gc.garbage) - del a, b - expect(gc.collect(), 4, "boom2_new") - expect(len(gc.garbage), garbagelen, "boom2_new") - -def test_get_referents(): - alist = [1, 3, 5] - got = gc.get_referents(alist) - got.sort() - expect(got, alist, "get_referents") - - atuple = tuple(alist) - got = gc.get_referents(atuple) - got.sort() - expect(got, alist, "get_referents") - - adict = {1: 3, 5: 7} - expected = [1, 3, 5, 7] - got = gc.get_referents(adict) - got.sort() - expect(got, expected, "get_referents") - - got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0)) - got.sort() - expect(got, [0, 0] + range(5), "get_referents") - - expect(gc.get_referents(1, 'a', 4j), [], "get_referents") +### Support code +############################################################################### # Bug 1055820 has several tests of longstanding bugs involving weakrefs and # cyclic gc. @@ -410,217 +31,556 @@ class GC_Detector(object): # gc collects it. self.wr = weakref.ref(C1055820(666), it_happened) -def test_bug1055820b(): - # Corresponds to temp2b.py in the bug report. - - ouch = [] - def callback(ignored): - ouch[:] = [wr() for wr in WRs] - - Cs = [C1055820(i) for i in range(2)] - WRs = [weakref.ref(c, callback) for c in Cs] - c = None - - gc.collect() - expect(len(ouch), 0, "bug1055820b") - # Make the two instances trash, and collect again. The bug was that - # the callback materialized a strong reference to an instance, but gc - # cleared the instance's dict anyway. - Cs = None - gc.collect() - expect(len(ouch), 2, "bug1055820b") # else the callbacks didn't run - for x in ouch: - # If the callback resurrected one of these guys, the instance - # would be damaged, with an empty __dict__. - expect(x, None, "bug1055820b") - -def test_bug1055820c(): - # Corresponds to temp2c.py in the bug report. This is pretty elaborate. - - c0 = C1055820(0) - # Move c0 into generation 2. - gc.collect() - - c1 = C1055820(1) - c1.keep_c0_alive = c0 - del c0.loop # now only c1 keeps c0 alive - - c2 = C1055820(2) - c2wr = weakref.ref(c2) # no callback! - - ouch = [] - def callback(ignored): - ouch[:] = [c2wr()] - - # The callback gets associated with a wr on an object in generation 2. - c0wr = weakref.ref(c0, callback) - - c0 = c1 = c2 = None - - # What we've set up: c0, c1, and c2 are all trash now. c0 is in - # generation 2. The only thing keeping it alive is that c1 points to it. - # c1 and c2 are in generation 0, and are in self-loops. There's a global - # weakref to c2 (c2wr), but that weakref has no callback. There's also - # a global weakref to c0 (c0wr), and that does have a callback, and that - # callback references c2 via c2wr(). - # - # c0 has a wr with callback, which references c2wr - # ^ - # | - # | Generation 2 above dots - #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . . - # | Generation 0 below dots - # | - # | - # ^->c1 ^->c2 has a wr but no callback - # | | | | - # <--v <--v - # - # So this is the nightmare: when generation 0 gets collected, we see that - # c2 has a callback-free weakref, and c1 doesn't even have a weakref. - # Collecting generation 0 doesn't see c0 at all, and c0 is the only object - # that has a weakref with a callback. gc clears c1 and c2. Clearing c1 - # has the side effect of dropping the refcount on c0 to 0, so c0 goes - # away (despite that it's in an older generation) and c0's wr callback - # triggers. That in turn materializes a reference to c2 via c2wr(), but - # c2 gets cleared anyway by gc. - - # We want to let gc happen "naturally", to preserve the distinction - # between generations. - junk = [] - i = 0 - detector = GC_Detector() - while not detector.gc_happened: - i += 1 - if i > 10000: - raise TestFailed("gc didn't happen after 10000 iterations") - expect(len(ouch), 0, "bug1055820c") - junk.append([]) # this will eventually trigger gc - - expect(len(ouch), 1, "bug1055820c") # else the callback wasn't invoked - for x in ouch: - # If the callback resurrected c2, the instance would be damaged, - # with an empty __dict__. - expect(x, None, "bug1055820c") - -def test_bug1055820d(): - # Corresponds to temp2d.py in the bug report. This is very much like - # test_bug1055820c, but uses a __del__ method instead of a weakref - # callback to sneak in a resurrection of cyclic trash. - - ouch = [] - class D(C1055820): - def __del__(self): - ouch[:] = [c2wr()] - d0 = D(0) - # Move all the above into generation 2. - gc.collect() - - c1 = C1055820(1) - c1.keep_d0_alive = d0 - del d0.loop # now only c1 keeps d0 alive - - c2 = C1055820(2) - c2wr = weakref.ref(c2) # no callback! - - d0 = c1 = c2 = None - - # What we've set up: d0, c1, and c2 are all trash now. d0 is in - # generation 2. The only thing keeping it alive is that c1 points to it. - # c1 and c2 are in generation 0, and are in self-loops. There's a global - # weakref to c2 (c2wr), but that weakref has no callback. There are no - # other weakrefs. - # - # d0 has a __del__ method that references c2wr - # ^ - # | - # | Generation 2 above dots - #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . . - # | Generation 0 below dots - # | - # | - # ^->c1 ^->c2 has a wr but no callback - # | | | | - # <--v <--v - # - # So this is the nightmare: when generation 0 gets collected, we see that - # c2 has a callback-free weakref, and c1 doesn't even have a weakref. - # Collecting generation 0 doesn't see d0 at all. gc clears c1 and c2. - # Clearing c1 has the side effect of dropping the refcount on d0 to 0, so - # d0 goes away (despite that it's in an older generation) and d0's __del__ - # triggers. That in turn materializes a reference to c2 via c2wr(), but - # c2 gets cleared anyway by gc. - - # We want to let gc happen "naturally", to preserve the distinction - # between generations. - detector = GC_Detector() - junk = [] - i = 0 - while not detector.gc_happened: - i += 1 - if i > 10000: - raise TestFailed("gc didn't happen after 10000 iterations") - expect(len(ouch), 0, "bug1055820d") - junk.append([]) # this will eventually trigger gc - - expect(len(ouch), 1, "bug1055820d") # else __del__ wasn't invoked - for x in ouch: - # If __del__ resurrected c2, the instance would be damaged, with an - # empty __dict__. - expect(x, None, "bug1055820d") - - -def test_all(): - gc.collect() # Delete 2nd generation garbage - run_test("lists", test_list) - run_test("dicts", test_dict) - run_test("tuples", test_tuple) - run_test("classes", test_class) - run_test("new style classes", test_newstyleclass) - run_test("instances", test_instance) - run_test("new instances", test_newinstance) - run_test("methods", test_method) - run_test("functions", test_function) - run_test("frames", test_frame) - run_test("finalizers", test_finalizer) - run_test("finalizers (new class)", test_finalizer_newclass) - run_test("__del__", test_del) - run_test("__del__ (new class)", test_del_newclass) - run_test("get_count()", test_get_count) - run_test("collect(n)", test_collect_generations) - run_test("saveall", test_saveall) - run_test("trashcan", test_trashcan) - run_test("boom", test_boom) - run_test("boom2", test_boom2) - run_test("boom_new", test_boom_new) - run_test("boom2_new", test_boom2_new) - run_test("get_referents", test_get_referents) - run_test("bug1055820b", test_bug1055820b) - - gc.enable() - try: - run_test("bug1055820c", test_bug1055820c) - finally: +### Tests +############################################################################### + +class GCTests(unittest.TestCase): + def test_list(self): + l = [] + l.append(l) + gc.collect() + del l + self.assertEqual(gc.collect(), 1) + + def test_dict(self): + d = {} + d[1] = d + gc.collect() + del d + self.assertEqual(gc.collect(), 1) + + def test_tuple(self): + # since tuples are immutable we close the loop with a list + l = [] + t = (l,) + l.append(t) + gc.collect() + del t + del l + self.assertEqual(gc.collect(), 2) + + def test_class(self): + class A: + pass + A.a = A + gc.collect() + del A + self.assertNotEqual(gc.collect(), 0) + + def test_newstyleclass(self): + class A(object): + pass + gc.collect() + del A + self.assertNotEqual(gc.collect(), 0) + + def test_instance(self): + class A: + pass + a = A() + a.a = a + gc.collect() + del a + self.assertNotEqual(gc.collect(), 0) + + def test_newinstance(self): + class A(object): + pass + a = A() + a.a = a + gc.collect() + del a + self.assertNotEqual(gc.collect(), 0) + class B(list): + pass + class C(B, A): + pass + a = C() + a.a = a + gc.collect() + del a + self.assertNotEqual(gc.collect(), 0) + del B, C + self.assertNotEqual(gc.collect(), 0) + A.a = A() + del A + self.assertNotEqual(gc.collect(), 0) + self.assertEqual(gc.collect(), 0) + + def test_method(self): + # Tricky: self.__init__ is a bound method, it references the instance. + class A: + def __init__(self): + self.init = self.__init__ + a = A() + gc.collect() + del a + self.assertNotEqual(gc.collect(), 0) + + def test_finalizer(self): + # A() is uncollectable if it is part of a cycle, make sure it shows up + # in gc.garbage. + class A: + def __del__(self): pass + class B: + pass + a = A() + a.a = a + id_a = id(a) + b = B() + b.b = b + gc.collect() + del a + del b + self.assertNotEqual(gc.collect(), 0) + for obj in gc.garbage: + if id(obj) == id_a: + del obj.a + break + else: + self.fail("didn't find obj in garbage (finalizer)") + gc.garbage.remove(obj) + + def test_finalizer_newclass(self): + # A() is uncollectable if it is part of a cycle, make sure it shows up + # in gc.garbage. + class A(object): + def __del__(self): pass + class B(object): + pass + a = A() + a.a = a + id_a = id(a) + b = B() + b.b = b + gc.collect() + del a + del b + self.assertNotEqual(gc.collect(), 0) + for obj in gc.garbage: + if id(obj) == id_a: + del obj.a + break + else: + self.fail("didn't find obj in garbage (finalizer)") + gc.garbage.remove(obj) + + def test_function(self): + # Tricky: f -> d -> f, code should call d.clear() after the exec to + # break the cycle. + d = {} + exec("def f(): pass\n", d) + gc.collect() + del d + self.assertEqual(gc.collect(), 2) + + def test_frame(self): + def f(): + frame = sys._getframe() + gc.collect() + f() + self.assertEqual(gc.collect(), 1) + + def test_saveall(self): + # Verify that cyclic garbage like lists show up in gc.garbage if the + # SAVEALL option is enabled. + + # First make sure we don't save away other stuff that just happens to + # be waiting for collection. + gc.collect() + # if this fails, someone else created immortal trash + self.assertEqual(gc.garbage, []) + + L = [] + L.append(L) + id_L = id(L) + + debug = gc.get_debug() + gc.set_debug(debug | gc.DEBUG_SAVEALL) + del L + gc.collect() + gc.set_debug(debug) + + self.assertEqual(len(gc.garbage), 1) + obj = gc.garbage.pop() + self.assertEqual(id(obj), id_L) + + def test_del(self): + # __del__ methods can trigger collection, make this to happen + thresholds = gc.get_threshold() + gc.enable() + gc.set_threshold(1) + + class A: + def __del__(self): + dir(self) + a = A() + del a + gc.disable() + gc.set_threshold(*thresholds) - gc.enable() - try: - run_test("bug1055820d", test_bug1055820d) - finally: + def test_del_newclass(self): + # __del__ methods can trigger collection, make this to happen + thresholds = gc.get_threshold() + gc.enable() + gc.set_threshold(1) + + class A(object): + def __del__(self): + dir(self) + a = A() + del a + + gc.disable() + gc.set_threshold(*thresholds) + + def test_get_count(self): + gc.collect() + self.assertEqual(gc.get_count(), (0, 0, 0)) + a = dict() + self.assertEqual(gc.get_count(), (1, 0, 0)) + + def test_collect_generations(self): + gc.collect() + a = dict() + gc.collect(0) + self.assertEqual(gc.get_count(), (0, 1, 0)) + gc.collect(1) + self.assertEqual(gc.get_count(), (0, 0, 1)) + gc.collect(2) + self.assertEqual(gc.get_count(), (0, 0, 0)) + + def test_trashcan(self): + class Ouch: + n = 0 + def __del__(self): + Ouch.n = Ouch.n + 1 + if Ouch.n % 17 == 0: + gc.collect() + + # "trashcan" is a hack to prevent stack overflow when deallocating + # very deeply nested tuples etc. It works in part by abusing the + # type pointer and refcount fields, and that can yield horrible + # problems when gc tries to traverse the structures. + # If this test fails (as it does in 2.0, 2.1 and 2.2), it will + # most likely die via segfault. + + # Note: In 2.3 the possibility for compiling without cyclic gc was + # removed, and that in turn allows the trashcan mechanism to work + # via much simpler means (e.g., it never abuses the type pointer or + # refcount fields anymore). Since it's much less likely to cause a + # problem now, the various constants in this expensive (we force a lot + # of full collections) test are cut back from the 2.2 version. + gc.enable() + N = 150 + for count in range(2): + t = [] + for i in range(N): + t = [t, Ouch()] + u = [] + for i in range(N): + u = [u, Ouch()] + v = {} + for i in range(N): + v = {1: v, 2: Ouch()} gc.disable() -def test(): - if verbose: - print("disabling automatic collection") + def test_boom(self): + class Boom: + def __getattr__(self, someattribute): + del self.attr + raise AttributeError + + a = Boom() + b = Boom() + a.attr = b + b.attr = a + + gc.collect() + garbagelen = len(gc.garbage) + del a, b + # a<->b are in a trash cycle now. Collection will invoke + # Boom.__getattr__ (to see whether a and b have __del__ methods), and + # __getattr__ deletes the internal "attr" attributes as a side effect. + # That causes the trash cycle to get reclaimed via refcounts falling to + # 0, thus mutating the trash graph as a side effect of merely asking + # whether __del__ exists. This used to (before 2.3b1) crash Python. + # Now __getattr__ isn't called. + self.assertEqual(gc.collect(), 4) + self.assertEqual(len(gc.garbage), garbagelen) + + def test_boom2(self): + class Boom2: + def __init__(self): + self.x = 0 + + def __getattr__(self, someattribute): + self.x += 1 + if self.x > 1: + del self.attr + raise AttributeError + + a = Boom2() + b = Boom2() + a.attr = b + b.attr = a + + gc.collect() + garbagelen = len(gc.garbage) + del a, b + # Much like test_boom(), except that __getattr__ doesn't break the + # cycle until the second time gc checks for __del__. As of 2.3b1, + # there isn't a second time, so this simply cleans up the trash cycle. + # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get + # reclaimed this way. + self.assertEqual(gc.collect(), 4) + self.assertEqual(len(gc.garbage), garbagelen) + + def test_boom_new(self): + # boom__new and boom2_new are exactly like boom and boom2, except use + # new-style classes. + + class Boom_New(object): + def __getattr__(self, someattribute): + del self.attr + raise AttributeError + + a = Boom_New() + b = Boom_New() + a.attr = b + b.attr = a + + gc.collect() + garbagelen = len(gc.garbage) + del a, b + self.assertEqual(gc.collect(), 4) + self.assertEqual(len(gc.garbage), garbagelen) + + def test_boom2_new(self): + class Boom2_New(object): + def __init__(self): + self.x = 0 + + def __getattr__(self, someattribute): + self.x += 1 + if self.x > 1: + del self.attr + raise AttributeError + + a = Boom2_New() + b = Boom2_New() + a.attr = b + b.attr = a + + gc.collect() + garbagelen = len(gc.garbage) + del a, b + self.assertEqual(gc.collect(), 4) + self.assertEqual(len(gc.garbage), garbagelen) + + def test_get_referents(self): + alist = [1, 3, 5] + got = gc.get_referents(alist) + got.sort() + self.assertEqual(got, alist) + + atuple = tuple(alist) + got = gc.get_referents(atuple) + got.sort() + self.assertEqual(got, alist) + + adict = {1: 3, 5: 7} + expected = [1, 3, 5, 7] + got = gc.get_referents(adict) + got.sort() + self.assertEqual(got, expected) + + got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0)) + got.sort() + self.assertEqual(got, [0, 0] + range(5)) + + self.assertEqual(gc.get_referents(1, 'a', 4j), []) + + def test_bug1055820b(self): + # Corresponds to temp2b.py in the bug report. + + ouch = [] + def callback(ignored): + ouch[:] = [wr() for wr in WRs] + + Cs = [C1055820(i) for i in range(2)] + WRs = [weakref.ref(c, callback) for c in Cs] + c = None + + gc.collect() + self.assertEqual(len(ouch), 0) + # Make the two instances trash, and collect again. The bug was that + # the callback materialized a strong reference to an instance, but gc + # cleared the instance's dict anyway. + Cs = None + gc.collect() + self.assertEqual(len(ouch), 2) # else the callbacks didn't run + for x in ouch: + # If the callback resurrected one of these guys, the instance + # would be damaged, with an empty __dict__. + self.assertEqual(x, None) + +class GCTogglingTests(unittest.TestCase): + def setUp(self): + gc.enable() + + def tearDown(self): + gc.disable() + + def test_bug1055820c(self): + # Corresponds to temp2c.py in the bug report. This is pretty + # elaborate. + + c0 = C1055820(0) + # Move c0 into generation 2. + gc.collect() + + c1 = C1055820(1) + c1.keep_c0_alive = c0 + del c0.loop # now only c1 keeps c0 alive + + c2 = C1055820(2) + c2wr = weakref.ref(c2) # no callback! + + ouch = [] + def callback(ignored): + ouch[:] = [c2wr()] + + # The callback gets associated with a wr on an object in generation 2. + c0wr = weakref.ref(c0, callback) + + c0 = c1 = c2 = None + + # What we've set up: c0, c1, and c2 are all trash now. c0 is in + # generation 2. The only thing keeping it alive is that c1 points to + # it. c1 and c2 are in generation 0, and are in self-loops. There's a + # global weakref to c2 (c2wr), but that weakref has no callback. + # There's also a global weakref to c0 (c0wr), and that does have a + # callback, and that callback references c2 via c2wr(). + # + # c0 has a wr with callback, which references c2wr + # ^ + # | + # | Generation 2 above dots + #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . . + # | Generation 0 below dots + # | + # | + # ^->c1 ^->c2 has a wr but no callback + # | | | | + # <--v <--v + # + # So this is the nightmare: when generation 0 gets collected, we see + # that c2 has a callback-free weakref, and c1 doesn't even have a + # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is + # the only object that has a weakref with a callback. gc clears c1 + # and c2. Clearing c1 has the side effect of dropping the refcount on + # c0 to 0, so c0 goes away (despite that it's in an older generation) + # and c0's wr callback triggers. That in turn materializes a reference + # to c2 via c2wr(), but c2 gets cleared anyway by gc. + + # We want to let gc happen "naturally", to preserve the distinction + # between generations. + junk = [] + i = 0 + detector = GC_Detector() + while not detector.gc_happened: + i += 1 + if i > 10000: + self.fail("gc didn't happen after 10000 iterations") + self.assertEqual(len(ouch), 0) + junk.append([]) # this will eventually trigger gc + + self.assertEqual(len(ouch), 1) # else the callback wasn't invoked + for x in ouch: + # If the callback resurrected c2, the instance would be damaged, + # with an empty __dict__. + self.assertEqual(x, None) + + def test_bug1055820d(self): + # Corresponds to temp2d.py in the bug report. This is very much like + # test_bug1055820c, but uses a __del__ method instead of a weakref + # callback to sneak in a resurrection of cyclic trash. + + ouch = [] + class D(C1055820): + def __del__(self): + ouch[:] = [c2wr()] + + d0 = D(0) + # Move all the above into generation 2. + gc.collect() + + c1 = C1055820(1) + c1.keep_d0_alive = d0 + del d0.loop # now only c1 keeps d0 alive + + c2 = C1055820(2) + c2wr = weakref.ref(c2) # no callback! + + d0 = c1 = c2 = None + + # What we've set up: d0, c1, and c2 are all trash now. d0 is in + # generation 2. The only thing keeping it alive is that c1 points to + # it. c1 and c2 are in generation 0, and are in self-loops. There's + # a global weakref to c2 (c2wr), but that weakref has no callback. + # There are no other weakrefs. + # + # d0 has a __del__ method that references c2wr + # ^ + # | + # | Generation 2 above dots + #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . . + # | Generation 0 below dots + # | + # | + # ^->c1 ^->c2 has a wr but no callback + # | | | | + # <--v <--v + # + # So this is the nightmare: when generation 0 gets collected, we see + # that c2 has a callback-free weakref, and c1 doesn't even have a + # weakref. Collecting generation 0 doesn't see d0 at all. gc clears + # c1 and c2. Clearing c1 has the side effect of dropping the refcount + # on d0 to 0, so d0 goes away (despite that it's in an older + # generation) and d0's __del__ triggers. That in turn materializes + # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc. + + # We want to let gc happen "naturally", to preserve the distinction + # between generations. + detector = GC_Detector() + junk = [] + i = 0 + while not detector.gc_happened: + i += 1 + if i > 10000: + self.fail("gc didn't happen after 10000 iterations") + self.assertEqual(len(ouch), 0) + junk.append([]) # this will eventually trigger gc + + self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked + for x in ouch: + # If __del__ resurrected c2, the instance would be damaged, with an + # empty __dict__. + self.assertEqual(x, None) + +def test_main(): enabled = gc.isenabled() gc.disable() - verify(not gc.isenabled()) + assert not gc.isenabled() debug = gc.get_debug() gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak try: - test_all() + gc.collect() # Delete 2nd generation garbage + run_unittest(GCTests, GCTogglingTests) finally: gc.set_debug(debug) # test gc.enable() even if GC is disabled by default @@ -628,9 +588,9 @@ def test(): print("restoring automatic collection") # make sure to always test gc.enable() gc.enable() - verify(gc.isenabled()) + assert gc.isenabled() if not enabled: gc.disable() - -test() +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 10bd256..a3a9940 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -1,180 +1,179 @@ # test_getopt.py # David Goodger <dgoodger@bigfoot.com> 2000-08-19 +from test.test_support import verbose, run_doctest, run_unittest +import unittest + import getopt -from getopt import GetoptError -from test.test_support import verify, verbose, run_doctest import os -def expectException(teststr, expected, failure=AssertionError): - """Executes a statement passed in teststr, and raises an exception - (failure) if the expected exception is *not* raised.""" - try: - exec(teststr) - except expected: - pass - else: - raise failure - -old_posixly_correct = os.environ.get("POSIXLY_CORRECT") -if old_posixly_correct is not None: - del os.environ["POSIXLY_CORRECT"] - -if verbose: - print('Running tests on getopt.short_has_arg') -verify(getopt.short_has_arg('a', 'a:')) -verify(not getopt.short_has_arg('a', 'a')) -expectException("tmp = getopt.short_has_arg('a', 'b')", GetoptError) -expectException("tmp = getopt.short_has_arg('a', '')", GetoptError) - -if verbose: - print('Running tests on getopt.long_has_args') -has_arg, option = getopt.long_has_args('abc', ['abc=']) -verify(has_arg) -verify(option == 'abc') -has_arg, option = getopt.long_has_args('abc', ['abc']) -verify(not has_arg) -verify(option == 'abc') -has_arg, option = getopt.long_has_args('abc', ['abcd']) -verify(not has_arg) -verify(option == 'abcd') -expectException("has_arg, option = getopt.long_has_args('abc', ['def'])", - GetoptError) -expectException("has_arg, option = getopt.long_has_args('abc', [])", - GetoptError) -expectException("has_arg, option = " + \ - "getopt.long_has_args('abc', ['abcd','abcde'])", - GetoptError) - -if verbose: - print('Running tests on getopt.do_shorts') -opts, args = getopt.do_shorts([], 'a', 'a', []) -verify(opts == [('-a', '')]) -verify(args == []) -opts, args = getopt.do_shorts([], 'a1', 'a:', []) -verify(opts == [('-a', '1')]) -verify(args == []) -#opts, args = getopt.do_shorts([], 'a=1', 'a:', []) -#verify(opts == [('-a', '1')]) -#verify(args == []) -opts, args = getopt.do_shorts([], 'a', 'a:', ['1']) -verify(opts == [('-a', '1')]) -verify(args == []) -opts, args = getopt.do_shorts([], 'a', 'a:', ['1', '2']) -verify(opts == [('-a', '1')]) -verify(args == ['2']) -expectException("opts, args = getopt.do_shorts([], 'a1', 'a', [])", - GetoptError) -expectException("opts, args = getopt.do_shorts([], 'a', 'a:', [])", - GetoptError) - -if verbose: - print('Running tests on getopt.do_longs') -opts, args = getopt.do_longs([], 'abc', ['abc'], []) -verify(opts == [('--abc', '')]) -verify(args == []) -opts, args = getopt.do_longs([], 'abc=1', ['abc='], []) -verify(opts == [('--abc', '1')]) -verify(args == []) -opts, args = getopt.do_longs([], 'abc=1', ['abcd='], []) -verify(opts == [('--abcd', '1')]) -verify(args == []) -opts, args = getopt.do_longs([], 'abc', ['ab', 'abc', 'abcd'], []) -verify(opts == [('--abc', '')]) -verify(args == []) -# Much like the preceding, except with a non-alpha character ("-") in -# option name that precedes "="; failed in -# http://sourceforge.net/bugs/?func=detailbug&bug_id=126863&group_id=5470 -opts, args = getopt.do_longs([], 'foo=42', ['foo-bar', 'foo=',], []) -verify(opts == [('--foo', '42')]) -verify(args == []) -expectException("opts, args = getopt.do_longs([], 'abc=1', ['abc'], [])", - GetoptError) -expectException("opts, args = getopt.do_longs([], 'abc', ['abc='], [])", - GetoptError) - -# note: the empty string between '-a' and '--beta' is significant: -# it simulates an empty string option argument ('-a ""') on the command line. -cmdline = ['-a', '1', '-b', '--alpha=2', '--beta', '-a', '3', '-a', '', - '--beta', 'arg1', 'arg2'] - -if verbose: - print('Running tests on getopt.getopt') -opts, args = getopt.getopt(cmdline, 'a:b', ['alpha=', 'beta']) -verify(opts == [('-a', '1'), ('-b', ''), ('--alpha', '2'), ('--beta', ''), - ('-a', '3'), ('-a', ''), ('--beta', '')] ) -# Note ambiguity of ('-b', '') and ('-a', '') above. This must be -# accounted for in the code that calls getopt(). -verify(args == ['arg1', 'arg2']) - -expectException( - "opts, args = getopt.getopt(cmdline, 'a:b', ['alpha', 'beta'])", - GetoptError) - -# Test handling of GNU style scanning mode. -if verbose: - print('Running tests on getopt.gnu_getopt') -cmdline = ['-a', 'arg1', '-b', '1', '--alpha', '--beta=2'] -# GNU style -opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) -verify(opts == [('-a', ''), ('-b', '1'), ('--alpha', ''), ('--beta', '2')]) -verify(args == ['arg1']) -# Posix style via + -opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta=']) -verify(opts == [('-a', '')]) -verify(args == ['arg1', '-b', '1', '--alpha', '--beta=2']) -# Posix style via POSIXLY_CORRECT -os.environ["POSIXLY_CORRECT"] = "1" -opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) -verify(opts == [('-a', '')]) -verify(args == ['arg1', '-b', '1', '--alpha', '--beta=2']) - - -if old_posixly_correct is None: - del os.environ["POSIXLY_CORRECT"] -else: - os.environ["POSIXLY_CORRECT"] = old_posixly_correct - -#------------------------------------------------------------------------------ - -libreftest = """ -Examples from the Library Reference: Doc/lib/libgetopt.tex - -An example using only Unix style options: - - ->>> import getopt ->>> args = '-a -b -cfoo -d bar a1 a2'.split() ->>> args -['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2'] ->>> optlist, args = getopt.getopt(args, 'abc:d:') ->>> optlist -[('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')] ->>> args -['a1', 'a2'] - -Using long option names is equally easy: - - ->>> s = '--condition=foo --testing --output-file abc.def -x a1 a2' ->>> args = s.split() ->>> args -['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2'] ->>> optlist, args = getopt.getopt(args, 'x', [ -... 'condition=', 'output-file=', 'testing']) ->>> optlist -[('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')] ->>> args -['a1', 'a2'] - -""" - -__test__ = {'libreftest' : libreftest} - -import sys -run_doctest(sys.modules[__name__], verbose) - -#------------------------------------------------------------------------------ - -if verbose: - print("Module getopt: tests completed successfully.") +sentinel = object() + +class GetoptTests(unittest.TestCase): + def setUp(self): + self.old_posixly_correct = os.environ.get("POSIXLY_CORRECT", sentinel) + if self.old_posixly_correct is not sentinel: + del os.environ["POSIXLY_CORRECT"] + + def tearDown(self): + if self.old_posixly_correct is sentinel: + os.environ.pop("POSIXLY_CORRECT", None) + else: + os.environ["POSIXLY_CORRECT"] = self.old_posixly_correct + + def assertError(self, *args, **kwargs): + self.assertRaises(getopt.GetoptError, *args, **kwargs) + + def test_short_has_arg(self): + self.failUnless(getopt.short_has_arg('a', 'a:')) + self.failIf(getopt.short_has_arg('a', 'a')) + self.assertError(getopt.short_has_arg, 'a', 'b') + + def test_long_has_args(self): + has_arg, option = getopt.long_has_args('abc', ['abc=']) + self.failUnless(has_arg) + self.assertEqual(option, 'abc') + + has_arg, option = getopt.long_has_args('abc', ['abc']) + self.failIf(has_arg) + self.assertEqual(option, 'abc') + + has_arg, option = getopt.long_has_args('abc', ['abcd']) + self.failIf(has_arg) + self.assertEqual(option, 'abcd') + + self.assertError(getopt.long_has_args, 'abc', ['def']) + self.assertError(getopt.long_has_args, 'abc', []) + self.assertError(getopt.long_has_args, 'abc', ['abcd','abcde']) + + def test_do_shorts(self): + opts, args = getopt.do_shorts([], 'a', 'a', []) + self.assertEqual(opts, [('-a', '')]) + self.assertEqual(args, []) + + opts, args = getopt.do_shorts([], 'a1', 'a:', []) + self.assertEqual(opts, [('-a', '1')]) + self.assertEqual(args, []) + + #opts, args = getopt.do_shorts([], 'a=1', 'a:', []) + #self.assertEqual(opts, [('-a', '1')]) + #self.assertEqual(args, []) + + opts, args = getopt.do_shorts([], 'a', 'a:', ['1']) + self.assertEqual(opts, [('-a', '1')]) + self.assertEqual(args, []) + + opts, args = getopt.do_shorts([], 'a', 'a:', ['1', '2']) + self.assertEqual(opts, [('-a', '1')]) + self.assertEqual(args, ['2']) + + self.assertError(getopt.do_shorts, [], 'a1', 'a', []) + self.assertError(getopt.do_shorts, [], 'a', 'a:', []) + + def test_do_longs(self): + opts, args = getopt.do_longs([], 'abc', ['abc'], []) + self.assertEqual(opts, [('--abc', '')]) + self.assertEqual(args, []) + + opts, args = getopt.do_longs([], 'abc=1', ['abc='], []) + self.assertEqual(opts, [('--abc', '1')]) + self.assertEqual(args, []) + + opts, args = getopt.do_longs([], 'abc=1', ['abcd='], []) + self.assertEqual(opts, [('--abcd', '1')]) + self.assertEqual(args, []) + + opts, args = getopt.do_longs([], 'abc', ['ab', 'abc', 'abcd'], []) + self.assertEqual(opts, [('--abc', '')]) + self.assertEqual(args, []) + + # Much like the preceding, except with a non-alpha character ("-") in + # option name that precedes "="; failed in + # http://python.org/sf/126863 + opts, args = getopt.do_longs([], 'foo=42', ['foo-bar', 'foo=',], []) + self.assertEqual(opts, [('--foo', '42')]) + self.assertEqual(args, []) + + self.assertError(getopt.do_longs, [], 'abc=1', ['abc'], []) + self.assertError(getopt.do_longs, [], 'abc', ['abc='], []) + + def test_getopt(self): + # note: the empty string between '-a' and '--beta' is significant: + # it simulates an empty string option argument ('-a ""') on the + # command line. + cmdline = ['-a', '1', '-b', '--alpha=2', '--beta', '-a', '3', '-a', + '', '--beta', 'arg1', 'arg2'] + + opts, args = getopt.getopt(cmdline, 'a:b', ['alpha=', 'beta']) + self.assertEqual(opts, [('-a', '1'), ('-b', ''), + ('--alpha', '2'), ('--beta', ''), + ('-a', '3'), ('-a', ''), ('--beta', '')]) + # Note ambiguity of ('-b', '') and ('-a', '') above. This must be + # accounted for in the code that calls getopt(). + self.assertEqual(args, ['arg1', 'arg2']) + + self.assertError(getopt.getopt, cmdline, 'a:b', ['alpha', 'beta']) + + def test_gnu_getopt(self): + # Test handling of GNU style scanning mode. + cmdline = ['-a', 'arg1', '-b', '1', '--alpha', '--beta=2'] + + # GNU style + opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) + self.assertEqual(args, ['arg1']) + self.assertEqual(opts, [('-a', ''), ('-b', '1'), + ('--alpha', ''), ('--beta', '2')]) + + # Posix style via + + opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta=']) + self.assertEqual(opts, [('-a', '')]) + self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2']) + + # Posix style via POSIXLY_CORRECT + os.environ["POSIXLY_CORRECT"] = "1" + opts, args = getopt.gnu_getopt(cmdline, 'ab:', ['alpha', 'beta=']) + self.assertEqual(opts, [('-a', '')]) + self.assertEqual(args, ['arg1', '-b', '1', '--alpha', '--beta=2']) + + def test_libref_examples(self): + s = """ + Examples from the Library Reference: Doc/lib/libgetopt.tex + + An example using only Unix style options: + + + >>> import getopt + >>> args = '-a -b -cfoo -d bar a1 a2'.split() + >>> args + ['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2'] + >>> optlist, args = getopt.getopt(args, 'abc:d:') + >>> optlist + [('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')] + >>> args + ['a1', 'a2'] + + Using long option names is equally easy: + + + >>> s = '--condition=foo --testing --output-file abc.def -x a1 a2' + >>> args = s.split() + >>> args + ['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2'] + >>> optlist, args = getopt.getopt(args, 'x', [ + ... 'condition=', 'output-file=', 'testing']) + >>> optlist + [('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')] + >>> args + ['a1', 'a2'] + """ + + import new + m = new.module("libreftest", s) + run_doctest(m, verbose) + + +def test_main(): + run_unittest(GetoptTests) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 76253de..ab6bc9a 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -4,7 +4,7 @@ import shutil import gettext import unittest -from test.test_support import run_suite +from test import test_support # TODO: @@ -336,19 +336,8 @@ class WeirdMetadataTest(GettextBaseTest): 'John Doe <jdoe@example.com>\nJane Foobar <jfoobar@example.com>') -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(GettextTestCase1)) - suite.addTest(unittest.makeSuite(GettextTestCase2)) - suite.addTest(unittest.makeSuite(PluralFormsTestCase)) - suite.addTest(unittest.makeSuite(UnicodeTranslationsTest)) - suite.addTest(unittest.makeSuite(WeirdMetadataTest)) - return suite - - def test_main(): - run_suite(suite()) - + test_support.run_unittest(__name__) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 5ce09f9..f1993ab 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -52,6 +52,16 @@ class GlobTests(unittest.TestCase): eq(self.glob('aab'), [self.norm('aab')]) eq(self.glob('zymurgy'), []) + # test return types are unicode, but only if os.listdir + # returns unicode filenames + uniset = set([unicode]) + tmp = os.listdir(u'.') + if set(type(x) for x in tmp) == uniset: + u1 = glob.glob(u'*') + u2 = glob.glob(u'./*') + self.assertEquals(set(type(r) for r in u1), uniset) + self.assertEquals(set(type(r) for r in u2), uniset) + def test_glob_one_directory(self): eq = self.assertSequencesEqual_noorder eq(self.glob('a*'), map(self.norm, ['a', 'aab', 'aaa'])) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 85be05b..96cf824 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -364,7 +364,7 @@ class GrammarTests(unittest.TestCase): x = 1; pass; del x; foo() - ### small_stmt: expr_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt + ### small_stmt: expr_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt # Tested below def testExprStmt(self): diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 54b90cd..229bbed 100755 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -309,6 +309,11 @@ DOCTYPE html [ ("endtag", "script"), ]) + def test_entityrefs_in_attributes(self): + self._run_check("<html foo='€&aa&unsupported;'>", [ + ("starttag", "html", [("foo", u"\u20AC&aa&unsupported;")]) + ]) + def test_main(): test_support.run_unittest(HTMLParserTestCase) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 90a4e55..035f0b9 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1,6 +1,7 @@ import httplib import StringIO import sys +import socket from unittest import TestCase @@ -149,8 +150,52 @@ class OfflineTest(TestCase): def test_responses(self): self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found") +PORT = 50003 +HOST = "localhost" + +class TimeoutTest(TestCase): + + def setUp(self): + self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + global PORT + PORT = test_support.bind_port(self.serv, HOST, PORT) + self.serv.listen(5) + + def tearDown(self): + self.serv.close() + self.serv = None + + def testTimeoutAttribute(self): + '''This will prove that the timeout gets through + HTTPConnection and into the socket. + ''' + # default + httpConn = httplib.HTTPConnection(HOST, PORT) + httpConn.connect() + self.assertTrue(httpConn.sock.gettimeout() is None) + httpConn.close() + + # a value + httpConn = httplib.HTTPConnection(HOST, PORT, timeout=30) + httpConn.connect() + self.assertEqual(httpConn.sock.gettimeout(), 30) + httpConn.close() + + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + httpConn = httplib.HTTPConnection(HOST, PORT, timeout=None) + httpConn.connect() + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(httpConn.sock.gettimeout(), 30) + httpConn.close() + + def test_main(verbose=None): - test_support.run_unittest(HeaderTests, OfflineTest, BasicTest) + test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index a8f912f..87907c8 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -193,6 +193,16 @@ class ImportTest(unittest.TestCase): if TESTFN in sys.modules: del sys.modules[TESTFN] + def test_infinite_reload(self): + # Bug #742342 reports that Python segfaults (infinite recursion in C) + # when faced with self-recursive reload()ing. + + sys.path.insert(0, os.path.dirname(__file__)) + try: + import infinite_reload + finally: + sys.path.pop(0) + def test_import_name_binding(self): # import x.y.z binds x in the current namespace import test as x diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index d74b9d5..98c79c7 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -215,20 +215,20 @@ class TestBasicOps(unittest.TestCase): self.assertEqual(list(izip_longest(*args, **{})), target) target = [tuple((e is None and 'X' or e) for e in t) for t in target] # Replace None fills with 'X' self.assertEqual(list(izip_longest(*args, **dict(fillvalue='X'))), target) - + self.assertEqual(take(3,izip_longest('abcdef', count())), list(zip('abcdef', range(3)))) # take 3 from infinite input self.assertEqual(list(izip_longest()), list(zip())) self.assertEqual(list(izip_longest([])), list(zip([]))) self.assertEqual(list(izip_longest('abcdef')), list(zip('abcdef'))) - + self.assertEqual(list(izip_longest('abc', 'defg', **{})), map(None, 'abc', 'defg')) # empty keyword dict self.assertRaises(TypeError, izip_longest, 3) self.assertRaises(TypeError, izip_longest, range(3), 3) for stmt in [ "izip_longest('abc', fv=1)", - "izip_longest('abc', fillvalue=1, bogus_keyword=None)", + "izip_longest('abc', fillvalue=1, bogus_keyword=None)", ]: try: eval(stmt, globals(), locals()) @@ -236,7 +236,7 @@ class TestBasicOps(unittest.TestCase): pass else: self.fail('Did not raise Type in: ' + stmt) - + # Check tuple re-use (implementation detail) self.assertEqual([tuple(list(pair)) for pair in izip_longest('abc', 'def')], list(zip('abc', 'def'))) @@ -818,7 +818,7 @@ libreftest = """ Doctest for examples in the library reference: libitertools.tex >>> amounts = [120.15, 764.05, 823.14] >>> for checknum, amount in izip(count(1200), amounts): ... print('Check %d is for $%.2f' % (checknum, amount)) -... +... Check 1200 is for $120.15 Check 1201 is for $764.05 Check 1202 is for $823.14 @@ -826,7 +826,7 @@ Check 1202 is for $823.14 >>> import operator >>> for cube in imap(operator.pow, xrange(1,4), repeat(3)): ... print(cube) -... +... 1 8 27 @@ -834,7 +834,7 @@ Check 1202 is for $823.14 >>> reportlines = ['EuroPython', 'Roster', '', 'alex', '', 'laura', '', 'martin', '', 'walter', '', 'samuele'] >>> for name in islice(reportlines, 3, None, 2): ... print(name.title()) -... +... Alex Laura Martin @@ -846,7 +846,7 @@ Samuele >>> di = sorted(sorted(d.items()), key=itemgetter(1)) >>> for k, g in groupby(di, itemgetter(1)): ... print(k, map(itemgetter(0), g)) -... +... 1 ['a', 'c', 'e'] 2 ['b', 'd', 'f'] 3 ['g'] @@ -857,7 +857,7 @@ Samuele >>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28] >>> for k, g in groupby(enumerate(data), lambda (i,x):i-x): ... print(map(operator.itemgetter(1), g)) -... +... [1] [4, 5, 6] [10] diff --git a/Lib/test/test_keywordonlyarg.py b/Lib/test/test_keywordonlyarg.py index 2e1f8bd..fc67c98 100644 --- a/Lib/test/test_keywordonlyarg.py +++ b/Lib/test/test_keywordonlyarg.py @@ -71,7 +71,7 @@ class KeywordOnlyArgTestCase(unittest.TestCase): fundef3 += "i%d, "%i fundef3 += "lastarg):\n pass\n" compile(fundef3, "<test>", "single") - + def testSyntaxErrorForFunctionCall(self): self.assertRaisesSyntaxError("f(p, k=1, p2)") self.assertRaisesSyntaxError("f(p, *(1,2), k1=100)") diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index ba7d653..eba2cfd 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -7,7 +7,7 @@ if sys.platform == 'darwin': oldlocale = locale.setlocale(locale.LC_NUMERIC) if sys.platform.startswith("win"): - tlocs = ("en",) + tlocs = ("En", "English") else: tlocs = ("en_US.UTF-8", "en_US.US-ASCII", "en_US") diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 843440a..e8e4a8d 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -554,6 +554,8 @@ def test5(): except KeyError: logging.exception("just testing") os.remove(fn) + hdlr = logging.getLogger().handlers[0] + logging.getLogger().handlers.remove(hdlr) finally: logging._acquireLock() try: diff --git a/Lib/test/test_long_future.py b/Lib/test/test_long_future.py index fc01001..36840b4 100644 --- a/Lib/test/test_long_future.py +++ b/Lib/test/test_long_future.py @@ -3,53 +3,53 @@ from __future__ import division # test_long.py instead. In the meantime, it's too obscure to try to # trick just part of test_long into using future division. -from test.test_support import TestFailed, verify, verbose - -def test_true_division(): - if verbose: - print("long true division") - huge = 1 << 40000 - mhuge = -huge - verify(huge / huge == 1.0) - verify(mhuge / mhuge == 1.0) - verify(huge / mhuge == -1.0) - verify(mhuge / huge == -1.0) - verify(1 / huge == 0.0) - verify(1 / huge == 0.0) - verify(1 / mhuge == 0.0) - verify(1 / mhuge == 0.0) - verify((666 * huge + (huge >> 1)) / huge == 666.5) - verify((666 * mhuge + (mhuge >> 1)) / mhuge == 666.5) - verify((666 * huge + (huge >> 1)) / mhuge == -666.5) - verify((666 * mhuge + (mhuge >> 1)) / huge == -666.5) - verify(huge / (huge << 1) == 0.5) - verify((1000000 * huge) / huge == 1000000) - - namespace = {'huge': huge, 'mhuge': mhuge} - - for overflow in ["float(huge)", "float(mhuge)", - "huge / 1", "huge / 2", "huge / -1", "huge / -2", - "mhuge / 100", "mhuge / 100"]: - try: - eval(overflow, namespace) - except OverflowError: - pass - else: - raise TestFailed("expected OverflowError from %r" % overflow) - - for underflow in ["1 / huge", "2 / huge", "-1 / huge", "-2 / huge", - "100 / mhuge", "100 / mhuge"]: - result = eval(underflow, namespace) - if result != 0.0: - raise TestFailed("expected underflow to 0 from %r" % underflow) - - for zero in ["huge / 0", "huge / 0", - "mhuge / 0", "mhuge / 0"]: - try: - eval(zero, namespace) - except ZeroDivisionError: - pass - else: - raise TestFailed("expected ZeroDivisionError from %r" % zero) - -test_true_division() +import unittest +from test.test_support import run_unittest + +class TrueDivisionTests(unittest.TestCase): + def test(self): + huge = 1 << 40000 + mhuge = -huge + self.assertEqual(huge / huge, 1.0) + self.assertEqual(mhuge / mhuge, 1.0) + self.assertEqual(huge / mhuge, -1.0) + self.assertEqual(mhuge / huge, -1.0) + self.assertEqual(1 / huge, 0.0) + self.assertEqual(1 / huge, 0.0) + self.assertEqual(1 / mhuge, 0.0) + self.assertEqual(1 / mhuge, 0.0) + self.assertEqual((666 * huge + (huge >> 1)) / huge, 666.5) + self.assertEqual((666 * mhuge + (mhuge >> 1)) / mhuge, 666.5) + self.assertEqual((666 * huge + (huge >> 1)) / mhuge, -666.5) + self.assertEqual((666 * mhuge + (mhuge >> 1)) / huge, -666.5) + self.assertEqual(huge / (huge << 1), 0.5) + self.assertEqual((1000000 * huge) / huge, 1000000) + + namespace = {'huge': huge, 'mhuge': mhuge} + + for overflow in ["float(huge)", "float(mhuge)", + "huge / 1", "huge / 2", "huge / -1", "huge / -2", + "mhuge / 100", "mhuge / 200"]: + # XXX(cwinter) this test doesn't pass when converted to + # use assertRaises. + try: + eval(overflow, namespace) + self.fail("expected OverflowError from %r" % overflow) + except OverflowError: + pass + + for underflow in ["1 / huge", "2 / huge", "-1 / huge", "-2 / huge", + "100 / mhuge", "200 / mhuge"]: + result = eval(underflow, namespace) + self.assertEqual(result, 0.0, + "expected underflow to 0 from %r" % underflow) + + for zero in ["huge / 0", "mhuge / 0"]: + self.assertRaises(ZeroDivisionError, eval, zero, namespace) + + +def test_main(): + run_unittest(TrueDivisionTests) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_macpath.py b/Lib/test/test_macpath.py index 3a3cf04..2449b0a 100644 --- a/Lib/test/test_macpath.py +++ b/Lib/test/test_macpath.py @@ -48,7 +48,7 @@ class MacPathTestCase(unittest.TestCase): splitext = macpath.splitext self.assertEquals(splitext(":foo.ext"), (':foo', '.ext')) self.assertEquals(splitext("foo:foo.ext"), ('foo:foo', '.ext')) - self.assertEquals(splitext(".ext"), ('', '.ext')) + self.assertEquals(splitext(".ext"), ('.ext', '')) self.assertEquals(splitext("foo.ext:foo"), ('foo.ext:foo', '')) self.assertEquals(splitext(":foo.ext:"), (':foo.ext:', '')) self.assertEquals(splitext(""), ('', '')) diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 8650cef..1972ca3 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -54,6 +54,7 @@ class TestMailbox(TestBase): def setUp(self): self._path = test_support.TESTFN + self._delete_recursively(self._path) self._box = self._factory(self._path) def tearDown(self): @@ -686,7 +687,7 @@ class _TestMboxMMDF(TestMailbox): self._box.close() self._delete_recursively(self._path) for lock_remnant in glob.glob(self._path + '.*'): - os.remove(lock_remnant) + test_support.unlink(lock_remnant) def test_add_from_string(self): # Add a string starting with 'From ' to the mailbox @@ -909,7 +910,7 @@ class TestBabyl(TestMailbox): self._box.close() self._delete_recursively(self._path) for lock_remnant in glob.glob(self._path + '.*'): - os.remove(lock_remnant) + test_support.unlink(lock_remnant) def test_labels(self): # Get labels from the mailbox diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index df81079..9126cf6 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -63,6 +63,8 @@ Use a metaclass with a __prepare__ static method. ... def __new__(cls, name, bases, namespace, **kwds): ... print("New called:", kwds) ... return type.__new__(cls, name, bases, namespace) + ... def __init__(cls, *args, **kwds): + ... pass ... >>> class C(metaclass=M): ... def meth(self): print("Hello") diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 6c4dd94..5f95365 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -5,7 +5,8 @@ import sys import pickle import traceback from StringIO import StringIO -from test.test_support import verbose +from test.test_support import verbose, run_unittest, TestSkipped +import unittest import xml.dom import xml.dom.minidom @@ -22,680 +23,9 @@ else: tstfile = os.path.join(os.path.dirname(base), "test"+os.extsep+"xml") del base -def confirm(test, testname = "Test"): - if not test: - print("Failed " + testname) - raise Exception - -def testParseFromFile(): - dom = parse(StringIO(open(tstfile).read())) - dom.unlink() - confirm(isinstance(dom,Document)) - -def testGetElementsByTagName(): - dom = parse(tstfile) - confirm(dom.getElementsByTagName("LI") == \ - dom.documentElement.getElementsByTagName("LI")) - dom.unlink() - -def testInsertBefore(): - dom = parseString("<doc><foo/></doc>") - root = dom.documentElement - elem = root.childNodes[0] - nelem = dom.createElement("element") - root.insertBefore(nelem, elem) - confirm(len(root.childNodes) == 2 - and root.childNodes.length == 2 - and root.childNodes[0] is nelem - and root.childNodes.item(0) is nelem - and root.childNodes[1] is elem - and root.childNodes.item(1) is elem - and root.firstChild is nelem - and root.lastChild is elem - and root.toxml() == "<doc><element/><foo/></doc>" - , "testInsertBefore -- node properly placed in tree") - nelem = dom.createElement("element") - root.insertBefore(nelem, None) - confirm(len(root.childNodes) == 3 - and root.childNodes.length == 3 - and root.childNodes[1] is elem - and root.childNodes.item(1) is elem - and root.childNodes[2] is nelem - and root.childNodes.item(2) is nelem - and root.lastChild is nelem - and nelem.previousSibling is elem - and root.toxml() == "<doc><element/><foo/><element/></doc>" - , "testInsertBefore -- node properly placed in tree") - nelem2 = dom.createElement("bar") - root.insertBefore(nelem2, nelem) - confirm(len(root.childNodes) == 4 - and root.childNodes.length == 4 - and root.childNodes[2] is nelem2 - and root.childNodes.item(2) is nelem2 - and root.childNodes[3] is nelem - and root.childNodes.item(3) is nelem - and nelem2.nextSibling is nelem - and nelem.previousSibling is nelem2 - and root.toxml() == "<doc><element/><foo/><bar/><element/></doc>" - , "testInsertBefore -- node properly placed in tree") - dom.unlink() - -def _create_fragment_test_nodes(): - dom = parseString("<doc/>") - orig = dom.createTextNode("original") - c1 = dom.createTextNode("foo") - c2 = dom.createTextNode("bar") - c3 = dom.createTextNode("bat") - dom.documentElement.appendChild(orig) - frag = dom.createDocumentFragment() - frag.appendChild(c1) - frag.appendChild(c2) - frag.appendChild(c3) - return dom, orig, c1, c2, c3, frag - -def testInsertBeforeFragment(): - dom, orig, c1, c2, c3, frag = _create_fragment_test_nodes() - dom.documentElement.insertBefore(frag, None) - confirm(tuple(dom.documentElement.childNodes) == (orig, c1, c2, c3), - "insertBefore(<fragment>, None)") - frag.unlink() - dom.unlink() - # - dom, orig, c1, c2, c3, frag = _create_fragment_test_nodes() - dom.documentElement.insertBefore(frag, orig) - confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3, orig), - "insertBefore(<fragment>, orig)") - frag.unlink() - dom.unlink() - -def testAppendChild(): - dom = parse(tstfile) - dom.documentElement.appendChild(dom.createComment(u"Hello")) - confirm(dom.documentElement.childNodes[-1].nodeName == "#comment") - confirm(dom.documentElement.childNodes[-1].data == "Hello") - dom.unlink() - -def testAppendChildFragment(): - dom, orig, c1, c2, c3, frag = _create_fragment_test_nodes() - dom.documentElement.appendChild(frag) - confirm(tuple(dom.documentElement.childNodes) == (orig, c1, c2, c3), - "appendChild(<fragment>)") - frag.unlink() - dom.unlink() - -def testReplaceChildFragment(): - dom, orig, c1, c2, c3, frag = _create_fragment_test_nodes() - dom.documentElement.replaceChild(frag, orig) - orig.unlink() - confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3), - "replaceChild(<fragment>)") - frag.unlink() - dom.unlink() - -def testLegalChildren(): - dom = Document() - elem = dom.createElement('element') - text = dom.createTextNode('text') - - try: dom.appendChild(text) - except xml.dom.HierarchyRequestErr: pass - else: - print("dom.appendChild didn't raise HierarchyRequestErr") - - dom.appendChild(elem) - try: dom.insertBefore(text, elem) - except xml.dom.HierarchyRequestErr: pass - else: - print("dom.appendChild didn't raise HierarchyRequestErr") - - try: dom.replaceChild(text, elem) - except xml.dom.HierarchyRequestErr: pass - else: - print("dom.appendChild didn't raise HierarchyRequestErr") - - nodemap = elem.attributes - try: nodemap.setNamedItem(text) - except xml.dom.HierarchyRequestErr: pass - else: - print("NamedNodeMap.setNamedItem didn't raise HierarchyRequestErr") - - try: nodemap.setNamedItemNS(text) - except xml.dom.HierarchyRequestErr: pass - else: - print("NamedNodeMap.setNamedItemNS didn't raise HierarchyRequestErr") - - elem.appendChild(text) - dom.unlink() - -def testNamedNodeMapSetItem(): - dom = Document() - elem = dom.createElement('element') - attrs = elem.attributes - attrs["foo"] = "bar" - a = attrs.item(0) - confirm(a.ownerDocument is dom, - "NamedNodeMap.__setitem__() sets ownerDocument") - confirm(a.ownerElement is elem, - "NamedNodeMap.__setitem__() sets ownerElement") - confirm(a.value == "bar", - "NamedNodeMap.__setitem__() sets value") - confirm(a.nodeValue == "bar", - "NamedNodeMap.__setitem__() sets nodeValue") - elem.unlink() - dom.unlink() - -def testNonZero(): - dom = parse(tstfile) - confirm(dom)# should not be zero - dom.appendChild(dom.createComment("foo")) - confirm(not dom.childNodes[-1].childNodes) - dom.unlink() - -def testUnlink(): - dom = parse(tstfile) - dom.unlink() - -def testElement(): - dom = Document() - dom.appendChild(dom.createElement("abc")) - confirm(dom.documentElement) - dom.unlink() - -def testAAA(): - dom = parseString("<abc/>") - el = dom.documentElement - el.setAttribute("spam", "jam2") - confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA") - a = el.getAttributeNode("spam") - confirm(a.ownerDocument is dom, - "setAttribute() sets ownerDocument") - confirm(a.ownerElement is dom.documentElement, - "setAttribute() sets ownerElement") - dom.unlink() - -def testAAB(): - dom = parseString("<abc/>") - el = dom.documentElement - el.setAttribute("spam", "jam") - el.setAttribute("spam", "jam2") - confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB") - dom.unlink() - -def testAddAttr(): - dom = Document() - child = dom.appendChild(dom.createElement("abc")) - - child.setAttribute("def", "ghi") - confirm(child.getAttribute("def") == "ghi") - confirm(child.attributes["def"].value == "ghi") - - child.setAttribute("jkl", "mno") - confirm(child.getAttribute("jkl") == "mno") - confirm(child.attributes["jkl"].value == "mno") - - confirm(len(child.attributes) == 2) - - child.setAttribute("def", "newval") - confirm(child.getAttribute("def") == "newval") - confirm(child.attributes["def"].value == "newval") - - confirm(len(child.attributes) == 2) - dom.unlink() - -def testDeleteAttr(): - dom = Document() - child = dom.appendChild(dom.createElement("abc")) - - confirm(len(child.attributes) == 0) - child.setAttribute("def", "ghi") - confirm(len(child.attributes) == 1) - del child.attributes["def"] - confirm(len(child.attributes) == 0) - dom.unlink() - -def testRemoveAttr(): - dom = Document() - child = dom.appendChild(dom.createElement("abc")) - - child.setAttribute("def", "ghi") - confirm(len(child.attributes) == 1) - child.removeAttribute("def") - confirm(len(child.attributes) == 0) - - dom.unlink() - -def testRemoveAttrNS(): - dom = Document() - child = dom.appendChild( - dom.createElementNS("http://www.python.org", "python:abc")) - child.setAttributeNS("http://www.w3.org", "xmlns:python", - "http://www.python.org") - child.setAttributeNS("http://www.python.org", "python:abcattr", "foo") - confirm(len(child.attributes) == 2) - child.removeAttributeNS("http://www.python.org", "abcattr") - confirm(len(child.attributes) == 1) - - dom.unlink() - -def testRemoveAttributeNode(): - dom = Document() - child = dom.appendChild(dom.createElement("foo")) - child.setAttribute("spam", "jam") - confirm(len(child.attributes) == 1) - node = child.getAttributeNode("spam") - child.removeAttributeNode(node) - confirm(len(child.attributes) == 0 - and child.getAttributeNode("spam") is None) - - dom.unlink() - -def testChangeAttr(): - dom = parseString("<abc/>") - el = dom.documentElement - el.setAttribute("spam", "jam") - confirm(len(el.attributes) == 1) - el.setAttribute("spam", "bam") - # Set this attribute to be an ID and make sure that doesn't change - # when changing the value: - el.setIdAttribute("spam") - confirm(len(el.attributes) == 1 - and el.attributes["spam"].value == "bam" - and el.attributes["spam"].nodeValue == "bam" - and el.getAttribute("spam") == "bam" - and el.getAttributeNode("spam").isId) - el.attributes["spam"] = "ham" - confirm(len(el.attributes) == 1 - and el.attributes["spam"].value == "ham" - and el.attributes["spam"].nodeValue == "ham" - and el.getAttribute("spam") == "ham" - and el.attributes["spam"].isId) - el.setAttribute("spam2", "bam") - confirm(len(el.attributes) == 2 - and el.attributes["spam"].value == "ham" - and el.attributes["spam"].nodeValue == "ham" - and el.getAttribute("spam") == "ham" - and el.attributes["spam2"].value == "bam" - and el.attributes["spam2"].nodeValue == "bam" - and el.getAttribute("spam2") == "bam") - el.attributes["spam2"] = "bam2" - confirm(len(el.attributes) == 2 - and el.attributes["spam"].value == "ham" - and el.attributes["spam"].nodeValue == "ham" - and el.getAttribute("spam") == "ham" - and el.attributes["spam2"].value == "bam2" - and el.attributes["spam2"].nodeValue == "bam2" - and el.getAttribute("spam2") == "bam2") - dom.unlink() - -def testGetAttrList(): - pass - -def testGetAttrValues(): pass - -def testGetAttrLength(): pass - -def testGetAttribute(): pass - -def testGetAttributeNS(): pass - -def testGetAttributeNode(): pass - -def testGetElementsByTagNameNS(): - d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'> - <minidom:myelem/> - </foo>""" - dom = parseString(d) - elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom", "myelem") - confirm(len(elems) == 1 - and elems[0].namespaceURI == "http://pyxml.sf.net/minidom" - and elems[0].localName == "myelem" - and elems[0].prefix == "minidom" - and elems[0].tagName == "minidom:myelem" - and elems[0].nodeName == "minidom:myelem") - dom.unlink() - -def get_empty_nodelist_from_elements_by_tagName_ns_helper(doc, nsuri, lname): - nodelist = doc.getElementsByTagNameNS(nsuri, lname) - confirm(len(nodelist) == 0) - -def testGetEmptyNodeListFromElementsByTagNameNS(): - doc = parseString('<doc/>') - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, 'http://xml.python.org/namespaces/a', 'localname') - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, '*', 'splat') - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, 'http://xml.python.org/namespaces/a', '*') - - doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>') - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, "http://xml.python.org/splat", "not-there") - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, "*", "not-there") - get_empty_nodelist_from_elements_by_tagName_ns_helper( - doc, "http://somewhere.else.net/not-there", "e") - -def testElementReprAndStr(): - dom = Document() - el = dom.appendChild(dom.createElement("abc")) - string1 = repr(el) - string2 = str(el) - confirm(string1 == string2) - dom.unlink() - -# commented out until Fredrick's fix is checked in -def _testElementReprAndStrUnicode(): - dom = Document() - el = dom.appendChild(dom.createElement(u"abc")) - string1 = repr(el) - string2 = str(el) - confirm(string1 == string2) - dom.unlink() - -# commented out until Fredrick's fix is checked in -def _testElementReprAndStrUnicodeNS(): - dom = Document() - el = dom.appendChild( - dom.createElementNS(u"http://www.slashdot.org", u"slash:abc")) - string1 = repr(el) - string2 = str(el) - confirm(string1 == string2) - confirm(string1.find("slash:abc") != -1) - dom.unlink() - -def testAttributeRepr(): - dom = Document() - el = dom.appendChild(dom.createElement(u"abc")) - node = el.setAttribute("abc", "def") - confirm(str(node) == repr(node)) - dom.unlink() - -def testTextNodeRepr(): pass - -def testWriteXML(): - str = '<?xml version="1.0" ?><a b="c"/>' - dom = parseString(str) - domstr = dom.toxml() - dom.unlink() - confirm(str == domstr) - -def testAltNewline(): - str = '<?xml version="1.0" ?>\n<a b="c"/>\n' - dom = parseString(str) - domstr = dom.toprettyxml(newl="\r\n") - dom.unlink() - confirm(domstr == str.replace("\n", "\r\n")) - -def testProcessingInstruction(): - dom = parseString('<e><?mypi \t\n data \t\n ?></e>') - pi = dom.documentElement.firstChild - confirm(pi.target == "mypi" - and pi.data == "data \t\n " - and pi.nodeName == "mypi" - and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE - and pi.attributes is None - and not pi.hasChildNodes() - and len(pi.childNodes) == 0 - and pi.firstChild is None - and pi.lastChild is None - and pi.localName is None - and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE) - -def testProcessingInstructionRepr(): pass - -def testTextRepr(): pass - -def testWriteText(): pass - -def testDocumentElement(): pass - -def testTooManyDocumentElements(): - doc = parseString("<doc/>") - elem = doc.createElement("extra") - try: - doc.appendChild(elem) - except xml.dom.HierarchyRequestErr: - pass - else: - print("Failed to catch expected exception when" \ - " adding extra document element.") - elem.unlink() - doc.unlink() - -def testCreateElementNS(): pass - -def testCreateAttributeNS(): pass - -def testParse(): pass - -def testParseString(): pass - -def testComment(): pass - -def testAttrListItem(): pass - -def testAttrListItems(): pass - -def testAttrListItemNS(): pass - -def testAttrListKeys(): pass - -def testAttrListKeysNS(): pass - -def testRemoveNamedItem(): - doc = parseString("<doc a=''/>") - e = doc.documentElement - attrs = e.attributes - a1 = e.getAttributeNode("a") - a2 = attrs.removeNamedItem("a") - confirm(a1.isSameNode(a2)) - try: - attrs.removeNamedItem("a") - except xml.dom.NotFoundErr: - pass - -def testRemoveNamedItemNS(): - doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>") - e = doc.documentElement - attrs = e.attributes - a1 = e.getAttributeNodeNS("http://xml.python.org/", "b") - a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b") - confirm(a1.isSameNode(a2)) - try: - attrs.removeNamedItemNS("http://xml.python.org/", "b") - except xml.dom.NotFoundErr: - pass - -def testAttrListValues(): pass - -def testAttrListLength(): pass - -def testAttrList__getitem__(): pass - -def testAttrList__setitem__(): pass - -def testSetAttrValueandNodeValue(): pass - -def testParseElement(): pass - -def testParseAttributes(): pass - -def testParseElementNamespaces(): pass - -def testParseAttributeNamespaces(): pass - -def testParseProcessingInstructions(): pass - -def testChildNodes(): pass - -def testFirstChild(): pass - -def testHasChildNodes(): pass - -def testCloneElementShallow(): - dom, clone = _setupCloneElement(0) - confirm(len(clone.childNodes) == 0 - and clone.childNodes.length == 0 - and clone.parentNode is None - and clone.toxml() == '<doc attr="value"/>' - , "testCloneElementShallow") - dom.unlink() - -def testCloneElementDeep(): - dom, clone = _setupCloneElement(1) - confirm(len(clone.childNodes) == 1 - and clone.childNodes.length == 1 - and clone.parentNode is None - and clone.toxml() == '<doc attr="value"><foo/></doc>' - , "testCloneElementDeep") - dom.unlink() - -def _setupCloneElement(deep): - dom = parseString("<doc attr='value'><foo/></doc>") - root = dom.documentElement - clone = root.cloneNode(deep) - _testCloneElementCopiesAttributes( - root, clone, "testCloneElement" + (deep and "Deep" or "Shallow")) - # mutilate the original so shared data is detected - root.tagName = root.nodeName = "MODIFIED" - root.setAttribute("attr", "NEW VALUE") - root.setAttribute("added", "VALUE") - return dom, clone - -def _testCloneElementCopiesAttributes(e1, e2, test): - attrs1 = e1.attributes - attrs2 = e2.attributes - keys1 = sorted(attrs1.keys()) - keys2 = sorted(attrs2.keys()) - confirm(keys1 == keys2, "clone of element has same attribute keys") - for i in range(len(keys1)): - a1 = attrs1.item(i) - a2 = attrs2.item(i) - confirm(a1 is not a2 - and a1.value == a2.value - and a1.nodeValue == a2.nodeValue - and a1.namespaceURI == a2.namespaceURI - and a1.localName == a2.localName - , "clone of attribute node has proper attribute values") - confirm(a2.ownerElement is e2, - "clone of attribute node correctly owned") - -def testCloneDocumentShallow(): - doc = parseString("<?xml version='1.0'?>\n" - "<!-- comment -->" - "<!DOCTYPE doc [\n" - "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" - "]>\n" - "<doc attr='value'/>") - doc2 = doc.cloneNode(0) - confirm(doc2 is None, - "testCloneDocumentShallow:" - " shallow cloning of documents makes no sense!") - -def testCloneDocumentDeep(): - doc = parseString("<?xml version='1.0'?>\n" - "<!-- comment -->" - "<!DOCTYPE doc [\n" - "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" - "]>\n" - "<doc attr='value'/>") - doc2 = doc.cloneNode(1) - confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)), - "testCloneDocumentDeep: document objects not distinct") - confirm(len(doc.childNodes) == len(doc2.childNodes), - "testCloneDocumentDeep: wrong number of Document children") - confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE, - "testCloneDocumentDeep: documentElement not an ELEMENT_NODE") - confirm(doc2.documentElement.ownerDocument.isSameNode(doc2), - "testCloneDocumentDeep: documentElement owner is not new document") - confirm(not doc.documentElement.isSameNode(doc2.documentElement), - "testCloneDocumentDeep: documentElement should not be shared") - if doc.doctype is not None: - # check the doctype iff the original DOM maintained it - confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE, - "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE") - confirm(doc2.doctype.ownerDocument.isSameNode(doc2)) - confirm(not doc.doctype.isSameNode(doc2.doctype)) - -def testCloneDocumentTypeDeepOk(): - doctype = create_nonempty_doctype() - clone = doctype.cloneNode(1) - confirm(clone is not None - and clone.nodeName == doctype.nodeName - and clone.name == doctype.name - and clone.publicId == doctype.publicId - and clone.systemId == doctype.systemId - and len(clone.entities) == len(doctype.entities) - and clone.entities.item(len(clone.entities)) is None - and len(clone.notations) == len(doctype.notations) - and clone.notations.item(len(clone.notations)) is None - and len(clone.childNodes) == 0) - for i in range(len(doctype.entities)): - se = doctype.entities.item(i) - ce = clone.entities.item(i) - confirm((not se.isSameNode(ce)) - and (not ce.isSameNode(se)) - and ce.nodeName == se.nodeName - and ce.notationName == se.notationName - and ce.publicId == se.publicId - and ce.systemId == se.systemId - and ce.encoding == se.encoding - and ce.actualEncoding == se.actualEncoding - and ce.version == se.version) - for i in range(len(doctype.notations)): - sn = doctype.notations.item(i) - cn = clone.notations.item(i) - confirm((not sn.isSameNode(cn)) - and (not cn.isSameNode(sn)) - and cn.nodeName == sn.nodeName - and cn.publicId == sn.publicId - and cn.systemId == sn.systemId) - -def testCloneDocumentTypeDeepNotOk(): - doc = create_doc_with_doctype() - clone = doc.doctype.cloneNode(1) - confirm(clone is None, "testCloneDocumentTypeDeepNotOk") - -def testCloneDocumentTypeShallowOk(): - doctype = create_nonempty_doctype() - clone = doctype.cloneNode(0) - confirm(clone is not None - and clone.nodeName == doctype.nodeName - and clone.name == doctype.name - and clone.publicId == doctype.publicId - and clone.systemId == doctype.systemId - and len(clone.entities) == 0 - and clone.entities.item(0) is None - and len(clone.notations) == 0 - and clone.notations.item(0) is None - and len(clone.childNodes) == 0) - -def testCloneDocumentTypeShallowNotOk(): - doc = create_doc_with_doctype() - clone = doc.doctype.cloneNode(0) - confirm(clone is None, "testCloneDocumentTypeShallowNotOk") - -def check_import_document(deep, testName): - doc1 = parseString("<doc/>") - doc2 = parseString("<doc/>") - try: - doc1.importNode(doc2, deep) - except xml.dom.NotSupportedErr: - pass - else: - raise Exception(testName + - ": expected NotSupportedErr when importing a document") - -def testImportDocumentShallow(): - check_import_document(0, "testImportDocumentShallow") - -def testImportDocumentDeep(): - check_import_document(1, "testImportDocumentDeep") - # The tests of DocumentType importing use these helpers to construct # the documents to work with, since not all DOM builders actually # create the DocumentType nodes. - def create_doc_without_doctype(doctype=None): return getDOMImplementation().createDocument(None, "doc", doctype) @@ -722,673 +52,1263 @@ def create_doc_with_doctype(): doctype.notations.item(0).ownerDocument = doc return doc -def testImportDocumentTypeShallow(): - src = create_doc_with_doctype() - target = create_doc_without_doctype() - try: - imported = target.importNode(src.doctype, 0) - except xml.dom.NotSupportedErr: - pass - else: - raise Exception( - "testImportDocumentTypeShallow: expected NotSupportedErr") - -def testImportDocumentTypeDeep(): - src = create_doc_with_doctype() - target = create_doc_without_doctype() - try: - imported = target.importNode(src.doctype, 1) - except xml.dom.NotSupportedErr: - pass - else: - raise Exception( - "testImportDocumentTypeDeep: expected NotSupportedErr") - -# Testing attribute clones uses a helper, and should always be deep, -# even if the argument to cloneNode is false. -def check_clone_attribute(deep, testName): - doc = parseString("<doc attr='value'/>") - attr = doc.documentElement.getAttributeNode("attr") - assert attr is not None - clone = attr.cloneNode(deep) - confirm(not clone.isSameNode(attr)) - confirm(not attr.isSameNode(clone)) - confirm(clone.ownerElement is None, - testName + ": ownerElement should be None") - confirm(clone.ownerDocument.isSameNode(attr.ownerDocument), - testName + ": ownerDocument does not match") - confirm(clone.specified, - testName + ": cloned attribute must have specified == True") - -def testCloneAttributeShallow(): - check_clone_attribute(0, "testCloneAttributeShallow") - -def testCloneAttributeDeep(): - check_clone_attribute(1, "testCloneAttributeDeep") - -def check_clone_pi(deep, testName): - doc = parseString("<?target data?><doc/>") - pi = doc.firstChild - assert pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE - clone = pi.cloneNode(deep) - confirm(clone.target == pi.target - and clone.data == pi.data) - -def testClonePIShallow(): - check_clone_pi(0, "testClonePIShallow") - -def testClonePIDeep(): - check_clone_pi(1, "testClonePIDeep") - -def testNormalize(): - doc = parseString("<doc/>") - root = doc.documentElement - root.appendChild(doc.createTextNode("first")) - root.appendChild(doc.createTextNode("second")) - confirm(len(root.childNodes) == 2 - and root.childNodes.length == 2, "testNormalize -- preparation") - doc.normalize() - confirm(len(root.childNodes) == 1 - and root.childNodes.length == 1 - and root.firstChild is root.lastChild - and root.firstChild.data == "firstsecond" - , "testNormalize -- result") - doc.unlink() - - doc = parseString("<doc/>") - root = doc.documentElement - root.appendChild(doc.createTextNode("")) - doc.normalize() - confirm(len(root.childNodes) == 0 - and root.childNodes.length == 0, - "testNormalize -- single empty node removed") - doc.unlink() - -def testSiblings(): - doc = parseString("<doc><?pi?>text?<elm/></doc>") - root = doc.documentElement - (pi, text, elm) = root.childNodes - - confirm(pi.nextSibling is text and - pi.previousSibling is None and - text.nextSibling is elm and - text.previousSibling is pi and - elm.nextSibling is None and - elm.previousSibling is text, "testSiblings") - - doc.unlink() - -def testParents(): - doc = parseString("<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>") - root = doc.documentElement - elm1 = root.childNodes[0] - (elm2a, elm2b) = elm1.childNodes - elm3 = elm2b.childNodes[0] - - confirm(root.parentNode is doc and - elm1.parentNode is root and - elm2a.parentNode is elm1 and - elm2b.parentNode is elm1 and - elm3.parentNode is elm2b, "testParents") - - doc.unlink() - -def testNodeListItem(): - doc = parseString("<doc><e/><e/></doc>") - children = doc.childNodes - docelem = children[0] - confirm(children[0] is children.item(0) - and children.item(1) is None - and docelem.childNodes.item(0) is docelem.childNodes[0] - and docelem.childNodes.item(1) is docelem.childNodes[1] - and docelem.childNodes.item(0).childNodes.item(0) is None, - "test NodeList.item()") - doc.unlink() - -def testSAX2DOM(): - from xml.dom import pulldom - - sax2dom = pulldom.SAX2DOM() - sax2dom.startDocument() - sax2dom.startElement("doc", {}) - sax2dom.characters("text") - sax2dom.startElement("subelm", {}) - sax2dom.characters("text") - sax2dom.endElement("subelm") - sax2dom.characters("text") - sax2dom.endElement("doc") - sax2dom.endDocument() - - doc = sax2dom.document - root = doc.documentElement - (text1, elm1, text2) = root.childNodes - text3 = elm1.childNodes[0] - - confirm(text1.previousSibling is None and - text1.nextSibling is elm1 and - elm1.previousSibling is text1 and - elm1.nextSibling is text2 and - text2.previousSibling is elm1 and - text2.nextSibling is None and - text3.previousSibling is None and - text3.nextSibling is None, "testSAX2DOM - siblings") - - confirm(root.parentNode is doc and - text1.parentNode is root and - elm1.parentNode is root and - text2.parentNode is root and - text3.parentNode is elm1, "testSAX2DOM - parents") - - doc.unlink() - -def testEncodings(): - doc = parseString('<foo>€</foo>') - confirm(doc.toxml() == u'<?xml version="1.0" ?><foo>\u20ac</foo>' - and doc.toxml('utf-8') == '<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>' - and doc.toxml('iso-8859-15') == '<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>', - "testEncodings - encoding EURO SIGN") - - # Verify that character decoding errors throw exceptions instead of crashing - try: - doc = parseString('<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>') - except UnicodeDecodeError: - pass - else: - print('parsing with bad encoding should raise a UnicodeDecodeError') - - doc.unlink() - -class UserDataHandler: - called = 0 - def handle(self, operation, key, data, src, dst): - dst.setUserData(key, data + 1, self) - src.setUserData(key, None, None) - self.called = 1 - -def testUserData(): - dom = Document() - n = dom.createElement('e') - confirm(n.getUserData("foo") is None) - n.setUserData("foo", None, None) - confirm(n.getUserData("foo") is None) - n.setUserData("foo", 12, 12) - n.setUserData("bar", 13, 13) - confirm(n.getUserData("foo") == 12) - confirm(n.getUserData("bar") == 13) - n.setUserData("foo", None, None) - confirm(n.getUserData("foo") is None) - confirm(n.getUserData("bar") == 13) - - handler = UserDataHandler() - n.setUserData("bar", 12, handler) - c = n.cloneNode(1) - confirm(handler.called - and n.getUserData("bar") is None - and c.getUserData("bar") == 13) - n.unlink() - c.unlink() - dom.unlink() - -def testRenameAttribute(): - doc = parseString("<doc a='v'/>") - elem = doc.documentElement - attrmap = elem.attributes - attr = elem.attributes['a'] - - # Simple renaming - attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") - confirm(attr.name == "b" - and attr.nodeName == "b" - and attr.localName is None - and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE - and attr.prefix is None - and attr.value == "v" - and elem.getAttributeNode("a") is None - and elem.getAttributeNode("b").isSameNode(attr) - and attrmap["b"].isSameNode(attr) - and attr.ownerDocument.isSameNode(doc) - and attr.ownerElement.isSameNode(elem)) - - # Rename to have a namespace, no prefix - attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") - confirm(attr.name == "c" - and attr.nodeName == "c" - and attr.localName == "c" - and attr.namespaceURI == "http://xml.python.org/ns" - and attr.prefix is None - and attr.value == "v" - and elem.getAttributeNode("a") is None - and elem.getAttributeNode("b") is None - and elem.getAttributeNode("c").isSameNode(attr) - and elem.getAttributeNodeNS( - "http://xml.python.org/ns", "c").isSameNode(attr) - and attrmap["c"].isSameNode(attr) - and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) - - # Rename to have a namespace, with prefix - attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") - confirm(attr.name == "p:d" - and attr.nodeName == "p:d" - and attr.localName == "d" - and attr.namespaceURI == "http://xml.python.org/ns2" - and attr.prefix == "p" - and attr.value == "v" - and elem.getAttributeNode("a") is None - and elem.getAttributeNode("b") is None - and elem.getAttributeNode("c") is None - and elem.getAttributeNodeNS( - "http://xml.python.org/ns", "c") is None - and elem.getAttributeNode("p:d").isSameNode(attr) - and elem.getAttributeNodeNS( - "http://xml.python.org/ns2", "d").isSameNode(attr) - and attrmap["p:d"].isSameNode(attr) - and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) - - # Rename back to a simple non-NS node - attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") - confirm(attr.name == "e" - and attr.nodeName == "e" - and attr.localName is None - and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE - and attr.prefix is None - and attr.value == "v" - and elem.getAttributeNode("a") is None - and elem.getAttributeNode("b") is None - and elem.getAttributeNode("c") is None - and elem.getAttributeNode("p:d") is None - and elem.getAttributeNodeNS( - "http://xml.python.org/ns", "c") is None - and elem.getAttributeNode("e").isSameNode(attr) - and attrmap["e"].isSameNode(attr)) - - try: - doc.renameNode(attr, "http://xml.python.org/ns", "xmlns") - except xml.dom.NamespaceErr: - pass - else: - print("expected NamespaceErr") - - checkRenameNodeSharedConstraints(doc, attr) - doc.unlink() - -def testRenameElement(): - doc = parseString("<doc/>") - elem = doc.documentElement - - # Simple renaming - elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") - confirm(elem.tagName == "a" - and elem.nodeName == "a" - and elem.localName is None - and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE - and elem.prefix is None - and elem.ownerDocument.isSameNode(doc)) - - # Rename to have a namespace, no prefix - elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") - confirm(elem.tagName == "b" - and elem.nodeName == "b" - and elem.localName == "b" - and elem.namespaceURI == "http://xml.python.org/ns" - and elem.prefix is None - and elem.ownerDocument.isSameNode(doc)) - - # Rename to have a namespace, with prefix - elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") - confirm(elem.tagName == "p:c" - and elem.nodeName == "p:c" - and elem.localName == "c" - and elem.namespaceURI == "http://xml.python.org/ns2" - and elem.prefix == "p" - and elem.ownerDocument.isSameNode(doc)) - - # Rename back to a simple non-NS node - elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") - confirm(elem.tagName == "d" - and elem.nodeName == "d" - and elem.localName is None - and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE - and elem.prefix is None - and elem.ownerDocument.isSameNode(doc)) - - checkRenameNodeSharedConstraints(doc, elem) - doc.unlink() - -def checkRenameNodeSharedConstraints(doc, node): - # Make sure illegal NS usage is detected: - try: - doc.renameNode(node, "http://xml.python.org/ns", "xmlns:foo") - except xml.dom.NamespaceErr: - pass - else: - print("expected NamespaceErr") +class MinidomTest(unittest.TestCase): + def tearDown(self): + try: + Node.allnodes + except AttributeError: + # We don't actually have the minidom from the standard library, + # but are picking up the PyXML version from site-packages. + pass + else: + self.confirm(len(Node.allnodes) == 0, + "assertion: len(Node.allnodes) == 0") + if len(Node.allnodes): + print("Garbage left over:") + if verbose: + print(list(Node.allnodes.items())[0:10]) + else: + # Don't print specific nodes if repeatable results + # are needed + print(len(Node.allnodes)) + Node.allnodes = {} - doc2 = parseString("<doc/>") - try: - doc2.renameNode(node, xml.dom.EMPTY_NAMESPACE, "foo") - except xml.dom.WrongDocumentErr: - pass - else: - print("expected WrongDocumentErr") - -def testRenameOther(): - # We have to create a comment node explicitly since not all DOM - # builders used with minidom add comments to the DOM. - doc = xml.dom.minidom.getDOMImplementation().createDocument( - xml.dom.EMPTY_NAMESPACE, "e", None) - node = doc.createComment("comment") - try: - doc.renameNode(node, xml.dom.EMPTY_NAMESPACE, "foo") - except xml.dom.NotSupportedErr: + def confirm(self, test, testname = "Test"): + self.assertTrue(test, testname) + + def checkWholeText(self, node, s): + t = node.wholeText + self.confirm(t == s, "looking for %s, found %s" % (repr(s), repr(t))) + + def testParseFromFile(self): + dom = parse(StringIO(open(tstfile).read())) + dom.unlink() + self.confirm(isinstance(dom, Document)) + + def testGetElementsByTagName(self): + dom = parse(tstfile) + self.confirm(dom.getElementsByTagName("LI") == \ + dom.documentElement.getElementsByTagName("LI")) + dom.unlink() + + def testInsertBefore(self): + dom = parseString("<doc><foo/></doc>") + root = dom.documentElement + elem = root.childNodes[0] + nelem = dom.createElement("element") + root.insertBefore(nelem, elem) + self.confirm(len(root.childNodes) == 2 + and root.childNodes.length == 2 + and root.childNodes[0] is nelem + and root.childNodes.item(0) is nelem + and root.childNodes[1] is elem + and root.childNodes.item(1) is elem + and root.firstChild is nelem + and root.lastChild is elem + and root.toxml() == "<doc><element/><foo/></doc>" + , "testInsertBefore -- node properly placed in tree") + nelem = dom.createElement("element") + root.insertBefore(nelem, None) + self.confirm(len(root.childNodes) == 3 + and root.childNodes.length == 3 + and root.childNodes[1] is elem + and root.childNodes.item(1) is elem + and root.childNodes[2] is nelem + and root.childNodes.item(2) is nelem + and root.lastChild is nelem + and nelem.previousSibling is elem + and root.toxml() == "<doc><element/><foo/><element/></doc>" + , "testInsertBefore -- node properly placed in tree") + nelem2 = dom.createElement("bar") + root.insertBefore(nelem2, nelem) + self.confirm(len(root.childNodes) == 4 + and root.childNodes.length == 4 + and root.childNodes[2] is nelem2 + and root.childNodes.item(2) is nelem2 + and root.childNodes[3] is nelem + and root.childNodes.item(3) is nelem + and nelem2.nextSibling is nelem + and nelem.previousSibling is nelem2 + and root.toxml() == + "<doc><element/><foo/><bar/><element/></doc>" + , "testInsertBefore -- node properly placed in tree") + dom.unlink() + + def _create_fragment_test_nodes(self): + dom = parseString("<doc/>") + orig = dom.createTextNode("original") + c1 = dom.createTextNode("foo") + c2 = dom.createTextNode("bar") + c3 = dom.createTextNode("bat") + dom.documentElement.appendChild(orig) + frag = dom.createDocumentFragment() + frag.appendChild(c1) + frag.appendChild(c2) + frag.appendChild(c3) + return dom, orig, c1, c2, c3, frag + + def testInsertBeforeFragment(self): + dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() + dom.documentElement.insertBefore(frag, None) + self.confirm(tuple(dom.documentElement.childNodes) == + (orig, c1, c2, c3), + "insertBefore(<fragment>, None)") + frag.unlink() + dom.unlink() + + dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() + dom.documentElement.insertBefore(frag, orig) + self.confirm(tuple(dom.documentElement.childNodes) == + (c1, c2, c3, orig), + "insertBefore(<fragment>, orig)") + frag.unlink() + dom.unlink() + + def testAppendChild(self): + dom = parse(tstfile) + dom.documentElement.appendChild(dom.createComment(u"Hello")) + self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment") + self.confirm(dom.documentElement.childNodes[-1].data == "Hello") + dom.unlink() + + def testAppendChildFragment(self): + dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() + dom.documentElement.appendChild(frag) + self.confirm(tuple(dom.documentElement.childNodes) == + (orig, c1, c2, c3), + "appendChild(<fragment>)") + frag.unlink() + dom.unlink() + + def testReplaceChildFragment(self): + dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() + dom.documentElement.replaceChild(frag, orig) + orig.unlink() + self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3), + "replaceChild(<fragment>)") + frag.unlink() + dom.unlink() + + def testLegalChildren(self): + dom = Document() + elem = dom.createElement('element') + text = dom.createTextNode('text') + self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text) + + dom.appendChild(elem) + self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text, + elem) + self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text, + elem) + + nodemap = elem.attributes + self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem, + text) + self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS, + text) + + elem.appendChild(text) + dom.unlink() + + def testNamedNodeMapSetItem(self): + dom = Document() + elem = dom.createElement('element') + attrs = elem.attributes + attrs["foo"] = "bar" + a = attrs.item(0) + self.confirm(a.ownerDocument is dom, + "NamedNodeMap.__setitem__() sets ownerDocument") + self.confirm(a.ownerElement is elem, + "NamedNodeMap.__setitem__() sets ownerElement") + self.confirm(a.value == "bar", + "NamedNodeMap.__setitem__() sets value") + self.confirm(a.nodeValue == "bar", + "NamedNodeMap.__setitem__() sets nodeValue") + elem.unlink() + dom.unlink() + + def testNonZero(self): + dom = parse(tstfile) + self.confirm(dom)# should not be zero + dom.appendChild(dom.createComment("foo")) + self.confirm(not dom.childNodes[-1].childNodes) + dom.unlink() + + def testUnlink(self): + dom = parse(tstfile) + dom.unlink() + + def testElement(self): + dom = Document() + dom.appendChild(dom.createElement("abc")) + self.confirm(dom.documentElement) + dom.unlink() + + def testAAA(self): + dom = parseString("<abc/>") + el = dom.documentElement + el.setAttribute("spam", "jam2") + self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA") + a = el.getAttributeNode("spam") + self.confirm(a.ownerDocument is dom, + "setAttribute() sets ownerDocument") + self.confirm(a.ownerElement is dom.documentElement, + "setAttribute() sets ownerElement") + dom.unlink() + + def testAAB(self): + dom = parseString("<abc/>") + el = dom.documentElement + el.setAttribute("spam", "jam") + el.setAttribute("spam", "jam2") + self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB") + dom.unlink() + + def testAddAttr(self): + dom = Document() + child = dom.appendChild(dom.createElement("abc")) + + child.setAttribute("def", "ghi") + self.confirm(child.getAttribute("def") == "ghi") + self.confirm(child.attributes["def"].value == "ghi") + + child.setAttribute("jkl", "mno") + self.confirm(child.getAttribute("jkl") == "mno") + self.confirm(child.attributes["jkl"].value == "mno") + + self.confirm(len(child.attributes) == 2) + + child.setAttribute("def", "newval") + self.confirm(child.getAttribute("def") == "newval") + self.confirm(child.attributes["def"].value == "newval") + + self.confirm(len(child.attributes) == 2) + dom.unlink() + + def testDeleteAttr(self): + dom = Document() + child = dom.appendChild(dom.createElement("abc")) + + self.confirm(len(child.attributes) == 0) + child.setAttribute("def", "ghi") + self.confirm(len(child.attributes) == 1) + del child.attributes["def"] + self.confirm(len(child.attributes) == 0) + dom.unlink() + + def testRemoveAttr(self): + dom = Document() + child = dom.appendChild(dom.createElement("abc")) + + child.setAttribute("def", "ghi") + self.confirm(len(child.attributes) == 1) + child.removeAttribute("def") + self.confirm(len(child.attributes) == 0) + dom.unlink() + + def testRemoveAttrNS(self): + dom = Document() + child = dom.appendChild( + dom.createElementNS("http://www.python.org", "python:abc")) + child.setAttributeNS("http://www.w3.org", "xmlns:python", + "http://www.python.org") + child.setAttributeNS("http://www.python.org", "python:abcattr", "foo") + self.confirm(len(child.attributes) == 2) + child.removeAttributeNS("http://www.python.org", "abcattr") + self.confirm(len(child.attributes) == 1) + dom.unlink() + + def testRemoveAttributeNode(self): + dom = Document() + child = dom.appendChild(dom.createElement("foo")) + child.setAttribute("spam", "jam") + self.confirm(len(child.attributes) == 1) + node = child.getAttributeNode("spam") + child.removeAttributeNode(node) + self.confirm(len(child.attributes) == 0 + and child.getAttributeNode("spam") is None) + dom.unlink() + + def testChangeAttr(self): + dom = parseString("<abc/>") + el = dom.documentElement + el.setAttribute("spam", "jam") + self.confirm(len(el.attributes) == 1) + el.setAttribute("spam", "bam") + # Set this attribute to be an ID and make sure that doesn't change + # when changing the value: + el.setIdAttribute("spam") + self.confirm(len(el.attributes) == 1 + and el.attributes["spam"].value == "bam" + and el.attributes["spam"].nodeValue == "bam" + and el.getAttribute("spam") == "bam" + and el.getAttributeNode("spam").isId) + el.attributes["spam"] = "ham" + self.confirm(len(el.attributes) == 1 + and el.attributes["spam"].value == "ham" + and el.attributes["spam"].nodeValue == "ham" + and el.getAttribute("spam") == "ham" + and el.attributes["spam"].isId) + el.setAttribute("spam2", "bam") + self.confirm(len(el.attributes) == 2 + and el.attributes["spam"].value == "ham" + and el.attributes["spam"].nodeValue == "ham" + and el.getAttribute("spam") == "ham" + and el.attributes["spam2"].value == "bam" + and el.attributes["spam2"].nodeValue == "bam" + and el.getAttribute("spam2") == "bam") + el.attributes["spam2"] = "bam2" + self.confirm(len(el.attributes) == 2 + and el.attributes["spam"].value == "ham" + and el.attributes["spam"].nodeValue == "ham" + and el.getAttribute("spam") == "ham" + and el.attributes["spam2"].value == "bam2" + and el.attributes["spam2"].nodeValue == "bam2" + and el.getAttribute("spam2") == "bam2") + dom.unlink() + + def testGetAttrList(self): pass - else: - print("expected NotSupportedErr when renaming comment node") - doc.unlink() - -def checkWholeText(node, s): - t = node.wholeText - confirm(t == s, "looking for %s, found %s" % (repr(s), repr(t))) - -def testWholeText(): - doc = parseString("<doc>a</doc>") - elem = doc.documentElement - text = elem.childNodes[0] - assert text.nodeType == Node.TEXT_NODE - - checkWholeText(text, "a") - elem.appendChild(doc.createTextNode("b")) - checkWholeText(text, "ab") - elem.insertBefore(doc.createCDATASection("c"), text) - checkWholeText(text, "cab") - - # make sure we don't cross other nodes - splitter = doc.createComment("comment") - elem.appendChild(splitter) - text2 = doc.createTextNode("d") - elem.appendChild(text2) - checkWholeText(text, "cab") - checkWholeText(text2, "d") - - x = doc.createElement("x") - elem.replaceChild(x, splitter) - splitter = x - checkWholeText(text, "cab") - checkWholeText(text2, "d") - - x = doc.createProcessingInstruction("y", "z") - elem.replaceChild(x, splitter) - splitter = x - checkWholeText(text, "cab") - checkWholeText(text2, "d") - - elem.removeChild(splitter) - checkWholeText(text, "cabd") - checkWholeText(text2, "cabd") - -def testPatch1094164 (): - doc = parseString("<doc><e/></doc>") - elem = doc.documentElement - e = elem.firstChild - confirm(e.parentNode is elem, "Before replaceChild()") - # Check that replacing a child with itself leaves the tree unchanged - elem.replaceChild(e, e) - confirm(e.parentNode is elem, "After replaceChild()") - - - -def testReplaceWholeText(): - def setup(): - doc = parseString("<doc>a<e/>d</doc>") + + def testGetAttrValues(self): pass + + def testGetAttrLength(self): pass + + def testGetAttribute(self): pass + + def testGetAttributeNS(self): pass + + def testGetAttributeNode(self): pass + + def testGetElementsByTagNameNS(self): + d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'> + <minidom:myelem/> + </foo>""" + dom = parseString(d) + elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom", + "myelem") + self.confirm(len(elems) == 1 + and elems[0].namespaceURI == "http://pyxml.sf.net/minidom" + and elems[0].localName == "myelem" + and elems[0].prefix == "minidom" + and elems[0].tagName == "minidom:myelem" + and elems[0].nodeName == "minidom:myelem") + dom.unlink() + + def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri, + lname): + nodelist = doc.getElementsByTagNameNS(nsuri, lname) + self.confirm(len(nodelist) == 0) + + def testGetEmptyNodeListFromElementsByTagNameNS(self): + doc = parseString('<doc/>') + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, 'http://xml.python.org/namespaces/a', 'localname') + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, '*', 'splat') + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, 'http://xml.python.org/namespaces/a', '*') + + doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>') + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, "http://xml.python.org/splat", "not-there") + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, "*", "not-there") + self.get_empty_nodelist_from_elements_by_tagName_ns_helper( + doc, "http://somewhere.else.net/not-there", "e") + + def testElementReprAndStr(self): + dom = Document() + el = dom.appendChild(dom.createElement("abc")) + string1 = repr(el) + string2 = str(el) + self.confirm(string1 == string2) + dom.unlink() + + def testElementReprAndStrUnicode(self): + dom = Document() + el = dom.appendChild(dom.createElement(u"abc")) + string1 = repr(el) + string2 = str(el) + self.confirm(string1 == string2) + dom.unlink() + + def testElementReprAndStrUnicodeNS(self): + dom = Document() + el = dom.appendChild( + dom.createElementNS(u"http://www.slashdot.org", u"slash:abc")) + string1 = repr(el) + string2 = str(el) + self.confirm(string1 == string2) + self.confirm(string1.find("slash:abc") != -1) + dom.unlink() + + def testAttributeRepr(self): + dom = Document() + el = dom.appendChild(dom.createElement(u"abc")) + node = el.setAttribute("abc", "def") + self.confirm(str(node) == repr(node)) + dom.unlink() + + def testTextNodeRepr(self): pass + + def testWriteXML(self): + str = '<?xml version="1.0" ?><a b="c"/>' + dom = parseString(str) + domstr = dom.toxml() + dom.unlink() + self.confirm(str == domstr) + + def testAltNewline(self): + str = '<?xml version="1.0" ?>\n<a b="c"/>\n' + dom = parseString(str) + domstr = dom.toprettyxml(newl="\r\n") + dom.unlink() + self.confirm(domstr == str.replace("\n", "\r\n")) + + def testProcessingInstruction(self): + dom = parseString('<e><?mypi \t\n data \t\n ?></e>') + pi = dom.documentElement.firstChild + self.confirm(pi.target == "mypi" + and pi.data == "data \t\n " + and pi.nodeName == "mypi" + and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE + and pi.attributes is None + and not pi.hasChildNodes() + and len(pi.childNodes) == 0 + and pi.firstChild is None + and pi.lastChild is None + and pi.localName is None + and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE) + + def testProcessingInstructionRepr(self): pass + + def testTextRepr(self): pass + + def testWriteText(self): pass + + def testDocumentElement(self): pass + + def testTooManyDocumentElements(self): + doc = parseString("<doc/>") + elem = doc.createElement("extra") + # Should raise an exception when adding an extra document element. + self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem) + elem.unlink() + doc.unlink() + + def testCreateElementNS(self): pass + + def testCreateAttributeNS(self): pass + + def testParse(self): pass + + def testParseString(self): pass + + def testComment(self): pass + + def testAttrListItem(self): pass + + def testAttrListItems(self): pass + + def testAttrListItemNS(self): pass + + def testAttrListKeys(self): pass + + def testAttrListKeysNS(self): pass + + def testRemoveNamedItem(self): + doc = parseString("<doc a=''/>") + e = doc.documentElement + attrs = e.attributes + a1 = e.getAttributeNode("a") + a2 = attrs.removeNamedItem("a") + self.confirm(a1.isSameNode(a2)) + self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a") + + def testRemoveNamedItemNS(self): + doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>") + e = doc.documentElement + attrs = e.attributes + a1 = e.getAttributeNodeNS("http://xml.python.org/", "b") + a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b") + self.confirm(a1.isSameNode(a2)) + self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS, + "http://xml.python.org/", "b") + + def testAttrListValues(self): pass + + def testAttrListLength(self): pass + + def testAttrList__getitem__(self): pass + + def testAttrList__setitem__(self): pass + + def testSetAttrValueandNodeValue(self): pass + + def testParseElement(self): pass + + def testParseAttributes(self): pass + + def testParseElementNamespaces(self): pass + + def testParseAttributeNamespaces(self): pass + + def testParseProcessingInstructions(self): pass + + def testChildNodes(self): pass + + def testFirstChild(self): pass + + def testHasChildNodes(self): pass + + def _testCloneElementCopiesAttributes(self, e1, e2, test): + attrs1 = e1.attributes + attrs2 = e2.attributes + keys1 = list(attrs1.keys()) + keys2 = list(attrs2.keys()) + keys1.sort() + keys2.sort() + self.confirm(keys1 == keys2, "clone of element has same attribute keys") + for i in range(len(keys1)): + a1 = attrs1.item(i) + a2 = attrs2.item(i) + self.confirm(a1 is not a2 + and a1.value == a2.value + and a1.nodeValue == a2.nodeValue + and a1.namespaceURI == a2.namespaceURI + and a1.localName == a2.localName + , "clone of attribute node has proper attribute values") + self.confirm(a2.ownerElement is e2, + "clone of attribute node correctly owned") + + def _setupCloneElement(self, deep): + dom = parseString("<doc attr='value'><foo/></doc>") + root = dom.documentElement + clone = root.cloneNode(deep) + self._testCloneElementCopiesAttributes( + root, clone, "testCloneElement" + (deep and "Deep" or "Shallow")) + # mutilate the original so shared data is detected + root.tagName = root.nodeName = "MODIFIED" + root.setAttribute("attr", "NEW VALUE") + root.setAttribute("added", "VALUE") + return dom, clone + + def testCloneElementShallow(self): + dom, clone = self._setupCloneElement(0) + self.confirm(len(clone.childNodes) == 0 + and clone.childNodes.length == 0 + and clone.parentNode is None + and clone.toxml() == '<doc attr="value"/>' + , "testCloneElementShallow") + dom.unlink() + + def testCloneElementDeep(self): + dom, clone = self._setupCloneElement(1) + self.confirm(len(clone.childNodes) == 1 + and clone.childNodes.length == 1 + and clone.parentNode is None + and clone.toxml() == '<doc attr="value"><foo/></doc>' + , "testCloneElementDeep") + dom.unlink() + + def testCloneDocumentShallow(self): + doc = parseString("<?xml version='1.0'?>\n" + "<!-- comment -->" + "<!DOCTYPE doc [\n" + "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" + "]>\n" + "<doc attr='value'/>") + doc2 = doc.cloneNode(0) + self.confirm(doc2 is None, + "testCloneDocumentShallow:" + " shallow cloning of documents makes no sense!") + + def testCloneDocumentDeep(self): + doc = parseString("<?xml version='1.0'?>\n" + "<!-- comment -->" + "<!DOCTYPE doc [\n" + "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" + "]>\n" + "<doc attr='value'/>") + doc2 = doc.cloneNode(1) + self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)), + "testCloneDocumentDeep: document objects not distinct") + self.confirm(len(doc.childNodes) == len(doc2.childNodes), + "testCloneDocumentDeep: wrong number of Document children") + self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE, + "testCloneDocumentDeep: documentElement not an ELEMENT_NODE") + self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2), + "testCloneDocumentDeep: documentElement owner is not new document") + self.confirm(not doc.documentElement.isSameNode(doc2.documentElement), + "testCloneDocumentDeep: documentElement should not be shared") + if doc.doctype is not None: + # check the doctype iff the original DOM maintained it + self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE, + "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE") + self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2)) + self.confirm(not doc.doctype.isSameNode(doc2.doctype)) + + def testCloneDocumentTypeDeepOk(self): + doctype = create_nonempty_doctype() + clone = doctype.cloneNode(1) + self.confirm(clone is not None + and clone.nodeName == doctype.nodeName + and clone.name == doctype.name + and clone.publicId == doctype.publicId + and clone.systemId == doctype.systemId + and len(clone.entities) == len(doctype.entities) + and clone.entities.item(len(clone.entities)) is None + and len(clone.notations) == len(doctype.notations) + and clone.notations.item(len(clone.notations)) is None + and len(clone.childNodes) == 0) + for i in range(len(doctype.entities)): + se = doctype.entities.item(i) + ce = clone.entities.item(i) + self.confirm((not se.isSameNode(ce)) + and (not ce.isSameNode(se)) + and ce.nodeName == se.nodeName + and ce.notationName == se.notationName + and ce.publicId == se.publicId + and ce.systemId == se.systemId + and ce.encoding == se.encoding + and ce.actualEncoding == se.actualEncoding + and ce.version == se.version) + for i in range(len(doctype.notations)): + sn = doctype.notations.item(i) + cn = clone.notations.item(i) + self.confirm((not sn.isSameNode(cn)) + and (not cn.isSameNode(sn)) + and cn.nodeName == sn.nodeName + and cn.publicId == sn.publicId + and cn.systemId == sn.systemId) + + def testCloneDocumentTypeDeepNotOk(self): + doc = create_doc_with_doctype() + clone = doc.doctype.cloneNode(1) + self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk") + + def testCloneDocumentTypeShallowOk(self): + doctype = create_nonempty_doctype() + clone = doctype.cloneNode(0) + self.confirm(clone is not None + and clone.nodeName == doctype.nodeName + and clone.name == doctype.name + and clone.publicId == doctype.publicId + and clone.systemId == doctype.systemId + and len(clone.entities) == 0 + and clone.entities.item(0) is None + and len(clone.notations) == 0 + and clone.notations.item(0) is None + and len(clone.childNodes) == 0) + + def testCloneDocumentTypeShallowNotOk(self): + doc = create_doc_with_doctype() + clone = doc.doctype.cloneNode(0) + self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk") + + def check_import_document(self, deep, testName): + doc1 = parseString("<doc/>") + doc2 = parseString("<doc/>") + self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep) + + def testImportDocumentShallow(self): + self.check_import_document(0, "testImportDocumentShallow") + + def testImportDocumentDeep(self): + self.check_import_document(1, "testImportDocumentDeep") + + def testImportDocumentTypeShallow(self): + src = create_doc_with_doctype() + target = create_doc_without_doctype() + self.assertRaises(xml.dom.NotSupportedErr, target.importNode, + src.doctype, 0) + + def testImportDocumentTypeDeep(self): + src = create_doc_with_doctype() + target = create_doc_without_doctype() + self.assertRaises(xml.dom.NotSupportedErr, target.importNode, + src.doctype, 1) + + # Testing attribute clones uses a helper, and should always be deep, + # even if the argument to cloneNode is false. + def check_clone_attribute(self, deep, testName): + doc = parseString("<doc attr='value'/>") + attr = doc.documentElement.getAttributeNode("attr") + self.failIfEqual(attr, None) + clone = attr.cloneNode(deep) + self.confirm(not clone.isSameNode(attr)) + self.confirm(not attr.isSameNode(clone)) + self.confirm(clone.ownerElement is None, + testName + ": ownerElement should be None") + self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument), + testName + ": ownerDocument does not match") + self.confirm(clone.specified, + testName + ": cloned attribute must have specified == True") + + def testCloneAttributeShallow(self): + self.check_clone_attribute(0, "testCloneAttributeShallow") + + def testCloneAttributeDeep(self): + self.check_clone_attribute(1, "testCloneAttributeDeep") + + def check_clone_pi(self, deep, testName): + doc = parseString("<?target data?><doc/>") + pi = doc.firstChild + self.assertEquals(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) + clone = pi.cloneNode(deep) + self.confirm(clone.target == pi.target + and clone.data == pi.data) + + def testClonePIShallow(self): + self.check_clone_pi(0, "testClonePIShallow") + + def testClonePIDeep(self): + self.check_clone_pi(1, "testClonePIDeep") + + def testNormalize(self): + doc = parseString("<doc/>") + root = doc.documentElement + root.appendChild(doc.createTextNode("first")) + root.appendChild(doc.createTextNode("second")) + self.confirm(len(root.childNodes) == 2 + and root.childNodes.length == 2, + "testNormalize -- preparation") + doc.normalize() + self.confirm(len(root.childNodes) == 1 + and root.childNodes.length == 1 + and root.firstChild is root.lastChild + and root.firstChild.data == "firstsecond" + , "testNormalize -- result") + doc.unlink() + + doc = parseString("<doc/>") + root = doc.documentElement + root.appendChild(doc.createTextNode("")) + doc.normalize() + self.confirm(len(root.childNodes) == 0 + and root.childNodes.length == 0, + "testNormalize -- single empty node removed") + doc.unlink() + + def testSiblings(self): + doc = parseString("<doc><?pi?>text?<elm/></doc>") + root = doc.documentElement + (pi, text, elm) = root.childNodes + + self.confirm(pi.nextSibling is text and + pi.previousSibling is None and + text.nextSibling is elm and + text.previousSibling is pi and + elm.nextSibling is None and + elm.previousSibling is text, "testSiblings") + + doc.unlink() + + def testParents(self): + doc = parseString( + "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>") + root = doc.documentElement + elm1 = root.childNodes[0] + (elm2a, elm2b) = elm1.childNodes + elm3 = elm2b.childNodes[0] + + self.confirm(root.parentNode is doc and + elm1.parentNode is root and + elm2a.parentNode is elm1 and + elm2b.parentNode is elm1 and + elm3.parentNode is elm2b, "testParents") + doc.unlink() + + def testNodeListItem(self): + doc = parseString("<doc><e/><e/></doc>") + children = doc.childNodes + docelem = children[0] + self.confirm(children[0] is children.item(0) + and children.item(1) is None + and docelem.childNodes.item(0) is docelem.childNodes[0] + and docelem.childNodes.item(1) is docelem.childNodes[1] + and docelem.childNodes.item(0).childNodes.item(0) is None, + "test NodeList.item()") + doc.unlink() + + def testSAX2DOM(self): + from xml.dom import pulldom + + sax2dom = pulldom.SAX2DOM() + sax2dom.startDocument() + sax2dom.startElement("doc", {}) + sax2dom.characters("text") + sax2dom.startElement("subelm", {}) + sax2dom.characters("text") + sax2dom.endElement("subelm") + sax2dom.characters("text") + sax2dom.endElement("doc") + sax2dom.endDocument() + + doc = sax2dom.document + root = doc.documentElement + (text1, elm1, text2) = root.childNodes + text3 = elm1.childNodes[0] + + self.confirm(text1.previousSibling is None and + text1.nextSibling is elm1 and + elm1.previousSibling is text1 and + elm1.nextSibling is text2 and + text2.previousSibling is elm1 and + text2.nextSibling is None and + text3.previousSibling is None and + text3.nextSibling is None, "testSAX2DOM - siblings") + + self.confirm(root.parentNode is doc and + text1.parentNode is root and + elm1.parentNode is root and + text2.parentNode is root and + text3.parentNode is elm1, "testSAX2DOM - parents") + doc.unlink() + + def testEncodings(self): + doc = parseString('<foo>€</foo>') + self.confirm(doc.toxml() == u'<?xml version="1.0" ?><foo>\u20ac</foo>' + and doc.toxml('utf-8') == + '<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>' + and doc.toxml('iso-8859-15') == + '<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>', + "testEncodings - encoding EURO SIGN") + + # Verify that character decoding errors throw exceptions instead + # of crashing + self.assertRaises(UnicodeDecodeError, parseString, + '<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>') + + doc.unlink() + + class UserDataHandler: + called = 0 + def handle(self, operation, key, data, src, dst): + dst.setUserData(key, data + 1, self) + src.setUserData(key, None, None) + self.called = 1 + + def testUserData(self): + dom = Document() + n = dom.createElement('e') + self.confirm(n.getUserData("foo") is None) + n.setUserData("foo", None, None) + self.confirm(n.getUserData("foo") is None) + n.setUserData("foo", 12, 12) + n.setUserData("bar", 13, 13) + self.confirm(n.getUserData("foo") == 12) + self.confirm(n.getUserData("bar") == 13) + n.setUserData("foo", None, None) + self.confirm(n.getUserData("foo") is None) + self.confirm(n.getUserData("bar") == 13) + + handler = self.UserDataHandler() + n.setUserData("bar", 12, handler) + c = n.cloneNode(1) + self.confirm(handler.called + and n.getUserData("bar") is None + and c.getUserData("bar") == 13) + n.unlink() + c.unlink() + dom.unlink() + + def checkRenameNodeSharedConstraints(self, doc, node): + # Make sure illegal NS usage is detected: + self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, + "http://xml.python.org/ns", "xmlns:foo") + doc2 = parseString("<doc/>") + self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, + xml.dom.EMPTY_NAMESPACE, "foo") + + def testRenameAttribute(self): + doc = parseString("<doc a='v'/>") + elem = doc.documentElement + attrmap = elem.attributes + attr = elem.attributes['a'] + + # Simple renaming + attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") + self.confirm(attr.name == "b" + and attr.nodeName == "b" + and attr.localName is None + and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE + and attr.prefix is None + and attr.value == "v" + and elem.getAttributeNode("a") is None + and elem.getAttributeNode("b").isSameNode(attr) + and attrmap["b"].isSameNode(attr) + and attr.ownerDocument.isSameNode(doc) + and attr.ownerElement.isSameNode(elem)) + + # Rename to have a namespace, no prefix + attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") + self.confirm(attr.name == "c" + and attr.nodeName == "c" + and attr.localName == "c" + and attr.namespaceURI == "http://xml.python.org/ns" + and attr.prefix is None + and attr.value == "v" + and elem.getAttributeNode("a") is None + and elem.getAttributeNode("b") is None + and elem.getAttributeNode("c").isSameNode(attr) + and elem.getAttributeNodeNS( + "http://xml.python.org/ns", "c").isSameNode(attr) + and attrmap["c"].isSameNode(attr) + and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) + + # Rename to have a namespace, with prefix + attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") + self.confirm(attr.name == "p:d" + and attr.nodeName == "p:d" + and attr.localName == "d" + and attr.namespaceURI == "http://xml.python.org/ns2" + and attr.prefix == "p" + and attr.value == "v" + and elem.getAttributeNode("a") is None + and elem.getAttributeNode("b") is None + and elem.getAttributeNode("c") is None + and elem.getAttributeNodeNS( + "http://xml.python.org/ns", "c") is None + and elem.getAttributeNode("p:d").isSameNode(attr) + and elem.getAttributeNodeNS( + "http://xml.python.org/ns2", "d").isSameNode(attr) + and attrmap["p:d"].isSameNode(attr) + and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) + + # Rename back to a simple non-NS node + attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") + self.confirm(attr.name == "e" + and attr.nodeName == "e" + and attr.localName is None + and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE + and attr.prefix is None + and attr.value == "v" + and elem.getAttributeNode("a") is None + and elem.getAttributeNode("b") is None + and elem.getAttributeNode("c") is None + and elem.getAttributeNode("p:d") is None + and elem.getAttributeNodeNS( + "http://xml.python.org/ns", "c") is None + and elem.getAttributeNode("e").isSameNode(attr) + and attrmap["e"].isSameNode(attr)) + + self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, + "http://xml.python.org/ns", "xmlns") + self.checkRenameNodeSharedConstraints(doc, attr) + doc.unlink() + + def testRenameElement(self): + doc = parseString("<doc/>") elem = doc.documentElement - text1 = elem.firstChild - text2 = elem.lastChild - splitter = text1.nextSibling - elem.insertBefore(doc.createTextNode("b"), splitter) - elem.insertBefore(doc.createCDATASection("c"), text1) - return doc, elem, text1, splitter, text2 - - doc, elem, text1, splitter, text2 = setup() - text = text1.replaceWholeText("new content") - checkWholeText(text, "new content") - checkWholeText(text2, "d") - confirm(len(elem.childNodes) == 3) - - doc, elem, text1, splitter, text2 = setup() - text = text2.replaceWholeText("new content") - checkWholeText(text, "new content") - checkWholeText(text1, "cab") - confirm(len(elem.childNodes) == 5) - - doc, elem, text1, splitter, text2 = setup() - text = text1.replaceWholeText("") - checkWholeText(text2, "d") - confirm(text is None - and len(elem.childNodes) == 2) - -def testSchemaType(): - doc = parseString( - "<!DOCTYPE doc [\n" - " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" - " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" - " <!ATTLIST doc id ID #IMPLIED \n" - " ref IDREF #IMPLIED \n" - " refs IDREFS #IMPLIED \n" - " enum (a|b) #IMPLIED \n" - " ent ENTITY #IMPLIED \n" - " ents ENTITIES #IMPLIED \n" - " nm NMTOKEN #IMPLIED \n" - " nms NMTOKENS #IMPLIED \n" - " text CDATA #IMPLIED \n" - " >\n" - "]><doc id='name' notid='name' text='splat!' enum='b'" - " ref='name' refs='name name' ent='e1' ents='e1 e2'" - " nm='123' nms='123 abc' />") - elem = doc.documentElement - # We don't want to rely on any specific loader at this point, so - # just make sure we can get to all the names, and that the - # DTD-based namespace is right. The names can vary by loader - # since each supports a different level of DTD information. - t = elem.schemaType - confirm(t.name is None - and t.namespace == xml.dom.EMPTY_NAMESPACE) - names = "id notid text enum ref refs ent ents nm nms".split() - for name in names: - a = elem.getAttributeNode(name) - t = a.schemaType - confirm(hasattr(t, "name") - and t.namespace == xml.dom.EMPTY_NAMESPACE) -def testSetIdAttribute(): - doc = parseString("<doc a1='v' a2='w'/>") - e = doc.documentElement - a1 = e.getAttributeNode("a1") - a2 = e.getAttributeNode("a2") - confirm(doc.getElementById("v") is None - and not a1.isId - and not a2.isId) - e.setIdAttribute("a1") - confirm(e.isSameNode(doc.getElementById("v")) - and a1.isId - and not a2.isId) - e.setIdAttribute("a2") - confirm(e.isSameNode(doc.getElementById("v")) - and e.isSameNode(doc.getElementById("w")) - and a1.isId - and a2.isId) - # replace the a1 node; the new node should *not* be an ID - a3 = doc.createAttribute("a1") - a3.value = "v" - e.setAttributeNode(a3) - confirm(doc.getElementById("v") is None - and e.isSameNode(doc.getElementById("w")) - and not a1.isId - and a2.isId - and not a3.isId) - # renaming an attribute should not affect its ID-ness: - doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") - confirm(e.isSameNode(doc.getElementById("w")) - and a2.isId) - -def testSetIdAttributeNS(): - NS1 = "http://xml.python.org/ns1" - NS2 = "http://xml.python.org/ns2" - doc = parseString("<doc" - " xmlns:ns1='" + NS1 + "'" - " xmlns:ns2='" + NS2 + "'" - " ns1:a1='v' ns2:a2='w'/>") - e = doc.documentElement - a1 = e.getAttributeNodeNS(NS1, "a1") - a2 = e.getAttributeNodeNS(NS2, "a2") - confirm(doc.getElementById("v") is None - and not a1.isId - and not a2.isId) - e.setIdAttributeNS(NS1, "a1") - confirm(e.isSameNode(doc.getElementById("v")) - and a1.isId - and not a2.isId) - e.setIdAttributeNS(NS2, "a2") - confirm(e.isSameNode(doc.getElementById("v")) - and e.isSameNode(doc.getElementById("w")) - and a1.isId - and a2.isId) - # replace the a1 node; the new node should *not* be an ID - a3 = doc.createAttributeNS(NS1, "a1") - a3.value = "v" - e.setAttributeNode(a3) - confirm(e.isSameNode(doc.getElementById("w"))) - confirm(not a1.isId) - confirm(a2.isId) - confirm(not a3.isId) - confirm(doc.getElementById("v") is None) - # renaming an attribute should not affect its ID-ness: - doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") - confirm(e.isSameNode(doc.getElementById("w")) - and a2.isId) - -def testSetIdAttributeNode(): - NS1 = "http://xml.python.org/ns1" - NS2 = "http://xml.python.org/ns2" - doc = parseString("<doc" - " xmlns:ns1='" + NS1 + "'" - " xmlns:ns2='" + NS2 + "'" - " ns1:a1='v' ns2:a2='w'/>") - e = doc.documentElement - a1 = e.getAttributeNodeNS(NS1, "a1") - a2 = e.getAttributeNodeNS(NS2, "a2") - confirm(doc.getElementById("v") is None - and not a1.isId - and not a2.isId) - e.setIdAttributeNode(a1) - confirm(e.isSameNode(doc.getElementById("v")) - and a1.isId - and not a2.isId) - e.setIdAttributeNode(a2) - confirm(e.isSameNode(doc.getElementById("v")) - and e.isSameNode(doc.getElementById("w")) - and a1.isId - and a2.isId) - # replace the a1 node; the new node should *not* be an ID - a3 = doc.createAttributeNS(NS1, "a1") - a3.value = "v" - e.setAttributeNode(a3) - confirm(e.isSameNode(doc.getElementById("w"))) - confirm(not a1.isId) - confirm(a2.isId) - confirm(not a3.isId) - confirm(doc.getElementById("v") is None) - # renaming an attribute should not affect its ID-ness: - doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") - confirm(e.isSameNode(doc.getElementById("w")) - and a2.isId) - -def testPickledDocument(): - doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n" - "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'" - " 'http://xml.python.org/system' [\n" - " <!ELEMENT e EMPTY>\n" - " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n" - "]><doc attr='value'> text\n" - "<?pi sample?> <!-- comment --> <e/> </doc>") - s = pickle.dumps(doc) - doc2 = pickle.loads(s) - stack = [(doc, doc2)] - while stack: - n1, n2 = stack.pop() - confirm(n1.nodeType == n2.nodeType - and len(n1.childNodes) == len(n2.childNodes) - and n1.nodeName == n2.nodeName - and not n1.isSameNode(n2) - and not n2.isSameNode(n1)) - if n1.nodeType == Node.DOCUMENT_TYPE_NODE: - len(n1.entities) - len(n2.entities) - len(n1.notations) - len(n2.notations) - confirm(len(n1.entities) == len(n2.entities) - and len(n1.notations) == len(n2.notations)) - for i in range(len(n1.notations)): - no1 = n1.notations.item(i) - no2 = n1.notations.item(i) - confirm(no1.name == no2.name - and no1.publicId == no2.publicId - and no1.systemId == no2.systemId) - statck.append((no1, no2)) - for i in range(len(n1.entities)): - e1 = n1.entities.item(i) - e2 = n2.entities.item(i) - confirm(e1.notationName == e2.notationName - and e1.publicId == e2.publicId - and e1.systemId == e2.systemId) - stack.append((e1, e2)) - if n1.nodeType != Node.DOCUMENT_NODE: - confirm(n1.ownerDocument.isSameNode(doc) - and n2.ownerDocument.isSameNode(doc2)) - for i in range(len(n1.childNodes)): - stack.append((n1.childNodes[i], n2.childNodes[i])) - - -# --- MAIN PROGRAM - -names = sorted(globals().keys()) - -failed = [] - -try: - Node.allnodes -except AttributeError: - # We don't actually have the minidom from the standard library, - # but are picking up the PyXML version from site-packages. - def check_allnodes(): - pass -else: - def check_allnodes(): - confirm(len(Node.allnodes) == 0, - "assertion: len(Node.allnodes) == 0") - if len(Node.allnodes): - print("Garbage left over:") - if verbose: - print(Node.allnodes.items()[0:10]) - else: - # Don't print specific nodes if repeatable results - # are needed - print(len(Node.allnodes)) - Node.allnodes = {} - -for name in names: - if name.startswith("test"): - func = globals()[name] - try: - func() - check_allnodes() - except: - failed.append(name) - print("Test Failed: ", name) - sys.stdout.flush() - traceback.print_exception(*sys.exc_info()) - print(repr(sys.exc_info()[1])) - Node.allnodes = {} + # Simple renaming + elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") + self.confirm(elem.tagName == "a" + and elem.nodeName == "a" + and elem.localName is None + and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE + and elem.prefix is None + and elem.ownerDocument.isSameNode(doc)) + + # Rename to have a namespace, no prefix + elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") + self.confirm(elem.tagName == "b" + and elem.nodeName == "b" + and elem.localName == "b" + and elem.namespaceURI == "http://xml.python.org/ns" + and elem.prefix is None + and elem.ownerDocument.isSameNode(doc)) + + # Rename to have a namespace, with prefix + elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") + self.confirm(elem.tagName == "p:c" + and elem.nodeName == "p:c" + and elem.localName == "c" + and elem.namespaceURI == "http://xml.python.org/ns2" + and elem.prefix == "p" + and elem.ownerDocument.isSameNode(doc)) + + # Rename back to a simple non-NS node + elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") + self.confirm(elem.tagName == "d" + and elem.nodeName == "d" + and elem.localName is None + and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE + and elem.prefix is None + and elem.ownerDocument.isSameNode(doc)) + + self.checkRenameNodeSharedConstraints(doc, elem) + doc.unlink() + + def testRenameOther(self): + # We have to create a comment node explicitly since not all DOM + # builders used with minidom add comments to the DOM. + doc = xml.dom.minidom.getDOMImplementation().createDocument( + xml.dom.EMPTY_NAMESPACE, "e", None) + node = doc.createComment("comment") + self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, + xml.dom.EMPTY_NAMESPACE, "foo") + doc.unlink() + + def testWholeText(self): + doc = parseString("<doc>a</doc>") + elem = doc.documentElement + text = elem.childNodes[0] + self.assertEquals(text.nodeType, Node.TEXT_NODE) + + self.checkWholeText(text, "a") + elem.appendChild(doc.createTextNode("b")) + self.checkWholeText(text, "ab") + elem.insertBefore(doc.createCDATASection("c"), text) + self.checkWholeText(text, "cab") + + # make sure we don't cross other nodes + splitter = doc.createComment("comment") + elem.appendChild(splitter) + text2 = doc.createTextNode("d") + elem.appendChild(text2) + self.checkWholeText(text, "cab") + self.checkWholeText(text2, "d") + + x = doc.createElement("x") + elem.replaceChild(x, splitter) + splitter = x + self.checkWholeText(text, "cab") + self.checkWholeText(text2, "d") + + x = doc.createProcessingInstruction("y", "z") + elem.replaceChild(x, splitter) + splitter = x + self.checkWholeText(text, "cab") + self.checkWholeText(text2, "d") + + elem.removeChild(splitter) + self.checkWholeText(text, "cabd") + self.checkWholeText(text2, "cabd") + + def testPatch1094164(self): + doc = parseString("<doc><e/></doc>") + elem = doc.documentElement + e = elem.firstChild + self.confirm(e.parentNode is elem, "Before replaceChild()") + # Check that replacing a child with itself leaves the tree unchanged + elem.replaceChild(e, e) + self.confirm(e.parentNode is elem, "After replaceChild()") + + def testReplaceWholeText(self): + def setup(): + doc = parseString("<doc>a<e/>d</doc>") + elem = doc.documentElement + text1 = elem.firstChild + text2 = elem.lastChild + splitter = text1.nextSibling + elem.insertBefore(doc.createTextNode("b"), splitter) + elem.insertBefore(doc.createCDATASection("c"), text1) + return doc, elem, text1, splitter, text2 + + doc, elem, text1, splitter, text2 = setup() + text = text1.replaceWholeText("new content") + self.checkWholeText(text, "new content") + self.checkWholeText(text2, "d") + self.confirm(len(elem.childNodes) == 3) + + doc, elem, text1, splitter, text2 = setup() + text = text2.replaceWholeText("new content") + self.checkWholeText(text, "new content") + self.checkWholeText(text1, "cab") + self.confirm(len(elem.childNodes) == 5) + + doc, elem, text1, splitter, text2 = setup() + text = text1.replaceWholeText("") + self.checkWholeText(text2, "d") + self.confirm(text is None + and len(elem.childNodes) == 2) + + def testSchemaType(self): + doc = parseString( + "<!DOCTYPE doc [\n" + " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" + " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" + " <!ATTLIST doc id ID #IMPLIED \n" + " ref IDREF #IMPLIED \n" + " refs IDREFS #IMPLIED \n" + " enum (a|b) #IMPLIED \n" + " ent ENTITY #IMPLIED \n" + " ents ENTITIES #IMPLIED \n" + " nm NMTOKEN #IMPLIED \n" + " nms NMTOKENS #IMPLIED \n" + " text CDATA #IMPLIED \n" + " >\n" + "]><doc id='name' notid='name' text='splat!' enum='b'" + " ref='name' refs='name name' ent='e1' ents='e1 e2'" + " nm='123' nms='123 abc' />") + elem = doc.documentElement + # We don't want to rely on any specific loader at this point, so + # just make sure we can get to all the names, and that the + # DTD-based namespace is right. The names can vary by loader + # since each supports a different level of DTD information. + t = elem.schemaType + self.confirm(t.name is None + and t.namespace == xml.dom.EMPTY_NAMESPACE) + names = "id notid text enum ref refs ent ents nm nms".split() + for name in names: + a = elem.getAttributeNode(name) + t = a.schemaType + self.confirm(hasattr(t, "name") + and t.namespace == xml.dom.EMPTY_NAMESPACE) + + def testSetIdAttribute(self): + doc = parseString("<doc a1='v' a2='w'/>") + e = doc.documentElement + a1 = e.getAttributeNode("a1") + a2 = e.getAttributeNode("a2") + self.confirm(doc.getElementById("v") is None + and not a1.isId + and not a2.isId) + e.setIdAttribute("a1") + self.confirm(e.isSameNode(doc.getElementById("v")) + and a1.isId + and not a2.isId) + e.setIdAttribute("a2") + self.confirm(e.isSameNode(doc.getElementById("v")) + and e.isSameNode(doc.getElementById("w")) + and a1.isId + and a2.isId) + # replace the a1 node; the new node should *not* be an ID + a3 = doc.createAttribute("a1") + a3.value = "v" + e.setAttributeNode(a3) + self.confirm(doc.getElementById("v") is None + and e.isSameNode(doc.getElementById("w")) + and not a1.isId + and a2.isId + and not a3.isId) + # renaming an attribute should not affect its ID-ness: + doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") + self.confirm(e.isSameNode(doc.getElementById("w")) + and a2.isId) + + def testSetIdAttributeNS(self): + NS1 = "http://xml.python.org/ns1" + NS2 = "http://xml.python.org/ns2" + doc = parseString("<doc" + " xmlns:ns1='" + NS1 + "'" + " xmlns:ns2='" + NS2 + "'" + " ns1:a1='v' ns2:a2='w'/>") + e = doc.documentElement + a1 = e.getAttributeNodeNS(NS1, "a1") + a2 = e.getAttributeNodeNS(NS2, "a2") + self.confirm(doc.getElementById("v") is None + and not a1.isId + and not a2.isId) + e.setIdAttributeNS(NS1, "a1") + self.confirm(e.isSameNode(doc.getElementById("v")) + and a1.isId + and not a2.isId) + e.setIdAttributeNS(NS2, "a2") + self.confirm(e.isSameNode(doc.getElementById("v")) + and e.isSameNode(doc.getElementById("w")) + and a1.isId + and a2.isId) + # replace the a1 node; the new node should *not* be an ID + a3 = doc.createAttributeNS(NS1, "a1") + a3.value = "v" + e.setAttributeNode(a3) + self.confirm(e.isSameNode(doc.getElementById("w"))) + self.confirm(not a1.isId) + self.confirm(a2.isId) + self.confirm(not a3.isId) + self.confirm(doc.getElementById("v") is None) + # renaming an attribute should not affect its ID-ness: + doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") + self.confirm(e.isSameNode(doc.getElementById("w")) + and a2.isId) + + def testSetIdAttributeNode(self): + NS1 = "http://xml.python.org/ns1" + NS2 = "http://xml.python.org/ns2" + doc = parseString("<doc" + " xmlns:ns1='" + NS1 + "'" + " xmlns:ns2='" + NS2 + "'" + " ns1:a1='v' ns2:a2='w'/>") + e = doc.documentElement + a1 = e.getAttributeNodeNS(NS1, "a1") + a2 = e.getAttributeNodeNS(NS2, "a2") + self.confirm(doc.getElementById("v") is None + and not a1.isId + and not a2.isId) + e.setIdAttributeNode(a1) + self.confirm(e.isSameNode(doc.getElementById("v")) + and a1.isId + and not a2.isId) + e.setIdAttributeNode(a2) + self.confirm(e.isSameNode(doc.getElementById("v")) + and e.isSameNode(doc.getElementById("w")) + and a1.isId + and a2.isId) + # replace the a1 node; the new node should *not* be an ID + a3 = doc.createAttributeNS(NS1, "a1") + a3.value = "v" + e.setAttributeNode(a3) + self.confirm(e.isSameNode(doc.getElementById("w"))) + self.confirm(not a1.isId) + self.confirm(a2.isId) + self.confirm(not a3.isId) + self.confirm(doc.getElementById("v") is None) + # renaming an attribute should not affect its ID-ness: + doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") + self.confirm(e.isSameNode(doc.getElementById("w")) + and a2.isId) + + def testPickledDocument(self): + doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n" + "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'" + " 'http://xml.python.org/system' [\n" + " <!ELEMENT e EMPTY>\n" + " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n" + "]><doc attr='value'> text\n" + "<?pi sample?> <!-- comment --> <e/> </doc>") + s = pickle.dumps(doc) + doc2 = pickle.loads(s) + stack = [(doc, doc2)] + while stack: + n1, n2 = stack.pop() + self.confirm(n1.nodeType == n2.nodeType + and len(n1.childNodes) == len(n2.childNodes) + and n1.nodeName == n2.nodeName + and not n1.isSameNode(n2) + and not n2.isSameNode(n1)) + if n1.nodeType == Node.DOCUMENT_TYPE_NODE: + len(n1.entities) + len(n2.entities) + len(n1.notations) + len(n2.notations) + self.confirm(len(n1.entities) == len(n2.entities) + and len(n1.notations) == len(n2.notations)) + for i in range(len(n1.notations)): + no1 = n1.notations.item(i) + no2 = n1.notations.item(i) + self.confirm(no1.name == no2.name + and no1.publicId == no2.publicId + and no1.systemId == no2.systemId) + statck.append((no1, no2)) + for i in range(len(n1.entities)): + e1 = n1.entities.item(i) + e2 = n2.entities.item(i) + self.confirm(e1.notationName == e2.notationName + and e1.publicId == e2.publicId + and e1.systemId == e2.systemId) + stack.append((e1, e2)) + if n1.nodeType != Node.DOCUMENT_NODE: + self.confirm(n1.ownerDocument.isSameNode(doc) + and n2.ownerDocument.isSameNode(doc2)) + for i in range(len(n1.childNodes)): + stack.append((n1.childNodes[i], n2.childNodes[i])) + +def test_main(): + run_unittest(MinidomTest) -if failed: - print("\n\n\n**** Check for failures in these tests:") - for name in failed: - print(" " + name) +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py index 7911a0e..cc8b192 100644 --- a/Lib/test/test_module.py +++ b/Lib/test/test_module.py @@ -1,48 +1,61 @@ # Test the module type - -from test.test_support import verify, vereq, verbose, TestFailed +import unittest +from test.test_support import verbose, run_unittest import sys -module = type(sys) - -# An uninitialized module has no __dict__ or __name__, and __doc__ is None -foo = module.__new__(module) -verify(foo.__dict__ is None) -try: - s = foo.__name__ -except AttributeError: - pass -else: - raise TestFailed, "__name__ = %s" % repr(s) -vereq(foo.__doc__, module.__doc__) - -# Regularly initialized module, no docstring -foo = module("foo") -vereq(foo.__name__, "foo") -vereq(foo.__doc__, None) -vereq(foo.__dict__, {"__name__": "foo", "__doc__": None}) - -# ASCII docstring -foo = module("foo", "foodoc") -vereq(foo.__name__, "foo") -vereq(foo.__doc__, "foodoc") -vereq(foo.__dict__, {"__name__": "foo", "__doc__": "foodoc"}) - -# Unicode docstring -foo = module("foo", u"foodoc\u1234") -vereq(foo.__name__, "foo") -vereq(foo.__doc__, u"foodoc\u1234") -vereq(foo.__dict__, {"__name__": "foo", "__doc__": u"foodoc\u1234"}) - -# Reinitialization should not replace the __dict__ -foo.bar = 42 -d = foo.__dict__ -foo.__init__("foo", "foodoc") -vereq(foo.__name__, "foo") -vereq(foo.__doc__, "foodoc") -vereq(foo.bar, 42) -vereq(foo.__dict__, {"__name__": "foo", "__doc__": "foodoc", "bar": 42}) -verify(foo.__dict__ is d) - -if verbose: - print("All OK") +ModuleType = type(sys) + +class ModuleTests(unittest.TestCase): + def test_uninitialized(self): + # An uninitialized module has no __dict__ or __name__, + # and __doc__ is None + foo = ModuleType.__new__(ModuleType) + self.failUnless(foo.__dict__ is None) + try: + s = foo.__name__ + self.fail("__name__ = %s" % repr(s)) + except AttributeError: + pass + self.assertEqual(foo.__doc__, ModuleType.__doc__) + + def test_no_docstring(self): + # Regularly initialized module, no docstring + foo = ModuleType("foo") + self.assertEqual(foo.__name__, "foo") + self.assertEqual(foo.__doc__, None) + self.assertEqual(foo.__dict__, {"__name__": "foo", "__doc__": None}) + + def test_ascii_docstring(self): + # ASCII docstring + foo = ModuleType("foo", "foodoc") + self.assertEqual(foo.__name__, "foo") + self.assertEqual(foo.__doc__, "foodoc") + self.assertEqual(foo.__dict__, + {"__name__": "foo", "__doc__": "foodoc"}) + + def test_unicode_docstring(self): + # Unicode docstring + foo = ModuleType("foo", u"foodoc\u1234") + self.assertEqual(foo.__name__, "foo") + self.assertEqual(foo.__doc__, u"foodoc\u1234") + self.assertEqual(foo.__dict__, + {"__name__": "foo", "__doc__": u"foodoc\u1234"}) + + def test_reinit(self): + # Reinitialization should not replace the __dict__ + foo = ModuleType("foo", u"foodoc\u1234") + foo.bar = 42 + d = foo.__dict__ + foo.__init__("foo", "foodoc") + self.assertEqual(foo.__name__, "foo") + self.assertEqual(foo.__doc__, "foodoc") + self.assertEqual(foo.bar, 42) + self.assertEqual(foo.__dict__, + {"__name__": "foo", "__doc__": "foodoc", "bar": 42}) + self.failUnless(foo.__dict__ is d) + +def test_main(): + run_unittest(ModuleTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py index 2ac7061..c5615a8 100644 --- a/Lib/test/test_multibytecodec.py +++ b/Lib/test/test_multibytecodec.py @@ -219,13 +219,7 @@ class Test_ISO2022(unittest.TestCase): myunichr(x).encode('iso_2022_jp', 'ignore') 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) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_normalization.py b/Lib/test/test_normalization.py index d890067..b571bdc 100644 --- a/Lib/test/test_normalization.py +++ b/Lib/test/test_normalization.py @@ -1,5 +1,6 @@ -from test.test_support import (verbose, TestFailed, TestSkipped, verify, - open_urlresource) +from test.test_support import run_unittest, open_urlresource +import unittest + import sys import os from unicodedata import normalize @@ -29,60 +30,67 @@ def unistr(data): raise RangeError return u"".join([unichr(x) for x in data]) -def test_main(): - part1_data = {} - for line in open_urlresource(TESTDATAURL): - if '#' in line: - line = line.split('#')[0] - line = line.strip() - if not line: - continue - if line.startswith("@Part"): - part = line.split()[0] - continue - if part == "@Part3": - # XXX we don't support PRI #29 yet, so skip these tests for now - continue - try: - c1,c2,c3,c4,c5 = [unistr(x) for x in line.split(';')[:-1]] - except RangeError: - # Skip unsupported characters; - # try atleast adding c1 if we are in part1 +class NormalizationTest(unittest.TestCase): + def test_main(self): + part1_data = {} + for line in open_urlresource(TESTDATAURL): + if '#' in line: + line = line.split('#')[0] + line = line.strip() + if not line: + continue + if line.startswith("@Part"): + part = line.split()[0] + continue + if part == "@Part3": + # XXX we don't support PRI #29 yet, so skip these tests for now + continue + try: + c1,c2,c3,c4,c5 = [unistr(x) for x in line.split(';')[:-1]] + except RangeError: + # Skip unsupported characters; + # try atleast adding c1 if we are in part1 + if part == "@Part1": + try: + c1 = unistr(line.split(';')[0]) + except RangeError: + pass + else: + part1_data[c1] = 1 + continue + + # Perform tests + self.failUnless(c2 == NFC(c1) == NFC(c2) == NFC(c3), line) + self.failUnless(c4 == NFC(c4) == NFC(c5), line) + self.failUnless(c3 == NFD(c1) == NFD(c2) == NFD(c3), line) + self.failUnless(c5 == NFD(c4) == NFD(c5), line) + self.failUnless(c4 == NFKC(c1) == NFKC(c2) == \ + NFKC(c3) == NFKC(c4) == NFKC(c5), + line) + self.failUnless(c5 == NFKD(c1) == NFKD(c2) == \ + NFKD(c3) == NFKD(c4) == NFKD(c5), + line) + + # Record part 1 data if part == "@Part1": - try: - c1=unistr(line.split(';')[0]) - except RangeError: - pass - else: - part1_data[c1] = 1 - continue - - if verbose: - print(line) - - # Perform tests - verify(c2 == NFC(c1) == NFC(c2) == NFC(c3), line) - verify(c4 == NFC(c4) == NFC(c5), line) - verify(c3 == NFD(c1) == NFD(c2) == NFD(c3), line) - verify(c5 == NFD(c4) == NFD(c5), line) - verify(c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5), - line) - verify(c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5), - line) - - # Record part 1 data - if part == "@Part1": - part1_data[c1] = 1 - - # Perform tests for all other data - for c in range(sys.maxunicode+1): - X = unichr(c) - if X in part1_data: - continue - assert X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X), c - - # Check for bug 834676 - normalize('NFC',u'\ud55c\uae00') + part1_data[c1] = 1 + + # Perform tests for all other data + for c in range(sys.maxunicode+1): + X = unichr(c) + if X in part1_data: + continue + self.failUnless(X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X), c) + + def test_bug_834676(self): + # Check for bug 834676 + normalize('NFC', u'\ud55c\uae00') + + +def test_main(): + # Hit the exception early + open_urlresource(TESTDATAURL) + run_unittest(NormalizationTest) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 939886d..c6dbf2e 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -18,13 +18,14 @@ def tester(fn, wantResult): tester('ntpath.splitext("foo.ext")', ('foo', '.ext')) tester('ntpath.splitext("/foo/foo.ext")', ('/foo/foo', '.ext')) -tester('ntpath.splitext(".ext")', ('', '.ext')) +tester('ntpath.splitext(".ext")', ('.ext', '')) tester('ntpath.splitext("\\foo.ext\\foo")', ('\\foo.ext\\foo', '')) tester('ntpath.splitext("foo.ext\\")', ('foo.ext\\', '')) tester('ntpath.splitext("")', ('', '')) tester('ntpath.splitext("foo.bar.ext")', ('foo.bar', '.ext')) tester('ntpath.splitext("xx/foo.bar.ext")', ('xx/foo.bar', '.ext')) tester('ntpath.splitext("xx\\foo.bar.ext")', ('xx\\foo.bar', '.ext')) +tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d')) tester('ntpath.splitdrive("c:\\foo\\bar")', ('c:', '\\foo\\bar')) @@ -133,6 +134,13 @@ try: tester('ntpath.expandvars("${{foo}}")', "baz1}") tester('ntpath.expandvars("$foo$foo")', "barbar") tester('ntpath.expandvars("$bar$bar")', "$bar$bar") + tester('ntpath.expandvars("%foo% bar")', "bar bar") + tester('ntpath.expandvars("%foo%bar")', "barbar") + tester('ntpath.expandvars("%foo%%foo%")', "barbar") + tester('ntpath.expandvars("%%foo%%foo%foo%")', "%foo%foobar") + tester('ntpath.expandvars("%?bar%")', "%?bar%") + tester('ntpath.expandvars("%foo%%bar")', "bar%bar") + tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar") finally: os.environ.clear() os.environ.update(oldenv) @@ -149,6 +157,16 @@ except ImportError: else: tester('ntpath.abspath("C:\\")', "C:\\") +currentdir = os.path.split(os.getcwd())[-1] +tester('ntpath.relpath("a")', 'a') +tester('ntpath.relpath(os.path.abspath("a"))', 'a') +tester('ntpath.relpath("a/b")', 'a\\b') +tester('ntpath.relpath("../a/b")', '..\\a\\b') +tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a') +tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b') +tester('ntpath.relpath("a", "b/c")', '..\\..\\a') +tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a') + if errors: raise TestFailed(str(errors) + " errors.") elif verbose: diff --git a/Lib/test/test_operations.py b/Lib/test/test_operations.py deleted file mode 100644 index e8b1ae8..0000000 --- a/Lib/test/test_operations.py +++ /dev/null @@ -1,77 +0,0 @@ -# Python test set -- part 3, built-in operations. - - -print('3. Operations') -print('XXX Mostly not yet implemented') - - -print('3.1 Dictionary lookups fail if __cmp__() raises an exception') - -class BadDictKey: - - def __hash__(self): - return hash(self.__class__) - - def __eq__(self, other): - if isinstance(other, self.__class__): - print("raising error") - raise RuntimeError, "gotcha" - return other - -d = {} -x1 = BadDictKey() -x2 = BadDictKey() -d[x1] = 1 -for stmt in ['d[x2] = 2', - 'z = d[x2]', - 'x2 in d', - 'd.get(x2)', - 'd.setdefault(x2, 42)', - 'd.pop(x2)', - 'd.update({x2: 2})']: - try: - exec(stmt) - except RuntimeError: - print("%s: caught the RuntimeError outside" % (stmt,)) - else: - print("%s: No exception passed through!" % (stmt,)) # old CPython behavior - - -# Dict resizing bug, found by Jack Jansen in 2.2 CVS development. -# This version got an assert failure in debug build, infinite loop in -# release build. Unfortunately, provoking this kind of stuff requires -# a mix of inserts and deletes hitting exactly the right hash codes in -# exactly the right order, and I can't think of a randomized approach -# that would be *likely* to hit a failing case in reasonable time. - -d = {} -for i in range(5): - d[i] = i -for i in range(5): - del d[i] -for i in range(5, 9): # i==8 was the problem - d[i] = i - - -# Another dict resizing bug (SF bug #1456209). -# This caused Segmentation faults or Illegal instructions. - -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 - -print('resize bugs not triggered.') diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index f9519b2..8d70564 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -143,6 +143,8 @@ class OperatorTestCase(unittest.TestCase): self.failUnlessRaises(TypeError, operator.delslice, a, None, None) self.failUnless(operator.delslice(a, 2, 8) is None) self.assert_(a == [0, 1, 8, 9]) + operator.delslice(a, 0, test_support.MAX_Py_ssize_t) + self.assert_(a == []) def test_floordiv(self): self.failUnlessRaises(TypeError, operator.floordiv, 5) @@ -165,6 +167,8 @@ class OperatorTestCase(unittest.TestCase): self.failUnlessRaises(TypeError, operator.getslice) self.failUnlessRaises(TypeError, operator.getslice, a, None, None) self.failUnless(operator.getslice(a, 4, 6) == [4, 5]) + b = operator.getslice(a, 0, test_support.MAX_Py_ssize_t) + self.assert_(b == a) def test_indexOf(self): self.failUnlessRaises(TypeError, operator.indexOf) @@ -300,6 +304,8 @@ class OperatorTestCase(unittest.TestCase): self.failUnlessRaises(TypeError, operator.setslice, a, None, None, None) self.failUnless(operator.setslice(a, 1, 3, [2, 1]) is None) self.assert_(a == [0, 2, 1, 3]) + operator.setslice(a, 0, test_support.MAX_Py_ssize_t, []) + self.assert_(a == []) def test_sub(self): self.failUnlessRaises(TypeError, operator.sub) diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 6ec2902..88e3a1f 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -1631,18 +1631,8 @@ class TestParseNumber(BaseTest): "option -l: invalid integer value: '0x12x'") -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(): - test_support.run_suite(suite()) + test_support.run_unittest(__name__) if __name__ == '__main__': - unittest.main() + test_main() diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index a7fc1da..ed044f6 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -240,6 +240,15 @@ class StatAttributeTests(unittest.TestCase): os.utime(self.fname, (t1, t1)) self.assertEquals(os.stat(self.fname).st_mtime, t1) + def test_1686475(self): + # Verify that an open file can be stat'ed + try: + os.stat(r"c:\pagefile.sys") + except WindowsError as e: + if e == 2: # file does not exist; cannot run test + return + self.fail("Could not stat pagefile.sys") + from test import mapping_tests class EnvironTests(mapping_tests.BasicTestMappingProtocol): @@ -272,75 +281,104 @@ class WalkTests(unittest.TestCase): from os.path import join # Build: - # TESTFN/ a file kid and two directory kids + # TESTFN/ + # TEST1/ a file kid and two directory kids # tmp1 # SUB1/ a file kid and a directory kid - # tmp2 - # SUB11/ no kids - # SUB2/ just a file kid - # tmp3 - sub1_path = join(test_support.TESTFN, "SUB1") + # tmp2 + # SUB11/ no kids + # SUB2/ a file kid and a dirsymlink kid + # tmp3 + # link/ a symlink to TESTFN.2 + # TEST2/ + # tmp4 a lone file + walk_path = join(test_support.TESTFN, "TEST1") + sub1_path = join(walk_path, "SUB1") sub11_path = join(sub1_path, "SUB11") - sub2_path = join(test_support.TESTFN, "SUB2") - tmp1_path = join(test_support.TESTFN, "tmp1") + sub2_path = join(walk_path, "SUB2") + tmp1_path = join(walk_path, "tmp1") tmp2_path = join(sub1_path, "tmp2") tmp3_path = join(sub2_path, "tmp3") + link_path = join(sub2_path, "link") + t2_path = join(test_support.TESTFN, "TEST2") + tmp4_path = join(test_support.TESTFN, "TEST2", "tmp4") # Create stuff. os.makedirs(sub11_path) os.makedirs(sub2_path) - for path in tmp1_path, tmp2_path, tmp3_path: + os.makedirs(t2_path) + for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: f = open(path, "w") f.write("I'm " + path + " and proud of it. Blame test_os.\n") f.close() + if hasattr(os, "symlink"): + os.symlink(os.path.abspath(t2_path), link_path) + sub2_tree = (sub2_path, ["link"], ["tmp3"]) + else: + sub2_tree = (sub2_path, [], ["tmp3"]) # Walk top-down. - all = list(os.walk(test_support.TESTFN)) + all = list(os.walk(walk_path)) self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: TESTFN, SUB1, SUB11, SUB2 # flipped: TESTFN, SUB2, SUB1, SUB11 flipped = all[0][1][0] != "SUB1" all[0][1].sort() - self.assertEqual(all[0], (test_support.TESTFN, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"])) self.assertEqual(all[2 + flipped], (sub11_path, [], [])) - self.assertEqual(all[3 - 2 * flipped], (sub2_path, [], ["tmp3"])) + self.assertEqual(all[3 - 2 * flipped], sub2_tree) # Prune the search. all = [] - for root, dirs, files in os.walk(test_support.TESTFN): + for root, dirs, files in os.walk(walk_path): all.append((root, dirs, files)) # Don't descend into SUB1. if 'SUB1' in dirs: # Note that this also mutates the dirs we appended to all! dirs.remove('SUB1') self.assertEqual(len(all), 2) - self.assertEqual(all[0], (test_support.TESTFN, ["SUB2"], ["tmp1"])) - self.assertEqual(all[1], (sub2_path, [], ["tmp3"])) + self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"])) + self.assertEqual(all[1], sub2_tree) # Walk bottom-up. - all = list(os.walk(test_support.TESTFN, topdown=False)) + all = list(os.walk(walk_path, topdown=False)) self.assertEqual(len(all), 4) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: SUB11, SUB1, SUB2, TESTFN # flipped: SUB2, SUB11, SUB1, TESTFN flipped = all[3][1][0] != "SUB1" all[3][1].sort() - self.assertEqual(all[3], (test_support.TESTFN, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) self.assertEqual(all[flipped], (sub11_path, [], [])) self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 - 2 * flipped], (sub2_path, [], ["tmp3"])) + self.assertEqual(all[2 - 2 * flipped], sub2_tree) + + if hasattr(os, "symlink"): + # Walk, following symlinks. + for root, dirs, files in os.walk(walk_path, followlinks=True): + if root == link_path: + self.assertEqual(dirs, []) + self.assertEqual(files, ["tmp4"]) + break + else: + self.fail("Didn't follow symlink with followlinks=True") + def tearDown(self): # Tear everything down. This is a decent use for bottom-up on # Windows, which doesn't have a recursive delete command. The # (not so) subtlety is that rmdir will fail unless the dir's # kids are removed first, so bottom up is essential. for root, dirs, files in os.walk(test_support.TESTFN, topdown=False): for name in files: - os.remove(join(root, name)) + os.remove(os.path.join(root, name)) for name in dirs: - os.rmdir(join(root, name)) + dirname = os.path.join(root, name) + if not os.path.islink(dirname): + os.rmdir(dirname) + else: + os.remove(dirname) os.rmdir(test_support.TESTFN) class MakedirTests (unittest.TestCase): diff --git a/Lib/test/test_ossaudiodev.py b/Lib/test/test_ossaudiodev.py index 6ca1f74..eb15e88 100644 --- a/Lib/test/test_ossaudiodev.py +++ b/Lib/test/test_ossaudiodev.py @@ -1,7 +1,7 @@ from test import test_support test_support.requires('audio') -from test.test_support import verbose, findfile, TestFailed, TestSkipped +from test.test_support import verbose, findfile, TestSkipped import errno import fcntl @@ -12,6 +12,7 @@ import select import sunaudio import time import audioop +import unittest # Arggh, AFMT_S16_NE not defined on all platforms -- seems to be a # fairly recent addition to OSS. @@ -33,131 +34,143 @@ def read_sound_file(path): fp.close() if enc != SND_FORMAT_MULAW_8: - print("Expect .au file with 8-bit mu-law samples") - return + raise RuntimeError("Expect .au file with 8-bit mu-law samples") # Convert the data to 16-bit signed. data = audioop.ulaw2lin(data, 2) return (data, rate, 16, nchannels) -# version of assert that still works with -O -def _assert(expr, message=None): - if not expr: - raise AssertionError(message or "assertion failed") +class OSSAudioDevTests(unittest.TestCase): -def play_sound_file(data, rate, ssize, nchannels): - try: - dsp = ossaudiodev.open('w') - except IOError as msg: - if msg.args[0] in (errno.EACCES, errno.ENOENT, errno.ENODEV, errno.EBUSY): - raise TestSkipped, msg - raise TestFailed, msg - - # at least check that these methods can be invoked - dsp.bufsize() - dsp.obufcount() - dsp.obuffree() - dsp.getptr() - dsp.fileno() - - # Make sure the read-only attributes work. - _assert(dsp.closed is False, "dsp.closed is not False") - _assert(dsp.name == "/dev/dsp") - _assert(dsp.mode == 'w', "bad dsp.mode: %r" % dsp.mode) - - # And make sure they're really read-only. - for attr in ('closed', 'name', 'mode'): + def play_sound_file(self, data, rate, ssize, nchannels): try: - setattr(dsp, attr, 42) - raise RuntimeError("dsp.%s not read-only" % attr) - except TypeError: - pass - - # Compute expected running time of sound sample (in seconds). - expected_time = float(len(data)) / (ssize/8) / nchannels / rate - - # set parameters based on .au file headers - dsp.setparameters(AFMT_S16_NE, nchannels, rate) - print(("playing test sound file (expected running time: %.2f sec)" - % expected_time)) - t1 = time.time() - dsp.write(data) - dsp.close() - t2 = time.time() - elapsed_time = t2 - t1 - - percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100 - _assert(percent_diff <= 10.0, \ - ("elapsed time (%.2f sec) > 10%% off of expected time (%.2f sec)" - % (elapsed_time, expected_time))) - -def test_setparameters(dsp): - # Two configurations for testing: - # config1 (8-bit, mono, 8 kHz) should work on even the most - # ancient and crufty sound card, but maybe not on special- - # purpose high-end hardware - # config2 (16-bit, stereo, 44.1kHz) should work on all but the - # most ancient and crufty hardware - config1 = (ossaudiodev.AFMT_U8, 1, 8000) - config2 = (AFMT_S16_NE, 2, 44100) - - for config in [config1, config2]: - (fmt, channels, rate) = config - if (dsp.setfmt(fmt) == fmt and - dsp.channels(channels) == channels and - dsp.speed(rate) == rate): - break - else: - raise RuntimeError("unable to set audio sampling parameters: " - "you must have really weird audio hardware") - - # setparameters() should be able to set this configuration in - # either strict or non-strict mode. - result = dsp.setparameters(fmt, channels, rate, False) - _assert(result == (fmt, channels, rate), - "setparameters%r: returned %r" % (config, result)) - result = dsp.setparameters(fmt, channels, rate, True) - _assert(result == (fmt, channels, rate), - "setparameters%r: returned %r" % (config, result)) - -def test_bad_setparameters(dsp): - - # Now try some configurations that are presumably bogus: eg. 300 - # channels currently exceeds even Hollywood's ambitions, and - # negative sampling rate is utter nonsense. setparameters() should - # accept these in non-strict mode, returning something other than - # was requested, but should barf in strict mode. - fmt = AFMT_S16_NE - rate = 44100 - channels = 2 - for config in [(fmt, 300, rate), # ridiculous nchannels - (fmt, -5, rate), # impossible nchannels - (fmt, channels, -50), # impossible rate - ]: - (fmt, channels, rate) = config + dsp = ossaudiodev.open('w') + except IOError as msg: + if msg.args[0] in (errno.EACCES, errno.ENOENT, + errno.ENODEV, errno.EBUSY): + raise TestSkipped(msg) + raise + + # at least check that these methods can be invoked + dsp.bufsize() + dsp.obufcount() + dsp.obuffree() + dsp.getptr() + dsp.fileno() + + # Make sure the read-only attributes work. + self.failUnless(dsp.close) + self.assertEqual(dsp.name, "/dev/dsp") + self.assertEqual(dsp.mode, "w", "bad dsp.mode: %r" % dsp.mode) + + # And make sure they're really read-only. + for attr in ('closed', 'name', 'mode'): + try: + setattr(dsp, attr, 42) + except TypeError: + pass + else: + self.fail("dsp.%s not read-only" % attr) + + # Compute expected running time of sound sample (in seconds). + expected_time = float(len(data)) / (ssize/8) / nchannels / rate + + # set parameters based on .au file headers + dsp.setparameters(AFMT_S16_NE, nchannels, rate) + print ("playing test sound file (expected running time: %.2f sec)" + % expected_time) + t1 = time.time() + dsp.write(data) + dsp.close() + t2 = time.time() + elapsed_time = t2 - t1 + + percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100 + self.failUnless(percent_diff <= 10.0, + "elapsed time > 10% off of expected time") + + def set_parameters(self, dsp): + # Two configurations for testing: + # config1 (8-bit, mono, 8 kHz) should work on even the most + # ancient and crufty sound card, but maybe not on special- + # purpose high-end hardware + # config2 (16-bit, stereo, 44.1kHz) should work on all but the + # most ancient and crufty hardware + config1 = (ossaudiodev.AFMT_U8, 1, 8000) + config2 = (AFMT_S16_NE, 2, 44100) + + for config in [config1, config2]: + (fmt, channels, rate) = config + if (dsp.setfmt(fmt) == fmt and + dsp.channels(channels) == channels and + dsp.speed(rate) == rate): + break + else: + raise RuntimeError("unable to set audio sampling parameters: " + "you must have really weird audio hardware") + + # setparameters() should be able to set this configuration in + # either strict or non-strict mode. result = dsp.setparameters(fmt, channels, rate, False) - _assert(result != config, - "setparameters: unexpectedly got requested configuration") - + self.assertEqual(result, (fmt, channels, rate), + "setparameters%r: returned %r" % (config, result)) + + result = dsp.setparameters(fmt, channels, rate, True) + self.assertEqual(result, (fmt, channels, rate), + "setparameters%r: returned %r" % (config, result)) + + def set_bad_parameters(self, dsp): + + # Now try some configurations that are presumably bogus: eg. 300 + # channels currently exceeds even Hollywood's ambitions, and + # negative sampling rate is utter nonsense. setparameters() should + # accept these in non-strict mode, returning something other than + # was requested, but should barf in strict mode. + fmt = AFMT_S16_NE + rate = 44100 + channels = 2 + for config in [(fmt, 300, rate), # ridiculous nchannels + (fmt, -5, rate), # impossible nchannels + (fmt, channels, -50), # impossible rate + ]: + (fmt, channels, rate) = config + result = dsp.setparameters(fmt, channels, rate, False) + self.failIfEqual(result, config, + "unexpectedly got requested configuration") + + try: + result = dsp.setparameters(fmt, channels, rate, True) + except ossaudiodev.OSSAudioError as err: + pass + else: + self.fail("expected OSSAudioError") + + def test_playback(self): + sound_info = read_sound_file(findfile('audiotest.au')) + self.play_sound_file(*sound_info) + + def test_set_parameters(self): + dsp = ossaudiodev.open("w") try: - result = dsp.setparameters(fmt, channels, rate, True) - raise AssertionError("setparameters: expected OSSAudioError") - except ossaudiodev.OSSAudioError as err: - print("setparameters: got OSSAudioError as expected") + self.set_parameters(dsp) -def test(): - (data, rate, ssize, nchannels) = read_sound_file(findfile('audiotest.au')) - play_sound_file(data, rate, ssize, nchannels) + # Disabled because it fails under Linux 2.6 with ALSA's OSS + # emulation layer. + #self.set_bad_parameters(dsp) + finally: + dsp.close() + self.failUnless(dsp.closed) - dsp = ossaudiodev.open("w") - try: - test_setparameters(dsp) - - # Disabled because it fails under Linux 2.6 with ALSA's OSS - # emulation layer. - #test_bad_setparameters(dsp) - finally: - dsp.close() - _assert(dsp.closed is True, "dsp.closed is not True") -test() +def test_main(): + try: + dsp = ossaudiodev.open('w') + except IOError as msg: + if msg.args[0] in (errno.EACCES, errno.ENOENT, + errno.ENODEV, errno.EBUSY): + raise TestSkipped(msg) + raise + test_support.run_unittest(__name__) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 9ed814a..1611e39 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -49,6 +49,11 @@ class TestTranforms(unittest.TestCase): self.assert_(elem not in asm) for elem in ('LOAD_CONST', '(None)'): self.assert_(elem in asm) + def f(): + 'Adding a docstring made this test fail in Py2.5.0' + return None + self.assert_('LOAD_CONST' in disassemble(f)) + self.assert_('LOAD_GLOBAL' not in disassemble(f)) def test_while_one(self): # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP @@ -195,14 +200,14 @@ class TestTranforms(unittest.TestCase): # There should be one jump for the while loop. self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1) self.assertEqual(asm.split().count('RETURN_VALUE'), 2) - + def test_make_function_doesnt_bail(self): def f(): - def g()->1+1: + def g()->1+1: pass return g asm = disassemble(f) - self.assert_('BINARY_ADD' not in asm) + self.assert_('BINARY_ADD' not in asm) def test_main(verbose=None): diff --git a/Lib/test/test_popen2.py b/Lib/test/test_popen2.py index 008a67a..31f22d6 100644 --- a/Lib/test/test_popen2.py +++ b/Lib/test/test_popen2.py @@ -1,78 +1,92 @@ #! /usr/bin/env python -"""Test script for popen2.py - Christian Tismer -""" +"""Test script for popen2.py""" import os import sys -from test.test_support import TestSkipped, reap_children - -# popen2 contains its own testing routine -# which is especially useful to see if open files -# like stdin can be read successfully by a forked -# subprocess. - -def main(): - print("Test popen2 module:") - if (sys.platform[:4] == 'beos' or sys.platform[:6] == 'atheos') \ - and __name__ != '__main__': - # Locks get messed up or something. Generally we're supposed - # to avoid mixing "posix" fork & exec with native threads, and - # they may be right about that after all. - raise TestSkipped, "popen2() doesn't work during import on " + sys.platform - try: - from os import popen - except ImportError: - # if we don't have os.popen, check that - # we have os.fork. if not, skip the test - # (by raising an ImportError) - from os import fork - import popen2 - popen2._test() - - -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" +import unittest +import popen2 + +from test.test_support import TestSkipped, run_unittest, reap_children + +if sys.platform[:4] == 'beos' or sys.platform[:6] == 'atheos': + # Locks get messed up or something. Generally we're supposed + # to avoid mixing "posix" fork & exec with native threads, and + # they may be right about that after all. + raise TestSkipped("popen2() doesn't work on " + sys.platform) + +# if we don't have os.popen, check that +# we have os.fork. if not, skip the test +# (by raising an ImportError) +try: + from os import popen + del popen +except ImportError: + from os import fork + del fork + +class Popen2Test(unittest.TestCase): + cmd = "cat" if os.name == "nt": cmd = "more" + teststr = "ab cd\n" # "more" doesn't act the same way across Windows flavors, # sometimes adding an extra newline at the start or the # end. So we strip whitespace off both ends for comparison. expected = teststr.strip() - print("testing popen2...") - w, r = os.popen2(cmd) - w.write(teststr) - w.close() - got = r.read() - if got.strip() != expected: - raise ValueError("wrote %r read %r" % (teststr, got)) - print("testing popen3...") - try: - w, r, e = os.popen3([cmd]) - except: - w, r, e = os.popen3(cmd) - w.write(teststr) - w.close() - got = r.read() - if got.strip() != expected: - raise ValueError("wrote %r read %r" % (teststr, got)) - got = e.read() - if got: - 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") - -main() -_test() -reap_children() + + def setUp(self): + popen2._cleanup() + # When the test runs, there shouldn't be any open pipes + self.assertFalse(popen2._active, "Active pipes when test starts" + + repr([c.cmd for c in popen2._active])) + + def tearDown(self): + for inst in popen2._active: + inst.wait() + popen2._cleanup() + self.assertFalse(popen2._active, "_active not empty") + reap_children() + + def validate_output(self, teststr, expected_out, r, w, e=None): + w.write(teststr) + w.close() + got = r.read() + self.assertEquals(expected_out, got.strip(), "wrote %r read %r" % + (teststr, got)) + + if e is not None: + got = e.read() + self.assertFalse(got, "unexpected %r on stderr" % got) + + def test_popen2(self): + r, w = popen2.popen2(self.cmd) + self.validate_output(self.teststr, self.expected, r, w) + + def test_popen3(self): + if os.name == 'posix': + r, w, e = popen2.popen3([self.cmd]) + self.validate_output(self.teststr, self.expected, r, w, e) + + r, w, e = popen2.popen3(self.cmd) + self.validate_output(self.teststr, self.expected, r, w, e) + + def test_os_popen2(self): + # same test as test_popen2(), but using the os.popen*() API + w, r = os.popen2(self.cmd) + self.validate_output(self.teststr, self.expected, r, w) + + def test_os_popen3(self): + # same test as test_popen3(), but using the os.popen*() API + if os.name == 'posix': + w, r, e = os.popen3([self.cmd]) + self.validate_output(self.teststr, self.expected, r, w, e) + + w, r, e = os.popen3(self.cmd) + self.validate_output(self.teststr, self.expected, r, w, e) + + +def test_main(): + run_unittest(Popen2Test) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py new file mode 100644 index 0000000..35ff636 --- /dev/null +++ b/Lib/test/test_poplib.py @@ -0,0 +1,71 @@ +import socket +import threading +import poplib +import time + +from unittest import TestCase +from test import test_support + + +def server(evt): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.settimeout(3) + serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + serv.bind(("", 9091)) + serv.listen(5) + try: + conn, addr = serv.accept() + except socket.timeout: + pass + else: + conn.send("+ Hola mundo\n") + conn.close() + finally: + serv.close() + evt.set() + +class GeneralTests(TestCase): + + def setUp(self): + self.evt = threading.Event() + threading.Thread(target=server, args=(self.evt,)).start() + time.sleep(.1) + + def tearDown(self): + self.evt.wait() + + def testBasic(self): + # connects + pop = poplib.POP3("localhost", 9091) + pop.sock.close() + + def testTimeoutDefault(self): + # default + pop = poplib.POP3("localhost", 9091) + self.assertTrue(pop.sock.gettimeout() is None) + pop.sock.close() + + def testTimeoutValue(self): + # a value + pop = poplib.POP3("localhost", 9091, timeout=30) + self.assertEqual(pop.sock.gettimeout(), 30) + pop.sock.close() + + def testTimeoutNone(self): + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + pop = poplib.POP3("localhost", 9091, timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(pop.sock.gettimeout(), 30) + pop.sock.close() + + + +def test_main(verbose=None): + test_support.run_unittest(GeneralTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 20a1fc5..0abf464 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -2,15 +2,29 @@ import unittest from test import test_support import posixpath, os -from posixpath import realpath, abspath, join, dirname, basename +from posixpath import realpath, abspath, join, dirname, basename, relpath # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. ABSTFN = abspath(test_support.TESTFN) +def safe_rmdir(dirname): + try: + os.rmdir(dirname) + except OSError: + pass + class PosixPathTest(unittest.TestCase): + def setUp(self): + self.tearDown() + + def tearDown(self): + for suffix in ["", "1", "2"]: + test_support.unlink(test_support.TESTFN + suffix) + safe_rmdir(test_support.TESTFN + suffix) + def assertIs(self, a, b): self.assert_(a is b) @@ -43,15 +57,27 @@ class PosixPathTest(unittest.TestCase): self.assertRaises(TypeError, posixpath.split) - def test_splitext(self): - self.assertEqual(posixpath.splitext("foo.ext"), ("foo", ".ext")) - self.assertEqual(posixpath.splitext("/foo/foo.ext"), ("/foo/foo", ".ext")) - self.assertEqual(posixpath.splitext(".ext"), ("", ".ext")) - self.assertEqual(posixpath.splitext("/foo.ext/foo"), ("/foo.ext/foo", "")) - self.assertEqual(posixpath.splitext("foo.ext/"), ("foo.ext/", "")) - self.assertEqual(posixpath.splitext(""), ("", "")) - self.assertEqual(posixpath.splitext("foo.bar.ext"), ("foo.bar", ".ext")) + def splitextTest(self, path, filename, ext): + self.assertEqual(posixpath.splitext(path), (filename, ext)) + self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext)) + self.assertEqual(posixpath.splitext("abc/" + path), ("abc/" + filename, ext)) + self.assertEqual(posixpath.splitext("abc.def/" + path), ("abc.def/" + filename, ext)) + self.assertEqual(posixpath.splitext("/abc.def/" + path), ("/abc.def/" + filename, ext)) + self.assertEqual(posixpath.splitext(path + "/"), (filename + ext + "/", "")) + def test_splitext(self): + self.splitextTest("foo.bar", "foo", ".bar") + self.splitextTest("foo.boo.bar", "foo.boo", ".bar") + self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar") + self.splitextTest(".csh.rc", ".csh", ".rc") + self.splitextTest("nodots", "nodots", "") + self.splitextTest(".cshrc", ".cshrc", "") + self.splitextTest("...manydots", "...manydots", "") + self.splitextTest("...manydots.ext", "...manydots", ".ext") + self.splitextTest(".", ".", "") + self.splitextTest("..", "..", "") + self.splitextTest("........", "........", "") + self.splitextTest("", "", "") self.assertRaises(TypeError, posixpath.splitext) def test_isabs(self): @@ -113,7 +139,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.closed: f.close() - os.remove(test_support.TESTFN) def test_time(self): f = open(test_support.TESTFN, "wb") @@ -135,7 +160,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.closed: f.close() - os.remove(test_support.TESTFN) def test_islink(self): self.assertIs(posixpath.islink(test_support.TESTFN + "1"), False) @@ -154,14 +178,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.close(): f.close() - try: - os.remove(test_support.TESTFN + "1") - except os.error: - pass - try: - os.remove(test_support.TESTFN + "2") - except os.error: - pass self.assertRaises(TypeError, posixpath.islink) @@ -176,10 +192,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.close(): f.close() - try: - os.remove(test_support.TESTFN) - except os.error: - pass self.assertRaises(TypeError, posixpath.exists) @@ -197,14 +209,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.close(): f.close() - try: - os.remove(test_support.TESTFN) - except os.error: - pass - try: - os.rmdir(test_support.TESTFN) - except os.error: - pass self.assertRaises(TypeError, posixpath.isdir) @@ -222,67 +226,51 @@ class PosixPathTest(unittest.TestCase): finally: if not f.close(): f.close() - try: - os.remove(test_support.TESTFN) - except os.error: - pass - try: - os.rmdir(test_support.TESTFN) - except os.error: - pass self.assertRaises(TypeError, posixpath.isdir) - def test_samefile(self): - f = open(test_support.TESTFN + "1", "wb") - try: - f.write("foo") - f.close() + def test_samefile(self): + f = open(test_support.TESTFN + "1", "wb") + try: + f.write("foo") + f.close() + self.assertIs( + posixpath.samefile( + test_support.TESTFN + "1", + test_support.TESTFN + "1" + ), + True + ) + # If we don't have links, assume that os.stat doesn't return resonable + # inode information and thus, that samefile() doesn't work + if hasattr(os, "symlink"): + os.symlink( + test_support.TESTFN + "1", + test_support.TESTFN + "2" + ) self.assertIs( posixpath.samefile( test_support.TESTFN + "1", - test_support.TESTFN + "1" + test_support.TESTFN + "2" ), True ) - # If we don't have links, assume that os.stat doesn't return resonable - # inode information and thus, that samefile() doesn't work - if hasattr(os, "symlink"): - os.symlink( + os.remove(test_support.TESTFN + "2") + f = open(test_support.TESTFN + "2", "wb") + f.write("bar") + f.close() + self.assertIs( + posixpath.samefile( test_support.TESTFN + "1", test_support.TESTFN + "2" - ) - self.assertIs( - posixpath.samefile( - test_support.TESTFN + "1", - test_support.TESTFN + "2" - ), - True - ) - os.remove(test_support.TESTFN + "2") - f = open(test_support.TESTFN + "2", "wb") - f.write("bar") - f.close() - self.assertIs( - posixpath.samefile( - test_support.TESTFN + "1", - test_support.TESTFN + "2" - ), - False - ) - finally: - if not f.close(): - f.close() - try: - os.remove(test_support.TESTFN + "1") - except os.error: - pass - try: - os.remove(test_support.TESTFN + "2") - except os.error: - pass + ), + False + ) + finally: + if not f.close(): + f.close() - self.assertRaises(TypeError, posixpath.samefile) + self.assertRaises(TypeError, posixpath.samefile) def test_samestat(self): f = open(test_support.TESTFN + "1", "wb") @@ -322,14 +310,6 @@ class PosixPathTest(unittest.TestCase): finally: if not f.close(): f.close() - try: - os.remove(test_support.TESTFN + "1") - except os.error: - pass - try: - os.remove(test_support.TESTFN + "2") - except os.error: - pass self.assertRaises(TypeError, posixpath.samestat) @@ -409,7 +389,7 @@ class PosixPathTest(unittest.TestCase): os.symlink(ABSTFN+"1", ABSTFN) self.assertEqual(realpath(ABSTFN), ABSTFN+"1") finally: - self.safe_remove(ABSTFN) + test_support.unlink(ABSTFN) def test_realpath_symlink_loops(self): # Bug #930024, return the path unchanged if we get into an infinite @@ -429,9 +409,9 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(basename(ABSTFN)), ABSTFN) finally: os.chdir(old_path) - self.safe_remove(ABSTFN) - self.safe_remove(ABSTFN+"1") - self.safe_remove(ABSTFN+"2") + test_support.unlink(ABSTFN) + test_support.unlink(ABSTFN+"1") + test_support.unlink(ABSTFN+"2") def test_realpath_resolve_parents(self): # We also need to resolve any symlinks in the parents of a relative @@ -448,9 +428,9 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath("a"), ABSTFN + "/y/a") finally: os.chdir(old_path) - self.safe_remove(ABSTFN + "/k") - self.safe_rmdir(ABSTFN + "/y") - self.safe_rmdir(ABSTFN) + test_support.unlink(ABSTFN + "/k") + safe_rmdir(ABSTFN + "/y") + safe_rmdir(ABSTFN) def test_realpath_resolve_before_normalizing(self): # Bug #990669: Symbolic links should be resolved before we @@ -474,10 +454,10 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."), ABSTFN + "/k") finally: os.chdir(old_path) - self.safe_remove(ABSTFN + "/link-y") - self.safe_rmdir(ABSTFN + "/k/y") - self.safe_rmdir(ABSTFN + "/k") - self.safe_rmdir(ABSTFN) + test_support.unlink(ABSTFN + "/link-y") + safe_rmdir(ABSTFN + "/k/y") + safe_rmdir(ABSTFN + "/k") + safe_rmdir(ABSTFN) def test_realpath_resolve_first(self): # Bug #1213894: The first component of the path, if not absolute, @@ -495,20 +475,24 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k") finally: os.chdir(old_path) - self.safe_remove(ABSTFN + "link") - self.safe_rmdir(ABSTFN + "/k") - self.safe_rmdir(ABSTFN) - - # Convenience functions for removing temporary files. - def pass_os_error(self, func, filename): - try: func(filename) - except OSError: pass + test_support.unlink(ABSTFN + "link") + safe_rmdir(ABSTFN + "/k") + safe_rmdir(ABSTFN) - def safe_remove(self, filename): - self.pass_os_error(os.remove, filename) - - def safe_rmdir(self, dirname): - self.pass_os_error(os.rmdir, dirname) + def test_relpath(self): + (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar") + try: + curdir = os.path.split(os.getcwd())[-1] + self.assertRaises(ValueError, posixpath.relpath, "") + self.assertEqual(posixpath.relpath("a"), "a") + self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a") + self.assertEqual(posixpath.relpath("a/b"), "a/b") + self.assertEqual(posixpath.relpath("../a/b"), "../a/b") + self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a") + self.assertEqual(posixpath.relpath("a/b", "../c"), "../"+curdir+"/a/b") + self.assertEqual(posixpath.relpath("a", "b/c"), "../../a") + finally: + os.getcwd = real_getcwd def test_main(): test_support.run_unittest(PosixPathTest) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 123c3f8..5ce387b 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,5 +1,9 @@ -import pty, os, sys, signal -from test.test_support import verbose, TestFailed, TestSkipped +import pty +import os +import sys +import signal +from test.test_support import verbose, TestSkipped, run_unittest +import unittest TEST_STRING_1 = "I wish to buy a fish license.\n" TEST_STRING_2 = "For my pet fish, Eric.\n" @@ -11,6 +15,7 @@ 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 @@ -32,116 +37,141 @@ def normalize_output(data): return data + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. - -def test_basic_pty(): - try: - debug("Calling master_open()") - master_fd, slave_name = pty.master_open() - debug("Got master_fd '%d', slave_name '%s'"%(master_fd, slave_name)) - debug("Calling slave_open(%r)"%(slave_name,)) - slave_fd = pty.slave_open(slave_name) - debug("Got slave_fd '%d'"%slave_fd) - except OSError: - # " An optional feature could not be imported " ... ? - raise TestSkipped, "Pseudo-terminals (seemingly) not functional." - - if not os.isatty(slave_fd): - raise TestFailed, "slave_fd is not a tty" - - debug("Writing to slave_fd") - os.write(slave_fd, TEST_STRING_1) - s1 = os.read(master_fd, 1024) - 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(normalize_output(s2)) - - os.close(slave_fd) - os.close(master_fd) - -def handle_sig(sig, frame): - raise TestFailed, "isatty hung" - -# isatty() and close() can hang on some platforms -# set an alarm before running the test to make sure we don't hang forever -old_alarm = signal.signal(signal.SIGALRM, handle_sig) -signal.alarm(10) - -try: - test_basic_pty() -finally: - # remove alarm, restore old alarm handler - signal.alarm(0) - signal.signal(signal.SIGALRM, old_alarm) - -# basic pty passed. - -debug("calling pty.fork()") -pid, master_fd = pty.fork() -if pid == pty.CHILD: - # stdout should be connected to a tty. - if not os.isatty(1): - debug("Child's fd 1 is not a tty?!") - os._exit(3) - - # After pty.fork(), the child should already be a session leader. - # (on those systems that have that concept.) - debug("In child, calling os.setsid()") - try: - os.setsid() - except OSError: - # Good, we already were session leader - debug("Good: OSError was raised.") - pass - except AttributeError: - # Have pty, but not setsid() ? - debug("No setsid() available ?") - pass - except: - # We don't want this error to propagate, escaping the call to - # os._exit() and causing very peculiar behavior in the calling - # regrtest.py ! - # Note: could add traceback printing here. - debug("An unexpected error was raised.") - os._exit(1) - else: - debug("os.setsid() succeeded! (bad!)") - os._exit(2) - os._exit(4) -else: - debug("Waiting for child (%d) to finish."%pid) - ##line = os.read(master_fd, 80) - ##lines = line.replace('\r\n', '\n').split('\n') - ##if False and lines != ['In child, calling os.setsid()', - ## 'Good: OSError was raised.', '']: - ## raise TestFailed("Unexpected output from child: %r" % line) - - (pid, status) = os.waitpid(pid, 0) - res = status >> 8 - debug("Child (%d) exited with status %d (%d)."%(pid, res, status)) - if res == 1: - raise TestFailed, "Child raised an unexpected exception in os.setsid()" - elif res == 2: - raise TestFailed, "pty.fork() failed to make child a session leader." - elif res == 3: - raise TestFailed, "Child spawned by pty.fork() did not have a tty as stdout" - elif res != 4: - raise TestFailed, "pty.fork() failed for unknown reasons." - - ##debug("Reading from master_fd now that the child has exited") - ##try: - ## s1 = os.read(master_fd, 1024) - ##except os.error: - ## pass - ##else: - ## raise TestFailed("Read from master_fd did not raise exception") - - -os.close(master_fd) - -# pty.fork() passed. +class PtyTest(unittest.TestCase): + def setUp(self): + # isatty() and close() can hang on some platforms. Set an alarm + # before running the test to make sure we don't hang forever. + self.old_alarm = signal.signal(signal.SIGALRM, self.handle_sig) + signal.alarm(10) + + def tearDown(self): + # remove alarm, restore old alarm handler + signal.alarm(0) + signal.signal(signal.SIGALRM, self.old_alarm) + + def handle_sig(self, sig, frame): + self.fail("isatty hung") + + def test_basic(self): + try: + debug("Calling master_open()") + master_fd, slave_name = pty.master_open() + debug("Got master_fd '%d', slave_name '%s'" % + (master_fd, slave_name)) + debug("Calling slave_open(%r)" % (slave_name,)) + slave_fd = pty.slave_open(slave_name) + debug("Got slave_fd '%d'" % slave_fd) + except OSError: + # " An optional feature could not be imported " ... ? + raise TestSkipped, "Pseudo-terminals (seemingly) not functional." + + self.assertTrue(os.isatty(slave_fd), 'slave_fd is not a tty') + + debug("Writing to slave_fd") + os.write(slave_fd, TEST_STRING_1) + s1 = os.read(master_fd, 1024) + self.assertEquals('I wish to buy a fish license.\n', + 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) + self.assertEquals('For my pet fish, Eric.\n', normalize_output(s2)) + + os.close(slave_fd) + os.close(master_fd) + + + def test_fork(self): + debug("calling pty.fork()") + pid, master_fd = pty.fork() + if pid == pty.CHILD: + # stdout should be connected to a tty. + if not os.isatty(1): + debug("Child's fd 1 is not a tty?!") + os._exit(3) + + # After pty.fork(), the child should already be a session leader. + # (on those systems that have that concept.) + debug("In child, calling os.setsid()") + try: + os.setsid() + except OSError: + # Good, we already were session leader + debug("Good: OSError was raised.") + pass + except AttributeError: + # Have pty, but not setsid()? + debug("No setsid() available?") + pass + except: + # We don't want this error to propagate, escaping the call to + # os._exit() and causing very peculiar behavior in the calling + # regrtest.py ! + # Note: could add traceback printing here. + debug("An unexpected error was raised.") + os._exit(1) + else: + debug("os.setsid() succeeded! (bad!)") + os._exit(2) + os._exit(4) + else: + debug("Waiting for child (%d) to finish." % pid) + # In verbose mode, we have to consume the debug output from the + # child or the child will block, causing this test to hang in the + # parent's waitpid() call. The child blocks after a + # platform-dependent amount of data is written to its fd. On + # Linux 2.6, it's 4000 bytes and the child won't block, but on OS + # X even the small writes in the child above will block it. Also + # on Linux, the read() will throw an OSError (input/output error) + # when it tries to read past the end of the buffer but the child's + # already exited, so catch and discard those exceptions. It's not + # worth checking for EIO. + while True: + try: + data = os.read(master_fd, 80) + except OSError: + break + if not data: + break + sys.stdout.write(data.replace('\r\n', '\n')) + + ##line = os.read(master_fd, 80) + ##lines = line.replace('\r\n', '\n').split('\n') + ##if False and lines != ['In child, calling os.setsid()', + ## 'Good: OSError was raised.', '']: + ## raise TestFailed("Unexpected output from child: %r" % line) + + (pid, status) = os.waitpid(pid, 0) + res = status >> 8 + debug("Child (%d) exited with status %d (%d)." % (pid, res, status)) + if res == 1: + self.fail("Child raised an unexpected exception in os.setsid()") + elif res == 2: + self.fail("pty.fork() failed to make child a session leader.") + elif res == 3: + self.fail("Child spawned by pty.fork() did not have a tty as stdout") + elif res != 4: + self.fail("pty.fork() failed for unknown reasons.") + + ##debug("Reading from master_fd now that the child has exited") + ##try: + ## s1 = os.read(master_fd, 1024) + ##except os.error: + ## pass + ##else: + ## raise TestFailed("Read from master_fd did not raise exception") + + os.close(master_fd) + + # pty.fork() passed. + +def test_main(verbose=None): + run_unittest(PtyTest) + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 9772a00..0900d1e 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -1,108 +1,40 @@ -# Very simple test - Parse a file and print what happens - # XXX TypeErrors on calling handlers, or on bad return values from a # handler, are obscure and unhelpful. +import StringIO +import unittest + import pyexpat from xml.parsers import expat -from test.test_support import sortdict, TestFailed +from test.test_support import sortdict, run_unittest -class Outputter: - def StartElementHandler(self, name, attrs): - print('Start element:\n\t' + repr(name), sortdict(attrs)) - def EndElementHandler(self, name): - print('End element:\n\t' + repr(name)) - - def CharacterDataHandler(self, data): - data = data.strip() - if data: - print('Character data:') - print('\t' + repr(data)) - - def ProcessingInstructionHandler(self, target, data): - print('PI:\n\t' + repr(target), repr(data)) - - def StartNamespaceDeclHandler(self, prefix, uri): - print('NS decl:\n\t' + repr(prefix), repr(uri)) - - def EndNamespaceDeclHandler(self, prefix): - print('End of NS decl:\n\t' + repr(prefix)) - - def StartCdataSectionHandler(self): - print('Start of CDATA section') - - def EndCdataSectionHandler(self): - print('End of CDATA section') - - def CommentHandler(self, text): - print('Comment:\n\t' + repr(text)) - - def NotationDeclHandler(self, *args): - name, base, sysid, pubid = args - print('Notation declared:', args) - - def UnparsedEntityDeclHandler(self, *args): - entityName, base, systemId, publicId, notationName = args - print('Unparsed entity decl:\n\t' + str(args)) - - def NotStandaloneHandler(self, userData): - print('Not standalone') - return 1 - - def ExternalEntityRefHandler(self, *args): - context, base, sysId, pubId = args - print('External entity ref:', args[1:]) - return 1 - - def DefaultHandler(self, userData): - pass - - def DefaultHandlerExpand(self, userData): - pass - - -def confirm(ok): - if ok: - print("OK.") - else: - print("Not OK.") - -out = Outputter() -parser = expat.ParserCreate(namespace_separator='!') - -# Test getting/setting returns_unicode -parser.returns_unicode = 0; confirm(parser.returns_unicode == 0) -parser.returns_unicode = 1; confirm(parser.returns_unicode == 1) -parser.returns_unicode = 2; confirm(parser.returns_unicode == 1) -parser.returns_unicode = 0; confirm(parser.returns_unicode == 0) - -# Test getting/setting ordered_attributes -parser.ordered_attributes = 0; confirm(parser.ordered_attributes == 0) -parser.ordered_attributes = 1; confirm(parser.ordered_attributes == 1) -parser.ordered_attributes = 2; confirm(parser.ordered_attributes == 1) -parser.ordered_attributes = 0; confirm(parser.ordered_attributes == 0) - -# Test getting/setting specified_attributes -parser.specified_attributes = 0; confirm(parser.specified_attributes == 0) -parser.specified_attributes = 1; confirm(parser.specified_attributes == 1) -parser.specified_attributes = 2; confirm(parser.specified_attributes == 1) -parser.specified_attributes = 0; confirm(parser.specified_attributes == 0) - -HANDLER_NAMES = [ - 'StartElementHandler', 'EndElementHandler', - 'CharacterDataHandler', 'ProcessingInstructionHandler', - 'UnparsedEntityDeclHandler', 'NotationDeclHandler', - 'StartNamespaceDeclHandler', 'EndNamespaceDeclHandler', - 'CommentHandler', 'StartCdataSectionHandler', - 'EndCdataSectionHandler', - 'DefaultHandler', 'DefaultHandlerExpand', - #'NotStandaloneHandler', - 'ExternalEntityRefHandler' - ] -for name in HANDLER_NAMES: - setattr(parser, name, getattr(out, name)) +class SetAttributeTest(unittest.TestCase): + def setUp(self): + self.parser = expat.ParserCreate(namespace_separator='!') + self.set_get_pairs = [ + [0, 0], + [1, 1], + [2, 1], + [0, 0], + ] + + def test_returns_unicode(self): + for x, y in self.set_get_pairs: + self.parser.returns_unicode = x + self.assertEquals(self.parser.returns_unicode, y) + + def test_ordered_attributes(self): + for x, y in self.set_get_pairs: + self.parser.ordered_attributes = x + self.assertEquals(self.parser.ordered_attributes, y) + + def test_specified_attributes(self): + for x, y in self.set_get_pairs: + self.parser.specified_attributes = x + self.assertEquals(self.parser.specified_attributes, y) + data = '''\ <?xml version="1.0" encoding="iso-8859-1" standalone="no"?> @@ -126,108 +58,228 @@ data = '''\ </root> ''' + # Produce UTF-8 output -parser.returns_unicode = 0 -try: - parser.Parse(data, 1) -except expat.error: - print('** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)) - print('** Line', parser.ErrorLineNumber) - print('** Column', parser.ErrorColumnNumber) - print('** Byte', parser.ErrorByteIndex) - -# Try the parse again, this time producing Unicode output -parser = expat.ParserCreate(namespace_separator='!') -parser.returns_unicode = 1 - -for name in HANDLER_NAMES: - setattr(parser, name, getattr(out, name)) -try: - parser.Parse(data, 1) -except expat.error: - print('** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)) - print('** Line', parser.ErrorLineNumber) - print('** Column', parser.ErrorColumnNumber) - print('** Byte', parser.ErrorByteIndex) - -# Try parsing a file -parser = expat.ParserCreate(namespace_separator='!') -parser.returns_unicode = 1 - -for name in HANDLER_NAMES: - setattr(parser, name, getattr(out, name)) -import StringIO -file = StringIO.StringIO(data) -try: - parser.ParseFile(file) -except expat.error: - print('** Error', parser.ErrorCode, expat.ErrorString(parser.ErrorCode)) - print('** Line', parser.ErrorLineNumber) - print('** Column', parser.ErrorColumnNumber) - print('** Byte', parser.ErrorByteIndex) - - -# Tests that make sure we get errors when the namespace_separator value -# is illegal, and that we don't for good values: -print() -print("Testing constructor for proper handling of namespace_separator values:") -expat.ParserCreate() -expat.ParserCreate(namespace_separator=None) -expat.ParserCreate(namespace_separator=' ') -print("Legal values tested o.k.") -try: - expat.ParserCreate(namespace_separator=42) -except TypeError as e: - print("Caught expected TypeError:") - print(e) -else: - print("Failed to catch expected TypeError.") - -try: - expat.ParserCreate(namespace_separator='too long') -except ValueError as e: - print("Caught expected ValueError:") - print(e) -else: - print("Failed to catch expected ValueError.") - -# ParserCreate() needs to accept a namespace_separator of zero length -# to satisfy the requirements of RDF applications that are required -# to simply glue together the namespace URI and the localname. Though -# considered a wart of the RDF specifications, it needs to be supported. -# -# See XML-SIG mailing list thread starting with -# http://mail.python.org/pipermail/xml-sig/2001-April/005202.html -# -expat.ParserCreate(namespace_separator='') # too short - -# Test the interning machinery. -p = expat.ParserCreate() -L = [] -def collector(name, *args): - L.append(name) -p.StartElementHandler = collector -p.EndElementHandler = collector -p.Parse("<e> <e/> <e></e> </e>", 1) -tag = L[0] -if len(L) != 6: - print("L should only contain 6 entries; found", len(L)) -for entry in L: - if tag is not entry: - print("expected L to contain many references to the same string", end=' ') - print("(it didn't)") - print("L =", repr(L)) - break - -# Tests of the buffer_text attribute. -import sys - -class TextCollector: - def __init__(self, parser): +class ParseTest(unittest.TestCase): + class Outputter: + def __init__(self): + self.out = [] + + def StartElementHandler(self, name, attrs): + self.out.append('Start element: ' + repr(name) + ' ' + + sortdict(attrs)) + + def EndElementHandler(self, name): + self.out.append('End element: ' + repr(name)) + + def CharacterDataHandler(self, data): + data = data.strip() + if data: + self.out.append('Character data: ' + repr(data)) + + def ProcessingInstructionHandler(self, target, data): + self.out.append('PI: ' + repr(target) + ' ' + repr(data)) + + def StartNamespaceDeclHandler(self, prefix, uri): + self.out.append('NS decl: ' + repr(prefix) + ' ' + repr(uri)) + + def EndNamespaceDeclHandler(self, prefix): + self.out.append('End of NS decl: ' + repr(prefix)) + + def StartCdataSectionHandler(self): + self.out.append('Start of CDATA section') + + def EndCdataSectionHandler(self): + self.out.append('End of CDATA section') + + def CommentHandler(self, text): + self.out.append('Comment: ' + repr(text)) + + def NotationDeclHandler(self, *args): + name, base, sysid, pubid = args + self.out.append('Notation declared: %s' %(args,)) + + def UnparsedEntityDeclHandler(self, *args): + entityName, base, systemId, publicId, notationName = args + self.out.append('Unparsed entity decl: %s' %(args,)) + + def NotStandaloneHandler(self, userData): + self.out.append('Not standalone') + return 1 + + def ExternalEntityRefHandler(self, *args): + context, base, sysId, pubId = args + self.out.append('External entity ref: %s' %(args[1:],)) + return 1 + + def DefaultHandler(self, userData): + pass + + def DefaultHandlerExpand(self, userData): + pass + + handler_names = [ + 'StartElementHandler', 'EndElementHandler', + 'CharacterDataHandler', 'ProcessingInstructionHandler', + 'UnparsedEntityDeclHandler', 'NotationDeclHandler', + 'StartNamespaceDeclHandler', 'EndNamespaceDeclHandler', + 'CommentHandler', 'StartCdataSectionHandler', + 'EndCdataSectionHandler', + 'DefaultHandler', 'DefaultHandlerExpand', + #'NotStandaloneHandler', + 'ExternalEntityRefHandler' + ] + + def test_utf8(self): + + out = self.Outputter() + parser = expat.ParserCreate(namespace_separator='!') + for name in self.handler_names: + setattr(parser, name, getattr(out, name)) + parser.returns_unicode = 0 + parser.Parse(data, 1) + + # Verify output + op = out.out + self.assertEquals(op[0], 'PI: \'xml-stylesheet\' \'href="stylesheet.css"\'') + self.assertEquals(op[1], "Comment: ' comment data '") + self.assertEquals(op[2], "Notation declared: ('notation', None, 'notation.jpeg', None)") + self.assertEquals(op[3], "Unparsed entity decl: ('unparsed_entity', None, 'entity.file', None, 'notation')") + self.assertEquals(op[4], "Start element: 'root' {'attr1': 'value1', 'attr2': 'value2\\xe1\\xbd\\x80'}") + self.assertEquals(op[5], "NS decl: 'myns' 'http://www.python.org/namespace'") + self.assertEquals(op[6], "Start element: 'http://www.python.org/namespace!subelement' {}") + self.assertEquals(op[7], "Character data: 'Contents of subelements'") + self.assertEquals(op[8], "End element: 'http://www.python.org/namespace!subelement'") + self.assertEquals(op[9], "End of NS decl: 'myns'") + self.assertEquals(op[10], "Start element: 'sub2' {}") + self.assertEquals(op[11], 'Start of CDATA section') + self.assertEquals(op[12], "Character data: 'contents of CDATA section'") + self.assertEquals(op[13], 'End of CDATA section') + self.assertEquals(op[14], "End element: 'sub2'") + self.assertEquals(op[15], "External entity ref: (None, 'entity.file', None)") + self.assertEquals(op[16], "End element: 'root'") + + def test_unicode(self): + # Try the parse again, this time producing Unicode output + out = self.Outputter() + parser = expat.ParserCreate(namespace_separator='!') + parser.returns_unicode = 1 + for name in self.handler_names: + setattr(parser, name, getattr(out, name)) + + parser.Parse(data, 1) + + op = out.out + self.assertEquals(op[0], 'PI: u\'xml-stylesheet\' u\'href="stylesheet.css"\'') + self.assertEquals(op[1], "Comment: u' comment data '") + self.assertEquals(op[2], "Notation declared: (u'notation', None, u'notation.jpeg', None)") + self.assertEquals(op[3], "Unparsed entity decl: (u'unparsed_entity', None, u'entity.file', None, u'notation')") + self.assertEquals(op[4], "Start element: u'root' {u'attr1': u'value1', u'attr2': u'value2\\u1f40'}") + self.assertEquals(op[5], "NS decl: u'myns' u'http://www.python.org/namespace'") + self.assertEquals(op[6], "Start element: u'http://www.python.org/namespace!subelement' {}") + self.assertEquals(op[7], "Character data: u'Contents of subelements'") + self.assertEquals(op[8], "End element: u'http://www.python.org/namespace!subelement'") + self.assertEquals(op[9], "End of NS decl: u'myns'") + self.assertEquals(op[10], "Start element: u'sub2' {}") + self.assertEquals(op[11], 'Start of CDATA section') + self.assertEquals(op[12], "Character data: u'contents of CDATA section'") + self.assertEquals(op[13], 'End of CDATA section') + self.assertEquals(op[14], "End element: u'sub2'") + self.assertEquals(op[15], "External entity ref: (None, u'entity.file', None)") + self.assertEquals(op[16], "End element: u'root'") + + def test_parse_file(self): + # Try parsing a file + out = self.Outputter() + parser = expat.ParserCreate(namespace_separator='!') + parser.returns_unicode = 1 + for name in self.handler_names: + setattr(parser, name, getattr(out, name)) + file = StringIO.StringIO(data) + + parser.ParseFile(file) + + op = out.out + self.assertEquals(op[0], 'PI: u\'xml-stylesheet\' u\'href="stylesheet.css"\'') + self.assertEquals(op[1], "Comment: u' comment data '") + self.assertEquals(op[2], "Notation declared: (u'notation', None, u'notation.jpeg', None)") + self.assertEquals(op[3], "Unparsed entity decl: (u'unparsed_entity', None, u'entity.file', None, u'notation')") + self.assertEquals(op[4], "Start element: u'root' {u'attr1': u'value1', u'attr2': u'value2\\u1f40'}") + self.assertEquals(op[5], "NS decl: u'myns' u'http://www.python.org/namespace'") + self.assertEquals(op[6], "Start element: u'http://www.python.org/namespace!subelement' {}") + self.assertEquals(op[7], "Character data: u'Contents of subelements'") + self.assertEquals(op[8], "End element: u'http://www.python.org/namespace!subelement'") + self.assertEquals(op[9], "End of NS decl: u'myns'") + self.assertEquals(op[10], "Start element: u'sub2' {}") + self.assertEquals(op[11], 'Start of CDATA section') + self.assertEquals(op[12], "Character data: u'contents of CDATA section'") + self.assertEquals(op[13], 'End of CDATA section') + self.assertEquals(op[14], "End element: u'sub2'") + self.assertEquals(op[15], "External entity ref: (None, u'entity.file', None)") + self.assertEquals(op[16], "End element: u'root'") + + +class NamespaceSeparatorTest(unittest.TestCase): + def test_legal(self): + # Tests that make sure we get errors when the namespace_separator value + # is illegal, and that we don't for good values: + expat.ParserCreate() + expat.ParserCreate(namespace_separator=None) + expat.ParserCreate(namespace_separator=' ') + + def test_illegal(self): + try: + expat.ParserCreate(namespace_separator=42) + self.fail() + except TypeError as e: + self.assertEquals(str(e), + 'ParserCreate() argument 2 must be string or None, not int') + + try: + expat.ParserCreate(namespace_separator='too long') + self.fail() + except ValueError as e: + self.assertEquals(str(e), + 'namespace_separator must be at most one character, omitted, or None') + + def test_zero_length(self): + # ParserCreate() needs to accept a namespace_separator of zero length + # to satisfy the requirements of RDF applications that are required + # to simply glue together the namespace URI and the localname. Though + # considered a wart of the RDF specifications, it needs to be supported. + # + # See XML-SIG mailing list thread starting with + # http://mail.python.org/pipermail/xml-sig/2001-April/005202.html + # + expat.ParserCreate(namespace_separator='') # too short + + +class InterningTest(unittest.TestCase): + def test(self): + # Test the interning machinery. + p = expat.ParserCreate() + L = [] + def collector(name, *args): + L.append(name) + p.StartElementHandler = collector + p.EndElementHandler = collector + p.Parse("<e> <e/> <e></e> </e>", 1) + tag = L[0] + self.assertEquals(len(L), 6) + for entry in L: + # L should have the same string repeated over and over. + self.assertTrue(tag is entry) + + +class BufferTextTest(unittest.TestCase): + def setUp(self): self.stuff = [] + self.parser = expat.ParserCreate() + self.parser.buffer_text = 1 + self.parser.CharacterDataHandler = self.CharacterDataHandler def check(self, expected, label): - require(self.stuff == expected, + self.assertEquals(self.stuff, expected, "%s\nstuff = %r\nexpected = %r" % (label, self.stuff, map(unicode, expected))) @@ -238,9 +290,9 @@ class TextCollector: self.stuff.append("<%s>" % name) bt = attrs.get("buffer-text") if bt == "yes": - parser.buffer_text = 1 + self.parser.buffer_text = 1 elif bt == "no": - parser.buffer_text = 0 + self.parser.buffer_text = 0 def EndElementHandler(self, name): self.stuff.append("</%s>" % name) @@ -248,95 +300,91 @@ class TextCollector: def CommentHandler(self, data): self.stuff.append("<!--%s-->" % data) -def require(cond, label): - # similar to confirm(), but no extraneous output - if not cond: - raise TestFailed(label) - -def setup(handlers=[]): - parser = expat.ParserCreate() - require(not parser.buffer_text, - "buffer_text not disabled by default") - parser.buffer_text = 1 - handler = TextCollector(parser) - parser.CharacterDataHandler = handler.CharacterDataHandler - for name in handlers: - setattr(parser, name, getattr(handler, name)) - return parser, handler - -parser, handler = setup() -require(parser.buffer_text, - "text buffering either not acknowledged or not enabled") -parser.Parse("<a>1<b/>2<c/>3</a>", 1) -handler.check(["123"], - "buffered text not properly collapsed") - -# XXX This test exposes more detail of Expat's text chunking than we -# XXX like, but it tests what we need to concisely. -parser, handler = setup(["StartElementHandler"]) -parser.Parse("<a>1<b buffer-text='no'/>2\n3<c buffer-text='yes'/>4\n5</a>", 1) -handler.check(["<a>", "1", "<b>", "2", "\n", "3", "<c>", "4\n5"], - "buffering control not reacting as expected") - -parser, handler = setup() -parser.Parse("<a>1<b/><2><c/> \n 3</a>", 1) -handler.check(["1<2> \n 3"], - "buffered text not properly collapsed") - -parser, handler = setup(["StartElementHandler"]) -parser.Parse("<a>1<b/>2<c/>3</a>", 1) -handler.check(["<a>", "1", "<b>", "2", "<c>", "3"], - "buffered text not properly split") - -parser, handler = setup(["StartElementHandler", "EndElementHandler"]) -parser.CharacterDataHandler = None -parser.Parse("<a>1<b/>2<c/>3</a>", 1) -handler.check(["<a>", "<b>", "</b>", "<c>", "</c>", "</a>"], - "huh?") - -parser, handler = setup(["StartElementHandler", "EndElementHandler"]) -parser.Parse("<a>1<b></b>2<c/>3</a>", 1) -handler.check(["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "3", "</a>"], - "huh?") - -parser, handler = setup(["CommentHandler", "EndElementHandler", - "StartElementHandler"]) -parser.Parse("<a>1<b/>2<c></c>345</a> ", 1) -handler.check(["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "345", "</a>"], - "buffered text not properly split") - -parser, handler = setup(["CommentHandler", "EndElementHandler", - "StartElementHandler"]) -parser.Parse("<a>1<b/>2<c></c>3<!--abc-->4<!--def-->5</a> ", 1) -handler.check(["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "3", - "<!--abc-->", "4", "<!--def-->", "5", "</a>"], - "buffered text not properly split") + def setHandlers(self, handlers=[]): + for name in handlers: + setattr(self.parser, name, getattr(self, name)) + + def test_default_to_disabled(self): + parser = expat.ParserCreate() + self.assertFalse(parser.buffer_text) + + def test_buffering_enabled(self): + # Make sure buffering is turned on + self.assertTrue(self.parser.buffer_text) + self.parser.Parse("<a>1<b/>2<c/>3</a>", 1) + self.assertEquals(self.stuff, ['123'], + "buffered text not properly collapsed") + + def test1(self): + # XXX This test exposes more detail of Expat's text chunking than we + # XXX like, but it tests what we need to concisely. + self.setHandlers(["StartElementHandler"]) + self.parser.Parse("<a>1<b buffer-text='no'/>2\n3<c buffer-text='yes'/>4\n5</a>", 1) + self.assertEquals(self.stuff, + ["<a>", "1", "<b>", "2", "\n", "3", "<c>", "4\n5"], + "buffering control not reacting as expected") + + def test2(self): + self.parser.Parse("<a>1<b/><2><c/> \n 3</a>", 1) + self.assertEquals(self.stuff, ["1<2> \n 3"], + "buffered text not properly collapsed") + + def test3(self): + self.setHandlers(["StartElementHandler"]) + self.parser.Parse("<a>1<b/>2<c/>3</a>", 1) + self.assertEquals(self.stuff, ["<a>", "1", "<b>", "2", "<c>", "3"], + "buffered text not properly split") + + def test4(self): + self.setHandlers(["StartElementHandler", "EndElementHandler"]) + self.parser.CharacterDataHandler = None + self.parser.Parse("<a>1<b/>2<c/>3</a>", 1) + self.assertEquals(self.stuff, + ["<a>", "<b>", "</b>", "<c>", "</c>", "</a>"]) + + def test5(self): + self.setHandlers(["StartElementHandler", "EndElementHandler"]) + self.parser.Parse("<a>1<b></b>2<c/>3</a>", 1) + self.assertEquals(self.stuff, + ["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "3", "</a>"]) + + def test6(self): + self.setHandlers(["CommentHandler", "EndElementHandler", + "StartElementHandler"]) + self.parser.Parse("<a>1<b/>2<c></c>345</a> ", 1) + self.assertEquals(self.stuff, + ["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "345", "</a>"], + "buffered text not properly split") + + def test7(self): + self.setHandlers(["CommentHandler", "EndElementHandler", + "StartElementHandler"]) + self.parser.Parse("<a>1<b/>2<c></c>3<!--abc-->4<!--def-->5</a> ", 1) + self.assertEquals(self.stuff, + ["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "3", + "<!--abc-->", "4", "<!--def-->", "5", "</a>"], + "buffered text not properly split") + # Test handling of exception from callback: -def StartElementHandler(name, attrs): - raise RuntimeError(name) +class HandlerExceptionTest(unittest.TestCase): + def StartElementHandler(self, name, attrs): + raise RuntimeError(name) -parser = expat.ParserCreate() -parser.StartElementHandler = StartElementHandler + def test(self): + parser = expat.ParserCreate() + parser.StartElementHandler = self.StartElementHandler + try: + parser.Parse("<a><b><c/></b></a>", 1) + self.fail() + except RuntimeError as e: + self.assertEquals(e.args[0], 'a', + "Expected RuntimeError for element 'a', but" + \ + " found %r" % e.args[0]) -try: - parser.Parse("<a><b><c/></b></a>", 1) -except RuntimeError as e: - if e.args[0] != "a": - print("Expected RuntimeError for element 'a'; found %r" % e.args[0]) -else: - print("Expected RuntimeError for 'a'") # Test Current* members: -class PositionTest: - - def __init__(self, expected_list, parser): - self.parser = parser - self.parser.StartElementHandler = self.StartElementHandler - self.parser.EndElementHandler = self.EndElementHandler - self.expected_list = expected_list - self.upto = 0 - +class PositionTest(unittest.TestCase): def StartElementHandler(self, name, attrs): self.check_pos('s') @@ -348,41 +396,54 @@ class PositionTest: self.parser.CurrentByteIndex, self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber) - require(self.upto < len(self.expected_list), - 'too many parser events') + self.assertTrue(self.upto < len(self.expected_list), + 'too many parser events') expected = self.expected_list[self.upto] - require(pos == expected, - 'expected position %s, got %s' % (expected, pos)) + self.assertEquals(pos, expected, + 'Expected position %s, got position %s' %(pos, expected)) self.upto += 1 + def test(self): + self.parser = expat.ParserCreate() + self.parser.StartElementHandler = self.StartElementHandler + self.parser.EndElementHandler = self.EndElementHandler + self.upto = 0 + self.expected_list = [('s', 0, 1, 0), ('s', 5, 2, 1), ('s', 11, 3, 2), + ('e', 15, 3, 6), ('e', 17, 4, 1), ('e', 22, 5, 0)] + + xml = '<a>\n <b>\n <c/>\n </b>\n</a>' + self.parser.Parse(xml, 1) + + +class sf1296433Test(unittest.TestCase): + def test_parse_only_xml_data(self): + # http://python.org/sf/1296433 + # + xml = "<?xml version='1.0' encoding='iso8859'?><s>%s</s>" % ('a' * 1025) + # this one doesn't crash + #xml = "<?xml version='1.0'?><s>%s</s>" % ('a' * 10000) -parser = expat.ParserCreate() -handler = PositionTest([('s', 0, 1, 0), ('s', 5, 2, 1), ('s', 11, 3, 2), - ('e', 15, 3, 6), ('e', 17, 4, 1), ('e', 22, 5, 0)], - parser) -parser.Parse('''<a> - <b> - <c/> - </b> -</a>''', 1) + class SpecificException(Exception): + pass + def handler(text): + raise SpecificException -def test_parse_only_xml_data(): - # http://python.org/sf/1296433 - # - xml = "<?xml version='1.0' encoding='iso8859'?><s>%s</s>" % ('a' * 1025) - # this one doesn't crash - #xml = "<?xml version='1.0'?><s>%s</s>" % ('a' * 10000) + parser = expat.ParserCreate() + parser.CharacterDataHandler = handler - def handler(text): - raise Exception + self.assertRaises(Exception, parser.Parse, xml) - parser = expat.ParserCreate() - parser.CharacterDataHandler = handler - try: - parser.Parse(xml) - except: - pass +def test_main(): + run_unittest(SetAttributeTest, + ParseTest, + NamespaceSeparatorTest, + InterningTest, + BufferTextTest, + HandlerExceptionTest, + PositionTest, + sf1296433Test) -test_parse_only_xml_data() +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index bca2ecf..13fa413 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,7 +1,7 @@ import sys sys.path = ['.'] + sys.path -from test.test_support import verbose, run_unittest +from test.test_support import verbose, run_unittest, guard_warnings_filter import re from re import Scanner import sys, os, traceback @@ -418,6 +418,12 @@ class ReTests(unittest.TestCase): pass # cPickle not found -- skip it else: self.pickle_test(cPickle) + # old pickles expect the _compile() reconstructor in sre module + import warnings + with guard_warnings_filter(): + warnings.filterwarnings("ignore", "The sre module is deprecated", + DeprecationWarning) + from sre import _compile def pickle_test(self, pickle): oldpat = re.compile('a(?:b|(c|e){1,2}?|d)+?(.)') @@ -599,6 +605,13 @@ class ReTests(unittest.TestCase): self.assertEqual(next(iter).span(), (4, 4)) self.assertRaises(StopIteration, next, iter) + def test_empty_array(self): + # SF buf 1647541 + import array + for typecode in 'cbBuhHiIlLfd': + a = array.array(typecode) + self.assertEqual(re.compile("bla").match(a), None) + self.assertEqual(re.compile("").match(a).groups(), ()) def run_re_tests(): from test.re_tests import benchmarks, tests, SUCCEED, FAIL, SYNTAX_ERROR diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 6a23b22..666d00a 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -135,8 +135,8 @@ bad = [] # Bug report says "/" should be denied, but that is not in the RFC RobotTest(7, doc, good, bad) def test_main(): - test_support.run_suite(tests) + test_support.run_unittest(tests) if __name__=='__main__': test_support.Verbose = 1 - test_support.run_suite(tests) + test_main() diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 5b071dd..0d76cbb 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -13,26 +13,66 @@ from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \ from xml.sax.expatreader import create_parser from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl from cStringIO import StringIO -from test.test_support import verify, verbose, TestFailed, findfile +from test.test_support import findfile, run_unittest +import unittest import os -# ===== Utilities - -tests = 0 -failures = [] - -def confirm(outcome, name): - global tests - - tests = tests + 1 - if outcome: - if verbose: - print("Passed", name) - else: - failures.append(name) +ns_uri = "http://www.python.org/xml-ns/saxtest/" -def test_make_parser2(): - try: +class XmlTestBase(unittest.TestCase): + def verify_empty_attrs(self, attrs): + self.assertRaises(KeyError, attrs.getValue, "attr") + self.assertRaises(KeyError, attrs.getValueByQName, "attr") + self.assertRaises(KeyError, attrs.getNameByQName, "attr") + self.assertRaises(KeyError, attrs.getQNameByName, "attr") + self.assertRaises(KeyError, attrs.__getitem__, "attr") + self.assertEquals(attrs.getLength(), 0) + self.assertEquals(attrs.getNames(), []) + self.assertEquals(attrs.getQNames(), []) + self.assertEquals(len(attrs), 0) + self.assertFalse("attr" in attrs) + self.assertEquals(list(attrs.keys()), []) + self.assertEquals(attrs.get("attrs"), None) + self.assertEquals(attrs.get("attrs", 25), 25) + self.assertEquals(list(attrs.items()), []) + self.assertEquals(list(attrs.values()), []) + + def verify_empty_nsattrs(self, attrs): + self.assertRaises(KeyError, attrs.getValue, (ns_uri, "attr")) + self.assertRaises(KeyError, attrs.getValueByQName, "ns:attr") + self.assertRaises(KeyError, attrs.getNameByQName, "ns:attr") + self.assertRaises(KeyError, attrs.getQNameByName, (ns_uri, "attr")) + self.assertRaises(KeyError, attrs.__getitem__, (ns_uri, "attr")) + self.assertEquals(attrs.getLength(), 0) + self.assertEquals(attrs.getNames(), []) + self.assertEquals(attrs.getQNames(), []) + self.assertEquals(len(attrs), 0) + self.assertFalse((ns_uri, "attr") in attrs) + self.assertEquals(list(attrs.keys()), []) + self.assertEquals(attrs.get((ns_uri, "attr")), None) + self.assertEquals(attrs.get((ns_uri, "attr"), 25), 25) + self.assertEquals(list(attrs.items()), []) + self.assertEquals(list(attrs.values()), []) + + def verify_attrs_wattr(self, attrs): + self.assertEquals(attrs.getLength(), 1) + self.assertEquals(attrs.getNames(), ["attr"]) + self.assertEquals(attrs.getQNames(), ["attr"]) + self.assertEquals(len(attrs), 1) + self.assertTrue("attr" in attrs) + self.assertEquals(list(attrs.keys()), ["attr"]) + self.assertEquals(attrs.get("attr"), "val") + self.assertEquals(attrs.get("attr", 25), "val") + self.assertEquals(list(attrs.items()), [("attr", "val")]) + self.assertEquals(list(attrs.values()), ["val"]) + self.assertEquals(attrs.getValue("attr"), "val") + self.assertEquals(attrs.getValueByQName("attr"), "val") + self.assertEquals(attrs.getNameByQName("attr"), "attr") + self.assertEquals(attrs["attr"], "val") + self.assertEquals(attrs.getQNameByName("attr"), "attr") + +class MakeParserTest(unittest.TestCase): + def test_make_parser2(self): # Creating parsers several times in a row should succeed. # Testing this because there have been failures of this kind # before. @@ -48,10 +88,6 @@ def test_make_parser2(): p = make_parser() from xml.sax import make_parser p = make_parser() - except: - return 0 - else: - return p # =========================================================================== @@ -60,215 +96,214 @@ def test_make_parser2(): # # =========================================================================== -# ===== escape - -def test_escape_basic(): - return escape("Donald Duck & Co") == "Donald Duck & Co" +class SaxutilsTest(unittest.TestCase): + # ===== escape + def test_escape_basic(self): + self.assertEquals(escape("Donald Duck & Co"), "Donald Duck & Co") -def test_escape_all(): - return escape("<Donald Duck & Co>") == "<Donald Duck & Co>" + def test_escape_all(self): + self.assertEquals(escape("<Donald Duck & Co>"), + "<Donald Duck & Co>") -def test_escape_extra(): - return escape("Hei på deg", {"å" : "å"}) == "Hei på deg" + def test_escape_extra(self): + self.assertEquals(escape("Hei på deg", {"å" : "å"}), + "Hei på deg") -# ===== unescape + # ===== unescape + def test_unescape_basic(self): + self.assertEquals(unescape("Donald Duck & Co"), "Donald Duck & Co") -def test_unescape_basic(): - return unescape("Donald Duck & Co") == "Donald Duck & Co" + def test_unescape_all(self): + self.assertEquals(unescape("<Donald Duck & Co>"), + "<Donald Duck & Co>") -def test_unescape_all(): - return unescape("<Donald Duck & Co>") == "<Donald Duck & Co>" + def test_unescape_extra(self): + self.assertEquals(unescape("Hei på deg", {"å" : "å"}), + "Hei på deg") -def test_unescape_extra(): - return unescape("Hei på deg", {"å" : "å"}) == "Hei på deg" + def test_unescape_amp_extra(self): + self.assertEquals(unescape("&foo;", {"&foo;": "splat"}), "&foo;") -def test_unescape_amp_extra(): - return unescape("&foo;", {"&foo;": "splat"}) == "&foo;" + # ===== quoteattr + def test_quoteattr_basic(self): + self.assertEquals(quoteattr("Donald Duck & Co"), + '"Donald Duck & Co"') -# ===== quoteattr + def test_single_quoteattr(self): + self.assertEquals(quoteattr('Includes "double" quotes'), + '\'Includes "double" quotes\'') -def test_quoteattr_basic(): - return quoteattr("Donald Duck & Co") == '"Donald Duck & Co"' + def test_double_quoteattr(self): + self.assertEquals(quoteattr("Includes 'single' quotes"), + "\"Includes 'single' quotes\"") -def test_single_quoteattr(): - return (quoteattr('Includes "double" quotes') - == '\'Includes "double" quotes\'') + def test_single_double_quoteattr(self): + self.assertEquals(quoteattr("Includes 'single' and \"double\" quotes"), + "\"Includes 'single' and "double" quotes\"") -def test_double_quoteattr(): - return (quoteattr("Includes 'single' quotes") - == "\"Includes 'single' quotes\"") - -def test_single_double_quoteattr(): - return (quoteattr("Includes 'single' and \"double\" quotes") - == "\"Includes 'single' and "double" quotes\"") - -# ===== make_parser - -def test_make_parser(): - try: + # ===== make_parser + def test_make_parser(self): # Creating a parser should succeed - it should fall back # to the expatreader p = make_parser(['xml.parsers.no_such_parser']) - except: - return 0 - else: - return p # ===== XMLGenerator start = '<?xml version="1.0" encoding="iso-8859-1"?>\n' -def test_xmlgen_basic(): - result = StringIO() - gen = XMLGenerator(result) - gen.startDocument() - gen.startElement("doc", {}) - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + "<doc></doc>" - -def test_xmlgen_content(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startElement("doc", {}) - gen.characters("huhei") - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + "<doc>huhei</doc>" - -def test_xmlgen_pi(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.processingInstruction("test", "data") - gen.startElement("doc", {}) - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + "<?test data?><doc></doc>" - -def test_xmlgen_content_escape(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startElement("doc", {}) - gen.characters("<huhei&") - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + "<doc><huhei&</doc>" - -def test_xmlgen_attr_escape(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startElement("doc", {"a": '"'}) - gen.startElement("e", {"a": "'"}) - gen.endElement("e") - gen.startElement("e", {"a": "'\""}) - gen.endElement("e") - gen.startElement("e", {"a": "\n\r\t"}) - gen.endElement("e") - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + ("<doc a='\"'><e a=\"'\"></e>" - "<e a=\"'"\"></e>" - "<e a=\" 	\"></e></doc>") - -def test_xmlgen_ignorable(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startElement("doc", {}) - gen.ignorableWhitespace(" ") - gen.endElement("doc") - gen.endDocument() - - return result.getvalue() == start + "<doc> </doc>" +class XmlgenTest(unittest.TestCase): + def test_xmlgen_basic(self): + result = StringIO() + gen = XMLGenerator(result) + gen.startDocument() + gen.startElement("doc", {}) + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + "<doc></doc>") + + def test_xmlgen_content(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.characters("huhei") + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + "<doc>huhei</doc>") + + def test_xmlgen_pi(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.processingInstruction("test", "data") + gen.startElement("doc", {}) + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + "<?test data?><doc></doc>") + + def test_xmlgen_content_escape(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.characters("<huhei&") + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), + start + "<doc><huhei&</doc>") + + def test_xmlgen_attr_escape(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {"a": '"'}) + gen.startElement("e", {"a": "'"}) + gen.endElement("e") + gen.startElement("e", {"a": "'\""}) + gen.endElement("e") + gen.startElement("e", {"a": "\n\r\t"}) + gen.endElement("e") + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + + ("<doc a='\"'><e a=\"'\"></e>" + "<e a=\"'"\"></e>" + "<e a=\" 	\"></e></doc>")) + + def test_xmlgen_ignorable(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startElement("doc", {}) + gen.ignorableWhitespace(" ") + gen.endElement("doc") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + "<doc> </doc>") + + def test_xmlgen_ns(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping("ns1", ns_uri) + gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) + # add an unqualified name + gen.startElementNS((None, "udoc"), None, {}) + gen.endElementNS((None, "udoc"), None) + gen.endElementNS((ns_uri, "doc"), "ns1:doc") + gen.endPrefixMapping("ns1") + gen.endDocument() + + self.assertEquals(result.getvalue(), start + \ + ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % + ns_uri)) -ns_uri = "http://www.python.org/xml-ns/saxtest/" + def test_1463026_1(self): + result = StringIO() + gen = XMLGenerator(result) -def test_xmlgen_ns(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startPrefixMapping("ns1", ns_uri) - gen.startElementNS((ns_uri, "doc"), "ns1:doc", {}) - # add an unqualified name - gen.startElementNS((None, "udoc"), None, {}) - gen.endElementNS((None, "udoc"), None) - gen.endElementNS((ns_uri, "doc"), "ns1:doc") - gen.endPrefixMapping("ns1") - gen.endDocument() - - return result.getvalue() == start + \ - ('<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % - ns_uri) - -def test_1463026_1(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) - gen.endElementNS((None, 'a'), 'a') - gen.endDocument() - - return result.getvalue() == start+'<a b="c"></a>' - -def test_1463026_2(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startPrefixMapping(None, 'qux') - gen.startElementNS(('qux', 'a'), 'a', {}) - gen.endElementNS(('qux', 'a'), 'a') - gen.endPrefixMapping(None) - gen.endDocument() - - return result.getvalue() == start+'<a xmlns="qux"></a>' - -def test_1463026_3(): - result = StringIO() - gen = XMLGenerator(result) - - gen.startDocument() - gen.startPrefixMapping('my', 'qux') - gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) - gen.endElementNS(('qux', 'a'), 'a') - gen.endPrefixMapping('my') - gen.endDocument() - - return result.getvalue() == start+'<my:a xmlns:my="qux" b="c"></my:a>' - -# ===== Xmlfilterbase - -def test_filter_basic(): - result = StringIO() - gen = XMLGenerator(result) - filter = XMLFilterBase() - filter.setContentHandler(gen) - - filter.startDocument() - filter.startElement("doc", {}) - filter.characters("content") - filter.ignorableWhitespace(" ") - filter.endElement("doc") - filter.endDocument() - - return result.getvalue() == start + "<doc>content </doc>" + gen.startDocument() + gen.startElementNS((None, 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS((None, 'a'), 'a') + gen.endDocument() + + self.assertEquals(result.getvalue(), start+'<a b="c"></a>') + + def test_1463026_2(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping(None, 'qux') + gen.startElementNS(('qux', 'a'), 'a', {}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping(None) + gen.endDocument() + + self.assertEquals(result.getvalue(), start+'<a xmlns="qux"></a>') + + def test_1463026_3(self): + result = StringIO() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping('my', 'qux') + gen.startElementNS(('qux', 'a'), 'a', {(None, 'b'):'c'}) + gen.endElementNS(('qux', 'a'), 'a') + gen.endPrefixMapping('my') + gen.endDocument() + + self.assertEquals(result.getvalue(), + start+'<my:a xmlns:my="qux" b="c"></my:a>') + + +class XMLFilterBaseTest(unittest.TestCase): + def test_filter_basic(self): + result = StringIO() + gen = XMLGenerator(result) + filter = XMLFilterBase() + filter.setContentHandler(gen) + + filter.startDocument() + filter.startElement("doc", {}) + filter.characters("content") + filter.ignorableWhitespace(" ") + filter.endElement("doc") + filter.endDocument() + + self.assertEquals(result.getvalue(), start + "<doc>content </doc>") # =========================================================================== # @@ -276,229 +311,233 @@ def test_filter_basic(): # # =========================================================================== -# ===== XMLReader support +xml_test_out = open(findfile("test"+os.extsep+"xml"+os.extsep+"out")).read() -def test_expat_file(): - parser = create_parser() - result = StringIO() - xmlgen = XMLGenerator(result) +class ExpatReaderTest(XmlTestBase): - parser.setContentHandler(xmlgen) - parser.parse(open(findfile("test"+os.extsep+"xml"))) + # ===== XMLReader support - return result.getvalue() == xml_test_out + def test_expat_file(self): + parser = create_parser() + result = StringIO() + xmlgen = XMLGenerator(result) -# ===== DTDHandler support + parser.setContentHandler(xmlgen) + parser.parse(open(findfile("test"+os.extsep+"xml"))) -class TestDTDHandler: + self.assertEquals(result.getvalue(), xml_test_out) - def __init__(self): - self._notations = [] - self._entities = [] + # ===== DTDHandler support - def notationDecl(self, name, publicId, systemId): - self._notations.append((name, publicId, systemId)) + class TestDTDHandler: - def unparsedEntityDecl(self, name, publicId, systemId, ndata): - self._entities.append((name, publicId, systemId, ndata)) + def __init__(self): + self._notations = [] + self._entities = [] -def test_expat_dtdhandler(): - parser = create_parser() - handler = TestDTDHandler() - parser.setDTDHandler(handler) + def notationDecl(self, name, publicId, systemId): + self._notations.append((name, publicId, systemId)) - parser.feed('<!DOCTYPE doc [\n') - parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n') - parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n') - parser.feed(']>\n') - parser.feed('<doc></doc>') - parser.close() + def unparsedEntityDecl(self, name, publicId, systemId, ndata): + self._entities.append((name, publicId, systemId, ndata)) - return handler._notations == [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)] and \ - handler._entities == [("img", None, "expat.gif", "GIF")] + def test_expat_dtdhandler(self): + parser = create_parser() + handler = self.TestDTDHandler() + parser.setDTDHandler(handler) -# ===== EntityResolver support + parser.feed('<!DOCTYPE doc [\n') + parser.feed(' <!ENTITY img SYSTEM "expat.gif" NDATA GIF>\n') + parser.feed(' <!NOTATION GIF PUBLIC "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN">\n') + parser.feed(']>\n') + parser.feed('<doc></doc>') + parser.close() -class TestEntityResolver: + self.assertEquals(handler._notations, + [("GIF", "-//CompuServe//NOTATION Graphics Interchange Format 89a//EN", None)]) + self.assertEquals(handler._entities, [("img", None, "expat.gif", "GIF")]) - def resolveEntity(self, publicId, systemId): - inpsrc = InputSource() - inpsrc.setByteStream(StringIO("<entity/>")) - return inpsrc + # ===== EntityResolver support -def test_expat_entityresolver(): - parser = create_parser() - parser.setEntityResolver(TestEntityResolver()) - result = StringIO() - parser.setContentHandler(XMLGenerator(result)) + class TestEntityResolver: - parser.feed('<!DOCTYPE doc [\n') - parser.feed(' <!ENTITY test SYSTEM "whatever">\n') - parser.feed(']>\n') - parser.feed('<doc>&test;</doc>') - parser.close() + def resolveEntity(self, publicId, systemId): + inpsrc = InputSource() + inpsrc.setByteStream(StringIO("<entity/>")) + return inpsrc - return result.getvalue() == start + "<doc><entity></entity></doc>" + def test_expat_entityresolver(self): + parser = create_parser() + parser.setEntityResolver(self.TestEntityResolver()) + result = StringIO() + parser.setContentHandler(XMLGenerator(result)) -# ===== Attributes support + parser.feed('<!DOCTYPE doc [\n') + parser.feed(' <!ENTITY test SYSTEM "whatever">\n') + parser.feed(']>\n') + parser.feed('<doc>&test;</doc>') + parser.close() -class AttrGatherer(ContentHandler): + self.assertEquals(result.getvalue(), start + + "<doc><entity></entity></doc>") - def startElement(self, name, attrs): - self._attrs = attrs + # ===== Attributes support - def startElementNS(self, name, qname, attrs): - self._attrs = attrs + class AttrGatherer(ContentHandler): -def test_expat_attrs_empty(): - parser = create_parser() - gather = AttrGatherer() - parser.setContentHandler(gather) + def startElement(self, name, attrs): + self._attrs = attrs - parser.feed("<doc/>") - parser.close() + def startElementNS(self, name, qname, attrs): + self._attrs = attrs - return verify_empty_attrs(gather._attrs) + def test_expat_attrs_empty(self): + parser = create_parser() + gather = self.AttrGatherer() + parser.setContentHandler(gather) -def test_expat_attrs_wattr(): - parser = create_parser() - gather = AttrGatherer() - parser.setContentHandler(gather) + parser.feed("<doc/>") + parser.close() - parser.feed("<doc attr='val'/>") - parser.close() + self.verify_empty_attrs(gather._attrs) - return verify_attrs_wattr(gather._attrs) + def test_expat_attrs_wattr(self): + parser = create_parser() + gather = self.AttrGatherer() + parser.setContentHandler(gather) -def test_expat_nsattrs_empty(): - parser = create_parser(1) - gather = AttrGatherer() - parser.setContentHandler(gather) + parser.feed("<doc attr='val'/>") + parser.close() - parser.feed("<doc/>") - parser.close() + self.verify_attrs_wattr(gather._attrs) - return verify_empty_nsattrs(gather._attrs) + def test_expat_nsattrs_empty(self): + parser = create_parser(1) + gather = self.AttrGatherer() + parser.setContentHandler(gather) -def test_expat_nsattrs_wattr(): - parser = create_parser(1) - gather = AttrGatherer() - parser.setContentHandler(gather) + parser.feed("<doc/>") + parser.close() - parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri) - parser.close() + self.verify_empty_nsattrs(gather._attrs) - attrs = gather._attrs + def test_expat_nsattrs_wattr(self): + parser = create_parser(1) + gather = self.AttrGatherer() + parser.setContentHandler(gather) - return attrs.getLength() == 1 and \ - attrs.getNames() == [(ns_uri, "attr")] and \ - (attrs.getQNames() == [] or attrs.getQNames() == ["ns:attr"]) and \ - len(attrs) == 1 and \ - (ns_uri, "attr") in attrs and \ - list(attrs.keys()) == [(ns_uri, "attr")] and \ - attrs.get((ns_uri, "attr")) == "val" and \ - attrs.get((ns_uri, "attr"), 25) == "val" and \ - list(attrs.items()) == [((ns_uri, "attr"), "val")] and \ - list(attrs.values()) == ["val"] and \ - attrs.getValue((ns_uri, "attr")) == "val" and \ - attrs[(ns_uri, "attr")] == "val" + parser.feed("<doc xmlns:ns='%s' ns:attr='val'/>" % ns_uri) + parser.close() -# ===== InputSource support + attrs = gather._attrs -xml_test_out = open(findfile("test"+os.extsep+"xml"+os.extsep+"out")).read() + self.assertEquals(attrs.getLength(), 1) + self.assertEquals(attrs.getNames(), [(ns_uri, "attr")]) + self.assertTrue((attrs.getQNames() == [] or + attrs.getQNames() == ["ns:attr"])) + self.assertEquals(len(attrs), 1) + self.assertTrue((ns_uri, "attr") in attrs) + self.assertEquals(attrs.get((ns_uri, "attr")), "val") + self.assertEquals(attrs.get((ns_uri, "attr"), 25), "val") + self.assertEquals(list(attrs.items()), [((ns_uri, "attr"), "val")]) + self.assertEquals(list(attrs.values()), ["val"]) + self.assertEquals(attrs.getValue((ns_uri, "attr")), "val") + self.assertEquals(attrs[(ns_uri, "attr")], "val") -def test_expat_inpsource_filename(): - parser = create_parser() - result = StringIO() - xmlgen = XMLGenerator(result) + # ===== InputSource support - parser.setContentHandler(xmlgen) - parser.parse(findfile("test"+os.extsep+"xml")) + def test_expat_inpsource_filename(self): + parser = create_parser() + result = StringIO() + xmlgen = XMLGenerator(result) - return result.getvalue() == xml_test_out + parser.setContentHandler(xmlgen) + parser.parse(findfile("test"+os.extsep+"xml")) -def test_expat_inpsource_sysid(): - parser = create_parser() - result = StringIO() - xmlgen = XMLGenerator(result) + self.assertEquals(result.getvalue(), xml_test_out) - parser.setContentHandler(xmlgen) - parser.parse(InputSource(findfile("test"+os.extsep+"xml"))) + def test_expat_inpsource_sysid(self): + parser = create_parser() + result = StringIO() + xmlgen = XMLGenerator(result) - return result.getvalue() == xml_test_out + parser.setContentHandler(xmlgen) + parser.parse(InputSource(findfile("test"+os.extsep+"xml"))) -def test_expat_inpsource_stream(): - parser = create_parser() - result = StringIO() - xmlgen = XMLGenerator(result) + self.assertEquals(result.getvalue(), xml_test_out) - parser.setContentHandler(xmlgen) - inpsrc = InputSource() - inpsrc.setByteStream(open(findfile("test"+os.extsep+"xml"))) - parser.parse(inpsrc) + def test_expat_inpsource_stream(self): + parser = create_parser() + result = StringIO() + xmlgen = XMLGenerator(result) - return result.getvalue() == xml_test_out + parser.setContentHandler(xmlgen) + inpsrc = InputSource() + inpsrc.setByteStream(open(findfile("test"+os.extsep+"xml"))) + parser.parse(inpsrc) -# ===== IncrementalParser support + self.assertEquals(result.getvalue(), xml_test_out) -def test_expat_incremental(): - result = StringIO() - xmlgen = XMLGenerator(result) - parser = create_parser() - parser.setContentHandler(xmlgen) + # ===== IncrementalParser support - parser.feed("<doc>") - parser.feed("</doc>") - parser.close() + def test_expat_incremental(self): + result = StringIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) - return result.getvalue() == start + "<doc></doc>" + parser.feed("<doc>") + parser.feed("</doc>") + parser.close() -def test_expat_incremental_reset(): - result = StringIO() - xmlgen = XMLGenerator(result) - parser = create_parser() - parser.setContentHandler(xmlgen) + self.assertEquals(result.getvalue(), start + "<doc></doc>") - parser.feed("<doc>") - parser.feed("text") + def test_expat_incremental_reset(self): + result = StringIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) - result = StringIO() - xmlgen = XMLGenerator(result) - parser.setContentHandler(xmlgen) - parser.reset() + parser.feed("<doc>") + parser.feed("text") - parser.feed("<doc>") - parser.feed("text") - parser.feed("</doc>") - parser.close() + result = StringIO() + xmlgen = XMLGenerator(result) + parser.setContentHandler(xmlgen) + parser.reset() - return result.getvalue() == start + "<doc>text</doc>" + parser.feed("<doc>") + parser.feed("text") + parser.feed("</doc>") + parser.close() -# ===== Locator support + self.assertEquals(result.getvalue(), start + "<doc>text</doc>") -def test_expat_locator_noinfo(): - result = StringIO() - xmlgen = XMLGenerator(result) - parser = create_parser() - parser.setContentHandler(xmlgen) + # ===== Locator support - parser.feed("<doc>") - parser.feed("</doc>") - parser.close() + def test_expat_locator_noinfo(self): + result = StringIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) - return parser.getSystemId() is None and \ - parser.getPublicId() is None and \ - parser.getLineNumber() == 1 + parser.feed("<doc>") + parser.feed("</doc>") + parser.close() -def test_expat_locator_withinfo(): - result = StringIO() - xmlgen = XMLGenerator(result) - parser = create_parser() - parser.setContentHandler(xmlgen) - parser.parse(findfile("test.xml")) + self.assertEquals(parser.getSystemId(), None) + self.assertEquals(parser.getPublicId(), None) + self.assertEquals(parser.getLineNumber(), 1) - return parser.getSystemId() == findfile("test.xml") and \ - parser.getPublicId() is None + def test_expat_locator_withinfo(self): + result = StringIO() + xmlgen = XMLGenerator(result) + parser = create_parser() + parser.setContentHandler(xmlgen) + parser.parse(findfile("test.xml")) + + self.assertEquals(parser.getSystemId(), findfile("test.xml")) + self.assertEquals(parser.getPublicId(), None) # =========================================================================== @@ -507,63 +546,59 @@ def test_expat_locator_withinfo(): # # =========================================================================== -def test_expat_inpsource_location(): - parser = create_parser() - parser.setContentHandler(ContentHandler()) # do nothing - source = InputSource() - source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed - name = "a file name" - source.setSystemId(name) - try: - parser.parse(source) - except SAXException as e: - return e.getSystemId() == name - -def test_expat_incomplete(): - parser = create_parser() - parser.setContentHandler(ContentHandler()) # do nothing - try: - parser.parse(StringIO("<foo>")) - except SAXParseException: - return 1 # ok, error found - else: - return 0 - -def test_sax_parse_exception_str(): - # pass various values from a locator to the SAXParseException to - # make sure that the __str__() doesn't fall apart when None is - # passed instead of an integer line and column number - # - # use "normal" values for the locator: - str(SAXParseException("message", None, - DummyLocator(1, 1))) - # use None for the line number: - str(SAXParseException("message", None, - DummyLocator(None, 1))) - # use None for the column number: - str(SAXParseException("message", None, - DummyLocator(1, None))) - # use None for both: - str(SAXParseException("message", None, - DummyLocator(None, None))) - return 1 - -class DummyLocator: - def __init__(self, lineno, colno): - self._lineno = lineno - self._colno = colno - - def getPublicId(self): - return "pubid" - - def getSystemId(self): - return "sysid" - - def getLineNumber(self): - return self._lineno - - def getColumnNumber(self): - return self._colno +class ErrorReportingTest(unittest.TestCase): + def test_expat_inpsource_location(self): + parser = create_parser() + parser.setContentHandler(ContentHandler()) # do nothing + source = InputSource() + source.setByteStream(StringIO("<foo bar foobar>")) #ill-formed + name = "a file name" + source.setSystemId(name) + try: + parser.parse(source) + self.fail() + except SAXException as e: + self.assertEquals(e.getSystemId(), name) + + def test_expat_incomplete(self): + parser = create_parser() + parser.setContentHandler(ContentHandler()) # do nothing + self.assertRaises(SAXParseException, parser.parse, StringIO("<foo>")) + + def test_sax_parse_exception_str(self): + # pass various values from a locator to the SAXParseException to + # make sure that the __str__() doesn't fall apart when None is + # passed instead of an integer line and column number + # + # use "normal" values for the locator: + str(SAXParseException("message", None, + self.DummyLocator(1, 1))) + # use None for the line number: + str(SAXParseException("message", None, + self.DummyLocator(None, 1))) + # use None for the column number: + str(SAXParseException("message", None, + self.DummyLocator(1, None))) + # use None for both: + str(SAXParseException("message", None, + self.DummyLocator(None, None))) + + class DummyLocator: + def __init__(self, lineno, colno): + self._lineno = lineno + self._colno = colno + + def getPublicId(self): + return "pubid" + + def getSystemId(self): + return "sysid" + + def getLineNumber(self): + return self._lineno + + def getColumnNumber(self): + return self._colno # =========================================================================== # @@ -571,217 +606,91 @@ class DummyLocator: # # =========================================================================== -# ===== AttributesImpl - -def verify_empty_attrs(attrs): - try: - attrs.getValue("attr") - gvk = 0 - except KeyError: - gvk = 1 - - try: - attrs.getValueByQName("attr") - gvqk = 0 - except KeyError: - gvqk = 1 - - try: - attrs.getNameByQName("attr") - gnqk = 0 - except KeyError: - gnqk = 1 - - try: - attrs.getQNameByName("attr") - gqnk = 0 - except KeyError: - gqnk = 1 - - try: - attrs["attr"] - gik = 0 - except KeyError: - gik = 1 - - return attrs.getLength() == 0 and \ - attrs.getNames() == [] and \ - attrs.getQNames() == [] and \ - len(attrs) == 0 and \ - "attr" not in attrs and \ - attrs.keys() == [] and \ - attrs.get("attrs") is None and \ - attrs.get("attrs", 25) == 25 and \ - attrs.items() == [] and \ - attrs.values() == [] and \ - gvk and gvqk and gnqk and gik and gqnk - -def verify_attrs_wattr(attrs): - return attrs.getLength() == 1 and \ - attrs.getNames() == ["attr"] and \ - attrs.getQNames() == ["attr"] and \ - len(attrs) == 1 and \ - "attr" in attrs and \ - attrs.keys() == ["attr"] and \ - attrs.get("attr") == "val" and \ - attrs.get("attr", 25) == "val" and \ - attrs.items() == [("attr", "val")] and \ - attrs.values() == ["val"] and \ - attrs.getValue("attr") == "val" and \ - attrs.getValueByQName("attr") == "val" and \ - attrs.getNameByQName("attr") == "attr" and \ - attrs["attr"] == "val" and \ - attrs.getQNameByName("attr") == "attr" - -def test_attrs_empty(): - return verify_empty_attrs(AttributesImpl({})) - -def test_attrs_wattr(): - return verify_attrs_wattr(AttributesImpl({"attr" : "val"})) - -# ===== AttributesImpl - -def verify_empty_nsattrs(attrs): - try: - attrs.getValue((ns_uri, "attr")) - gvk = 0 - except KeyError: - gvk = 1 - - try: - attrs.getValueByQName("ns:attr") - gvqk = 0 - except KeyError: - gvqk = 1 - - try: - attrs.getNameByQName("ns:attr") - gnqk = 0 - except KeyError: - gnqk = 1 - - try: - attrs.getQNameByName((ns_uri, "attr")) - gqnk = 0 - except KeyError: - gqnk = 1 - - try: - attrs[(ns_uri, "attr")] - gik = 0 - except KeyError: - gik = 1 - - return attrs.getLength() == 0 and \ - attrs.getNames() == [] and \ - attrs.getQNames() == [] and \ - len(attrs) == 0 and \ - (ns_uri, "attr") not in attrs and \ - attrs.keys() == [] and \ - attrs.get((ns_uri, "attr")) is None and \ - attrs.get((ns_uri, "attr"), 25) == 25 and \ - attrs.items() == [] and \ - attrs.values() == [] and \ - gvk and gvqk and gnqk and gik and gqnk - -def test_nsattrs_empty(): - return verify_empty_nsattrs(AttributesNSImpl({}, {})) - -def test_nsattrs_wattr(): - attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, - {(ns_uri, "attr") : "ns:attr"}) - - return attrs.getLength() == 1 and \ - attrs.getNames() == [(ns_uri, "attr")] and \ - attrs.getQNames() == ["ns:attr"] and \ - len(attrs) == 1 and \ - (ns_uri, "attr") in attrs and \ - attrs.keys() == [(ns_uri, "attr")] and \ - attrs.get((ns_uri, "attr")) == "val" and \ - attrs.get((ns_uri, "attr"), 25) == "val" and \ - attrs.items() == [((ns_uri, "attr"), "val")] and \ - attrs.values() == ["val"] and \ - attrs.getValue((ns_uri, "attr")) == "val" and \ - attrs.getValueByQName("ns:attr") == "val" and \ - attrs.getNameByQName("ns:attr") == (ns_uri, "attr") and \ - attrs[(ns_uri, "attr")] == "val" and \ - attrs.getQNameByName((ns_uri, "attr")) == "ns:attr" - - -# During the development of Python 2.5, an attempt to move the "xml" -# package implementation to a new package ("xmlcore") proved painful. -# The goal of this change was to allow applications to be able to -# obtain and rely on behavior in the standard library implementation -# of the XML support without needing to be concerned about the -# availability of the PyXML implementation. -# -# While the existing import hackery in Lib/xml/__init__.py can cause -# PyXML's _xmlpus package to supplant the "xml" package, that only -# works because either implementation uses the "xml" package name for -# imports. -# -# The move resulted in a number of problems related to the fact that -# the import machinery's "package context" is based on the name that's -# being imported rather than the __name__ of the actual package -# containment; it wasn't possible for the "xml" package to be replaced -# by a simple module that indirected imports to the "xmlcore" package. -# -# The following two tests exercised bugs that were introduced in that -# attempt. Keeping these tests around will help detect problems with -# other attempts to provide reliable access to the standard library's -# implementation of the XML support. - -def test_sf_1511497(): - # Bug report: http://www.python.org/sf/1511497 - import sys - old_modules = sys.modules.copy() - for modname in list(sys.modules.keys()): - if modname.startswith("xml."): - del sys.modules[modname] - try: - import xml.sax.expatreader - module = xml.sax.expatreader - return module.__name__ == "xml.sax.expatreader" - finally: - sys.modules.update(old_modules) - -def test_sf_1513611(): - # Bug report: http://www.python.org/sf/1513611 - sio = StringIO("invalid") - parser = make_parser() - from xml.sax import SAXParseException - try: - parser.parse(sio) - except SAXParseException: - return True - else: - return False - -# ===== Main program - -def make_test_output(): - parser = create_parser() - result = StringIO() - xmlgen = XMLGenerator(result) - - parser.setContentHandler(xmlgen) - parser.parse(findfile("test"+os.extsep+"xml")) - - outf = open(findfile("test"+os.extsep+"xml"+os.extsep+"out"), "w") - outf.write(result.getvalue()) - outf.close() - -items = sorted(locals().items()) -for (name, value) in items: - if name[ : 5] == "test_": - confirm(value(), name) -# We delete the items variable so that the assignment to items above -# doesn't pick up the old value of items (which messes with attempts -# to find reference leaks). -del items - -if verbose: - print("%d tests, %d failures" % (tests, len(failures))) -if failures: - raise TestFailed("%d of %d tests failed: %s" - % (len(failures), tests, ", ".join(failures))) +class XmlReaderTest(XmlTestBase): + + # ===== AttributesImpl + def test_attrs_empty(self): + self.verify_empty_attrs(AttributesImpl({})) + + def test_attrs_wattr(self): + self.verify_attrs_wattr(AttributesImpl({"attr" : "val"})) + + def test_nsattrs_empty(self): + self.verify_empty_nsattrs(AttributesNSImpl({}, {})) + + def test_nsattrs_wattr(self): + attrs = AttributesNSImpl({(ns_uri, "attr") : "val"}, + {(ns_uri, "attr") : "ns:attr"}) + + self.assertEquals(attrs.getLength(), 1) + self.assertEquals(attrs.getNames(), [(ns_uri, "attr")]) + self.assertEquals(attrs.getQNames(), ["ns:attr"]) + self.assertEquals(len(attrs), 1) + self.assertTrue((ns_uri, "attr") in attrs) + self.assertEquals(list(attrs.keys()), [(ns_uri, "attr")]) + self.assertEquals(attrs.get((ns_uri, "attr")), "val") + self.assertEquals(attrs.get((ns_uri, "attr"), 25), "val") + self.assertEquals(list(attrs.items()), [((ns_uri, "attr"), "val")]) + self.assertEquals(list(attrs.values()), ["val"]) + self.assertEquals(attrs.getValue((ns_uri, "attr")), "val") + self.assertEquals(attrs.getValueByQName("ns:attr"), "val") + self.assertEquals(attrs.getNameByQName("ns:attr"), (ns_uri, "attr")) + self.assertEquals(attrs[(ns_uri, "attr")], "val") + self.assertEquals(attrs.getQNameByName((ns_uri, "attr")), "ns:attr") + + + # During the development of Python 2.5, an attempt to move the "xml" + # package implementation to a new package ("xmlcore") proved painful. + # The goal of this change was to allow applications to be able to + # obtain and rely on behavior in the standard library implementation + # of the XML support without needing to be concerned about the + # availability of the PyXML implementation. + # + # While the existing import hackery in Lib/xml/__init__.py can cause + # PyXML's _xmlpus package to supplant the "xml" package, that only + # works because either implementation uses the "xml" package name for + # imports. + # + # The move resulted in a number of problems related to the fact that + # the import machinery's "package context" is based on the name that's + # being imported rather than the __name__ of the actual package + # containment; it wasn't possible for the "xml" package to be replaced + # by a simple module that indirected imports to the "xmlcore" package. + # + # The following two tests exercised bugs that were introduced in that + # attempt. Keeping these tests around will help detect problems with + # other attempts to provide reliable access to the standard library's + # implementation of the XML support. + + def test_sf_1511497(self): + # Bug report: http://www.python.org/sf/1511497 + import sys + old_modules = sys.modules.copy() + for modname in list(sys.modules.keys()): + if modname.startswith("xml."): + del sys.modules[modname] + try: + import xml.sax.expatreader + module = xml.sax.expatreader + self.assertEquals(module.__name__, "xml.sax.expatreader") + finally: + sys.modules.update(old_modules) + + def test_sf_1513611(self): + # Bug report: http://www.python.org/sf/1513611 + sio = StringIO("invalid") + parser = make_parser() + from xml.sax import SAXParseException + self.assertRaises(SAXParseException, parser.parse, sio) + + +def unittest_main(): + run_unittest(MakeParserTest, + SaxutilsTest, + XmlgenTest, + ExpatReaderTest, + ErrorReportingTest, + XmlReaderTest) + +if __name__ == "__main__": + unittest_main() diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py index f52ab91..f5c1462 100644 --- a/Lib/test/test_scope.py +++ b/Lib/test/test_scope.py @@ -477,6 +477,39 @@ self.assert_(X.passed) del d['h'] self.assertEqual(d, {'x': 2, 'y': 7, 'w': 6}) + def testLocalsClass(self): + # This test verifies that calling locals() does not pollute + # the local namespace of the class with free variables. Old + # versions of Python had a bug, where a free variable being + # passed through a class namespace would be inserted into + # locals() by locals() or exec or a trace function. + # + # The real bug lies in frame code that copies variables + # between fast locals and the locals dict, e.g. when executing + # a trace function. + + def f(x): + class C: + x = 12 + def m(self): + return x + locals() + return C + + self.assertEqual(f(1).x, 12) + + def f(x): + class C: + y = x + def m(self): + return x + z = list(locals()) + return C + + varnames = f(1).z + self.assert_("x" not in varnames) + self.assert_("y" in varnames) + def testBoundAndFree(self): # var is bound and free in class @@ -607,7 +640,7 @@ self.assert_(X.passed) c = f(0) self.assertEqual(c.get(), 1) self.assert_("x" not in c.__class__.__dict__) - + def testNonLocalGenerator(self): diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 997e17f..45bf32c 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -288,10 +288,17 @@ class TestJointOps(unittest.TestCase): s = self.thetype(d) self.assertEqual(sum(elem.hash_count for elem in d), n) s.difference(d) - self.assertEqual(sum(elem.hash_count for elem in d), n) + self.assertEqual(sum(elem.hash_count for elem in d), n) if hasattr(s, 'symmetric_difference_update'): s.symmetric_difference_update(d) - self.assertEqual(sum(elem.hash_count for elem in d), n) + self.assertEqual(sum(elem.hash_count for elem in d), n) + d2 = dict.fromkeys(set(d)) + self.assertEqual(sum(elem.hash_count for elem in d), n) + d3 = dict.fromkeys(frozenset(d)) + self.assertEqual(sum(elem.hash_count for elem in d), n) + d3 = dict.fromkeys(frozenset(d), 123) + self.assertEqual(sum(elem.hash_count for elem in d), n) + self.assertEqual(d3, dict.fromkeys(d, 123)) class TestSet(TestJointOps): thetype = set diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index f22f619..977b28a 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -2,6 +2,7 @@ import unittest from test import test_support +from cPickle import loads, dumps import sys @@ -92,6 +93,24 @@ class SliceTest(unittest.TestCase): self.assertRaises(OverflowError, slice(None).indices, 1<<100) + def test_setslice_without_getslice(self): + tmp = [] + class X(object): + def __setslice__(self, i, j, k): + tmp.append((i, j, k)) + + x = X() + x[1:2] = 42 + self.assertEquals(tmp, [(1, 2, 42)]) + + def test_pickle(self): + s = slice(10, 20, 3) + for protocol in (0,1,2): + t = loads(dumps(s, protocol)) + self.assertEqual(s, t) + self.assertEqual(s.indices(15), t.indices(15)) + self.assertNotEqual(id(s), id(t)) + def test_main(): test_support.run_unittest(SliceTest) diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py new file mode 100644 index 0000000..3542ddb --- /dev/null +++ b/Lib/test/test_smtplib.py @@ -0,0 +1,71 @@ +import socket +import threading +import smtplib +import time + +from unittest import TestCase +from test import test_support + + +def server(evt): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.settimeout(3) + serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + serv.bind(("", 9091)) + serv.listen(5) + try: + conn, addr = serv.accept() + except socket.timeout: + pass + else: + conn.send("220 Hola mundo\n") + conn.close() + finally: + serv.close() + evt.set() + +class GeneralTests(TestCase): + + def setUp(self): + self.evt = threading.Event() + threading.Thread(target=server, args=(self.evt,)).start() + time.sleep(.1) + + def tearDown(self): + self.evt.wait() + + def testBasic(self): + # connects + smtp = smtplib.SMTP("localhost", 9091) + smtp.sock.close() + + def testTimeoutDefault(self): + # default + smtp = smtplib.SMTP("localhost", 9091) + self.assertTrue(smtp.sock.gettimeout() is None) + smtp.sock.close() + + def testTimeoutValue(self): + # a value + smtp = smtplib.SMTP("localhost", 9091, timeout=30) + self.assertEqual(smtp.sock.gettimeout(), 30) + smtp.sock.close() + + def testTimeoutNone(self): + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + smtp = smtplib.SMTP("localhost", 9091, timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(smtp.sock.gettimeout(), 30) + smtp.sock.close() + + + +def test_main(verbose=None): + test_support.run_unittest(GeneralTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 4f74186..ead3e4f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -75,7 +75,7 @@ class ThreadableTest: Note, the server setup function cannot call any blocking functions that rely on the client thread during setup, - unless serverExplicityReady() is called just before + unless serverExplicitReady() is called just before the blocking call (such as in setting up a client/server connection and performing the accept() in setUp(). """ @@ -597,6 +597,13 @@ class BasicUDPTest(ThreadedUDPSocketTest): def _testRecvFrom(self): self.cli.sendto(MSG, 0, (HOST, PORT)) + def testRecvFromNegative(self): + # Negative lengths passed to recvfrom should give ValueError. + self.assertRaises(ValueError, self.serv.recvfrom, -1) + + def _testRecvFromNegative(self): + self.cli.sendto(MSG, 0, (HOST, PORT)) + class TCPCloserTest(ThreadedTCPSocketTest): def testClose(self): @@ -810,6 +817,98 @@ class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase): bufsize = 2 # Exercise the buffering code +class NetworkConnectionTest(object): + """Prove network connection.""" + def clientSetUp(self): + self.cli = socket.create_connection((HOST, PORT)) + self.serv_conn = self.cli + +class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest): + """Tests that NetworkConnection does not break existing TCP functionality. + """ + +class NetworkConnectionNoServer(unittest.TestCase): + def testWithoutServer(self): + self.failUnlessRaises(socket.error, lambda: socket.create_connection((HOST, PORT))) + +class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): + + def __init__(self, methodName='runTest'): + SocketTCPTest.__init__(self, methodName=methodName) + ThreadableTest.__init__(self) + + def clientSetUp(self): + pass + + def clientTearDown(self): + self.cli.close() + self.cli = None + ThreadableTest.clientTearDown(self) + + def _justAccept(self): + conn, addr = self.serv.accept() + + testFamily = _justAccept + def _testFamily(self): + self.cli = socket.create_connection((HOST, PORT), timeout=30) + self.assertEqual(self.cli.family, 2) + + testTimeoutDefault = _justAccept + def _testTimeoutDefault(self): + self.cli = socket.create_connection((HOST, PORT)) + self.assertTrue(self.cli.gettimeout() is None) + + testTimeoutValueNamed = _justAccept + def _testTimeoutValueNamed(self): + self.cli = socket.create_connection((HOST, PORT), timeout=30) + self.assertEqual(self.cli.gettimeout(), 30) + + testTimeoutValueNonamed = _justAccept + def _testTimeoutValueNonamed(self): + self.cli = socket.create_connection((HOST, PORT), 30) + self.assertEqual(self.cli.gettimeout(), 30) + + testTimeoutNone = _justAccept + def _testTimeoutNone(self): + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + self.cli = socket.create_connection((HOST, PORT), timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(self.cli.gettimeout(), 30) + + +class NetworkConnectionBehaviourTest(SocketTCPTest, ThreadableTest): + + def __init__(self, methodName='runTest'): + SocketTCPTest.__init__(self, methodName=methodName) + ThreadableTest.__init__(self) + + def clientSetUp(self): + pass + + def clientTearDown(self): + self.cli.close() + self.cli = None + ThreadableTest.clientTearDown(self) + + def testInsideTimeout(self): + conn, addr = self.serv.accept() + time.sleep(3) + conn.send("done!") + testOutsideTimeout = testInsideTimeout + + def _testInsideTimeout(self): + self.cli = sock = socket.create_connection((HOST, PORT)) + data = sock.recv(5) + self.assertEqual(data, "done!") + + def _testOutsideTimeout(self): + self.cli = sock = socket.create_connection((HOST, PORT), timeout=1) + self.failUnlessRaises(socket.timeout, lambda: sock.recv(5)) + + class Urllib2FileobjectTest(unittest.TestCase): # urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that @@ -977,7 +1076,7 @@ class BufferIOTest(SocketConnectedTest): def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, - TestExceptions, BufferIOTest] + TestExceptions, BufferIOTest, BasicTCPTest2] if sys.platform != 'mac': tests.extend([ BasicUDPTest, UDPTimeoutTest ]) @@ -988,6 +1087,9 @@ def test_main(): LineBufferedFileObjectClassTestCase, SmallBufferedFileObjectClassTestCase, Urllib2FileobjectTest, + NetworkConnectionNoServer, + NetworkConnectionAttributesTest, + NetworkConnectionBehaviourTest, ]) if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) diff --git a/Lib/test/test_socket_ssl.py b/Lib/test/test_socket_ssl.py index b04effe..42efb6e 100644 --- a/Lib/test/test_socket_ssl.py +++ b/Lib/test/test_socket_ssl.py @@ -1,128 +1,221 @@ # Test just the SSL support in the socket module, in a moderately bogus way. import sys +import unittest from test import test_support import socket import errno +import threading +import subprocess +import time +import os +import urllib -# Optionally test SSL support. This requires the 'network' resource as given -# on the regrtest command line. -skip_expected = not (test_support.is_resource_enabled('network') and - hasattr(socket, "ssl")) +# Optionally test SSL support, if we have it in the tested platform +skip_expected = not hasattr(socket, "ssl") -def test_basic(): - test_support.requires('network') +class ConnectedTests(unittest.TestCase): - import urllib - - if test_support.verbose: - print("test_basic ...") - - socket.RAND_status() - try: - socket.RAND_egd(1) - except TypeError: - pass - else: - print("didn't raise TypeError") - socket.RAND_add("this is a random string", 75.0) - - f = urllib.urlopen('https://sf.net') - buf = f.read() - f.close() - -def test_timeout(): - test_support.requires('network') - - def error_msg(extra_msg): - print("""\ - WARNING: an attempt to connect to %r %s, 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, extra_msg), file=sys.stderr) - - 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: - error_msg('timed out') - return - except socket.error as exc: # In case connection is refused. - if exc.args[0] == errno.ECONNREFUSED: - error_msg('was refused') - return + def testBasic(self): + socket.RAND_status() + try: + socket.RAND_egd(1) + except TypeError: + pass else: - raise + print("didn't raise TypeError") + socket.RAND_add("this is a random string", 75.0) + + with test_support.transient_internet(): + f = urllib.urlopen('https://sf.net') + buf = f.read() + f.close() + + def testTimeout(self): + def error_msg(extra_msg): + print("""\ + WARNING: an attempt to connect to %r %s, 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, extra_msg), file=sys.stderr) + + # A service which issues a welcome banner (without need to write + # anything). + # XXX ("gmail.org", 995) has been unreliable so far, from time to + # XXX time non-responsive for hours on end (& across all buildbot + # XXX slaves, so that's not just a local thing). + ADDR = "gmail.org", 995 - 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: - return + s = socket.socket() + s.settimeout(30.0) + try: + s.connect(ADDR) + except socket.timeout: + error_msg('timed out') + return + except socket.error as exc: # In case connection is refused. + if exc.args[0] == errno.ECONNREFUSED: + error_msg('was refused') + return + else: + raise + + ss = socket.ssl(s) + # Read part of return welcome banner twice. + ss.read(1) + ss.read(1) + s.close() + +class BasicTests(unittest.TestCase): + + def testRudeShutdown(self): + # Some random port to connect to. + PORT = [9934] + + listener_ready = threading.Event() + listener_gone = threading.Event() + + # `listener` runs in a thread. It opens a socket listening on + # PORT, and sits in an accept() until the main thread connects. + # Then it rudely closes the socket, and sets Event `listener_gone` + # to let the main thread know the socket is gone. + def listener(): + s = socket.socket() + PORT[0] = test_support.bind_port(s, '', PORT[0]) + s.listen(5) + listener_ready.set() + s.accept() + s = None # reclaim the socket object, which also closes it + listener_gone.set() + + def connector(): + listener_ready.wait() + s = socket.socket() + s.connect(('localhost', PORT[0])) + listener_gone.wait() + try: + ssl_sock = socket.ssl(s) + except socket.sslerror: + pass + else: + raise test_support.TestFailed( + 'connecting to closed SSL socket should have failed') - # Some random port to connect to. - PORT = [9934] + t = threading.Thread(target=listener) + t.start() + connector() + t.join() - listener_ready = threading.Event() - listener_gone = threading.Event() +class OpenSSLTests(unittest.TestCase): - # `listener` runs in a thread. It opens a socket listening on PORT, and - # sits in an accept() until the main thread connects. Then it rudely - # closes the socket, and sets Event `listener_gone` to let the main thread - # know the socket is gone. - def listener(): + def testBasic(self): s = socket.socket() - PORT[0] = test_support.bind_port(s, '', PORT[0]) - s.listen(5) - listener_ready.set() - s.accept() - s = None # reclaim the socket object, which also closes it - listener_gone.set() - - def connector(): - listener_ready.wait() + s.connect(("localhost", 4433)) + ss = socket.ssl(s) + ss.write("Foo\n") + i = ss.read(4) + self.assertEqual(i, "Foo\n") + s.close() + + def testMethods(self): + # read & write is already tried in the Basic test + # now we'll try to get the server info about certificates + # this came from the certificate I used, one I found in /usr/share/openssl + info = "/C=PT/ST=Queensland/L=Lisboa/O=Neuronio, Lda./OU=Desenvolvimento/CN=brutus.neuronio.pt/emailAddress=sampo@iki.fi" + s = socket.socket() - s.connect(('localhost', PORT[0])) - listener_gone.wait() + s.connect(("localhost", 4433)) + ss = socket.ssl(s) + cert = ss.server() + self.assertEqual(cert, info) + cert = ss.issuer() + self.assertEqual(cert, info) + s.close() + + +class OpenSSLServer(threading.Thread): + def __init__(self): + self.s = None + self.keepServing = True + self._external() + if self.haveServer: + threading.Thread.__init__(self) + + def _external(self): + # let's find the .pem files + curdir = os.path.dirname(__file__) or os.curdir + cert_file = os.path.join(curdir, "ssl_cert.pem") + if not os.access(cert_file, os.F_OK): + raise ValueError("No cert file found! (tried %r)" % cert_file) + key_file = os.path.join(curdir, "ssl_key.pem") + if not os.access(key_file, os.F_OK): + raise ValueError("No key file found! (tried %r)" % key_file) + try: - ssl_sock = socket.ssl(s) - except socket.sslerror: - pass + cmd = "openssl s_server -cert %s -key %s -quiet" % (cert_file, key_file) + self.s = subprocess.Popen(cmd.split(), stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + time.sleep(1) + except: + self.haveServer = False else: - raise test_support.TestFailed( - 'connecting to closed SSL socket should have failed') - - t = threading.Thread(target=listener) - t.start() - connector() - t.join() + # let's try if it is actually up + try: + s = socket.socket() + s.connect(("localhost", 4433)) + s.close() + if self.s.stdout.readline() != "ERROR\n": + raise ValueError + except: + self.haveServer = False + else: + self.haveServer = True + + def run(self): + while self.keepServing: + time.sleep(.5) + l = self.s.stdout.readline() + self.s.stdin.write(l) + + def shutdown(self): + self.keepServing = False + if not self.s: + return + if sys.platform == "win32": + subprocess.TerminateProcess(int(self.s._handle), -1) + else: + os.kill(self.s.pid, 15) def test_main(): if not hasattr(socket, "ssl"): raise test_support.TestSkipped("socket module has no ssl support") - test_rude_shutdown() - test_basic() - test_timeout() + + tests = [BasicTests] + + if test_support.is_resource_enabled('network'): + tests.append(ConnectedTests) + + # in these platforms we can kill the openssl process + if sys.platform in ("sunos5", "darwin", "linux1", + "linux2", "win32", "hp-ux11"): + + server = OpenSSLServer() + if server.haveServer: + tests.append(OpenSSLTests) + server.start() + else: + server = None + + thread_info = test_support.threading_setup() + + try: + test_support.run_unittest(*tests) + finally: + if server is not None and server.haveServer: + server.shutdown() + + test_support.threading_cleanup(*thread_info) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 062be65..da936a4 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -74,6 +74,7 @@ class ServerThread(threading.Thread): self.__addr = addr self.__svrcls = svrcls self.__hdlrcls = hdlrcls + self.ready = threading.Event() def run(self): class svrcls(MyMixinServer, self.__svrcls): pass @@ -81,9 +82,13 @@ class ServerThread(threading.Thread): svr = svrcls(self.__addr, self.__hdlrcls) # pull the address out of the server in case it changed # this can happen if another process is using the port - addr = getattr(svr, 'server_address') + addr = svr.server_address if addr: self.__addr = addr + if self.__addr != svr.socket.getsockname(): + raise RuntimeError('server_address was %s, expected %s' % + (self.__addr, svr.socket.getsockname())) + self.ready.set() if verbose: print("thread: serving three times") svr.serve_a_few() if verbose: print("thread: done") @@ -136,7 +141,9 @@ def testloop(proto, servers, hdlrcls, testfunc): t.start() if verbose: print("server running") for i in range(NREQ): - time.sleep(DELAY) + t.ready.wait(10*DELAY) + if not t.ready.isSet(): + raise RuntimeError("Server not ready within a reasonable time") if verbose: print("test client", i) testfunc(proto, addr) if verbose: print("waiting for server") diff --git a/Lib/test/test_stringprep.py b/Lib/test/test_stringprep.py index 2baf4a5..60425dd 100644 --- a/Lib/test/test_stringprep.py +++ b/Lib/test/test_stringprep.py @@ -1,88 +1,96 @@ # To fully test this module, we would need a copy of the stringprep tables. # Since we don't have them, this test checks only a few codepoints. -from test.test_support import verify, vereq +import unittest +from test import test_support -import stringprep from stringprep import * -verify(in_table_a1(u"\u0221")) -verify(not in_table_a1(u"\u0222")) +class StringprepTests(unittest.TestCase): + def test(self): + self.failUnless(in_table_a1(u"\u0221")) + self.failIf(in_table_a1(u"\u0222")) -verify(in_table_b1(u"\u00ad")) -verify(not in_table_b1(u"\u00ae")) + self.failUnless(in_table_b1(u"\u00ad")) + self.failIf(in_table_b1(u"\u00ae")) -verify(map_table_b2(u"\u0041"), u"\u0061") -verify(map_table_b2(u"\u0061"), u"\u0061") + self.failUnless(map_table_b2(u"\u0041"), u"\u0061") + self.failUnless(map_table_b2(u"\u0061"), u"\u0061") -verify(map_table_b3(u"\u0041"), u"\u0061") -verify(map_table_b3(u"\u0061"), u"\u0061") + self.failUnless(map_table_b3(u"\u0041"), u"\u0061") + self.failUnless(map_table_b3(u"\u0061"), u"\u0061") -verify(in_table_c11(u"\u0020")) -verify(not in_table_c11(u"\u0021")) + self.failUnless(in_table_c11(u"\u0020")) + self.failIf(in_table_c11(u"\u0021")) -verify(in_table_c12(u"\u00a0")) -verify(not in_table_c12(u"\u00a1")) + self.failUnless(in_table_c12(u"\u00a0")) + self.failIf(in_table_c12(u"\u00a1")) -verify(in_table_c12(u"\u00a0")) -verify(not in_table_c12(u"\u00a1")) + self.failUnless(in_table_c12(u"\u00a0")) + self.failIf(in_table_c12(u"\u00a1")) -verify(in_table_c11_c12(u"\u00a0")) -verify(not in_table_c11_c12(u"\u00a1")) + self.failUnless(in_table_c11_c12(u"\u00a0")) + self.failIf(in_table_c11_c12(u"\u00a1")) -verify(in_table_c21(u"\u001f")) -verify(not in_table_c21(u"\u0020")) + self.failUnless(in_table_c21(u"\u001f")) + self.failIf(in_table_c21(u"\u0020")) -verify(in_table_c22(u"\u009f")) -verify(not in_table_c22(u"\u00a0")) + self.failUnless(in_table_c22(u"\u009f")) + self.failIf(in_table_c22(u"\u00a0")) -verify(in_table_c21_c22(u"\u009f")) -verify(not in_table_c21_c22(u"\u00a0")) + self.failUnless(in_table_c21_c22(u"\u009f")) + self.failIf(in_table_c21_c22(u"\u00a0")) -verify(in_table_c3(u"\ue000")) -verify(not in_table_c3(u"\uf900")) + self.failUnless(in_table_c3(u"\ue000")) + self.failIf(in_table_c3(u"\uf900")) -verify(in_table_c4(u"\uffff")) -verify(not in_table_c4(u"\u0000")) + self.failUnless(in_table_c4(u"\uffff")) + self.failIf(in_table_c4(u"\u0000")) -verify(in_table_c5(u"\ud800")) -verify(not in_table_c5(u"\ud7ff")) + self.failUnless(in_table_c5(u"\ud800")) + self.failIf(in_table_c5(u"\ud7ff")) -verify(in_table_c6(u"\ufff9")) -verify(not in_table_c6(u"\ufffe")) + self.failUnless(in_table_c6(u"\ufff9")) + self.failIf(in_table_c6(u"\ufffe")) -verify(in_table_c7(u"\u2ff0")) -verify(not in_table_c7(u"\u2ffc")) + self.failUnless(in_table_c7(u"\u2ff0")) + self.failIf(in_table_c7(u"\u2ffc")) -verify(in_table_c8(u"\u0340")) -verify(not in_table_c8(u"\u0342")) + self.failUnless(in_table_c8(u"\u0340")) + self.failIf(in_table_c8(u"\u0342")) -# C.9 is not in the bmp -# verify(in_table_c9(u"\U000E0001")) -# verify(not in_table_c8(u"\U000E0002")) + # C.9 is not in the bmp + # self.failUnless(in_table_c9(u"\U000E0001")) + # self.failIf(in_table_c8(u"\U000E0002")) -verify(in_table_d1(u"\u05be")) -verify(not in_table_d1(u"\u05bf")) + self.failUnless(in_table_d1(u"\u05be")) + self.failIf(in_table_d1(u"\u05bf")) -verify(in_table_d2(u"\u0041")) -verify(not in_table_d2(u"\u0040")) + self.failUnless(in_table_d2(u"\u0041")) + self.failIf(in_table_d2(u"\u0040")) -# This would generate a hash of all predicates. However, running -# it is quite expensive, and only serves to detect changes in the -# unicode database. Instead, stringprep.py asserts the version of -# the database. + # This would generate a hash of all predicates. However, running + # it is quite expensive, and only serves to detect changes in the + # unicode database. Instead, stringprep.py asserts the version of + # the database. -# import hashlib -# predicates = [k for k in dir(stringprep) if k.startswith("in_table")] -# predicates.sort() -# for p in predicates: -# f = getattr(stringprep, p) -# # Collect all BMP code points -# data = ["0"] * 0x10000 -# for i in range(0x10000): -# if f(unichr(i)): -# data[i] = "1" -# data = "".join(data) -# h = hashlib.sha1() -# h.update(data) -# print p, h.hexdigest() + # import hashlib + # predicates = [k for k in dir(stringprep) if k.startswith("in_table")] + # predicates.sort() + # for p in predicates: + # f = getattr(stringprep, p) + # # Collect all BMP code points + # data = ["0"] * 0x10000 + # for i in range(0x10000): + # if f(unichr(i)): + # data[i] = "1" + # data = "".join(data) + # h = hashlib.sha1() + # h.update(data) + # print p, h.hexdigest() + +def test_main(): + test_support.run_unittest(StringprepTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index c1af281..0e1909e 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -505,6 +505,35 @@ class CacheTests(unittest.TestCase): self.failIfEqual(locale_time_id, id(_strptime._TimeRE_cache.locale_time)) + def test_TimeRE_recreation(self): + # The TimeRE instance should be recreated upon changing the locale. + locale_info = locale.getlocale(locale.LC_TIME) + try: + locale.setlocale(locale.LC_TIME, ('en_US', 'UTF8')) + except locale.Error: + return + try: + _strptime.strptime('10', '%d') + # Get id of current cache object. + first_time_re_id = id(_strptime._TimeRE_cache) + try: + # Change the locale and force a recreation of the cache. + locale.setlocale(locale.LC_TIME, ('de_DE', 'UTF8')) + _strptime.strptime('10', '%d') + # Get the new cache object's id. + second_time_re_id = id(_strptime._TimeRE_cache) + # They should not be equal. + self.failIfEqual(first_time_re_id, second_time_re_id) + # Possible test locale is not supported while initial locale is. + # If this is the case just suppress the exception and fall-through + # to the reseting to the original locale. + except locale.Error: + pass + # Make sure we don't trample on the locale setting once we leave the + # test. + finally: + locale.setlocale(locale.LC_TIME, locale_info) + def test_main(): test_support.run_unittest( diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 0678761..b62d74c 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -614,53 +614,61 @@ def test_pack_into_fn(): assertRaises(struct.error, pack_into, small_buf, 0, test_string) assertRaises(struct.error, pack_into, small_buf, 2, test_string) +def test_unpack_with_buffer(): + # SF bug 1563759: struct.unpack doens't support buffer protocol objects + data1 = array.array('B', '\x12\x34\x56\x78') + data2 = buffer('......\x12\x34\x56\x78......', 6, 4) + for data in [data1, data2]: + value, = struct.unpack('>I', data) + vereq(value, 0x12345678) # Test methods to pack and unpack from buffers rather than strings. test_unpack_from() test_pack_into() test_pack_into_fn() +test_unpack_with_buffer() def test_bool(): for prefix in tuple("<>!=")+('',): false = (), [], [], '', 0 true = [1], 'test', 5, -1, 0xffffffff+1, 0xffffffff/2 - + falseFormat = prefix + 't' * len(false) if verbose: print('trying bool pack/unpack on', false, 'using format', falseFormat) packedFalse = struct.pack(falseFormat, *false) unpackedFalse = struct.unpack(falseFormat, packedFalse) - + trueFormat = prefix + 't' * len(true) if verbose: print('trying bool pack/unpack on', true, 'using format', trueFormat) packedTrue = struct.pack(trueFormat, *true) unpackedTrue = struct.unpack(trueFormat, packedTrue) - + if len(true) != len(unpackedTrue): raise TestFailed('unpacked true array is not of same size as input') if len(false) != len(unpackedFalse): raise TestFailed('unpacked false array is not of same size as input') - + for t in unpackedFalse: if t is not False: raise TestFailed('%r did not unpack as False' % t) for t in unpackedTrue: if t is not True: raise TestFailed('%r did not unpack as false' % t) - + if prefix and verbose: print('trying size of bool with format %r' % (prefix+'t')) packed = struct.pack(prefix+'t', 1) - + if len(packed) != struct.calcsize(prefix+'t'): raise TestFailed('packed length is not equal to calculated size') - + if len(packed) != 1 and prefix: raise TestFailed('encoded bool is not one byte: %r' % packed) elif not prefix and verbose: print('size of bool in native format is %i' % (len(packed))) - + for c in '\x01\x7f\xff\x0f\xf0': if struct.unpack('>t', c)[0] is not True: raise TestFailed('%c did not unpack as True' % c) diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py index 0713b87..599c6fb 100644 --- a/Lib/test/test_structmembers.py +++ b/Lib/test/test_structmembers.py @@ -4,7 +4,7 @@ from _testcapi import test_structmembersType, \ INT_MAX, INT_MIN, UINT_MAX, \ LONG_MAX, LONG_MIN, ULONG_MAX -import warnings, unittest, test.test_warnings +import warnings, unittest from test import test_support ts=test_structmembersType(1,2,3,4,5,6,7,8,9.99999,10.1010101010) @@ -39,34 +39,39 @@ class ReadWriteTests(unittest.TestCase): ts.T_ULONG=ULONG_MAX self.assertEquals(ts.T_ULONG, ULONG_MAX) -class TestWarnings(test.test_warnings.TestModule): - def has_warned(self): - self.assertEqual(test.test_warnings.msg.category, - RuntimeWarning.__name__) +class TestWarnings(unittest.TestCase): + def has_warned(self, w): + self.assert_(w.category is RuntimeWarning) def test_byte_max(self): - ts.T_BYTE=CHAR_MAX+1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_BYTE=CHAR_MAX+1 + self.has_warned(w) def test_byte_min(self): - ts.T_BYTE=CHAR_MIN-1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_BYTE=CHAR_MIN-1 + self.has_warned(w) def test_ubyte_max(self): - ts.T_UBYTE=UCHAR_MAX+1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_UBYTE=UCHAR_MAX+1 + self.has_warned(w) def test_short_max(self): - ts.T_SHORT=SHRT_MAX+1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_SHORT=SHRT_MAX+1 + self.has_warned(w) def test_short_min(self): - ts.T_SHORT=SHRT_MIN-1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_SHORT=SHRT_MIN-1 + self.has_warned(w) def test_ushort_max(self): - ts.T_USHORT=USHRT_MAX+1 - self.has_warned() + with test_support.catch_warning() as w: + ts.T_USHORT=USHRT_MAX+1 + self.has_warned(w) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 6fbb3cc..1ff0e4d 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -1,11 +1,17 @@ """Supporting definitions for the Python regression tests.""" if __name__ != 'test.test_support': - raise ImportError, 'test_support must be imported from the test package' + raise ImportError('test_support must be imported from the test package') -from contextlib import contextmanager +import contextlib +import errno +import socket import sys +import os +import os.path import warnings +import types +import unittest class Error(Exception): """Base class for regression test exceptions.""" @@ -54,7 +60,6 @@ def unload(name): pass def unlink(filename): - import os try: os.unlink(filename) except OSError: @@ -64,7 +69,6 @@ def forget(modname): '''"Forget" a module was ever imported by removing it from sys.modules and deleting any .pyc and .pyo files.''' unload(modname) - import os for dirname in sys.path: unlink(os.path.join(dirname, modname + os.extsep + 'pyc')) # Deleting the .pyo file cannot be within the 'try' for the .pyc since @@ -96,7 +100,6 @@ def bind_port(sock, host='', preferred_port=54321): tests and we don't try multiple ports, the test can fails. This makes the test more robust.""" - import socket, errno # some random ports that hopefully no one is listening on. for port in [preferred_port, 9907, 10243, 32999]: try: @@ -107,7 +110,7 @@ def bind_port(sock, host='', preferred_port=54321): if err != errno.EADDRINUSE: raise print(' WARNING: failed to listen on port %d, trying another' % port, file=sys.__stderr__) - raise TestFailed, 'unable to find port to listen on' + raise TestFailed('unable to find port to listen on') FUZZ = 1e-6 @@ -135,7 +138,6 @@ except NameError: is_jython = sys.platform.startswith('java') -import os # Filename used for testing if os.name == 'java': # Jython disallows @ in module names @@ -197,13 +199,12 @@ except IOError: if fp is not None: fp.close() unlink(TESTFN) -del os, fp +del fp def findfile(file, here=__file__): """Try to find a file on sys.path and the working directory. If it is not found the argument passed to the function is returned (this does not necessarily signal failure; could still be the legitimate path).""" - import os if os.path.isabs(file): return file path = sys.path @@ -235,7 +236,7 @@ def vereq(a, b): """ if not (a == b): - raise TestFailed, "%r == %r" % (a, b) + raise TestFailed("%r == %r" % (a, b)) def sortdict(dict): "Like repr(dict), but in sorted order." @@ -254,7 +255,6 @@ def check_syntax_error(testcase, statement): def open_urlresource(url): import urllib, urlparse - import os.path filename = urlparse.urlparse(url)[2].split('/')[-1] # '/': it's URL! @@ -268,7 +268,7 @@ def open_urlresource(url): fn, _ = urllib.urlretrieve(url, filename) return open(fn) -@contextmanager +@contextlib.contextmanager def guard_warnings_filter(): """Guard the warnings filter from being permanently changed.""" original_filters = warnings.filters[:] @@ -277,14 +277,49 @@ def guard_warnings_filter(): finally: warnings.filters = original_filters +class WarningMessage(object): + "Holds the result of the latest showwarning() call" + def __init__(self): + self.message = None + self.category = None + self.filename = None + self.lineno = None + + def _showwarning(self, message, category, filename, lineno, file=None): + self.message = message + self.category = category + self.filename = filename + self.lineno = lineno + +@contextlib.contextmanager +def catch_warning(): + """ + Guard the warnings filter from being permanently changed and record the + data of the last warning that has been issued. + + Use like this: + + with catch_warning as w: + warnings.warn("foo") + assert str(w.message) == "foo" + """ + warning = WarningMessage() + original_filters = warnings.filters[:] + original_showwarning = warnings.showwarning + warnings.showwarning = warning._showwarning + try: + yield warning + finally: + warnings.showwarning = original_showwarning + warnings.filters = original_filters + class EnvironmentVarGuard(object): """Class to help protect the environment variable properly. Can be used as a context manager.""" def __init__(self): - from os import environ - self._environ = environ + self._environ = os.environ self._unset = set() self._reset = dict() @@ -309,6 +344,40 @@ class EnvironmentVarGuard(object): for unset in self._unset: del self._environ[unset] +class TransientResource(object): + + """Raise ResourceDenied if an exception is raised while the context manager + is in effect that matches the specified exception and attributes.""" + + def __init__(self, exc, **kwargs): + self.exc = exc + self.attrs = kwargs + + def __enter__(self): + return self + + def __exit__(self, type_=None, value=None, traceback=None): + """If type_ is a subclass of self.exc and value has attributes matching + self.attrs, raise ResourceDenied. Otherwise let the exception + propagate (if any).""" + if type_ is not None and issubclass(self.exc, type_): + for attr, attr_value in self.attrs.items(): + if not hasattr(value, attr): + break + if getattr(value, attr) != attr_value: + break + else: + raise ResourceDenied("an optional resource is not available") + + +def transient_internet(): + """Return a context manager that raises ResourceDenied when various issues + with the Internet connection manifest themselves as exceptions.""" + time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) + socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) + ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) + return contextlib.nested(time_out, socket_peer_reset, ioerror_peer_reset) + #======================================================================= # Decorator for running a function in a different locale, correctly resetting @@ -432,10 +501,7 @@ def bigaddrspacetest(f): return wrapper #======================================================================= -# Preliminary PyUNIT integration. - -import unittest - +# unittest integration. class BasicTestRunner: def run(self, test): @@ -444,7 +510,7 @@ class BasicTestRunner: return result -def run_suite(suite, testclass=None): +def _run_suite(suite): """Run tests from a unittest.TestSuite-derived class.""" if verbose: runner = unittest.TextTestRunner(sys.stdout, verbosity=2) @@ -458,28 +524,26 @@ def run_suite(suite, testclass=None): elif len(result.failures) == 1 and not result.errors: err = result.failures[0][1] else: - if testclass is None: - msg = "errors occurred; run in verbose mode for details" - else: - msg = "errors occurred in %s.%s" \ - % (testclass.__module__, testclass.__name__) + msg = "errors occurred; run in verbose mode for details" raise TestFailed(msg) raise TestFailed(err) def run_unittest(*classes): """Run tests from unittest.TestCase-derived classes.""" + valid_types = (unittest.TestSuite, unittest.TestCase) suite = unittest.TestSuite() for cls in classes: - if isinstance(cls, (unittest.TestSuite, unittest.TestCase)): + if isinstance(cls, str): + if cls in sys.modules: + suite.addTest(unittest.findTestCases(sys.modules[cls])) + else: + raise ValueError("str arguments must be keys in sys.modules") + elif isinstance(cls, valid_types): suite.addTest(cls) else: suite.addTest(unittest.makeSuite(cls)) - if len(classes)==1: - testclass = classes[0] - else: - testclass = None - run_suite(suite, testclass) + _run_suite(suite) #======================================================================= @@ -545,7 +609,6 @@ def reap_children(): # Reap all our dead child processes so we don't leave zombies around. # These hog resources and might be causing some of the buildbots to die. - import os if hasattr(os, 'waitpid'): any_process = -1 while True: diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index b5a5c5d..2b48ea6 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -374,7 +374,7 @@ Misuse of the nonlocal statement can lead to a few unique syntax errors. Traceback (most recent call last): ... SyntaxError: name 'x' is parameter and nonlocal - + >>> def f(): ... global x ... nonlocal x @@ -403,7 +403,7 @@ TODO(jhylton): Figure out how to test SyntaxWarning with doctest. ## Traceback (most recent call last): ## ... ## SyntaxWarning: name 'x' is assigned to before nonlocal declaration - + ## >>> def f(): ## ... x = 1 ## ... nonlocal x @@ -411,7 +411,56 @@ TODO(jhylton): Figure out how to test SyntaxWarning with doctest. ## ... ## SyntaxWarning: name 'x' is assigned to before nonlocal declaration - + +This tests assignment-context; there was a bug in Python 2.5 where compiling +a complex 'if' (one with 'elif') would fail to notice an invalid suite, +leading to spurious errors. + + >>> if 1: + ... x() = 1 + ... elif 1: + ... pass + Traceback (most recent call last): + ... + SyntaxError: can't assign to function call (<doctest test.test_syntax[48]>, line 2) + + >>> if 1: + ... pass + ... elif 1: + ... x() = 1 + Traceback (most recent call last): + ... + SyntaxError: can't assign to function call (<doctest test.test_syntax[49]>, line 4) + + >>> if 1: + ... x() = 1 + ... elif 1: + ... pass + ... else: + ... pass + Traceback (most recent call last): + ... + SyntaxError: can't assign to function call (<doctest test.test_syntax[50]>, line 2) + + >>> if 1: + ... pass + ... elif 1: + ... x() = 1 + ... else: + ... pass + Traceback (most recent call last): + ... + SyntaxError: can't assign to function call (<doctest test.test_syntax[51]>, line 4) + + >>> if 1: + ... pass + ... elif 1: + ... pass + ... else: + ... x() = 1 + Traceback (most recent call last): + ... + SyntaxError: can't assign to function call (<doctest test.test_syntax[52]>, line 6) """ diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index e9bf497..ac7dca3 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1,8 +1,12 @@ +# encoding: iso8859-1 + import sys import os import shutil import tempfile import StringIO +import md5 +import errno import unittest import tarfile @@ -20,452 +24,547 @@ try: except ImportError: bz2 = None +def md5sum(data): + return md5.new(data).hexdigest() + def path(path): return test_support.findfile(path) -testtar = path("testtar.tar") -tempdir = os.path.join(tempfile.gettempdir(), "testtar" + os.extsep + "dir") -tempname = test_support.TESTFN -membercount = 12 - -def tarname(comp=""): - if not comp: - return testtar - return os.path.join(tempdir, "%s%s%s" % (testtar, os.extsep, comp)) +TEMPDIR = os.path.join(tempfile.gettempdir(), "test_tarfile_tmp") +tarname = path("testtar.tar") +gzipname = os.path.join(TEMPDIR, "testtar.tar.gz") +bz2name = os.path.join(TEMPDIR, "testtar.tar.bz2") +tmpname = os.path.join(TEMPDIR, "tmp.tar") -def dirname(): - if not os.path.exists(tempdir): - os.mkdir(tempdir) - return tempdir +md5_regtype = "65f477c818ad9e15f7feab0c6d37742f" +md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6" -def tmpname(): - return tempname +class ReadTest(unittest.TestCase): -class BaseTest(unittest.TestCase): - comp = '' - mode = 'r' - sep = ':' + tarname = tarname + mode = "r:" def setUp(self): - mode = self.mode + self.sep + self.comp - self.tar = tarfile.open(tarname(self.comp), mode) + self.tar = tarfile.open(self.tarname, mode=self.mode, encoding="iso8859-1") def tearDown(self): self.tar.close() -class ReadTest(BaseTest): - def test(self): - """Test member extraction. - """ - members = 0 +class UstarReadTest(ReadTest): + + def test_fileobj_regular_file(self): + tarinfo = self.tar.getmember("ustar/regtype") + fobj = self.tar.extractfile(tarinfo) + data = fobj.read() + self.assert_((len(data), md5sum(data)) == (tarinfo.size, md5_regtype), + "regular file extraction failed") + + def test_fileobj_readlines(self): + self.tar.extract("ustar/regtype", TEMPDIR) + tarinfo = self.tar.getmember("ustar/regtype") + fobj1 = open(os.path.join(TEMPDIR, "ustar/regtype"), "rU") + fobj2 = self.tar.extractfile(tarinfo) + + lines1 = fobj1.readlines() + lines2 = fobj2.readlines() + self.assert_(lines1 == lines2, + "fileobj.readlines() failed") + self.assert_(len(lines2) == 114, + "fileobj.readlines() failed") + self.assert_(lines2[83] == \ + "I will gladly admit that Python is not the fastest running scripting language.\n", + "fileobj.readlines() failed") + + def test_fileobj_iter(self): + self.tar.extract("ustar/regtype", TEMPDIR) + tarinfo = self.tar.getmember("ustar/regtype") + fobj1 = open(os.path.join(TEMPDIR, "ustar/regtype"), "rU") + fobj2 = self.tar.extractfile(tarinfo) + lines1 = fobj1.readlines() + lines2 = [line for line in fobj2] + self.assert_(lines1 == lines2, + "fileobj.__iter__() failed") + + def test_fileobj_seek(self): + self.tar.extract("ustar/regtype", TEMPDIR) + fobj = open(os.path.join(TEMPDIR, "ustar/regtype"), "rb") + data = fobj.read() + fobj.close() + + tarinfo = self.tar.getmember("ustar/regtype") + fobj = self.tar.extractfile(tarinfo) + + text = fobj.read() + fobj.seek(0) + self.assert_(0 == fobj.tell(), + "seek() to file's start failed") + fobj.seek(2048, 0) + self.assert_(2048 == fobj.tell(), + "seek() to absolute position failed") + fobj.seek(-1024, 1) + self.assert_(1024 == fobj.tell(), + "seek() to negative relative position failed") + fobj.seek(1024, 1) + self.assert_(2048 == fobj.tell(), + "seek() to positive relative position failed") + s = fobj.read(10) + self.assert_(s == data[2048:2058], + "read() after seek failed") + fobj.seek(0, 2) + self.assert_(tarinfo.size == fobj.tell(), + "seek() to file's end failed") + self.assert_(fobj.read() == "", + "read() at file's end did not return empty string") + fobj.seek(-tarinfo.size, 2) + self.assert_(0 == fobj.tell(), + "relative seek() to file's start failed") + fobj.seek(512) + s1 = fobj.readlines() + fobj.seek(512) + s2 = fobj.readlines() + self.assert_(s1 == s2, + "readlines() after seek failed") + fobj.seek(0) + self.assert_(len(fobj.readline()) == fobj.tell(), + "tell() after readline() failed") + fobj.seek(512) + self.assert_(len(fobj.readline()) + 512 == fobj.tell(), + "tell() after seek() and readline() failed") + fobj.seek(0) + line = fobj.readline() + self.assert_(fobj.read() == data[len(line):], + "read() after readline() failed") + fobj.close() + + +class MiscReadTest(ReadTest): + + def test_no_filename(self): + fobj = open(self.tarname, "rb") + tar = tarfile.open(fileobj=fobj, mode=self.mode) + self.assertEqual(tar.name, os.path.abspath(fobj.name)) + + def test_fail_comp(self): + # For Gzip and Bz2 Tests: fail with a ReadError on an uncompressed file. + if self.mode == "r:": + return + self.assertRaises(tarfile.ReadError, tarfile.open, tarname, self.mode) + fobj = open(tarname, "rb") + self.assertRaises(tarfile.ReadError, tarfile.open, fileobj=fobj, mode=self.mode) + + def test_v7_dirtype(self): + # Test old style dirtype member (bug #1336623): + # Old V7 tars create directory members using an AREGTYPE + # header with a "/" appended to the filename field. + tarinfo = self.tar.getmember("misc/dirtype-old-v7") + self.assert_(tarinfo.type == tarfile.DIRTYPE, + "v7 dirtype failed") + + def test_check_members(self): for tarinfo in self.tar: - members += 1 - if not tarinfo.isreg(): + self.assert_(int(tarinfo.mtime) == 07606136617, + "wrong mtime for %s" % tarinfo.name) + if not tarinfo.name.startswith("ustar/"): continue - f = self.tar.extractfile(tarinfo) - self.assert_(len(f.read()) == tarinfo.size, - "size read does not match expected size") - f.close() - - self.assert_(members == membercount, - "could not find all members") - - def test_sparse(self): - """Test sparse member extraction. - """ - if self.sep != "|": - f1 = self.tar.extractfile("S-SPARSE") - f2 = self.tar.extractfile("S-SPARSE-WITH-NULLS") - self.assert_(f1.read() == f2.read(), - "_FileObject failed on sparse file member") - - def test_readlines(self): - """Test readlines() method of _FileObject. - """ - if self.sep != "|": - filename = "0-REGTYPE-TEXT" - self.tar.extract(filename, dirname()) - f = open(os.path.join(dirname(), filename), "rU") - lines1 = f.readlines() - f.close() - lines2 = self.tar.extractfile(filename).readlines() - self.assert_(lines1 == lines2, - "_FileObject.readline() does not work correctly") - - def test_iter(self): - # Test iteration over ExFileObject. - if self.sep != "|": - filename = "0-REGTYPE-TEXT" - self.tar.extract(filename, dirname()) - f = open(os.path.join(dirname(), filename), "rU") - lines1 = f.readlines() - f.close() - lines2 = [line for line in self.tar.extractfile(filename)] - self.assert_(lines1 == lines2, - "ExFileObject iteration does not work correctly") - - def test_seek(self): - """Test seek() method of _FileObject, incl. random reading. - """ - if self.sep != "|": - filename = "0-REGTYPE-TEXT" - self.tar.extract(filename, dirname()) - f = open(os.path.join(dirname(), filename), "rb") - data = f.read() - f.close() - - tarinfo = self.tar.getmember(filename) - fobj = self.tar.extractfile(tarinfo) - - text = fobj.read() - fobj.seek(0) - self.assert_(0 == fobj.tell(), - "seek() to file's start failed") - fobj.seek(2048, 0) - self.assert_(2048 == fobj.tell(), - "seek() to absolute position failed") - fobj.seek(-1024, 1) - self.assert_(1024 == fobj.tell(), - "seek() to negative relative position failed") - fobj.seek(1024, 1) - self.assert_(2048 == fobj.tell(), - "seek() to positive relative position failed") - s = fobj.read(10) - self.assert_(s == data[2048:2058], - "read() after seek failed") - fobj.seek(0, 2) - self.assert_(tarinfo.size == fobj.tell(), - "seek() to file's end failed") - self.assert_(fobj.read() == "", - "read() at file's end did not return empty string") - fobj.seek(-tarinfo.size, 2) - self.assert_(0 == fobj.tell(), - "relative seek() to file's start failed") - fobj.seek(512) - s1 = fobj.readlines() - fobj.seek(512) - s2 = fobj.readlines() - self.assert_(s1 == s2, - "readlines() after seek failed") - fobj.seek(0) - self.assert_(len(fobj.readline()) == fobj.tell(), - "tell() after readline() failed") - fobj.seek(512) - self.assert_(len(fobj.readline()) + 512 == fobj.tell(), - "tell() after seek() and readline() failed") - fobj.seek(0) - line = fobj.readline() - self.assert_(fobj.read() == data[len(line):], - "read() after readline() failed") - fobj.close() + self.assert_(tarinfo.uname == "tarfile", + "wrong uname for %s" % tarinfo.name) - def test_old_dirtype(self): - """Test old style dirtype member (bug #1336623). - """ - # Old tars create directory members using a REGTYPE - # header with a "/" appended to the filename field. + def test_find_members(self): + self.assert_(self.tar.getmembers()[-1].name == "misc/eof", + "could not find all members") - # Create an old tar style directory entry. - filename = tmpname() - tarinfo = tarfile.TarInfo("directory/") - tarinfo.type = tarfile.REGTYPE + def test_extract_hardlink(self): + # Test hardlink extraction (e.g. bug #857297). + tar = tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") - fobj = open(filename, "w") - fobj.write(tarinfo.tobuf()) - fobj.close() + tar.extract("ustar/regtype", TEMPDIR) + try: + tar.extract("ustar/lnktype", TEMPDIR) + except EnvironmentError as e: + if e.errno == errno.ENOENT: + self.fail("hardlink not extracted properly") + + data = open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb").read() + self.assertEqual(md5sum(data), md5_regtype) try: - # Test if it is still a directory entry when - # read back. - tar = tarfile.open(filename) - tarinfo = tar.getmembers()[0] - tar.close() - - self.assert_(tarinfo.type == tarfile.DIRTYPE) - self.assert_(tarinfo.name.endswith("/")) - finally: - try: - os.unlink(filename) - except: - pass - -class ReadStreamTest(ReadTest): - sep = "|" - - def test(self): - """Test member extraction, and for StreamError when - seeking backwards. - """ - ReadTest.test(self) - tarinfo = self.tar.getmembers()[0] - f = self.tar.extractfile(tarinfo) + tar.extract("ustar/symtype", TEMPDIR) + except EnvironmentError as e: + if e.errno == errno.ENOENT: + self.fail("symlink not extracted properly") + + data = open(os.path.join(TEMPDIR, "ustar/symtype"), "rb").read() + self.assertEqual(md5sum(data), md5_regtype) + + +class StreamReadTest(ReadTest): + + mode="r|" + + def test_fileobj_regular_file(self): + tarinfo = self.tar.next() # get "regtype" (can't use getmember) + fobj = self.tar.extractfile(tarinfo) + data = fobj.read() + self.assert_((len(data), md5sum(data)) == (tarinfo.size, md5_regtype), + "regular file extraction failed") + + def test_provoke_stream_error(self): + tarinfos = self.tar.getmembers() + f = self.tar.extractfile(tarinfos[0]) # read the first member self.assertRaises(tarfile.StreamError, f.read) - def test_stream(self): - """Compare the normal tar and the stream tar. - """ - stream = self.tar - tar = tarfile.open(tarname(), 'r') + def test_compare_members(self): + tar1 = tarfile.open(tarname, encoding="iso8859-1") + tar2 = self.tar - while 1: - t1 = tar.next() - t2 = stream.next() + while True: + t1 = tar1.next() + t2 = tar2.next() if t1 is None: break self.assert_(t2 is not None, "stream.next() failed.") if t2.islnk() or t2.issym(): - self.assertRaises(tarfile.StreamError, stream.extractfile, t2) + self.assertRaises(tarfile.StreamError, tar2.extractfile, t2) continue - v1 = tar.extractfile(t1) - v2 = stream.extractfile(t2) + + v1 = tar1.extractfile(t1) + v2 = tar2.extractfile(t2) if v1 is None: continue self.assert_(v2 is not None, "stream.extractfile() failed") self.assert_(v1.read() == v2.read(), "stream extraction failed") - tar.close() - stream.close() + tar1.close() -class ReadDetectTest(ReadTest): - def setUp(self): - self.tar = tarfile.open(tarname(self.comp), self.mode) +class DetectReadTest(unittest.TestCase): -class ReadDetectFileobjTest(ReadTest): + def _testfunc_file(self, name, mode): + try: + tarfile.open(name, mode) + except tarfile.ReadError: + self.fail() - def setUp(self): - name = tarname(self.comp) - self.tar = tarfile.open(name, mode=self.mode, - fileobj=open(name, "rb")) + def _testfunc_fileobj(self, name, mode): + try: + tarfile.open(name, mode, fileobj=open(name, "rb")) + except tarfile.ReadError: + self.fail() -class ReadAsteriskTest(ReadTest): + def _test_modes(self, testfunc): + testfunc(tarname, "r") + testfunc(tarname, "r:") + testfunc(tarname, "r:*") + testfunc(tarname, "r|") + testfunc(tarname, "r|*") - def setUp(self): - mode = self.mode + self.sep + "*" - self.tar = tarfile.open(tarname(self.comp), mode) + if gzip: + self.assertRaises(tarfile.ReadError, tarfile.open, tarname, mode="r:gz") + self.assertRaises(tarfile.ReadError, tarfile.open, tarname, mode="r|gz") + self.assertRaises(tarfile.ReadError, tarfile.open, gzipname, mode="r:") + self.assertRaises(tarfile.ReadError, tarfile.open, gzipname, mode="r|") -class ReadStreamAsteriskTest(ReadStreamTest): + testfunc(gzipname, "r") + testfunc(gzipname, "r:*") + testfunc(gzipname, "r:gz") + testfunc(gzipname, "r|*") + testfunc(gzipname, "r|gz") - def setUp(self): - mode = self.mode + self.sep + "*" - self.tar = tarfile.open(tarname(self.comp), mode) + if bz2: + self.assertRaises(tarfile.ReadError, tarfile.open, tarname, mode="r:bz2") + self.assertRaises(tarfile.ReadError, tarfile.open, tarname, mode="r|bz2") + self.assertRaises(tarfile.ReadError, tarfile.open, bz2name, mode="r:") + self.assertRaises(tarfile.ReadError, tarfile.open, bz2name, mode="r|") -class WriteTest(BaseTest): - mode = 'w' + testfunc(bz2name, "r") + testfunc(bz2name, "r:*") + testfunc(bz2name, "r:bz2") + testfunc(bz2name, "r|*") + testfunc(bz2name, "r|bz2") - def setUp(self): - mode = self.mode + self.sep + self.comp - self.src = tarfile.open(tarname(self.comp), 'r') - self.dstname = tmpname() - self.dst = tarfile.open(self.dstname, mode) + def test_detect_file(self): + self._test_modes(self._testfunc_file) - def tearDown(self): - self.src.close() - self.dst.close() + def test_detect_fileobj(self): + self._test_modes(self._testfunc_fileobj) - def test_posix(self): - self.dst.posix = 1 - self._test() - def test_nonposix(self): - self.dst.posix = 0 - self._test() +class MemberReadTest(ReadTest): - def test_small(self): - self.dst.add(os.path.join(os.path.dirname(__file__),"cfgparser.1")) - self.dst.close() - self.assertNotEqual(os.stat(self.dstname).st_size, 0) + def _test_member(self, tarinfo, chksum=None, **kwargs): + if chksum is not None: + self.assert_(md5sum(self.tar.extractfile(tarinfo).read()) == chksum, + "wrong md5sum for %s" % tarinfo.name) - def _test(self): - for tarinfo in self.src: - if not tarinfo.isreg(): - continue - f = self.src.extractfile(tarinfo) - if self.dst.posix and len(tarinfo.name) > tarfile.LENGTH_NAME and "/" not in tarinfo.name: - self.assertRaises(ValueError, self.dst.addfile, - tarinfo, f) - else: - self.dst.addfile(tarinfo, f) + kwargs["mtime"] = 07606136617 + kwargs["uid"] = 1000 + kwargs["gid"] = 100 + if "old-v7" not in tarinfo.name: + # V7 tar can't handle alphabetic owners. + kwargs["uname"] = "tarfile" + kwargs["gname"] = "tarfile" + for k, v in kwargs.items(): + self.assert_(getattr(tarinfo, k) == v, + "wrong value in %s field of %s" % (k, tarinfo.name)) - def test_add_self(self): - dstname = os.path.abspath(self.dstname) + def test_find_regtype(self): + tarinfo = self.tar.getmember("ustar/regtype") + self._test_member(tarinfo, size=7011, chksum=md5_regtype) - self.assertEqual(self.dst.name, dstname, "archive name must be absolute") + def test_find_conttype(self): + tarinfo = self.tar.getmember("ustar/conttype") + self._test_member(tarinfo, size=7011, chksum=md5_regtype) - self.dst.add(dstname) - self.assertEqual(self.dst.getnames(), [], "added the archive to itself") + def test_find_dirtype(self): + tarinfo = self.tar.getmember("ustar/dirtype") + self._test_member(tarinfo, size=0) - cwd = os.getcwd() - os.chdir(dirname()) - self.dst.add(dstname) - os.chdir(cwd) - self.assertEqual(self.dst.getnames(), [], "added the archive to itself") + def test_find_dirtype_with_size(self): + tarinfo = self.tar.getmember("ustar/dirtype-with-size") + self._test_member(tarinfo, size=255) + def test_find_lnktype(self): + tarinfo = self.tar.getmember("ustar/lnktype") + self._test_member(tarinfo, size=0, linkname="ustar/regtype") -class AppendTest(unittest.TestCase): - # Test append mode (cp. patch #1652681). + def test_find_symtype(self): + tarinfo = self.tar.getmember("ustar/symtype") + self._test_member(tarinfo, size=0, linkname="regtype") - def setUp(self): - self.tarname = tmpname() - if os.path.exists(self.tarname): - os.remove(self.tarname) + def test_find_blktype(self): + tarinfo = self.tar.getmember("ustar/blktype") + self._test_member(tarinfo, size=0, devmajor=3, devminor=0) - def _add_testfile(self, fileobj=None): - tar = tarfile.open(self.tarname, "a", fileobj=fileobj) - tar.addfile(tarfile.TarInfo("bar")) - tar.close() + def test_find_chrtype(self): + tarinfo = self.tar.getmember("ustar/chrtype") + self._test_member(tarinfo, size=0, devmajor=1, devminor=3) - def _create_testtar(self): - src = tarfile.open(tarname()) - t = src.getmember("0-REGTYPE") - t.name = "foo" - f = src.extractfile(t) - tar = tarfile.open(self.tarname, "w") - tar.addfile(t, f) - tar.close() + def test_find_fifotype(self): + tarinfo = self.tar.getmember("ustar/fifotype") + self._test_member(tarinfo, size=0) - def _test(self, names=["bar"], fileobj=None): - tar = tarfile.open(self.tarname, fileobj=fileobj) - self.assert_(tar.getnames() == names) + def test_find_sparse(self): + tarinfo = self.tar.getmember("ustar/sparse") + self._test_member(tarinfo, size=86016, chksum=md5_sparse) - def test_non_existing(self): - self._add_testfile() - self._test() + def test_find_umlauts(self): + tarinfo = self.tar.getmember("ustar/umlauts-ÄÖÜäöüß") + self._test_member(tarinfo, size=7011, chksum=md5_regtype) - def test_empty(self): - open(self.tarname, "wb").close() - self._add_testfile() - self._test() + def test_find_ustar_longname(self): + name = "ustar/" + "12345/" * 39 + "1234567/longname" + self.assert_(name in self.tar.getnames()) - def test_empty_fileobj(self): - fobj = StringIO.StringIO() - self._add_testfile(fobj) - fobj.seek(0) - self._test(fileobj=fobj) + def test_find_regtype_oldv7(self): + tarinfo = self.tar.getmember("misc/regtype-old-v7") + self._test_member(tarinfo, size=7011, chksum=md5_regtype) - def test_fileobj(self): - self._create_testtar() - data = open(self.tarname, "rb").read() - fobj = StringIO.StringIO(data) - self._add_testfile(fobj) - fobj.seek(0) - self._test(names=["foo", "bar"], fileobj=fobj) + def test_find_pax_umlauts(self): + self.tar = tarfile.open(self.tarname, mode=self.mode, encoding="iso8859-1") + tarinfo = self.tar.getmember("pax/umlauts-ÄÖÜäöüß") + self._test_member(tarinfo, size=7011, chksum=md5_regtype) - def test_existing(self): - self._create_testtar() - self._add_testfile() - self._test(names=["foo", "bar"]) +class LongnameTest(ReadTest): -class Write100Test(BaseTest): - # The name field in a tar header stores strings of at most 100 chars. - # If a string is shorter than 100 chars it has to be padded with '\0', - # which implies that a string of exactly 100 chars is stored without - # a trailing '\0'. + def test_read_longname(self): + # Test reading of longname (bug #1471427). + name = self.subdir + "/" + "123/" * 125 + "longname" + try: + tarinfo = self.tar.getmember(name) + except KeyError: + self.fail("longname not found") + self.assert_(tarinfo.type != tarfile.DIRTYPE, "read longname as dirtype") - def setUp(self): - self.name = "01234567890123456789012345678901234567890123456789" - self.name += "01234567890123456789012345678901234567890123456789" + def test_read_longlink(self): + longname = self.subdir + "/" + "123/" * 125 + "longname" + longlink = self.subdir + "/" + "123/" * 125 + "longlink" + try: + tarinfo = self.tar.getmember(longlink) + except KeyError: + self.fail("longlink not found") + self.assert_(tarinfo.linkname == longname, "linkname wrong") - self.tar = tarfile.open(tmpname(), "w") - t = tarfile.TarInfo(self.name) - self.tar.addfile(t) - self.tar.close() + def test_truncated_longname(self): + longname = self.subdir + "/" + "123/" * 125 + "longname" + tarinfo = self.tar.getmember(longname) + offset = tarinfo.offset + self.tar.fileobj.seek(offset) + fobj = StringIO.StringIO(self.tar.fileobj.read(1536)) + self.assertRaises(tarfile.ReadError, tarfile.open, name="foo.tar", fileobj=fobj) - self.tar = tarfile.open(tmpname()) - def tearDown(self): - self.tar.close() +class GNUReadTest(LongnameTest): - def test(self): - self.assertEqual(self.tar.getnames()[0], self.name, - "failed to store 100 char filename") + subdir = "gnu" + def test_sparse_file(self): + tarinfo1 = self.tar.getmember("ustar/sparse") + fobj1 = self.tar.extractfile(tarinfo1) + tarinfo2 = self.tar.getmember("gnu/sparse") + fobj2 = self.tar.extractfile(tarinfo2) + self.assert_(fobj1.read() == fobj2.read(), + "sparse file extraction failed") -class WriteSize0Test(BaseTest): - mode = 'w' - def setUp(self): - self.tmpdir = dirname() - self.dstname = tmpname() - self.dst = tarfile.open(self.dstname, "w") +class PaxReadTest(ReadTest): - def tearDown(self): - self.dst.close() + subdir = "pax" + + def test_pax_globheaders(self): + tar = tarfile.open(tarname, encoding="iso8859-1") + tarinfo = tar.getmember("pax/regtype1") + self.assertEqual(tarinfo.uname, "foo") + self.assertEqual(tarinfo.gname, "bar") + self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"), "ÄÖÜäöüß") + + tarinfo = tar.getmember("pax/regtype2") + self.assertEqual(tarinfo.uname, "") + self.assertEqual(tarinfo.gname, "bar") + self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"), "ÄÖÜäöüß") + + tarinfo = tar.getmember("pax/regtype3") + self.assertEqual(tarinfo.uname, "tarfile") + self.assertEqual(tarinfo.gname, "tarfile") + self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"), "ÄÖÜäöüß") + + +class WriteTest(unittest.TestCase): + + mode = "w:" + + def test_100_char_name(self): + # The name field in a tar header stores strings of at most 100 chars. + # If a string is shorter than 100 chars it has to be padded with '\0', + # which implies that a string of exactly 100 chars is stored without + # a trailing '\0'. + name = "0123456789" * 10 + tar = tarfile.open(tmpname, self.mode) + t = tarfile.TarInfo(name) + tar.addfile(t) + tar.close() + + tar = tarfile.open(tmpname) + self.assert_(tar.getnames()[0] == name, + "failed to store 100 char filename") + tar.close() + + def test_tar_size(self): + # Test for bug #1013882. + tar = tarfile.open(tmpname, self.mode) + path = os.path.join(TEMPDIR, "file") + fobj = open(path, "wb") + fobj.write("aaa") + fobj.close() + tar.add(path) + tar.close() + self.assert_(os.path.getsize(tmpname) > 0, + "tarfile is empty") + + # The test_*_size tests test for bug #1167128. + def test_file_size(self): + tar = tarfile.open(tmpname, self.mode) - def test_file(self): - path = os.path.join(self.tmpdir, "file") - f = open(path, "w") - f.close() - tarinfo = self.dst.gettarinfo(path) + path = os.path.join(TEMPDIR, "file") + fobj = open(path, "wb") + fobj.close() + tarinfo = tar.gettarinfo(path) self.assertEqual(tarinfo.size, 0) - f = open(path, "w") - f.write("aaa") - f.close() - tarinfo = self.dst.gettarinfo(path) + + fobj = open(path, "wb") + fobj.write("aaa") + fobj.close() + tarinfo = tar.gettarinfo(path) self.assertEqual(tarinfo.size, 3) - def test_directory(self): - path = os.path.join(self.tmpdir, "directory") - if os.path.exists(path): - # This shouldn't be necessary, but is <wink> if a previous - # run was killed in mid-stream. - shutil.rmtree(path) - os.mkdir(path) - tarinfo = self.dst.gettarinfo(path) - self.assertEqual(tarinfo.size, 0) + tar.close() - def test_symlink(self): + def test_directory_size(self): + path = os.path.join(TEMPDIR, "directory") + os.mkdir(path) + try: + tar = tarfile.open(tmpname, self.mode) + tarinfo = tar.gettarinfo(path) + self.assertEqual(tarinfo.size, 0) + finally: + os.rmdir(path) + + def test_link_size(self): + if hasattr(os, "link"): + link = os.path.join(TEMPDIR, "link") + target = os.path.join(TEMPDIR, "link_target") + open(target, "wb").close() + os.link(target, link) + try: + tar = tarfile.open(tmpname, self.mode) + tarinfo = tar.gettarinfo(link) + self.assertEqual(tarinfo.size, 0) + finally: + os.remove(target) + os.remove(link) + + def test_symlink_size(self): if hasattr(os, "symlink"): - path = os.path.join(self.tmpdir, "symlink") + path = os.path.join(TEMPDIR, "symlink") os.symlink("link_target", path) - tarinfo = self.dst.gettarinfo(path) - self.assertEqual(tarinfo.size, 0) + try: + tar = tarfile.open(tmpname, self.mode) + tarinfo = tar.gettarinfo(path) + self.assertEqual(tarinfo.size, 0) + finally: + os.remove(path) + def test_add_self(self): + # Test for #1257255. + dstname = os.path.abspath(tmpname) -class WriteStreamTest(WriteTest): - sep = '|' + tar = tarfile.open(tmpname, self.mode) + self.assert_(tar.name == dstname, "archive name must be absolute") - def test_padding(self): - self.dst.close() + tar.add(dstname) + self.assert_(tar.getnames() == [], "added the archive to itself") - if self.comp == "gz": - f = gzip.GzipFile(self.dstname) - s = f.read() - f.close() - elif self.comp == "bz2": - f = bz2.BZ2Decompressor() - s = open(self.dstname).read() - s = f.decompress(s) - self.assertEqual(len(f.unused_data), 0, "trailing data") - else: - f = open(self.dstname) - s = f.read() - f.close() + cwd = os.getcwd() + os.chdir(TEMPDIR) + tar.add(dstname) + os.chdir(cwd) + self.assert_(tar.getnames() == [], "added the archive to itself") - self.assertEqual(s.count("\0"), tarfile.RECORDSIZE, - "incorrect zero padding") +class StreamWriteTest(unittest.TestCase): -class WriteGNULongTest(unittest.TestCase): - """This testcase checks for correct creation of GNU Longname - and Longlink extensions. + mode = "w|" - It creates a tarfile and adds empty members with either - long names, long linknames or both and compares the size - of the tarfile with the expected size. + def test_stream_padding(self): + # Test for bug #1543303. + tar = tarfile.open(tmpname, self.mode) + tar.close() - It checks for SF bug #812325 in TarFile._create_gnulong(). + if self.mode.endswith("gz"): + fobj = gzip.GzipFile(tmpname) + data = fobj.read() + fobj.close() + elif self.mode.endswith("bz2"): + dec = bz2.BZ2Decompressor() + data = open(tmpname, "rb").read() + data = dec.decompress(data) + self.assert_(len(dec.unused_data) == 0, + "found trailing data") + else: + fobj = open(tmpname, "rb") + data = fobj.read() + fobj.close() + + self.assert_(data.count("\0") == tarfile.RECORDSIZE, + "incorrect zero padding") - While I was writing this testcase, I noticed a second bug - in the same method: - Long{names,links} weren't null-terminated which lead to - bad tarfiles when their length was a multiple of 512. This - is tested as well. - """ + +class GNUWriteTest(unittest.TestCase): + # This testcase checks for correct creation of GNU Longname + # and Longlink extended headers (cp. bug #812325). def _length(self, s): blocks, remainder = divmod(len(s) + 1, 512) @@ -474,19 +573,17 @@ class WriteGNULongTest(unittest.TestCase): return blocks * 512 def _calc_size(self, name, link=None): - # initial tar header + # Initial tar header count = 512 if len(name) > tarfile.LENGTH_NAME: - # gnu longname extended header + longname + # GNU longname extended header + longname count += 512 count += self._length(name) - if link is not None and len(link) > tarfile.LENGTH_LINK: - # gnu longlink extended header + longlink + # GNU longlink extended header + longlink count += 512 count += self._length(link) - return count def _test(self, name, link=None): @@ -495,17 +592,17 @@ class WriteGNULongTest(unittest.TestCase): tarinfo.linkname = link tarinfo.type = tarfile.LNKTYPE - tar = tarfile.open(tmpname(), "w") - tar.posix = False + tar = tarfile.open(tmpname, "w") + tar.format = tarfile.GNU_FORMAT tar.addfile(tarinfo) v1 = self._calc_size(name, link) v2 = tar.offset - self.assertEqual(v1, v2, "GNU longname/longlink creation failed") + self.assert_(v1 == v2, "GNU longname/longlink creation failed") tar.close() - tar = tarfile.open(tmpname()) + tar = tarfile.open(tmpname) member = tar.next() self.failIf(member is None, "unable to read longname member") self.assert_(tarinfo.name == member.name and \ @@ -542,268 +639,351 @@ class WriteGNULongTest(unittest.TestCase): self._test(("longnam/" * 127) + "longname_", ("longlnk/" * 127) + "longlink_") -class ReadGNULongTest(unittest.TestCase): + +class HardlinkTest(unittest.TestCase): + # Test the creation of LNKTYPE (hardlink) members in an archive. def setUp(self): - self.tar = tarfile.open(tarname()) + self.foo = os.path.join(TEMPDIR, "foo") + self.bar = os.path.join(TEMPDIR, "bar") + + fobj = open(self.foo, "wb") + fobj.write("foo") + fobj.close() + + os.link(self.foo, self.bar) + + self.tar = tarfile.open(tmpname, "w") + self.tar.add(self.foo) def tearDown(self): - self.tar.close() + os.remove(self.foo) + os.remove(self.bar) - def test_1471427(self): - """Test reading of longname (bug #1471427). - """ - name = "test/" * 20 + "0-REGTYPE" - try: - tarinfo = self.tar.getmember(name) - except KeyError: - tarinfo = None - self.assert_(tarinfo is not None, "longname not found") - self.assert_(tarinfo.type != tarfile.DIRTYPE, "read longname as dirtype") + def test_add_twice(self): + # The same name will be added as a REGTYPE every + # time regardless of st_nlink. + tarinfo = self.tar.gettarinfo(self.foo) + self.assert_(tarinfo.type == tarfile.REGTYPE, + "add file as regular failed") - def test_read_name(self): - name = ("0-LONGNAME-" * 10)[:101] - try: - tarinfo = self.tar.getmember(name) - except KeyError: - tarinfo = None - self.assert_(tarinfo is not None, "longname not found") + def test_add_hardlink(self): + tarinfo = self.tar.gettarinfo(self.bar) + self.assert_(tarinfo.type == tarfile.LNKTYPE, + "add file as hardlink failed") - def test_read_link(self): - link = ("1-LONGLINK-" * 10)[:101] - name = ("0-LONGNAME-" * 10)[:101] - try: - tarinfo = self.tar.getmember(link) - except KeyError: - tarinfo = None - self.assert_(tarinfo is not None, "longlink not found") - self.assert_(tarinfo.linkname == name, "linkname wrong") + def test_dereference_hardlink(self): + self.tar.dereference = True + tarinfo = self.tar.gettarinfo(self.bar) + self.assert_(tarinfo.type == tarfile.REGTYPE, + "dereferencing hardlink failed") - def test_truncated_longname(self): - f = open(tarname()) - fobj = StringIO.StringIO(f.read(1024)) - f.close() - tar = tarfile.open(name="foo.tar", fileobj=fobj) - self.assert_(len(tar.getmembers()) == 0, "") + +class PaxWriteTest(GNUWriteTest): + + def _test(self, name, link=None): + # See GNUWriteTest. + tarinfo = tarfile.TarInfo(name) + if link: + tarinfo.linkname = link + tarinfo.type = tarfile.LNKTYPE + + tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT) + tar.addfile(tarinfo) tar.close() + tar = tarfile.open(tmpname) + if link: + l = tar.getmembers()[0].linkname + self.assert_(link == l, "PAX longlink creation failed") + else: + n = tar.getmembers()[0].name + self.assert_(name == n, "PAX longname creation failed") -class ExtractHardlinkTest(BaseTest): + def test_iso8859_15_filename(self): + self._test_unicode_filename("iso8859-15") - def test_hardlink(self): - """Test hardlink extraction (bug #857297) - """ - # Prevent errors from being caught - self.tar.errorlevel = 1 + def test_utf8_filename(self): + self._test_unicode_filename("utf8") - self.tar.extract("0-REGTYPE", dirname()) - try: - # Extract 1-LNKTYPE which is a hardlink to 0-REGTYPE - self.tar.extract("1-LNKTYPE", dirname()) - except EnvironmentError as e: - import errno - if e.errno == errno.ENOENT: - self.fail("hardlink not extracted properly") + def test_utf16_filename(self): + self._test_unicode_filename("utf16") -class CreateHardlinkTest(BaseTest): - """Test the creation of LNKTYPE (hardlink) members in an archive. - In this respect tarfile.py mimics the behaviour of GNU tar: If - a file has a st_nlink > 1, it will be added a REGTYPE member - only the first time. - """ + def _test_unicode_filename(self, encoding): + tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT) + name = u"\u20ac".encode(encoding) # Euro sign + tar.encoding = encoding + tar.addfile(tarfile.TarInfo(name)) + tar.close() - def setUp(self): - self.tar = tarfile.open(tmpname(), "w") + tar = tarfile.open(tmpname, encoding=encoding) + self.assertEqual(tar.getmembers()[0].name, name) + tar.close() - self.foo = os.path.join(dirname(), "foo") - self.bar = os.path.join(dirname(), "bar") + def test_unicode_filename_error(self): + # The euro sign filename cannot be translated to iso8859-1 encoding. + tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT, encoding="utf8") + name = u"\u20ac".encode("utf8") # Euro sign + tar.addfile(tarfile.TarInfo(name)) + tar.close() - if os.path.exists(self.foo): - os.remove(self.foo) - if os.path.exists(self.bar): - os.remove(self.bar) + self.assertRaises(UnicodeError, tarfile.open, tmpname, encoding="iso8859-1") - f = open(self.foo, "w") - f.write("foo") - f.close() - self.tar.add(self.foo) + def test_pax_headers(self): + self._test_pax_headers({"foo": "bar", "uid": 0, "mtime": 1.23}) - def test_add_twice(self): - # If st_nlink == 1 then the same file will be added as - # REGTYPE every time. - tarinfo = self.tar.gettarinfo(self.foo) - self.assertEqual(tarinfo.type, tarfile.REGTYPE, - "add file as regular failed") + self._test_pax_headers({"euro": u"\u20ac".encode("utf8")}) - def test_add_hardlink(self): - # If st_nlink > 1 then the same file will be added as - # LNKTYPE. - os.link(self.foo, self.bar) - tarinfo = self.tar.gettarinfo(self.foo) - self.assertEqual(tarinfo.type, tarfile.LNKTYPE, - "add file as hardlink failed") + self._test_pax_headers({"euro": u"\u20ac"}, + {"euro": u"\u20ac".encode("utf8")}) - tarinfo = self.tar.gettarinfo(self.bar) - self.assertEqual(tarinfo.type, tarfile.LNKTYPE, - "add file as hardlink failed") + self._test_pax_headers({u"\u20ac": "euro"}, + {u"\u20ac".encode("utf8"): "euro"}) - def test_dereference_hardlink(self): - self.tar.dereference = True - os.link(self.foo, self.bar) - tarinfo = self.tar.gettarinfo(self.bar) - self.assertEqual(tarinfo.type, tarfile.REGTYPE, - "dereferencing hardlink failed") + def _test_pax_headers(self, pax_headers, cmp_headers=None): + if cmp_headers is None: + cmp_headers = pax_headers + tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT, \ + pax_headers=pax_headers, encoding="utf8") + tar.addfile(tarfile.TarInfo("test")) + tar.close() -# Gzip TestCases -class ReadTestGzip(ReadTest): - comp = "gz" -class ReadStreamTestGzip(ReadStreamTest): - comp = "gz" -class WriteTestGzip(WriteTest): - comp = "gz" -class WriteStreamTestGzip(WriteStreamTest): - comp = "gz" -class ReadDetectTestGzip(ReadDetectTest): - comp = "gz" -class ReadDetectFileobjTestGzip(ReadDetectFileobjTest): - comp = "gz" -class ReadAsteriskTestGzip(ReadAsteriskTest): - comp = "gz" -class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest): - comp = "gz" - -# Filemode test cases - -class FileModeTest(unittest.TestCase): - def test_modes(self): - self.assertEqual(tarfile.filemode(0755), '-rwxr-xr-x') - self.assertEqual(tarfile.filemode(07111), '---s--s--t') - -class HeaderErrorTest(unittest.TestCase): + tar = tarfile.open(tmpname, encoding="utf8") + self.assertEqual(tar.pax_headers, cmp_headers) def test_truncated_header(self): - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, "") - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, "filename\0") - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, "\0" * 511) - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, "\0" * 513) - - def test_empty_header(self): - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, "\0" * 512) - - def test_invalid_header(self): - buf = tarfile.TarInfo("filename").tobuf() - buf = buf[:148] + "foo\0\0\0\0\0" + buf[156:] # invalid number field. - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, buf) - - def test_bad_checksum(self): - buf = tarfile.TarInfo("filename").tobuf() - b = buf[:148] + " " + buf[156:] # clear the checksum field. - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, b) - b = "a" + buf[1:] # manipulate the buffer, so checksum won't match. - self.assertRaises(tarfile.HeaderError, tarfile.TarInfo.frombuf, b) - -class OpenFileobjTest(BaseTest): - # Test for SF bug #1496501. - - def test_opener(self): - fobj = StringIO.StringIO("foo\n") - try: - tarfile.open("", "r", fileobj=fobj) - except tarfile.ReadError: - self.assertEqual(fobj.tell(), 0, "fileobj's position has moved") - -if bz2: - # Bzip2 TestCases - class ReadTestBzip2(ReadTestGzip): - comp = "bz2" - class ReadStreamTestBzip2(ReadStreamTestGzip): - comp = "bz2" - class WriteTestBzip2(WriteTest): - comp = "bz2" - class WriteStreamTestBzip2(WriteStreamTestGzip): - comp = "bz2" - class ReadDetectTestBzip2(ReadDetectTest): - comp = "bz2" - class ReadDetectFileobjTestBzip2(ReadDetectFileobjTest): - comp = "bz2" - class ReadAsteriskTestBzip2(ReadAsteriskTest): - comp = "bz2" - class ReadStreamAsteriskTestBzip2(ReadStreamAsteriskTest): - comp = "bz2" - -# If importing gzip failed, discard the Gzip TestCases. -if not gzip: - del ReadTestGzip - del ReadStreamTestGzip - del WriteTestGzip - del WriteStreamTestGzip + tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT) + tarinfo = tarfile.TarInfo("123/" * 126 + "longname") + tar.addfile(tarinfo) + tar.close() -def test_main(): - # Create archive. - f = open(tarname(), "rb") - fguts = f.read() - f.close() - if gzip: - # create testtar.tar.gz - tar = gzip.open(tarname("gz"), "wb") - tar.write(fguts) + # Simulate a premature EOF. + open(tmpname, "rb+").truncate(1536) + tar = tarfile.open(tmpname) + self.assertEqual(tar.getmembers(), []) + + +class AppendTest(unittest.TestCase): + # Test append mode (cp. patch #1652681). + + def setUp(self): + self.tarname = tmpname + if os.path.exists(self.tarname): + os.remove(self.tarname) + + def _add_testfile(self, fileobj=None): + tar = tarfile.open(self.tarname, "a", fileobj=fileobj) + tar.addfile(tarfile.TarInfo("bar")) tar.close() - if bz2: - # create testtar.tar.bz2 - tar = bz2.BZ2File(tarname("bz2"), "wb") - tar.write(fguts) + + def _create_testtar(self, mode="w:"): + src = tarfile.open(tarname, encoding="iso8859-1") + t = src.getmember("ustar/regtype") + t.name = "foo" + f = src.extractfile(t) + tar = tarfile.open(self.tarname, mode) + tar.addfile(t, f) tar.close() + def _test(self, names=["bar"], fileobj=None): + tar = tarfile.open(self.tarname, fileobj=fileobj) + self.assertEqual(tar.getnames(), names) + + def test_non_existing(self): + self._add_testfile() + self._test() + + def test_empty(self): + open(self.tarname, "w").close() + self._add_testfile() + self._test() + + def test_empty_fileobj(self): + fobj = StringIO.StringIO() + self._add_testfile(fobj) + fobj.seek(0) + self._test(fileobj=fobj) + + def test_fileobj(self): + self._create_testtar() + data = open(self.tarname).read() + fobj = StringIO.StringIO(data) + self._add_testfile(fobj) + fobj.seek(0) + self._test(names=["foo", "bar"], fileobj=fobj) + + def test_existing(self): + self._create_testtar() + self._add_testfile() + self._test(names=["foo", "bar"]) + + def test_append_gz(self): + if gzip is None: + return + self._create_testtar("w:gz") + self.assertRaises(tarfile.ReadError, tarfile.open, tmpname, "a") + + def test_append_bz2(self): + if bz2 is None: + return + self._create_testtar("w:bz2") + self.assertRaises(tarfile.ReadError, tarfile.open, tmpname, "a") + + +class LimitsTest(unittest.TestCase): + + def test_ustar_limits(self): + # 100 char name + tarinfo = tarfile.TarInfo("0123456789" * 10) + tarinfo.create_ustar_header() + + # 101 char name that cannot be stored + tarinfo = tarfile.TarInfo("0123456789" * 10 + "0") + self.assertRaises(ValueError, tarinfo.create_ustar_header) + + # 256 char name with a slash at pos 156 + tarinfo = tarfile.TarInfo("123/" * 62 + "longname") + tarinfo.create_ustar_header() + + # 256 char name that cannot be stored + tarinfo = tarfile.TarInfo("1234567/" * 31 + "longname") + self.assertRaises(ValueError, tarinfo.create_ustar_header) + + # 512 char name + tarinfo = tarfile.TarInfo("123/" * 126 + "longname") + self.assertRaises(ValueError, tarinfo.create_ustar_header) + + # 512 char linkname + tarinfo = tarfile.TarInfo("longlink") + tarinfo.linkname = "123/" * 126 + "longname" + self.assertRaises(ValueError, tarinfo.create_ustar_header) + + # uid > 8 digits + tarinfo = tarfile.TarInfo("name") + tarinfo.uid = 010000000 + self.assertRaises(ValueError, tarinfo.create_ustar_header) + + def test_gnu_limits(self): + tarinfo = tarfile.TarInfo("123/" * 126 + "longname") + tarinfo.create_gnu_header() + + tarinfo = tarfile.TarInfo("longlink") + tarinfo.linkname = "123/" * 126 + "longname" + tarinfo.create_gnu_header() + + # uid >= 256 ** 7 + tarinfo = tarfile.TarInfo("name") + tarinfo.uid = 04000000000000000000 + self.assertRaises(ValueError, tarinfo.create_gnu_header) + + def test_pax_limits(self): + # A 256 char name that can be stored without an extended header. + tarinfo = tarfile.TarInfo("123/" * 62 + "longname") + self.assert_(len(tarinfo.create_pax_header("utf8")) == 512, + "create_pax_header attached superfluous extended header") + + tarinfo = tarfile.TarInfo("123/" * 126 + "longname") + tarinfo.create_pax_header("utf8") + + tarinfo = tarfile.TarInfo("longlink") + tarinfo.linkname = "123/" * 126 + "longname" + tarinfo.create_pax_header("utf8") + + tarinfo = tarfile.TarInfo("name") + tarinfo.uid = 04000000000000000000 + tarinfo.create_pax_header("utf8") + + +class GzipMiscReadTest(MiscReadTest): + tarname = gzipname + mode = "r:gz" +class GzipUstarReadTest(UstarReadTest): + tarname = gzipname + mode = "r:gz" +class GzipStreamReadTest(StreamReadTest): + tarname = gzipname + mode = "r|gz" +class GzipWriteTest(WriteTest): + mode = "w:gz" +class GzipStreamWriteTest(StreamWriteTest): + mode = "w|gz" + + +class Bz2MiscReadTest(MiscReadTest): + tarname = bz2name + mode = "r:bz2" +class Bz2UstarReadTest(UstarReadTest): + tarname = bz2name + mode = "r:bz2" +class Bz2StreamReadTest(StreamReadTest): + tarname = bz2name + mode = "r|bz2" +class Bz2WriteTest(WriteTest): + mode = "w:bz2" +class Bz2StreamWriteTest(StreamWriteTest): + mode = "w|bz2" + +def test_main(): + if not os.path.exists(TEMPDIR): + os.mkdir(TEMPDIR) + tests = [ - FileModeTest, - HeaderErrorTest, - OpenFileobjTest, - ReadTest, - ReadStreamTest, - ReadDetectTest, - ReadDetectFileobjTest, - ReadAsteriskTest, - ReadStreamAsteriskTest, + UstarReadTest, + MiscReadTest, + StreamReadTest, + DetectReadTest, + MemberReadTest, + GNUReadTest, + PaxReadTest, WriteTest, + StreamWriteTest, + GNUWriteTest, + PaxWriteTest, AppendTest, - Write100Test, - WriteSize0Test, - WriteStreamTest, - WriteGNULongTest, - ReadGNULongTest, + LimitsTest, ] if hasattr(os, "link"): - tests.append(ExtractHardlinkTest) - tests.append(CreateHardlinkTest) + tests.append(HardlinkTest) + + fobj = open(tarname, "rb") + data = fobj.read() + fobj.close() if gzip: - tests.extend([ - ReadTestGzip, ReadStreamTestGzip, - WriteTestGzip, WriteStreamTestGzip, - ReadDetectTestGzip, ReadDetectFileobjTestGzip, - ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip - ]) + # Create testtar.tar.gz and add gzip-specific tests. + tar = gzip.open(gzipname, "wb") + tar.write(data) + tar.close() + + tests += [ + GzipMiscReadTest, + GzipUstarReadTest, + GzipStreamReadTest, + GzipWriteTest, + GzipStreamWriteTest, + ] if bz2: - tests.extend([ - ReadTestBzip2, ReadStreamTestBzip2, - WriteTestBzip2, WriteStreamTestBzip2, - ReadDetectTestBzip2, ReadDetectFileobjTestBzip2, - ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2 - ]) + # Create testtar.tar.bz2 and add bz2-specific tests. + tar = bz2.BZ2File(bz2name, "wb") + tar.write(data) + tar.close() + + tests += [ + Bz2MiscReadTest, + Bz2UstarReadTest, + Bz2StreamReadTest, + Bz2WriteTest, + Bz2StreamWriteTest, + ] + try: test_support.run_unittest(*tests) finally: - if gzip: - os.remove(tarname("gz")) - if bz2: - os.remove(tarname("bz2")) - if os.path.exists(dirname()): - shutil.rmtree(dirname()) - if os.path.exists(tmpname()): - os.remove(tmpname()) + if os.path.exists(TEMPDIR): + shutil.rmtree(TEMPDIR) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_telnetlib.py b/Lib/test/test_telnetlib.py new file mode 100644 index 0000000..0a3604e --- /dev/null +++ b/Lib/test/test_telnetlib.py @@ -0,0 +1,74 @@ +import socket +import threading +import telnetlib +import time + +from unittest import TestCase +from test import test_support + + +def server(evt): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.settimeout(3) + serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + serv.bind(("", 9091)) + serv.listen(5) + try: + conn, addr = serv.accept() + except socket.timeout: + pass + finally: + serv.close() + evt.set() + +class GeneralTests(TestCase): + + def setUp(self): + self.evt = threading.Event() + threading.Thread(target=server, args=(self.evt,)).start() + time.sleep(.1) + + def tearDown(self): + self.evt.wait() + + def testBasic(self): + # connects + telnet = telnetlib.Telnet("localhost", 9091) + telnet.sock.close() + + def testTimeoutDefault(self): + # default + telnet = telnetlib.Telnet("localhost", 9091) + self.assertTrue(telnet.sock.gettimeout() is None) + telnet.sock.close() + + def testTimeoutValue(self): + # a value + telnet = telnetlib.Telnet("localhost", 9091, timeout=30) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + def testTimeoutDifferentOrder(self): + telnet = telnetlib.Telnet(timeout=30) + telnet.open("localhost", 9091) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + def testTimeoutNone(self): + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + telnet = telnetlib.Telnet("localhost", 9091, timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + + +def test_main(verbose=None): + test_support.run_unittest(GeneralTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index a398d37..20f22ed 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -81,7 +81,8 @@ class test_exports(TC): "gettempprefix" : 1, "gettempdir" : 1, "tempdir" : 1, - "template" : 1 + "template" : 1, + "SpooledTemporaryFile" : 1 } unexp = [] @@ -561,11 +562,12 @@ test_classes.append(test_mktemp) class test_NamedTemporaryFile(TC): """Test NamedTemporaryFile().""" - def do_create(self, dir=None, pre="", suf=""): + def do_create(self, dir=None, pre="", suf="", delete=True): if dir is None: dir = tempfile.gettempdir() try: - file = tempfile.NamedTemporaryFile(dir=dir, prefix=pre, suffix=suf) + file = tempfile.NamedTemporaryFile(dir=dir, prefix=pre, suffix=suf, + delete=delete) except: self.failOnException("NamedTemporaryFile") @@ -599,6 +601,22 @@ class test_NamedTemporaryFile(TC): finally: os.rmdir(dir) + def test_dis_del_on_close(self): + # Tests that delete-on-close can be disabled + dir = tempfile.mkdtemp() + tmp = None + try: + f = tempfile.NamedTemporaryFile(dir=dir, delete=False) + tmp = f.name + f.write('blat') + f.close() + self.failUnless(os.path.exists(f.name), + "NamedTemporaryFile %s missing after close" % f.name) + finally: + if tmp is not None: + os.unlink(tmp) + os.rmdir(dir) + def test_multiple_close(self): # A NamedTemporaryFile can be closed many times without error @@ -615,6 +633,107 @@ class test_NamedTemporaryFile(TC): test_classes.append(test_NamedTemporaryFile) +class test_SpooledTemporaryFile(TC): + """Test SpooledTemporaryFile().""" + + def do_create(self, max_size=0, dir=None, pre="", suf=""): + if dir is None: + dir = tempfile.gettempdir() + try: + file = tempfile.SpooledTemporaryFile(max_size=max_size, dir=dir, prefix=pre, suffix=suf) + except: + self.failOnException("SpooledTemporaryFile") + + return file + + + def test_basic(self): + # SpooledTemporaryFile can create files + f = self.do_create() + self.failIf(f._rolled) + f = self.do_create(max_size=100, pre="a", suf=".txt") + self.failIf(f._rolled) + + def test_del_on_close(self): + # A SpooledTemporaryFile is deleted when closed + dir = tempfile.mkdtemp() + try: + f = tempfile.SpooledTemporaryFile(max_size=10, dir=dir) + self.failIf(f._rolled) + f.write('blat ' * 5) + self.failUnless(f._rolled) + filename = f.name + f.close() + self.failIf(os.path.exists(filename), + "SpooledTemporaryFile %s exists after close" % filename) + finally: + os.rmdir(dir) + + def test_rewrite_small(self): + # A SpooledTemporaryFile can be written to multiple within the max_size + f = self.do_create(max_size=30) + self.failIf(f._rolled) + for i in range(5): + f.seek(0, 0) + f.write('x' * 20) + self.failIf(f._rolled) + + def test_write_sequential(self): + # A SpooledTemporaryFile should hold exactly max_size bytes, and roll + # over afterward + f = self.do_create(max_size=30) + self.failIf(f._rolled) + f.write('x' * 20) + self.failIf(f._rolled) + f.write('x' * 10) + self.failIf(f._rolled) + f.write('x') + self.failUnless(f._rolled) + + def test_sparse(self): + # A SpooledTemporaryFile that is written late in the file will extend + # when that occurs + f = self.do_create(max_size=30) + self.failIf(f._rolled) + f.seek(100, 0) + self.failIf(f._rolled) + f.write('x') + self.failUnless(f._rolled) + + def test_fileno(self): + # A SpooledTemporaryFile should roll over to a real file on fileno() + f = self.do_create(max_size=30) + self.failIf(f._rolled) + self.failUnless(f.fileno() > 0) + self.failUnless(f._rolled) + + def test_multiple_close(self): + # A SpooledTemporaryFile can be closed many times without error + f = tempfile.SpooledTemporaryFile() + f.write('abc\n') + f.close() + try: + f.close() + f.close() + except: + self.failOnException("close") + + def test_bound_methods(self): + # It should be OK to steal a bound method from a SpooledTemporaryFile + # and use it independently; when the file rolls over, those bound + # methods should continue to function + f = self.do_create(max_size=30) + read = f.read + write = f.write + seek = f.seek + + write("a" * 35) + write("b" * 35) + seek(0, 0) + self.failUnless(read(70) == 'a'*35 + 'b'*35) + +test_classes.append(test_SpooledTemporaryFile) + class test_TemporaryFile(TC): """Test TemporaryFile().""" diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index 500eceb..5f0b51b 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -328,6 +328,14 @@ What a mess! self.check_wrap(text, 30, [" This is a sentence with", "leading whitespace."]) + def test_no_drop_whitespace(self): + # SF patch #1581073 + text = " This is a sentence with much whitespace." + self.check_wrap(text, 10, + [" This is a", " ", "sentence ", + "with ", "much white", "space."], + drop_whitespace=False) + if test_support.have_unicode: def test_unicode(self): # *Very* simple test of wrapping Unicode strings. I'm sure diff --git a/Lib/test/test_threadedtempfile.py b/Lib/test/test_threadedtempfile.py index 75d719d..753f388 100644 --- a/Lib/test/test_threadedtempfile.py +++ b/Lib/test/test_threadedtempfile.py @@ -10,22 +10,20 @@ failures. A failure is a bug in tempfile, and may be due to: By default, NUM_THREADS == 20 and FILES_PER_THREAD == 50. This is enough to create about 150 failures per run under Win98SE in 2.0, and runs pretty quickly. Guido reports needing to boost FILES_PER_THREAD to 500 before -provoking a 2.0 failure under Linux. Run the test alone to boost either -via cmdline switches: - --f FILES_PER_THREAD (int) --t NUM_THREADS (int) +provoking a 2.0 failure under Linux. """ -NUM_THREADS = 20 # change w/ -t option -FILES_PER_THREAD = 50 # change w/ -f option +NUM_THREADS = 20 +FILES_PER_THREAD = 50 import thread # If this fails, we can't test this module import threading -from test.test_support import TestFailed, threading_setup, threading_cleanup +import tempfile + +from test.test_support import threading_setup, threading_cleanup, run_unittest +import unittest import StringIO from traceback import print_exc -import tempfile startEvent = threading.Event() @@ -46,41 +44,36 @@ class TempFileGreedy(threading.Thread): else: self.ok_count += 1 + +class ThreadedTempFileTest(unittest.TestCase): + def test_main(self): + threads = [] + thread_info = threading_setup() + + for i in range(NUM_THREADS): + t = TempFileGreedy() + threads.append(t) + t.start() + + startEvent.set() + + ok = 0 + errors = [] + for t in threads: + t.join() + ok += t.ok_count + if t.error_count: + errors.append(str(t.getName()) + str(t.errors.getvalue())) + + threading_cleanup(*thread_info) + + msg = "Errors: errors %d ok %d\n%s" % (len(errors), ok, + '\n'.join(errors)) + self.assertEquals(errors, [], msg) + self.assertEquals(ok, NUM_THREADS * FILES_PER_THREAD) + def test_main(): - threads = [] - thread_info = threading_setup() - - print("Creating") - for i in range(NUM_THREADS): - t = TempFileGreedy() - threads.append(t) - t.start() - - print("Starting") - startEvent.set() - - print("Reaping") - ok = errors = 0 - for t in threads: - t.join() - ok += t.ok_count - errors += t.error_count - if t.error_count: - print('%s errors:\n%s' % (t.getName(), t.errors.getvalue())) - - msg = "Done: errors %d ok %d" % (errors, ok) - print(msg) - if errors: - raise TestFailed(msg) - - threading_cleanup(*thread_info) + run_unittest(ThreadedTempFileTest) if __name__ == "__main__": - import sys, getopt - opts, args = getopt.getopt(sys.argv[1:], "t:f:") - for o, v in opts: - if o == "-f": - FILES_PER_THREAD = int(v) - elif o == "-t": - NUM_THREADS = int(v) test_main() diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py index 56fbedd..0aaedbc 100644 --- a/Lib/test/test_threading_local.py +++ b/Lib/test/test_threading_local.py @@ -20,7 +20,7 @@ def test_main(): setUp=setUp, tearDown=tearDown) ) - test_support.run_suite(suite) + test_support.run_unittest(suite) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index cb7586c..a704cc9 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -829,7 +829,7 @@ class UnicodeTest( def test_main(): - test_support.run_unittest(UnicodeTest) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py index 0058d98..328b5b6 100644 --- a/Lib/test/test_unicode_file.py +++ b/Lib/test/test_unicode_file.py @@ -5,7 +5,7 @@ import os, glob, time, shutil import unicodedata import unittest -from test.test_support import run_suite, TestSkipped, TESTFN_UNICODE +from test.test_support import run_unittest, TestSkipped, TESTFN_UNICODE from test.test_support import TESTFN_ENCODING, TESTFN_UNICODE_UNENCODEABLE try: TESTFN_ENCODED = TESTFN_UNICODE.encode(TESTFN_ENCODING) @@ -205,9 +205,7 @@ class TestUnicodeFiles(unittest.TestCase): False) def test_main(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestUnicodeFiles)) - run_suite(suite) + run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py index 9151166..70f12d2 100644 --- a/Lib/test/test_unittest.py +++ b/Lib/test/test_unittest.py @@ -1,31 +1,2302 @@ """Test script for unittest. -This just includes tests for new features. We really need a -full set of tests. +By Collin Winter <collinw at gmail.com> + +Still need testing: + TestCase.{assert,fail}* methods (some are tested implicitly) """ +from test import test_support import unittest +from unittest import TestCase + +### Support code +################################################################ + +class LoggingResult(unittest.TestResult): + def __init__(self, log): + self._events = log + super(LoggingResult, self).__init__() + + def startTest(self, test): + self._events.append('startTest') + super(LoggingResult, self).startTest(test) + + def stopTest(self, test): + self._events.append('stopTest') + super(LoggingResult, self).stopTest(test) + + def addFailure(self, *args): + self._events.append('addFailure') + super(LoggingResult, self).addFailure(*args) + + def addError(self, *args): + self._events.append('addError') + super(LoggingResult, self).addError(*args) + +class TestEquality(object): + # Check for a valid __eq__ implementation + def test_eq(self): + for obj_1, obj_2 in self.eq_pairs: + self.assertEqual(obj_1, obj_2) + self.assertEqual(obj_2, obj_1) + + # Check for a valid __ne__ implementation + def test_ne(self): + for obj_1, obj_2 in self.ne_pairs: + self.failIfEqual(obj_1, obj_2) + self.failIfEqual(obj_2, obj_1) + +class TestHashing(object): + # Check for a valid __hash__ implementation + def test_hash(self): + for obj_1, obj_2 in self.eq_pairs: + try: + assert hash(obj_1) == hash(obj_2) + except KeyboardInterrupt: + raise + except AssertionError: + self.fail("%s and %s do not hash equal" % (obj_1, obj_2)) + except Exception as e: + self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e)) + + for obj_1, obj_2 in self.ne_pairs: + try: + assert hash(obj_1) != hash(obj_2) + except KeyboardInterrupt: + raise + except AssertionError: + self.fail("%s and %s hash equal, but shouldn't" % (obj_1, obj_2)) + except Exception as e: + self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e)) + + +################################################################ +### /Support code + +class Test_TestLoader(TestCase): + + ### Tests for TestLoader.loadTestsFromTestCase + ################################################################ + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + def test_loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure it does the right thing even if no tests were found + def test_loadTestsFromTestCase__no_matches(self): + class Foo(unittest.TestCase): + def foo_bar(self): pass + + empty_suite = unittest.TestSuite() + + loader = unittest.TestLoader() + self.assertEqual(loader.loadTestsFromTestCase(Foo), empty_suite) + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # What happens if loadTestsFromTestCase() is given an object + # that isn't a subclass of TestCase? Specifically, what happens + # if testCaseClass is a subclass of TestSuite? + # + # This is checked for specifically in the code, so we better add a + # test for it. + def test_loadTestsFromTestCase__TestSuite_subclass(self): + class NotATestCase(unittest.TestSuite): + pass + + loader = unittest.TestLoader() + try: + loader.loadTestsFromTestCase(NotATestCase) + except TypeError: + pass + else: + self.fail('Should raise TypeError') + + # "Return a suite of all tests cases contained in the TestCase-derived + # class testCaseClass" + # + # Make sure loadTestsFromTestCase() picks up the default test method + # name (as specified by TestCase), even though the method name does + # not match the default TestLoader.testMethodPrefix string + def test_loadTestsFromTestCase__default_method_name(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + loader = unittest.TestLoader() + # This has to be false for the test to succeed + self.failIf('runTest'.startswith(loader.testMethodPrefix)) + + suite = loader.loadTestsFromTestCase(Foo) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), [Foo('runTest')]) + + ################################################################ + ### /Tests for TestLoader.loadTestsFromTestCase + + ### Tests for TestLoader.loadTestsFromModule + ################################################################ + + # "This method searches `module` for classes derived from TestCase" + def test_loadTestsFromModule__TestCase_subclass(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + expected = [loader.suiteClass([MyTestCase('test')])] + self.assertEqual(list(suite), expected) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (no TestCase instances)? + def test_loadTestsFromModule__no_TestCase_instances(self): + import new + m = new.module('m') + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), []) + + # "This method searches `module` for classes derived from TestCase" + # + # What happens if no tests are found (TestCases instances, but no tests)? + def test_loadTestsFromModule__no_TestCase_tests(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + self.assertEqual(list(suite), [loader.suiteClass()]) + + # "This method searches `module` for classes derived from TestCase"s + # + # What happens if loadTestsFromModule() is given something other + # than a module? + # + # XXX Currently, it succeeds anyway. This flexibility + # should either be documented or loadTestsFromModule() should + # raise a TypeError + # + # XXX Certain people are using this behaviour. We'll add a test for it + def test_loadTestsFromModule__not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(NotAModule) + + reference = [unittest.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + ################################################################ + ### /Tests for TestLoader.loadTestsFromModule() + + ### Tests for TestLoader.loadTestsFromName() + ################################################################ + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromName__empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('') + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the name contains invalid characters? + def test_loadTestsFromName__malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise ValueError or ImportError? + try: + loader.loadTestsFromName('abc () //') + except ValueError: + pass + except ImportError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve ... to a + # module" + # + # What happens when a module by that name can't be found? + def test_loadTestsFromName__unknown_module_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('sdasfasfasdf') + except ImportError as e: + self.assertEqual(str(e), "No module named sdasfasfasdf") + else: + self.fail("TestLoader.loadTestsFromName failed to raise ImportError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module is found, but the attribute can't? + def test_loadTestsFromName__unknown_attr_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('unittest.sdasfasfasdf') + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when we provide the module, but the attribute can't be + # found? + def test_loadTestsFromName__relative_unknown_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('sdasfasfasdf', unittest) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise ValueError when passed an empty + # name relative to a provided module? + # + # XXX Should probably raise a ValueError instead of an AttributeError + def test_loadTestsFromName__relative_empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromName('', unittest) + except AttributeError as e: + pass + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when an impossible name is given, relative to the provided + # `module`? + def test_loadTestsFromName__relative_malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + try: + loader.loadTestsFromName('abc () //', unittest) + except ValueError: + pass + except AttributeError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise ValueError") + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromName raise TypeError when the `module` argument + # isn't a module object? + # + # XXX Accepts the not-a-module object, ignorning the object's type + # This should raise an exception or the method name should be changed + # + # XXX Some people are relying on this, so keep it for now + def test_loadTestsFromName__relative_not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('test_2', NotAModule) + + reference = [MyTestCase('test')] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromName__relative_bad_object(self): + import new + m = new.module('m') + m.testcase_1 = object() + + loader = unittest.TestLoader() + try: + loader.loadTestsFromName('testcase_1', m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may + # resolve either to ... a test case class" + def test_loadTestsFromName__relative_TestCase_subclass(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testcase_1', m) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + def test_loadTestsFromName__relative_TestSuite(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testsuite = unittest.TestSuite([MyTestCase('test')]) + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testsuite', m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test method within a test case class" + def test_loadTestsFromName__relative_testmethod(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('testcase_1.test', m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + self.assertEqual(list(suite), [MyTestCase('test')]) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does loadTestsFromName() raise the proper exception when trying to + # resolve "a test method within a test case class" that doesn't exist + # for the given name (relative to a provided module)? + def test_loadTestsFromName__relative_invalid_testmethod(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + try: + loader.loadTestsFromName('testcase_1.testfoo', m) + except AttributeError as e: + self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'") + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromName__callable__TestSuite(self): + import new + m = new.module('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + testcase_2 = unittest.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('return_TestSuite', m) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), [testcase_1, testcase_2]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromName__callable__TestCase_instance(self): + import new + m = new.module('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromName('return_TestCase', m) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), [testcase_1]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens if the callable returns something else? + def test_loadTestsFromName__callable__wrong_type(self): + import new + m = new.module('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromName('return_wrong', m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromName failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromName__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + # Why pick audioop? Google shows it isn't used very often, so there's + # a good chance that it won't be imported when this test is run + module_name = 'audioop' + + import sys + if module_name in sys.modules: + del sys.modules[module_name] + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromName(module_name) + + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), []) + + # audioop should now be loaded, thanks to loadTestsFromName() + self.failUnless(module_name in sys.modules) + finally: + del sys.modules[module_name] + + ################################################################ + ### Tests for TestLoader.loadTestsFromName() + + ### Tests for TestLoader.loadTestsFromNames() + ################################################################ + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # + # What happens if that sequence of names is empty? + def test_loadTestsFromNames__empty_name_list(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames([]) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), []) + + # "Similar to loadTestsFromName(), but takes a sequence of names rather + # than a single name." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens if that sequence of names is empty? + # + # XXX Should this raise a ValueError or just return an empty TestSuite? + def test_loadTestsFromNames__relative_empty_name_list(self): + loader = unittest.TestLoader() + + suite = loader.loadTestsFromNames([], unittest) + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), []) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Is ValueError raised in response to an empty name? + def test_loadTestsFromNames__empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['']) + except ValueError as e: + self.assertEqual(str(e), "Empty module name") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when presented with an impossible module name? + def test_loadTestsFromNames__malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise ValueError or ImportError? + try: + loader.loadTestsFromNames(['abc () //']) + except ValueError: + pass + except ImportError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when no module can be found for the given name? + def test_loadTestsFromNames__unknown_module_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['sdasfasfasdf']) + except ImportError as e: + self.assertEqual(str(e), "No module named sdasfasfasdf") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ImportError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # What happens when the module can be found, but not the attribute? + def test_loadTestsFromNames__unknown_attr_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['unittest.sdasfasfasdf', 'unittest']) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromNames failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when given an unknown attribute on a specified `module` + # argument? + def test_loadTestsFromNames__unknown_name_relative_1(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['sdasfasfasdf'], unittest) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # Do unknown attributes (relative to a provided module) still raise an + # exception even in the presence of valid attribute names? + def test_loadTestsFromNames__unknown_name_relative_2(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest) + except AttributeError as e: + self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'") + else: + self.fail("TestLoader.loadTestsFromName failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when faced with the empty string? + # + # XXX This currently raises AttributeError, though ValueError is probably + # more appropriate + def test_loadTestsFromNames__relative_empty_name(self): + loader = unittest.TestLoader() + + try: + loader.loadTestsFromNames([''], unittest) + except AttributeError: + pass + else: + self.fail("Failed to raise ValueError") + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # ... + # "The method optionally resolves name relative to the given module" + # + # What happens when presented with an impossible attribute name? + def test_loadTestsFromNames__relative_malformed_name(self): + loader = unittest.TestLoader() + + # XXX Should this raise AttributeError or ValueError? + try: + loader.loadTestsFromNames(['abc () //'], unittest) + except AttributeError: + pass + except ValueError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise ValueError") + + # "The method optionally resolves name relative to the given module" + # + # Does loadTestsFromNames() make sure the provided `module` is in fact + # a module? + # + # XXX This validation is currently not done. This flexibility should + # either be documented or a TypeError should be raised. + def test_loadTestsFromNames__relative_not_a_module(self): + class MyTestCase(unittest.TestCase): + def test(self): + pass + + class NotAModule(object): + test_2 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['test_2'], NotAModule) + + reference = [unittest.TestSuite([MyTestCase('test')])] + self.assertEqual(list(suite), reference) + + # "The specifier name is a ``dotted name'' that may resolve either to + # a module, a test case class, a TestSuite instance, a test method + # within a test case class, or a callable object which returns a + # TestCase or TestSuite instance." + # + # Does it raise an exception if the name resolves to an invalid + # object? + def test_loadTestsFromNames__relative_bad_object(self): + import new + m = new.module('m') + m.testcase_1 = object() + + loader = unittest.TestLoader() + try: + loader.loadTestsFromNames(['testcase_1'], m) + except TypeError: + pass + else: + self.fail("Should have raised TypeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a test case class" + def test_loadTestsFromNames__relative_TestCase_subclass(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + expected = loader.suiteClass([MyTestCase('test')]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a TestSuite instance" + def test_loadTestsFromNames__relative_TestSuite(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testsuite = unittest.TestSuite([MyTestCase('test')]) + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testsuite'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + self.assertEqual(list(suite), [m.testsuite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + def test_loadTestsFromNames__relative_testmethod(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['testcase_1.test'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + ref_suite = unittest.TestSuite([MyTestCase('test')]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to ... a + # test method within a test case class" + # + # Does the method gracefully handle names that initially look like they + # resolve to "a test method within a test case class" but don't? + def test_loadTestsFromNames__relative_invalid_testmethod(self): + import new + m = new.module('m') + class MyTestCase(unittest.TestCase): + def test(self): + pass + m.testcase_1 = MyTestCase + + loader = unittest.TestLoader() + try: + loader.loadTestsFromNames(['testcase_1.testfoo'], m) + except AttributeError as e: + self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'") + else: + self.fail("Failed to raise AttributeError") + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a ... TestSuite instance" + def test_loadTestsFromNames__callable__TestSuite(self): + import new + m = new.module('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + testcase_2 = unittest.FunctionTestCase(lambda: None) + def return_TestSuite(): + return unittest.TestSuite([testcase_1, testcase_2]) + m.return_TestSuite = return_TestSuite + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['return_TestSuite'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + expected = unittest.TestSuite([testcase_1, testcase_2]) + self.assertEqual(list(suite), [expected]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase ... instance" + def test_loadTestsFromNames__callable__TestCase_instance(self): + import new + m = new.module('m') + testcase_1 = unittest.FunctionTestCase(lambda: None) + def return_TestCase(): + return testcase_1 + m.return_TestCase = return_TestCase + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['return_TestCase'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + ref_suite = unittest.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # Are staticmethods handled correctly? + def test_loadTestsFromNames__callable__call_staticmethod(self): + import new + m = new.module('m') + class Test1(unittest.TestCase): + def test(self): + pass + + testcase_1 = Test1('test') + class Foo(unittest.TestCase): + @staticmethod + def foo(): + return testcase_1 + m.Foo = Foo + + loader = unittest.TestLoader() + suite = loader.loadTestsFromNames(['Foo.foo'], m) + self.failUnless(isinstance(suite, loader.suiteClass)) + + ref_suite = unittest.TestSuite([testcase_1]) + self.assertEqual(list(suite), [ref_suite]) + + # "The specifier name is a ``dotted name'' that may resolve ... to + # ... a callable object which returns a TestCase or TestSuite instance" + # + # What happens when the callable returns something else? + def test_loadTestsFromNames__callable__wrong_type(self): + import new + m = new.module('m') + def return_wrong(): + return 6 + m.return_wrong = return_wrong + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromNames(['return_wrong'], m) + except TypeError: + pass + else: + self.fail("TestLoader.loadTestsFromNames failed to raise TypeError") + + # "The specifier can refer to modules and packages which have not been + # imported; they will be imported as a side-effect" + def test_loadTestsFromNames__module_not_loaded(self): + # We're going to try to load this module as a side-effect, so it + # better not be loaded before we try. + # + # Why pick audioop? Google shows it isn't used very often, so there's + # a good chance that it won't be imported when this test is run + module_name = 'audioop' + + import sys + if module_name in sys.modules: + del sys.modules[module_name] + + loader = unittest.TestLoader() + try: + suite = loader.loadTestsFromNames([module_name]) + + self.failUnless(isinstance(suite, loader.suiteClass)) + self.assertEqual(list(suite), [unittest.TestSuite()]) + + # audioop should now be loaded, thanks to loadTestsFromName() + self.failUnless(module_name in sys.modules) + finally: + del sys.modules[module_name] + + ################################################################ + ### /Tests for TestLoader.loadTestsFromNames() + + ### Tests for TestLoader.getTestCaseNames() + ################################################################ + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Test.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames(self): + class Test(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + loader = unittest.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), ['test_1', 'test_2']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Does getTestCaseNames() behave appropriately if no tests are found? + def test_getTestCaseNames__no_tests(self): + class Test(unittest.TestCase): + def foobar(self): pass + + loader = unittest.TestLoader() + + self.assertEqual(loader.getTestCaseNames(Test), []) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Are not-TestCases handled gracefully? + # + # XXX This should raise a TypeError, not return a list + # + # XXX It's too late in the 2.5 release cycle to fix this, but it should + # probably be revisited for 2.6 + def test_getTestCaseNames__not_a_TestCase(self): + class BadCase(int): + def test_foo(self): + pass + + loader = unittest.TestLoader() + names = loader.getTestCaseNames(BadCase) + + self.assertEqual(names, ['test_foo']) + + # "Return a sorted sequence of method names found within testCaseClass" + # + # Make sure inherited names are handled. + # + # TestP.foobar is defined to make sure getTestCaseNames() respects + # loader.testMethodPrefix + def test_getTestCaseNames__inheritance(self): + class TestP(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foobar(self): pass + + class TestC(TestP): + def test_1(self): pass + def test_3(self): pass + + loader = unittest.TestLoader() + + names = ['test_1', 'test_2', 'test_3'] + self.assertEqual(loader.getTestCaseNames(TestC), names) + + ################################################################ + ### /Tests for TestLoader.getTestCaseNames() + + ### Tests for TestLoader.testMethodPrefix + ################################################################ + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests_1 = unittest.TestSuite([Foo('foo_bar')]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromModule(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = [unittest.TestSuite([Foo('foo_bar')])] + tests_2 = [unittest.TestSuite([Foo('test_1'), Foo('test_2')])] + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(list(loader.loadTestsFromModule(m)), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromName(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest.TestSuite([Foo('foo_bar')]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromName('Foo', m), tests_2) + + # "String giving the prefix of method names which will be interpreted as + # test methods" + # + # Implicit in the documentation is that testMethodPrefix is respected by + # all loadTestsFrom* methods. + def test_testMethodPrefix__loadTestsFromNames(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests_1 = unittest.TestSuite([unittest.TestSuite([Foo('foo_bar')])]) + tests_2 = unittest.TestSuite([Foo('test_1'), Foo('test_2')]) + tests_2 = unittest.TestSuite([tests_2]) + + loader = unittest.TestLoader() + loader.testMethodPrefix = 'foo' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_1) + + loader.testMethodPrefix = 'test' + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests_2) + + # "The default value is 'test'" + def test_testMethodPrefix__default_value(self): + loader = unittest.TestLoader() + self.failUnless(loader.testMethodPrefix == 'test') + + ################################################################ + ### /Tests for TestLoader.testMethodPrefix + + ### Tests for TestLoader.sortTestMethodsUsing + ################################################################ + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromTestCase(self): + def reversed_cmp(x, y): + return -cmp(x, y) + + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromModule(self): + def reversed_cmp(x, y): + return -cmp(x, y) + + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromModule(m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromName(self): + def reversed_cmp(x, y): + return -cmp(x, y) + + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = loader.suiteClass([Foo('test_2'), Foo('test_1')]) + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames() and all the loadTestsFromX() methods" + def test_sortTestMethodsUsing__loadTestsFromNames(self): + def reversed_cmp(x, y): + return -cmp(x, y) + + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + m.Foo = Foo + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + tests = [loader.suiteClass([Foo('test_2'), Foo('test_1')])] + self.assertEqual(list(loader.loadTestsFromNames(['Foo'], m)), tests) + + # "Function to be used to compare method names when sorting them in + # getTestCaseNames()" + # + # Does it actually affect getTestCaseNames()? + def test_sortTestMethodsUsing__getTestCaseNames(self): + def reversed_cmp(x, y): + return -cmp(x, y) + + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = reversed_cmp + + test_names = ['test_2', 'test_1'] + self.assertEqual(loader.getTestCaseNames(Foo), test_names) + + # "The default value is the built-in cmp() function" + def test_sortTestMethodsUsing__default_value(self): + loader = unittest.TestLoader() + self.failUnless(loader.sortTestMethodsUsing is cmp) + + # "it can be set to None to disable the sort." + # + # XXX How is this different from reassigning cmp? Are the tests returned + # in a random order or something? This behaviour should die + def test_sortTestMethodsUsing__None(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = None + + test_names = ['test_2', 'test_1'] + self.assertEqual(set(loader.getTestCaseNames(Foo)), set(test_names)) + + ################################################################ + ### /Tests for TestLoader.sortTestMethodsUsing + + ### Tests for TestLoader.suiteClass + ################################################################ + + # "Callable object that constructs a test suite from a list of tests." + def test_suiteClass__loadTestsFromTestCase(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromTestCase(Foo), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromModule(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromModule(m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromName(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [Foo('test_1'), Foo('test_2')] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromName('Foo', m), tests) + + # It is implicit in the documentation for TestLoader.suiteClass that + # all TestLoader.loadTestsFrom* methods respect it. Let's make sure + def test_suiteClass__loadTestsFromNames(self): + import new + m = new.module('m') + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def foo_bar(self): pass + m.Foo = Foo + + tests = [[Foo('test_1'), Foo('test_2')]] + + loader = unittest.TestLoader() + loader.suiteClass = list + self.assertEqual(loader.loadTestsFromNames(['Foo'], m), tests) + + # "The default value is the TestSuite class" + def test_suiteClass__default_value(self): + loader = unittest.TestLoader() + self.failUnless(loader.suiteClass is unittest.TestSuite) + + ################################################################ + ### /Tests for TestLoader.suiteClass + +### Support code for Test_TestSuite +################################################################ + +class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + def test_3(self): pass + def runTest(self): pass + +def _mk_TestSuite(*names): + return unittest.TestSuite(Foo(n) for n in names) + +################################################################ +### /Support code for Test_TestSuite + +class Test_TestSuite(TestCase, TestEquality): + + ### Set up attributes needed by inherited tests + ################################################################ + + # Used by TestEquality.test_eq + eq_pairs = [(unittest.TestSuite(), unittest.TestSuite()) + ,(unittest.TestSuite(), unittest.TestSuite([])) + ,(_mk_TestSuite('test_1'), _mk_TestSuite('test_1'))] + + # Used by TestEquality.test_ne + ne_pairs = [(unittest.TestSuite(), _mk_TestSuite('test_1')) + ,(unittest.TestSuite([]), _mk_TestSuite('test_1')) + ,(_mk_TestSuite('test_1', 'test_2'), _mk_TestSuite('test_1', 'test_3')) + ,(_mk_TestSuite('test_1'), _mk_TestSuite('test_2'))] + + ################################################################ + ### /Set up attributes needed by inherited tests + + ### Tests for TestSuite.__init__ + ################################################################ + + # "class TestSuite([tests])" + # + # The tests iterable should be optional + def test_init__tests_optional(self): + suite = unittest.TestSuite() + + self.assertEqual(suite.countTestCases(), 0) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # TestSuite should deal with empty tests iterables by allowing the + # creation of an empty suite + def test_init__empty_tests(self): + suite = unittest.TestSuite([]) + + self.assertEqual(suite.countTestCases(), 0) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # TestSuite should allow any iterable to provide tests + def test_init__tests_from_any_iterable(self): + def tests(): + yield unittest.FunctionTestCase(lambda: None) + yield unittest.FunctionTestCase(lambda: None) + + suite_1 = unittest.TestSuite(tests()) + self.assertEqual(suite_1.countTestCases(), 2) + + suite_2 = unittest.TestSuite(suite_1) + self.assertEqual(suite_2.countTestCases(), 2) + + suite_3 = unittest.TestSuite(set(suite_1)) + self.assertEqual(suite_3.countTestCases(), 2) + + # "class TestSuite([tests])" + # ... + # "If tests is given, it must be an iterable of individual test cases + # or other test suites that will be used to build the suite initially" + # + # Does TestSuite() also allow other TestSuite() instances to be present + # in the tests iterable? + def test_init__TestSuite_instances_in_tests(self): + def tests(): + ftc = unittest.FunctionTestCase(lambda: None) + yield unittest.TestSuite([ftc]) + yield unittest.FunctionTestCase(lambda: None) + + suite = unittest.TestSuite(tests()) + self.assertEqual(suite.countTestCases(), 2) + + ################################################################ + ### /Tests for TestSuite.__init__ + + # Container types should support the iter protocol + def test_iter(self): + test1 = unittest.FunctionTestCase(lambda: None) + test2 = unittest.FunctionTestCase(lambda: None) + suite = unittest.TestSuite((test1, test2)) + + self.assertEqual(list(suite), [test1, test2]) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Presumably an empty TestSuite returns 0? + def test_countTestCases_zero_simple(self): + suite = unittest.TestSuite() + + self.assertEqual(suite.countTestCases(), 0) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Presumably an empty TestSuite (even if it contains other empty + # TestSuite instances) returns 0? + def test_countTestCases_zero_nested(self): + class Test1(unittest.TestCase): + def test(self): + pass + + suite = unittest.TestSuite([unittest.TestSuite()]) + + self.assertEqual(suite.countTestCases(), 0) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + def test_countTestCases_simple(self): + test1 = unittest.FunctionTestCase(lambda: None) + test2 = unittest.FunctionTestCase(lambda: None) + suite = unittest.TestSuite((test1, test2)) + + self.assertEqual(suite.countTestCases(), 2) + + # "Return the number of tests represented by the this test object. + # ...this method is also implemented by the TestSuite class, which can + # return larger [greater than 1] values" + # + # Make sure this holds for nested TestSuite instances, too + def test_countTestCases_nested(self): + class Test1(unittest.TestCase): + def test1(self): pass + def test2(self): pass + + test2 = unittest.FunctionTestCase(lambda: None) + test3 = unittest.FunctionTestCase(lambda: None) + child = unittest.TestSuite((Test1('test2'), test2)) + parent = unittest.TestSuite((test3, child, Test1('test1'))) + + self.assertEqual(parent.countTestCases(), 4) + + # "Run the tests associated with this suite, collecting the result into + # the test result object passed as result." + # + # And if there are no tests? What then? + def test_run__empty_suite(self): + events = [] + result = LoggingResult(events) + + suite = unittest.TestSuite() + + suite.run(result) + + self.assertEqual(events, []) + + # "Note that unlike TestCase.run(), TestSuite.run() requires the + # "result object to be passed in." + def test_run__requires_result(self): + suite = unittest.TestSuite() + + try: + suite.run() + except TypeError: + pass + else: + self.fail("Failed to raise TypeError") + + # "Run the tests associated with this suite, collecting the result into + # the test result object passed as result." + def test_run(self): + events = [] + result = LoggingResult(events) + + class LoggingCase(unittest.TestCase): + def run(self, result): + events.append('run %s' % self._testMethodName) + + def test1(self): pass + def test2(self): pass + + tests = [LoggingCase('test1'), LoggingCase('test2')] + + unittest.TestSuite(tests).run(result) + + self.assertEqual(events, ['run test1', 'run test2']) + + # "Add a TestCase ... to the suite" + def test_addTest__TestCase(self): + class Foo(unittest.TestCase): + def test(self): pass + + test = Foo('test') + suite = unittest.TestSuite() + + suite.addTest(test) + + self.assertEqual(suite.countTestCases(), 1) + self.assertEqual(list(suite), [test]) + + # "Add a ... TestSuite to the suite" + def test_addTest__TestSuite(self): + class Foo(unittest.TestCase): + def test(self): pass + + suite_2 = unittest.TestSuite([Foo('test')]) + + suite = unittest.TestSuite() + suite.addTest(suite_2) + + self.assertEqual(suite.countTestCases(), 1) + self.assertEqual(list(suite), [suite_2]) + + # "Add all the tests from an iterable of TestCase and TestSuite + # instances to this test suite." + # + # "This is equivalent to iterating over tests, calling addTest() for + # each element" + def test_addTests(self): + class Foo(unittest.TestCase): + def test_1(self): pass + def test_2(self): pass + + test_1 = Foo('test_1') + test_2 = Foo('test_2') + inner_suite = unittest.TestSuite([test_2]) + + def gen(): + yield test_1 + yield test_2 + yield inner_suite + + suite_1 = unittest.TestSuite() + suite_1.addTests(gen()) + + self.assertEqual(list(suite_1), list(gen())) + + # "This is equivalent to iterating over tests, calling addTest() for + # each element" + suite_2 = unittest.TestSuite() + for t in gen(): + suite_2.addTest(t) + + self.assertEqual(suite_1, suite_2) + + # "Add all the tests from an iterable of TestCase and TestSuite + # instances to this test suite." + # + # What happens if it doesn't get an iterable? + def test_addTest__noniterable(self): + suite = unittest.TestSuite() + + try: + suite.addTests(5) + except TypeError: + pass + else: + self.fail("Failed to raise TypeError") + + def test_addTest__noncallable(self): + suite = unittest.TestSuite() + self.assertRaises(TypeError, suite.addTest, 5) + + def test_addTest__casesuiteclass(self): + suite = unittest.TestSuite() + self.assertRaises(TypeError, suite.addTest, Test_TestSuite) + self.assertRaises(TypeError, suite.addTest, unittest.TestSuite) + + def test_addTests__string(self): + suite = unittest.TestSuite() + self.assertRaises(TypeError, suite.addTests, "foo") + + +class Test_FunctionTestCase(TestCase): + + # "Return the number of tests represented by the this test object. For + # TestCase instances, this will always be 1" + def test_countTestCases(self): + test = unittest.FunctionTestCase(lambda: None) + + self.assertEqual(test.countTestCases(), 1) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if setUp() raises + # an exception. + def test_run_call_order__error_in_setUp(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + raise RuntimeError('raised by setUp') + + def test(): + events.append('test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'addError', 'stopTest'] + unittest.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test raises + # an error (as opposed to a failure). + def test_run_call_order__error_in_test(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + raise RuntimeError('raised by test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown', + 'stopTest'] + unittest.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test signals + # a failure (as opposed to an error). + def test_run_call_order__failure_in_test(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + self.fail('raised by test') + + def tearDown(): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown', + 'stopTest'] + unittest.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if tearDown() raises + # an exception. + def test_run_call_order__error_in_tearDown(self): + events = [] + result = LoggingResult(events) + + def setUp(): + events.append('setUp') + + def test(): + events.append('test') + + def tearDown(): + events.append('tearDown') + raise RuntimeError('raised by tearDown') + + expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError', + 'stopTest'] + unittest.FunctionTestCase(test, setUp, tearDown).run(result) + self.assertEqual(events, expected) + + # "Return a string identifying the specific test case." + # + # Because of the vague nature of the docs, I'm not going to lock this + # test down too much. Really all that can be asserted is that the id() + # will be a string (either 8-byte or unicode -- again, because the docs + # just say "string") + def test_id(self): + test = unittest.FunctionTestCase(lambda: None) + + self.failUnless(isinstance(test.id(), basestring)) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__no_docstring(self): + test = unittest.FunctionTestCase(lambda: None) + + self.assertEqual(test.shortDescription(), None) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__singleline_docstring(self): + desc = "this tests foo" + test = unittest.FunctionTestCase(lambda: None, description=desc) + + self.assertEqual(test.shortDescription(), "this tests foo") + +class Test_TestResult(TestCase): + # Note: there are not separate tests for TestResult.wasSuccessful(), + # TestResult.errors, TestResult.failures, TestResult.testsRun or + # TestResult.shouldStop because these only have meaning in terms of + # other TestResult methods. + # + # Accordingly, tests for the aforenamed attributes are incorporated + # in with the tests for the defining methods. + ################################################################ + + def test_init(self): + result = unittest.TestResult() + + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 0) + self.assertEqual(result.shouldStop, False) + + # "This method can be called to signal that the set of tests being + # run should be aborted by setting the TestResult's shouldStop + # attribute to True." + def test_stop(self): + result = unittest.TestResult() + + result.stop() + + self.assertEqual(result.shouldStop, True) + + # "Called when the test case test is about to be run. The default + # implementation simply increments the instance's testsRun counter." + def test_startTest(self): + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest.TestResult() + + result.startTest(test) + + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + result.stopTest(test) + + # "Called after the test case test has been executed, regardless of + # the outcome. The default implementation does nothing." + def test_stopTest(self): + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest.TestResult() + + result.startTest(test) + + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + result.stopTest(test) + + # Same tests as above; make sure nothing has changed + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + # "addSuccess(test)" + # ... + # "Called when the test case test succeeds" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addSuccess(self): + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest.TestResult() + + result.startTest(test) + result.addSuccess(test) + result.stopTest(test) + + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + # "addFailure(test, err)" + # ... + # "Called when the test case test signals a failure. err is a tuple of + # the form returned by sys.exc_info(): (type, value, traceback)" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addFailure(self): + import sys + + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + try: + test.fail("foo") + except: + exc_info_tuple = sys.exc_info() + + result = unittest.TestResult() + + result.startTest(test) + result.addFailure(test, exc_info_tuple) + result.stopTest(test) + + self.failIf(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 1) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + test_case, formatted_exc = result.failures[0] + self.failUnless(test_case is test) + self.failUnless(isinstance(formatted_exc, str)) + + # "addError(test, err)" + # ... + # "Called when the test case test raises an unexpected exception err + # is a tuple of the form returned by sys.exc_info(): + # (type, value, traceback)" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False" + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + def test_addError(self): + import sys + + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + try: + raise TypeError() + except: + exc_info_tuple = sys.exc_info() + + result = unittest.TestResult() + + result.startTest(test) + result.addError(test, exc_info_tuple) + result.stopTest(test) + + self.failIf(result.wasSuccessful()) + self.assertEqual(len(result.errors), 1) + self.assertEqual(len(result.failures), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + test_case, formatted_exc = result.errors[0] + self.failUnless(test_case is test) + self.failUnless(isinstance(formatted_exc, str)) + +### Support code for Test_TestCase +################################################################ + +class Foo(unittest.TestCase): + def runTest(self): pass + def test1(self): pass + +class Bar(Foo): + def test2(self): pass + +################################################################ +### /Support code for Test_TestCase + +class Test_TestCase(TestCase, TestEquality, TestHashing): + + ### Set up attributes used by inherited tests + ################################################################ + + # Used by TestHashing.test_hash and TestEquality.test_eq + eq_pairs = [(Foo('test1'), Foo('test1'))] + + # Used by TestEquality.test_ne + ne_pairs = [(Foo('test1'), Foo('runTest')) + ,(Foo('test1'), Bar('test1')) + ,(Foo('test1'), Bar('test2'))] + + ################################################################ + ### /Set up attributes used by inherited tests + + + # "class TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + # ... + # "methodName defaults to "runTest"." + # + # Make sure it really is optional, and that it defaults to the proper + # thing. + def test_init__no_test_name(self): + class Test(unittest.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + self.assertEqual(Test().id()[-13:], '.Test.runTest') + + # "class TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + def test_init__test_name__valid(self): + class Test(unittest.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + self.assertEqual(Test('test').id()[-10:], '.Test.test') + + # "class TestCase([methodName])" + # ... + # "Each instance of TestCase will run a single test method: the + # method named methodName." + def test_init__test_name__invalid(self): + class Test(unittest.TestCase): + def runTest(self): raise MyException() + def test(self): pass + + try: + Test('testfoo') + except ValueError: + pass + else: + self.fail("Failed to raise ValueError") + + # "Return the number of tests represented by the this test object. For + # TestCase instances, this will always be 1" + def test_countTestCases(self): + class Foo(unittest.TestCase): + def test(self): pass + + self.assertEqual(Foo('test').countTestCases(), 1) + + # "Return the default type of test result object to be used to run this + # test. For TestCase instances, this will always be + # unittest.TestResult; subclasses of TestCase should + # override this as necessary." + def test_defaultTestResult(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + result = Foo().defaultTestResult() + self.assertEqual(type(result), unittest.TestResult) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if setUp() raises + # an exception. + def test_run_call_order__error_in_setUp(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def setUp(self): + events.append('setUp') + raise RuntimeError('raised by Foo.setUp') + + def test(self): + events.append('test') + + def tearDown(self): + events.append('tearDown') + + Foo('test').run(result) + expected = ['startTest', 'setUp', 'addError', 'stopTest'] + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test raises + # an error (as opposed to a failure). + def test_run_call_order__error_in_test(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def setUp(self): + events.append('setUp') + + def test(self): + events.append('test') + raise RuntimeError('raised by Foo.test') + + def tearDown(self): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown', + 'stopTest'] + Foo('test').run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if the test signals + # a failure (as opposed to an error). + def test_run_call_order__failure_in_test(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def setUp(self): + events.append('setUp') + + def test(self): + events.append('test') + self.fail('raised by Foo.test') + + def tearDown(self): + events.append('tearDown') + + expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown', + 'stopTest'] + Foo('test').run(result) + self.assertEqual(events, expected) + + # "When a setUp() method is defined, the test runner will run that method + # prior to each test. Likewise, if a tearDown() method is defined, the + # test runner will invoke that method after each test. In the example, + # setUp() was used to create a fresh sequence for each test." + # + # Make sure the proper call order is maintained, even if tearDown() raises + # an exception. + def test_run_call_order__error_in_tearDown(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def setUp(self): + events.append('setUp') + + def test(self): + events.append('test') + + def tearDown(self): + events.append('tearDown') + raise RuntimeError('raised by Foo.tearDown') + + Foo('test').run(result) + expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError', + 'stopTest'] + self.assertEqual(events, expected) + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework. The initial value of this + # attribute is AssertionError" + def test_failureException__default(self): + class Foo(unittest.TestCase): + def test(self): + pass + + self.failUnless(Foo('test').failureException is AssertionError) + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework." + # + # Make sure TestCase.run() respects the designated failureException + def test_failureException__subclassing__explicit_raise(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def test(self): + raise RuntimeError() + + failureException = RuntimeError + + self.failUnless(Foo('test').failureException is RuntimeError) + + + Foo('test').run(result) + expected = ['startTest', 'addFailure', 'stopTest'] + self.assertEqual(events, expected) + + # "This class attribute gives the exception raised by the test() method. + # If a test framework needs to use a specialized exception, possibly to + # carry additional information, it must subclass this exception in + # order to ``play fair'' with the framework." + # + # Make sure TestCase.run() respects the designated failureException + def test_failureException__subclassing__implicit_raise(self): + events = [] + result = LoggingResult(events) + + class Foo(unittest.TestCase): + def test(self): + self.fail("foo") + + failureException = RuntimeError + + self.failUnless(Foo('test').failureException is RuntimeError) + + + Foo('test').run(result) + expected = ['startTest', 'addFailure', 'stopTest'] + self.assertEqual(events, expected) + + # "The default implementation does nothing." + def test_setUp(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + # ... and nothing should happen + Foo().setUp() + + # "The default implementation does nothing." + def test_tearDown(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + # ... and nothing should happen + Foo().tearDown() + + # "Return a string identifying the specific test case." + # + # Because of the vague nature of the docs, I'm not going to lock this + # test down too much. Really all that can be asserted is that the id() + # will be a string (either 8-byte or unicode -- again, because the docs + # just say "string") + def test_id(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + self.failUnless(isinstance(Foo().id(), basestring)) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__no_docstring(self): + class Foo(unittest.TestCase): + def runTest(self): + pass + + self.assertEqual(Foo().shortDescription(), None) + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__singleline_docstring(self): + class Foo(unittest.TestCase): + def runTest(self): + "this tests foo" + pass + + self.assertEqual(Foo().shortDescription(), "this tests foo") + + # "Returns a one-line description of the test, or None if no description + # has been provided. The default implementation of this method returns + # the first line of the test method's docstring, if available, or None." + def test_shortDescription__multiline_docstring(self): + class Foo(unittest.TestCase): + def runTest(self): + """this tests foo + blah, bar and baz are also tested""" + pass + + self.assertEqual(Foo().shortDescription(), "this tests foo") + + # "If result is omitted or None, a temporary result object is created + # and used, but is not made available to the caller" + def test_run__uses_defaultTestResult(self): + events = [] + + class Foo(unittest.TestCase): + def test(self): + events.append('test') + + def defaultTestResult(self): + return LoggingResult(events) -def test_TestSuite_iter(): - """ - >>> test1 = unittest.FunctionTestCase(lambda: None) - >>> test2 = unittest.FunctionTestCase(lambda: None) - >>> suite = unittest.TestSuite((test1, test2)) - >>> tests = [] - >>> for test in suite: - ... tests.append(test) - >>> tests == [test1, test2] - True - """ + # Make run() find a result object on its own + Foo('test').run() + expected = ['startTest', 'test', 'stopTest'] + self.assertEqual(events, expected) ###################################################################### ## Main ###################################################################### def test_main(): - from test import test_support, test_unittest - test_support.run_doctest(test_unittest, verbosity=True) + test_support.run_unittest(Test_TestCase, Test_TestLoader, + Test_TestSuite, Test_TestResult, Test_FunctionTestCase) -if __name__ == '__main__': +if __name__ == "__main__": test_main() diff --git a/Lib/test/test_unpack.py b/Lib/test/test_unpack.py index cd48689..75033ed 100644 --- a/Lib/test/test_unpack.py +++ b/Lib/test/test_unpack.py @@ -55,7 +55,7 @@ Unpacking non-sequence >>> a, b, c = 7 Traceback (most recent call last): ... - TypeError: unpack non-sequence + TypeError: 'int' object is not iterable Unpacking tuple of wrong size diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 16c612e..3a37525 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -122,6 +122,15 @@ class urlopen_HttpTests(unittest.TestCase): finally: self.unfakehttp() + def test_empty_socket(self): + """urlopen() raises IOError if the underlying socket does not send any + data. (#1680230) """ + self.fakehttp('') + try: + self.assertRaises(IOError, urllib.urlopen, 'http://something') + finally: + self.unfakehttp() + class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 6187dad..10d8c46 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -625,11 +625,11 @@ class HandlerTests(unittest.TestCase): for url in [ "file://localhost:80%s" % urlpath, -# XXXX bug: these fail with socket.gaierror, should be URLError -## "file://%s:80%s/%s" % (socket.gethostbyname('localhost'), -## os.getcwd(), TESTFN), -## "file://somerandomhost.ontheinternet.com%s/%s" % -## (os.getcwd(), TESTFN), + "file:///file_does_not_exist.txt", + "file://%s:80%s/%s" % (socket.gethostbyname('localhost'), + os.getcwd(), TESTFN), + "file://somerandomhost.ontheinternet.com%s/%s" % + (os.getcwd(), TESTFN), ]: try: f = open(TESTFN, "wb") @@ -765,16 +765,24 @@ class HandlerTests(unittest.TestCase): url = "http://example.com/" req = Request(url) - # 200 OK is passed through + # all 2xx are passed through r = MockResponse(200, "OK", {}, "", url) newr = h.http_response(req, r) self.assert_(r is newr) self.assert_(not hasattr(o, "proto")) # o.error not called + r = MockResponse(202, "Accepted", {}, "", url) + newr = h.http_response(req, r) + self.assert_(r is newr) + self.assert_(not hasattr(o, "proto")) # o.error not called + r = MockResponse(206, "Partial content", {}, "", url) + newr = h.http_response(req, r) + self.assert_(r is newr) + self.assert_(not hasattr(o, "proto")) # o.error not called # anything else calls o.error (and MockOpener returns None, here) - r = MockResponse(201, "Created", {}, "", url) + r = MockResponse(502, "Bad gateway", {}, "", url) self.assert_(h.http_response(req, r) is None) self.assertEqual(o.proto, "http") # o.error called - self.assertEqual(o.args, (req, r, 201, "Created", {})) + self.assertEqual(o.args, (req, r, 502, "Bad gateway", {})) def test_cookies(self): cj = MockCookieJar() diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 60d5f48..a52c3dd 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -264,7 +264,8 @@ class OtherNetworkTests(unittest.TestCase): (expected_err, url, req, err)) self.assert_(isinstance(err, expected_err), msg) else: - buf = f.read() + with test_support.transient_internet(): + buf = f.read() f.close() debug("read %d bytes" % len(buf)) debug("******** next url coming up...") diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 500971b..8c72463 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -171,7 +171,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): except RuntimeError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("e[42] didn't raise RuntimeError") + self.fail("e[42] didn't raise RuntimeError") class F(UserDict.UserDict): def __init__(self): # An instance variable __missing__ should have no effect @@ -183,7 +183,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): except KeyError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("f[42] didn't raise KeyError") + self.fail("f[42] didn't raise KeyError") class G(UserDict.UserDict): pass g = G() @@ -192,7 +192,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): except KeyError as err: self.assertEqual(err.args, (42,)) else: - self.fail_("g[42] didn't raise KeyError") + self.fail("g[42] didn't raise KeyError") ########################## # Test Dict Mixin diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index a7ccb6b..283806f 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -3,95 +3,97 @@ import os import unittest from test import test_support -# The warnings module isn't easily tested, because it relies on module -# globals to store configuration information. setUp() and tearDown() -# preserve the current settings to avoid bashing them while running tests. - -# To capture the warning messages, a replacement for showwarning() is -# used to save warning information in a global variable. - -class WarningMessage: - "Holds results of latest showwarning() call" - pass - -def showwarning(message, category, filename, lineno, file=None): - msg.message = str(message) - msg.category = category.__name__ - msg.filename = os.path.basename(filename) - msg.lineno = lineno +import warning_tests class TestModule(unittest.TestCase): - def setUp(self): - global msg - msg = WarningMessage() - self._filters = warnings.filters[:] - self._showwarning = warnings.showwarning - warnings.showwarning = showwarning - self.ignored = [w[2].__name__ for w in self._filters + self.ignored = [w[2].__name__ for w in warnings.filters if w[0]=='ignore' and w[1] is None and w[3] is None] - def tearDown(self): - warnings.filters = self._filters[:] - warnings.showwarning = self._showwarning - def test_warn_default_category(self): - for i in range(4): - text = 'multi %d' %i # Different text on each call - warnings.warn(text) - self.assertEqual(msg.message, text) - self.assertEqual(msg.category, 'UserWarning') + with test_support.catch_warning() as w: + for i in range(4): + text = 'multi %d' %i # Different text on each call + warnings.warn(text) + self.assertEqual(str(w.message), text) + self.assert_(w.category is UserWarning) def test_warn_specific_category(self): - text = 'None' - for category in [DeprecationWarning, FutureWarning, - PendingDeprecationWarning, RuntimeWarning, - SyntaxWarning, UserWarning, Warning]: - if category.__name__ in self.ignored: - text = 'filtered out' + category.__name__ - warnings.warn(text, category) - self.assertNotEqual(msg.message, text) - else: - text = 'unfiltered %s' % category.__name__ - warnings.warn(text, category) - self.assertEqual(msg.message, text) - self.assertEqual(msg.category, category.__name__) + with test_support.catch_warning() as w: + text = 'None' + for category in [DeprecationWarning, FutureWarning, + PendingDeprecationWarning, RuntimeWarning, + SyntaxWarning, UserWarning, Warning]: + if category.__name__ in self.ignored: + text = 'filtered out' + category.__name__ + warnings.warn(text, category) + self.assertNotEqual(w.message, text) + else: + text = 'unfiltered %s' % category.__name__ + warnings.warn(text, category) + self.assertEqual(str(w.message), text) + self.assert_(w.category is category) def test_filtering(self): + with test_support.catch_warning() as w: + warnings.filterwarnings("error", "", Warning, "", 0) + self.assertRaises(UserWarning, warnings.warn, 'convert to error') - warnings.filterwarnings("error", "", Warning, "", 0) - self.assertRaises(UserWarning, warnings.warn, 'convert to error') - - warnings.resetwarnings() - text = 'handle normally' - warnings.warn(text) - self.assertEqual(msg.message, text) - self.assertEqual(msg.category, 'UserWarning') + warnings.resetwarnings() + text = 'handle normally' + warnings.warn(text) + self.assertEqual(str(w.message), text) + self.assert_(w.category is UserWarning) - warnings.filterwarnings("ignore", "", Warning, "", 0) - text = 'filtered out' - warnings.warn(text) - self.assertNotEqual(msg.message, text) + warnings.filterwarnings("ignore", "", Warning, "", 0) + text = 'filtered out' + warnings.warn(text) + self.assertNotEqual(str(w.message), text) - warnings.resetwarnings() - warnings.filterwarnings("error", "hex*", Warning, "", 0) - self.assertRaises(UserWarning, warnings.warn, 'hex/oct') - text = 'nonmatching text' - warnings.warn(text) - self.assertEqual(msg.message, text) - self.assertEqual(msg.category, 'UserWarning') + warnings.resetwarnings() + warnings.filterwarnings("error", "hex*", Warning, "", 0) + self.assertRaises(UserWarning, warnings.warn, 'hex/oct') + text = 'nonmatching text' + warnings.warn(text) + self.assertEqual(str(w.message), text) + self.assert_(w.category is UserWarning) def test_options(self): # Uses the private _setoption() function to test the parsing # of command-line warning arguments - self.assertRaises(warnings._OptionError, - warnings._setoption, '1:2:3:4:5:6') - self.assertRaises(warnings._OptionError, - warnings._setoption, 'bogus::Warning') - self.assertRaises(warnings._OptionError, - warnings._setoption, 'ignore:2::4:-5') - warnings._setoption('error::Warning::0') - self.assertRaises(UserWarning, warnings.warn, 'convert to error') + with test_support.guard_warnings_filter(): + self.assertRaises(warnings._OptionError, + warnings._setoption, '1:2:3:4:5:6') + self.assertRaises(warnings._OptionError, + warnings._setoption, 'bogus::Warning') + self.assertRaises(warnings._OptionError, + warnings._setoption, 'ignore:2::4:-5') + warnings._setoption('error::Warning::0') + self.assertRaises(UserWarning, warnings.warn, 'convert to error') + + def test_filename(self): + with test_support.catch_warning() as w: + warning_tests.inner("spam1") + self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + warning_tests.outer("spam2") + self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + + def test_stacklevel(self): + # Test stacklevel argument + # make sure all messages are different, so the warning won't be skipped + with test_support.catch_warning() as w: + warning_tests.inner("spam3", stacklevel=1) + self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + warning_tests.outer("spam4", stacklevel=1) + self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + + warning_tests.inner("spam5", stacklevel=2) + self.assertEqual(os.path.basename(w.filename), "test_warnings.py") + warning_tests.outer("spam6", stacklevel=2) + self.assertEqual(os.path.basename(w.filename), "warning_tests.py") + + warning_tests.inner("spam7", stacklevel=9999) + self.assertEqual(os.path.basename(w.filename), "sys") def test_main(verbose=None): diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 46dd5ff..99a5178a 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -841,7 +841,7 @@ class MappingTestCase(TestBase): items = dict.items() for item in dict.items(): items.remove(item) - self.assert_(len(items) == 0, "iteritems() did not touch all items") + self.assert_(len(items) == 0, "items() did not touch all items") # key iterator, via __iter__(): keys = list(dict.keys()) @@ -1104,7 +1104,7 @@ None ... self.__counter += 1 ... ob = (ob, self.__counter) ... return ob -... +... >>> class A: # not in docs from here, just testing the ExtendedRef ... pass ... diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index d77e861..213c5cf 100755 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -1,5 +1,5 @@ from __future__ import nested_scopes # Backward compat for 2.1 -from unittest import TestSuite, TestCase, makeSuite +from unittest import TestCase from wsgiref.util import setup_testing_defaults from wsgiref.headers import Headers from wsgiref.handlers import BaseHandler, BaseCGIHandler @@ -11,6 +11,7 @@ from StringIO import StringIO from SocketServer import BaseServer import re, sys +from test import test_support class MockServer(WSGIServer): """Non-socket HTTP server""" @@ -575,11 +576,7 @@ class HandlerTests(TestCase): # This epilogue is needed for compatibility with the Python 2.5 regrtest module def test_main(): - import unittest - from test.test_support import run_suite - run_suite( - unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]) - ) + test_support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index d6f5534..a6500da 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -4,26 +4,30 @@ try: except ImportError: zlib = None -import zipfile, os, unittest, sys, shutil +import zipfile, os, unittest, sys, shutil, struct from StringIO import StringIO from tempfile import TemporaryFile +from random import randint, random +import test.test_support as support from test.test_support import TESTFN, run_unittest TESTFN2 = TESTFN + "2" +FIXEDTEST_SIZE = 10 class TestsWithSourceFile(unittest.TestCase): def setUp(self): - line_gen = ("Test of zipfile line %d." % i for i in range(0, 1000)) - self.data = '\n'.join(line_gen) + self.line_gen = ("Zipfile test line %d. random float: %f" % (i, random()) + for i in xrange(FIXEDTEST_SIZE)) + self.data = '\n'.join(self.line_gen) + '\n' # Make a source file with some lines fp = open(TESTFN, "wb") fp.write(self.data) fp.close() - def zipTest(self, f, compression): + def makeTestArchive(self, f, compression): # Create the ZIP archive zipfp = zipfile.ZipFile(f, "w", compression) zipfp.write(TESTFN, "another"+os.extsep+"name") @@ -31,6 +35,9 @@ class TestsWithSourceFile(unittest.TestCase): zipfp.writestr("strfile", self.data) zipfp.close() + def zipTest(self, f, compression): + self.makeTestArchive(f, compression) + # Read the ZIP archive zipfp = zipfile.ZipFile(f, "r", compression) self.assertEqual(zipfp.read(TESTFN), self.data) @@ -85,22 +92,144 @@ class TestsWithSourceFile(unittest.TestCase): # Check that testzip doesn't raise an exception zipfp.testzip() + zipfp.close() + def testStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipTest(f, zipfile.ZIP_STORED) + + def zipOpenTest(self, f, compression): + self.makeTestArchive(f, compression) + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r", compression) + zipdata1 = [] + zipopen1 = zipfp.open(TESTFN) + while 1: + read_data = zipopen1.read(256) + if not read_data: + break + zipdata1.append(read_data) + + zipdata2 = [] + zipopen2 = zipfp.open("another"+os.extsep+"name") + while 1: + read_data = zipopen2.read(256) + if not read_data: + break + zipdata2.append(read_data) + + self.assertEqual(''.join(zipdata1), self.data) + self.assertEqual(''.join(zipdata2), self.data) zipfp.close() + def testOpenStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipOpenTest(f, zipfile.ZIP_STORED) + def zipRandomOpenTest(self, f, compression): + self.makeTestArchive(f, compression) + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r", compression) + zipdata1 = [] + zipopen1 = zipfp.open(TESTFN) + while 1: + read_data = zipopen1.read(randint(1, 1024)) + if not read_data: + break + zipdata1.append(read_data) + + self.assertEqual(''.join(zipdata1), self.data) + zipfp.close() - def testStored(self): + def testRandomOpenStored(self): for f in (TESTFN2, TemporaryFile(), StringIO()): - self.zipTest(f, zipfile.ZIP_STORED) + self.zipRandomOpenTest(f, zipfile.ZIP_STORED) + + def zipReadlineTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + zipopen = zipfp.open(TESTFN) + for line in self.line_gen: + linedata = zipopen.readline() + self.assertEqual(linedata, line + '\n') + + zipfp.close() + + def zipReadlinesTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + ziplines = zipfp.open(TESTFN).readlines() + for line, zipline in zip(self.line_gen, ziplines): + self.assertEqual(zipline, line + '\n') + + zipfp.close() + + def zipIterlinesTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + for line, zipline in zip(self.line_gen, zipfp.open(TESTFN)): + self.assertEqual(zipline, line + '\n') + + zipfp.close() + + def testReadlineStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipReadlineTest(f, zipfile.ZIP_STORED) + + def testReadlinesStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipReadlinesTest(f, zipfile.ZIP_STORED) + + def testIterlinesStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipIterlinesTest(f, zipfile.ZIP_STORED) if zlib: def testDeflated(self): for f in (TESTFN2, TemporaryFile(), StringIO()): self.zipTest(f, zipfile.ZIP_DEFLATED) + def testOpenDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipOpenTest(f, zipfile.ZIP_DEFLATED) + + def testRandomOpenDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipRandomOpenTest(f, zipfile.ZIP_DEFLATED) + + def testReadlineDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipReadlineTest(f, zipfile.ZIP_DEFLATED) + + def testReadlinesDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipReadlinesTest(f, zipfile.ZIP_DEFLATED) + + def testIterlinesDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipIterlinesTest(f, zipfile.ZIP_DEFLATED) + + def testLowCompression(self): + # Checks for cases where compressed data is larger than original + # Create the ZIP archive + zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_DEFLATED) + zipfp.writestr("strfile", '12') + zipfp.close() + + # Get an open object for strfile + zipfp = zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_DEFLATED) + openobj = zipfp.open("strfile") + self.assertEqual(openobj.read(1), '1') + self.assertEqual(openobj.read(1), '2') + def testAbsoluteArcnames(self): zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) zipfp.write(TESTFN, "/absolute") @@ -110,7 +239,6 @@ class TestsWithSourceFile(unittest.TestCase): self.assertEqual(zipfp.namelist(), ["absolute"]) zipfp.close() - def tearDown(self): os.remove(TESTFN) os.remove(TESTFN2) @@ -123,7 +251,7 @@ class TestZip64InSmallFiles(unittest.TestCase): self._limit = zipfile.ZIP64_LIMIT zipfile.ZIP64_LIMIT = 5 - line_gen = ("Test of zipfile line %d." % i for i in range(0, 1000)) + line_gen = ("Test of zipfile line %d." % i for i in range(0, FIXEDTEST_SIZE)) self.data = '\n'.join(line_gen) # Make a source file with some lines @@ -310,10 +438,10 @@ class OtherTests(unittest.TestCase): def testCreateNonExistentFileForAppend(self): if os.path.exists(TESTFN): os.unlink(TESTFN) - + filename = 'testfile.txt' content = 'hello, world. this is some content.' - + try: zf = zipfile.ZipFile(TESTFN, 'a') zf.writestr(filename, content) @@ -326,9 +454,7 @@ class OtherTests(unittest.TestCase): zf = zipfile.ZipFile(TESTFN, 'r') self.assertEqual(zf.read(filename), content) zf.close() - - os.unlink(TESTFN) - + def testCloseErroneousFile(self): # This test checks that the ZipFile constructor closes the file object # it opens if there's an error in the file. If it doesn't, the traceback @@ -342,7 +468,25 @@ class OtherTests(unittest.TestCase): try: zf = zipfile.ZipFile(TESTFN) except zipfile.BadZipfile: - os.unlink(TESTFN) + pass + + def testIsZipErroneousFile(self): + # This test checks that the is_zipfile function correctly identifies + # a file that is not a zip file + fp = open(TESTFN, "w") + fp.write("this is not a legal zip file\n") + fp.close() + chk = zipfile.is_zipfile(TESTFN) + self.assert_(chk is False) + + def testIsZipValidFile(self): + # This test checks that the is_zipfile function correctly identifies + # a file that is a zip file + zipf = zipfile.ZipFile(TESTFN, mode="w") + zipf.writestr("foo.txt", "O, for a Muse of Fire!") + zipf.close() + chk = zipfile.is_zipfile(TESTFN) + self.assert_(chk is True) def testNonExistentFileRaisesIOError(self): # make sure we don't raise an AttributeError when a partially-constructed @@ -371,6 +515,9 @@ class OtherTests(unittest.TestCase): # and report that the first file in the archive was corrupt. self.assertRaises(RuntimeError, zipf.testzip) + def tearDown(self): + support.unlink(TESTFN) + support.unlink(TESTFN2) class DecryptionTests(unittest.TestCase): # This test checks that ZIP decryption works. Since the library does not @@ -406,15 +553,265 @@ class DecryptionTests(unittest.TestCase): def testBadPassword(self): self.zip.setpassword("perl") self.assertRaises(RuntimeError, self.zip.read, "test.txt") - + def testGoodPassword(self): self.zip.setpassword("python") self.assertEquals(self.zip.read("test.txt"), self.plain) + +class TestsWithRandomBinaryFiles(unittest.TestCase): + def setUp(self): + datacount = randint(16, 64)*1024 + randint(1, 1024) + self.data = ''.join((struct.pack('<f', random()*randint(-1000, 1000)) for i in xrange(datacount))) + + # Make a source file with some lines + fp = open(TESTFN, "wb") + fp.write(self.data) + fp.close() + + def tearDown(self): + support.unlink(TESTFN) + support.unlink(TESTFN2) + + def makeTestArchive(self, f, compression): + # Create the ZIP archive + zipfp = zipfile.ZipFile(f, "w", compression) + zipfp.write(TESTFN, "another"+os.extsep+"name") + zipfp.write(TESTFN, TESTFN) + zipfp.close() + + def zipTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r", compression) + testdata = zipfp.read(TESTFN) + self.assertEqual(len(testdata), len(self.data)) + self.assertEqual(testdata, self.data) + self.assertEqual(zipfp.read("another"+os.extsep+"name"), self.data) + zipfp.close() + + def testStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipTest(f, zipfile.ZIP_STORED) + + def zipOpenTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r", compression) + zipdata1 = [] + zipopen1 = zipfp.open(TESTFN) + while 1: + read_data = zipopen1.read(256) + if not read_data: + break + zipdata1.append(read_data) + + zipdata2 = [] + zipopen2 = zipfp.open("another"+os.extsep+"name") + while 1: + read_data = zipopen2.read(256) + if not read_data: + break + zipdata2.append(read_data) + + testdata1 = ''.join(zipdata1) + self.assertEqual(len(testdata1), len(self.data)) + self.assertEqual(testdata1, self.data) + + testdata2 = ''.join(zipdata2) + self.assertEqual(len(testdata1), len(self.data)) + self.assertEqual(testdata1, self.data) + zipfp.close() + + def testOpenStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipOpenTest(f, zipfile.ZIP_STORED) + + def zipRandomOpenTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r", compression) + zipdata1 = [] + zipopen1 = zipfp.open(TESTFN) + while 1: + read_data = zipopen1.read(randint(1, 1024)) + if not read_data: + break + zipdata1.append(read_data) + + testdata = ''.join(zipdata1) + self.assertEqual(len(testdata), len(self.data)) + self.assertEqual(testdata, self.data) + zipfp.close() + + def testRandomOpenStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipRandomOpenTest(f, zipfile.ZIP_STORED) + +class TestsWithMultipleOpens(unittest.TestCase): + def setUp(self): + # Create the ZIP archive + zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_DEFLATED) + zipfp.writestr('ones', '1'*FIXEDTEST_SIZE) + zipfp.writestr('twos', '2'*FIXEDTEST_SIZE) + zipfp.close() + + def testSameFile(self): + # Verify that (when the ZipFile is in control of creating file objects) + # multiple open() calls can be made without interfering with each other. + zipf = zipfile.ZipFile(TESTFN2, mode="r") + zopen1 = zipf.open('ones') + zopen2 = zipf.open('ones') + data1 = zopen1.read(500) + data2 = zopen2.read(500) + data1 += zopen1.read(500) + data2 += zopen2.read(500) + self.assertEqual(data1, data2) + zipf.close() + + def testDifferentFile(self): + # Verify that (when the ZipFile is in control of creating file objects) + # multiple open() calls can be made without interfering with each other. + zipf = zipfile.ZipFile(TESTFN2, mode="r") + zopen1 = zipf.open('ones') + zopen2 = zipf.open('twos') + data1 = zopen1.read(500) + data2 = zopen2.read(500) + data1 += zopen1.read(500) + data2 += zopen2.read(500) + self.assertEqual(data1, '1'*FIXEDTEST_SIZE) + self.assertEqual(data2, '2'*FIXEDTEST_SIZE) + zipf.close() + + def testInterleaved(self): + # Verify that (when the ZipFile is in control of creating file objects) + # multiple open() calls can be made without interfering with each other. + zipf = zipfile.ZipFile(TESTFN2, mode="r") + zopen1 = zipf.open('ones') + data1 = zopen1.read(500) + zopen2 = zipf.open('twos') + data2 = zopen2.read(500) + data1 += zopen1.read(500) + data2 += zopen2.read(500) + self.assertEqual(data1, '1'*FIXEDTEST_SIZE) + self.assertEqual(data2, '2'*FIXEDTEST_SIZE) + zipf.close() + + def tearDown(self): + os.remove(TESTFN2) + + +class UniversalNewlineTests(unittest.TestCase): + def setUp(self): + self.line_gen = ["Test of zipfile line %d." % i for i in xrange(FIXEDTEST_SIZE)] + self.seps = ('\r', '\r\n', '\n') + self.arcdata, self.arcfiles = {}, {} + for n, s in enumerate(self.seps): + self.arcdata[s] = s.join(self.line_gen) + s + self.arcfiles[s] = '%s-%d' % (TESTFN, n) + file(self.arcfiles[s], "wb").write(self.arcdata[s]) + + def makeTestArchive(self, f, compression): + # Create the ZIP archive + zipfp = zipfile.ZipFile(f, "w", compression) + for fn in self.arcfiles.values(): + zipfp.write(fn, fn) + zipfp.close() + + def readTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + for sep, fn in self.arcfiles.items(): + zipdata = zipfp.open(fn, "rU").read() + self.assertEqual(self.arcdata[sep], zipdata) + + zipfp.close() + + def readlineTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + for sep, fn in self.arcfiles.items(): + zipopen = zipfp.open(fn, "rU") + for line in self.line_gen: + linedata = zipopen.readline() + self.assertEqual(linedata, line + '\n') + + zipfp.close() + + def readlinesTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + for sep, fn in self.arcfiles.items(): + ziplines = zipfp.open(fn, "rU").readlines() + for line, zipline in zip(self.line_gen, ziplines): + self.assertEqual(zipline, line + '\n') + + zipfp.close() + + def iterlinesTest(self, f, compression): + self.makeTestArchive(f, compression) + + # Read the ZIP archive + zipfp = zipfile.ZipFile(f, "r") + for sep, fn in self.arcfiles.items(): + for line, zipline in zip(self.line_gen, zipfp.open(fn, "rU")): + self.assertEqual(zipline, line + '\n') + + zipfp.close() + + def testReadStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readTest(f, zipfile.ZIP_STORED) + + def testReadlineStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readlineTest(f, zipfile.ZIP_STORED) + + def testReadlinesStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readlinesTest(f, zipfile.ZIP_STORED) + + def testIterlinesStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.iterlinesTest(f, zipfile.ZIP_STORED) + + if zlib: + def testReadDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readTest(f, zipfile.ZIP_DEFLATED) + + def testReadlineDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readlineTest(f, zipfile.ZIP_DEFLATED) + + def testReadlinesDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.readlinesTest(f, zipfile.ZIP_DEFLATED) + + def testIterlinesDeflated(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.iterlinesTest(f, zipfile.ZIP_DEFLATED) + + def tearDown(self): + for sep, fn in self.arcfiles.items(): + os.remove(fn) + support.unlink(TESTFN) + support.unlink(TESTFN2) + + def test_main(): - run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, - PyZipFileTests, DecryptionTests) - #run_unittest(TestZip64InSmallFiles) + run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, + PyZipFileTests, DecryptionTests, TestsWithMultipleOpens, + UniversalNewlineTests, TestsWithRandomBinaryFiles) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 0be9668..dc4a7d8 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -3,22 +3,6 @@ from test import test_support import zlib import random -# print test_support.TESTFN - -def getbuf(): - # This was in the original. Avoid non-repeatable sources. - # Left here (unused) in case something wants to be done with it. - import imp - try: - t = imp.find_module('test_zlib') - file = t[0] - except ImportError: - file = open(__file__) - buf = file.read() * 8 - file.close() - return buf - - class ChecksumTestCase(unittest.TestCase): # checksum test cases @@ -461,21 +445,3 @@ def test_main(): if __name__ == "__main__": test_main() - -def test(tests=''): - if not tests: tests = 'o' - testcases = [] - if 'k' in tests: testcases.append(ChecksumTestCase) - if 'x' in tests: testcases.append(ExceptionTestCase) - if 'c' in tests: testcases.append(CompressTestCase) - if 'o' in tests: testcases.append(CompressObjectTestCase) - test_support.run_unittest(*testcases) - -if False: - import sys - sys.path.insert(1, '/Py23Src/python/dist/src/Lib/test') - import test_zlib as tz - ts, ut = tz.test_support, tz.unittest - su = ut.TestSuite() - su.addTest(ut.makeSuite(tz.CompressTestCase)) - ts.run_suite(su) diff --git a/Lib/test/testtar.tar b/Lib/test/testtar.tar Binary files differindex 1f4493f..c4c82b8 100644 --- a/Lib/test/testtar.tar +++ b/Lib/test/testtar.tar diff --git a/Lib/test/warning_tests.py b/Lib/test/warning_tests.py new file mode 100644 index 0000000..d0519ef --- /dev/null +++ b/Lib/test/warning_tests.py @@ -0,0 +1,9 @@ +# Helper module for testing the skipmodules argument of warnings.warn() + +import warnings + +def outer(message, stacklevel=1): + inner(message, stacklevel) + +def inner(message, stacklevel=1): + warnings.warn(message, stacklevel=stacklevel) diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 0917d75..e6e089f 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -63,6 +63,8 @@ class TextWrapper: break_long_words (default: true) Break words longer than 'width'. If false, those words will not be broken, and some lines might be longer than 'width'. + drop_whitespace (default: true) + Drop leading and trailing whitespace from lines. """ whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace)) @@ -98,7 +100,8 @@ class TextWrapper: expand_tabs=True, replace_whitespace=True, fix_sentence_endings=False, - break_long_words=True): + break_long_words=True, + drop_whitespace=True): self.width = width self.initial_indent = initial_indent self.subsequent_indent = subsequent_indent @@ -106,6 +109,7 @@ class TextWrapper: self.replace_whitespace = replace_whitespace self.fix_sentence_endings = fix_sentence_endings self.break_long_words = break_long_words + self.drop_whitespace = drop_whitespace # -- Private methods ----------------------------------------------- @@ -140,7 +144,7 @@ class TextWrapper: 'use', ' ', 'the', ' ', '-b', ' ', 'option!' """ chunks = self.wordsep_re.split(text) - chunks = filter(None, chunks) + chunks = filter(None, chunks) # remove empty chunks return chunks def _fix_sentence_endings(self, chunks): @@ -228,7 +232,7 @@ class TextWrapper: # First chunk on line is whitespace -- drop it, unless this # is the very beginning of the text (ie. no lines started yet). - if chunks[-1].strip() == '' and lines: + if self.drop_whitespace and chunks[-1].strip() == '' and lines: del chunks[-1] while chunks: @@ -249,7 +253,7 @@ class TextWrapper: self._handle_long_word(chunks, cur_line, cur_len, width) # If the last chunk on this line is all whitespace, drop it. - if cur_line and cur_line[-1].strip() == '': + if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': del cur_line[-1] # Convert current line back to a string and store it in list diff --git a/Lib/timeit.py b/Lib/timeit.py index e760c62..a1a9b36 100644 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -90,6 +90,17 @@ def reindent(src, indent): """Helper to reindent a multi-line statement.""" return src.replace("\n", "\n" + " "*indent) +def _template_func(setup, func): + """Create a timer function. Used if the "statement" is a callable.""" + def inner(_it, _timer): + setup() + _t0 = _timer() + for _i in _it: + func() + _t1 = _timer() + return _t1 - _t0 + return inner + class Timer: """Class for timing execution speed of small code snippets. @@ -109,14 +120,32 @@ class Timer: def __init__(self, stmt="pass", setup="pass", timer=default_timer): """Constructor. See class doc string.""" self.timer = timer - stmt = reindent(stmt, 8) - setup = reindent(setup, 4) - src = template % {'stmt': stmt, 'setup': setup} - self.src = src # Save for traceback display - code = compile(src, dummy_src_name, "exec") ns = {} - exec(code, globals(), ns) - self.inner = ns["inner"] + if isinstance(stmt, basestring): + stmt = reindent(stmt, 8) + if isinstance(setup, basestring): + setup = reindent(setup, 4) + src = template % {'stmt': stmt, 'setup': setup} + elif callable(setup): + src = template % {'stmt': stmt, 'setup': '_setup()'} + ns['_setup'] = setup + else: + raise ValueError("setup is neither a string nor callable") + self.src = src # Save for traceback display + code = compile(src, dummy_src_name, "exec") + exec(code, globals(), ns) + self.inner = ns["inner"] + elif callable(stmt): + self.src = None + if isinstance(setup, basestring): + _setup = setup + def setup(): + exec(_setup, globals(), ns) + elif not callable(setup): + raise ValueError("setup is neither a string nor callable") + self.inner = _template_func(setup, stmt) + else: + raise ValueError("stmt is neither a string nor callable") def print_exc(self, file=None): """Helper to print a traceback from the timed code. @@ -136,10 +165,13 @@ class Timer: sent; it defaults to sys.stderr. """ import linecache, traceback - linecache.cache[dummy_src_name] = (len(self.src), - None, - self.src.split("\n"), - dummy_src_name) + if self.src is not None: + linecache.cache[dummy_src_name] = (len(self.src), + None, + self.src.split("\n"), + dummy_src_name) + # else the source is already stored somewhere else + traceback.print_exc(file=file) def timeit(self, number=default_number): @@ -189,6 +221,16 @@ class Timer: r.append(t) return r +def timeit(stmt="pass", setup="pass", timer=default_timer, + number=default_number): + """Convenience function to create Timer object and call timeit method.""" + return Timer(stmt, setup, timer).timeit(number) + +def repeat(stmt="pass", setup="pass", timer=default_timer, + repeat=default_repeat, number=default_number): + """Convenience function to create Timer object and call repeat method.""" + return Timer(stmt, setup, timer).repeat(repeat, number) + def main(args=None): """Main program, used when run as a script. diff --git a/Lib/unittest.py b/Lib/unittest.py index eab0372..12017dd 100644 --- a/Lib/unittest.py +++ b/Lib/unittest.py @@ -25,7 +25,7 @@ Simple usage: Further information is available in the bundled documentation, and from - http://pyunit.sourceforge.net/ + http://docs.python.org/lib/module-unittest.html Copyright (c) 1999-2003 Steve Purcell This module is free software, and you may redistribute it and/or modify @@ -104,7 +104,7 @@ class TestResult: self.failures = [] self.errors = [] self.testsRun = 0 - self.shouldStop = 0 + self.shouldStop = False def startTest(self, test): "Called when the given test is about to be run" @@ -232,6 +232,18 @@ class TestCase: def id(self): return "%s.%s" % (_strclass(self.__class__), self._testMethodName) + def __eq__(self, other): + if type(self) is not type(other): + return False + + return self._testMethodName == other._testMethodName + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self._testMethodName)) + def __str__(self): return "%s (%s)" % (self._testMethodName, _strclass(self.__class__)) @@ -288,10 +300,7 @@ class TestCase: minimised; usually the top level of the traceback frame is not needed. """ - exctype, excvalue, tb = sys.exc_info() - if sys.platform[:4] == 'java': ## tracebacks look different in Jython - return (exctype, excvalue, tb) - return (exctype, excvalue, tb) + return sys.exc_info() def fail(self, msg=None): """Fail immediately, with the given message.""" @@ -398,6 +407,14 @@ class TestSuite: __str__ = __repr__ + def __eq__(self, other): + if type(self) is not type(other): + return False + return self._tests == other._tests + + def __ne__(self, other): + return not self == other + def __iter__(self): return iter(self._tests) @@ -408,9 +425,18 @@ class TestSuite: return cases def addTest(self, test): + # sanity checks + if not callable(test): + raise TypeError("the test to add must be callable") + if (isinstance(test, (type, types.ClassType)) and + issubclass(test, (TestCase, TestSuite))): + raise TypeError("TestCases and TestSuites must be instantiated " + "before passing them to addTest()") self._tests.append(test) def addTests(self, tests): + if isinstance(tests, basestring): + raise TypeError("tests must be an iterable of tests, not a string") for test in tests: self.addTest(test) @@ -433,7 +459,7 @@ class FunctionTestCase(TestCase): """A test case that wraps a test function. This is useful for slipping pre-existing test functions into the - PyUnit framework. Optionally, set-up and tidy-up functions can be + unittest framework. Optionally, set-up and tidy-up functions can be supplied. As with TestCase, the tidy-up ('tearDown') function will always be called if the set-up ('setUp') function ran successfully. """ @@ -460,6 +486,22 @@ class FunctionTestCase(TestCase): def id(self): return self.__testFunc.__name__ + def __eq__(self, other): + if type(self) is not type(other): + return False + + return self.__setUpFunc == other.__setUpFunc and \ + self.__tearDownFunc == other.__tearDownFunc and \ + self.__testFunc == other.__testFunc and \ + self.__description == other.__description + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self.__setUpFunc, self.__tearDownFunc, + self.__testFunc, self.__description)) + def __str__(self): return "%s (%s)" % (_strclass(self.__class__), self.__testFunc.__name__) @@ -479,7 +521,7 @@ class FunctionTestCase(TestCase): class TestLoader: """This class is responsible for loading tests according to various - criteria and returning them wrapped in a Test + criteria and returning them wrapped in a TestSuite """ testMethodPrefix = 'test' sortTestMethodsUsing = cmp @@ -533,18 +575,23 @@ class TestLoader: elif (isinstance(obj, (type, types.ClassType)) and issubclass(obj, TestCase)): return self.loadTestsFromTestCase(obj) - elif type(obj) == types.UnboundMethodType: - return parent(obj.__name__) + elif (type(obj) == types.UnboundMethodType and + isinstance(parent, (type, types.ClassType)) and + issubclass(parent, TestCase)): + return TestSuite([parent(obj.__name__)]) elif isinstance(obj, TestSuite): return obj elif callable(obj): test = obj() - if not isinstance(test, (TestCase, TestSuite)): - raise ValueError, \ - "calling %s returned %s, not a test" % (obj,test) - return test + if isinstance(test, TestSuite): + return test + elif isinstance(test, TestCase): + return TestSuite([test]) + else: + raise TypeError("calling %s returned %s, not a test" % + (obj, test)) else: - raise ValueError, "don't know how to make test from: %s" % obj + raise TypeError("don't know how to make test from: %s" % obj) def loadTestsFromNames(self, names, module=None): """Return a suite of all tests cases found using the given sequence @@ -559,10 +606,6 @@ class TestLoader: def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix): return attrname.startswith(prefix) and callable(getattr(testCaseClass, attrname)) testFnNames = filter(isTestMethod, dir(testCaseClass)) - for baseclass in testCaseClass.__bases__: - for testFnName in self.getTestCaseNames(baseclass): - if testFnName not in testFnNames: # handle overridden methods - testFnNames.append(testFnName) if self.sortTestMethodsUsing: testFnNames.sort(self.sortTestMethodsUsing) return testFnNames @@ -738,7 +781,8 @@ Examples: in MyTestCase """ def __init__(self, module='__main__', defaultTest=None, - argv=None, testRunner=None, testLoader=defaultTestLoader): + argv=None, testRunner=TextTestRunner, + testLoader=defaultTestLoader): if type(module) == type(''): self.module = __import__(module) for part in module.split('.')[1:]: @@ -788,9 +832,16 @@ Examples: self.module) def runTests(self): - if self.testRunner is None: - self.testRunner = TextTestRunner(verbosity=self.verbosity) - result = self.testRunner.run(self.test) + if isinstance(self.testRunner, (type, types.ClassType)): + try: + testRunner = self.testRunner(verbosity=self.verbosity) + except TypeError: + # didn't accept the verbosity argument + testRunner = self.testRunner() + else: + # it is assumed to be a TestRunner instance + testRunner = self.testRunner + result = testRunner.run(self.test) sys.exit(not result.wasSuccessful()) main = TestProgram diff --git a/Lib/urllib.py b/Lib/urllib.py index 4e24a8f..b83b574 100644 --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -326,6 +326,11 @@ class URLopener: h.send(data) errcode, errmsg, headers = h.getreply() fp = h.getfile() + if errcode == -1: + if fp: fp.close() + # something went wrong with the HTTP status line + raise IOError, ('http protocol error', 0, + 'got a bad status line', None) if errcode == 200: return addinfourl(fp, headers, "http:" + url) else: @@ -413,6 +418,11 @@ class URLopener: h.send(data) errcode, errmsg, headers = h.getreply() fp = h.getfile() + if errcode == -1: + if fp: fp.close() + # something went wrong with the HTTP status line + raise IOError, ('http protocol error', 0, + 'got a bad status line', None) if errcode == 200: return addinfourl(fp, headers, "https:" + url) else: @@ -1470,7 +1480,7 @@ def test(args=[]): '/etc/passwd', 'file:/etc/passwd', 'file://localhost/etc/passwd', - 'ftp://ftp.python.org/pub/python/README', + 'ftp://ftp.gnu.org/pub/README', ## 'gopher://gopher.micro.umn.edu/1/', 'http://www.python.org/index.html', ] diff --git a/Lib/urllib2.py b/Lib/urllib2.py index 60ff260..a0be039 100644 --- a/Lib/urllib2.py +++ b/Lib/urllib2.py @@ -14,36 +14,36 @@ 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 the 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 IOError); for HTTP errors, raises an HTTPError, which can also be treated as a valid response. -build_opener -- function that creates a new OpenerDirector instance. -will install the default handlers. accepts one or more Handlers as +build_opener -- Function that creates a new OpenerDirector instance. +Will install the default handlers. Accepts one or more Handlers as arguments, either instances or Handler classes that it will -instantiate. if one of the argument is a subclass of the default +instantiate. If one of the argument is a subclass of the default handler, the argument will be installed instead of the default. -install_opener -- installs a new opener as the default opener. +install_opener -- Installs a new opener as the default opener. objects of interest: OpenerDirector -- -Request -- an object that encapsulates the state of a request. the -state can be a simple as the URL. it can also include extra HTTP +Request -- An object that encapsulates the state of a request. The +state can be as simple as the URL. It can also include extra HTTP headers, e.g. a User-Agent. BaseHandler -- exceptions: -URLError-- a subclass of IOError, individual protocols have their own -specific subclass +URLError -- A subclass of IOError, individual protocols have their own +specific subclass. -HTTPError-- also a valid HTTP response, so you can treat an HTTP error -as an exceptional event or valid response +HTTPError -- Also a valid HTTP response, so you can treat an HTTP error +as an exceptional event or valid response. internals: BaseHandler and parent @@ -55,7 +55,10 @@ import urllib2 # set up authentication info authinfo = urllib2.HTTPBasicAuthHandler() -authinfo.add_password('realm', 'host', 'username', 'password') +authinfo.add_password(realm='PDQ Application', + uri='https://mahler:8092/site-updates.py', + user='klem', + passwd='geheim$parole') proxy_support = urllib2.ProxyHandler({"http" : "http://ahad-haam:3128"}) @@ -334,7 +337,8 @@ class OpenerDirector: added = True if added: - # XXX why does self.handlers need to be sorted? + # the handlers must work in an specific order, the order + # is specified in a Handler attribute bisect.insort(self.handlers, handler) handler.add_parent(self) @@ -486,7 +490,9 @@ class HTTPErrorProcessor(BaseHandler): def http_response(self, request, response): code, msg, hdrs = response.code, response.msg, response.info() - if code not in (200, 206): + # According to RFC 2616, "2xx" code indicates that the client's + # request was successfully received, understood, and accepted. + if not (200 <= code < 300): response = self.parent.error( 'http', request, response, code, msg, hdrs) @@ -766,11 +772,10 @@ class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr): class AbstractBasicAuthHandler: - rx = re.compile('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', re.I) + # XXX this allows for multiple auth-schemes, but will stupidly pick + # the last one with a realm specified. - # XXX there can actually be multiple auth-schemes in a - # www-authenticate header. should probably be a lot more careful - # in parsing them to extract multiple alternatives + rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', re.I) # XXX could pre-emptively send auth info already accepted (RFC 2617, # end of section 2, and section 1.2 immediately after "credentials" @@ -1214,19 +1219,23 @@ class FileHandler(BaseHandler): host = req.get_host() file = req.get_selector() localfile = url2pathname(file) - stats = os.stat(localfile) - size = stats.st_size - modified = email.utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(file)[0] - headers = mimetools.Message(StringIO( - 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified))) - if host: - host, port = splitport(host) - if not host or \ - (not port and socket.gethostbyname(host) in self.get_names()): - return addinfourl(open(localfile, 'rb'), - headers, 'file:'+file) + try: + stats = os.stat(localfile) + size = stats.st_size + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + mtype = mimetypes.guess_type(file)[0] + headers = mimetools.Message(StringIO( + 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % + (mtype or 'text/plain', size, modified))) + if host: + host, port = splitport(host) + if not host or \ + (not port and socket.gethostbyname(host) in self.get_names()): + return addinfourl(open(localfile, 'rb'), + headers, 'file:'+file) + except OSError as msg: + # urllib2 users shouldn't expect OSErrors coming from urlopen() + raise URLError(msg) raise URLError('file not on local host') class FTPHandler(BaseHandler): diff --git a/Lib/wave.py b/Lib/wave.py index dd5a47a..81a7141 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -159,7 +159,12 @@ class Wave_read: f = __builtin__.open(f, 'rb') self._i_opened_the_file = f # else, assume it is an open file object already - self.initfp(f) + try: + self.initfp(f) + except: + if self._i_opened_the_file: + f.close() + raise def __del__(self): self.close() @@ -297,7 +302,12 @@ class Wave_write: if isinstance(f, basestring): f = __builtin__.open(f, 'wb') self._i_opened_the_file = f - self.initfp(f) + try: + self.initfp(f) + except: + if self._i_opened_the_file: + f.close() + raise def initfp(self, file): self._file = file diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 55aa04c..dd5e019 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -2,6 +2,7 @@ """Interfaces for launching and remotely controlling Web browsers.""" import os +import shlex import sys import stat import subprocess @@ -32,7 +33,11 @@ def get(using=None): for browser in alternatives: if '%s' in browser: # User gave us a command line, split it into name and args - return GenericBrowser(browser.split()) + browser = shlex.split(browser) + if browser[-1] == '&': + return BackgroundBrowser(browser[:-1]) + else: + return GenericBrowser(browser) else: # User gave us a browser name or path. try: @@ -437,19 +442,16 @@ class Grail(BaseBrowser): # a console terminal or an X display to run. def register_X_browsers(): - # The default Gnome browser - if _iscommand("gconftool-2"): - # get the web browser string from gconftool - gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command 2>/dev/null' - out = os.popen(gc) - commd = out.read().strip() - retncode = out.close() - - # if successful, register it - if retncode is None and commd: - register("gnome", None, BackgroundBrowser(commd.split())) - - # First, the Mozilla/Netscape browsers + + # The default GNOME browser + if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"): + register("gnome-open", None, BackgroundBrowser("gnome-open")) + + # The default KDE browser + if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"): + register("kfmclient", Konqueror, Konqueror("kfmclient")) + + # The Mozilla/Netscape browsers for browser in ("mozilla-firefox", "firefox", "mozilla-firebird", "firebird", "seamonkey", "mozilla", "netscape"): @@ -508,17 +510,28 @@ if os.environ.get("TERM"): if sys.platform[:3] == "win": class WindowsDefault(BaseBrowser): def open(self, url, new=0, autoraise=1): - os.startfile(url) - return True # Oh, my... + try: + os.startfile(url) + except WindowsError: + # [Error 22] No application is associated with the specified + # file for this operation: '<URL>' + return False + else: + return True _tryorder = [] _browsers = {} - # Prefer mozilla/netscape/opera if present + + # First try to use the default Windows browser + register("windows-default", WindowsDefault) + + # Detect some common Windows browsers, fallback to IE + iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), + "Internet Explorer\\IEXPLORE.EXE") for browser in ("firefox", "firebird", "seamonkey", "mozilla", - "netscape", "opera"): + "netscape", "opera", iexplore): if _iscommand(browser): register(browser, None, BackgroundBrowser(browser)) - register("windows-default", WindowsDefault) # # Platform support for MacOS diff --git a/Lib/zipfile.py b/Lib/zipfile.py index d0a1f65..fa7e910 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -361,6 +361,200 @@ class _ZipDecrypter: self._UpdateKeys(c) return c +class ZipExtFile: + """File-like object for reading an archive member. + Is returned by ZipFile.open(). + """ + + def __init__(self, fileobj, zipinfo, decrypt=None): + self.fileobj = fileobj + self.decrypter = decrypt + self.bytes_read = 0 + self.rawbuffer = '' + self.readbuffer = '' + self.linebuffer = '' + self.eof = False + self.univ_newlines = False + self.nlSeps = ("\n", ) + self.lastdiscard = '' + + self.compress_type = zipinfo.compress_type + self.compress_size = zipinfo.compress_size + + self.closed = False + self.mode = "r" + self.name = zipinfo.filename + + # read from compressed files in 64k blocks + self.compreadsize = 64*1024 + if self.compress_type == ZIP_DEFLATED: + self.dc = zlib.decompressobj(-15) + + def set_univ_newlines(self, univ_newlines): + self.univ_newlines = univ_newlines + + # pick line separator char(s) based on universal newlines flag + self.nlSeps = ("\n", ) + if self.univ_newlines: + self.nlSeps = ("\r\n", "\r", "\n") + + def __iter__(self): + return self + + def __next__(self): + nextline = self.readline() + if not nextline: + raise StopIteration() + + return nextline + + def close(self): + self.closed = True + + def _checkfornewline(self): + nl, nllen = -1, -1 + if self.linebuffer: + # ugly check for cases where half of an \r\n pair was + # read on the last pass, and the \r was discarded. In this + # case we just throw away the \n at the start of the buffer. + if (self.lastdiscard, self.linebuffer[0]) == ('\r','\n'): + self.linebuffer = self.linebuffer[1:] + + for sep in self.nlSeps: + nl = self.linebuffer.find(sep) + if nl >= 0: + nllen = len(sep) + return nl, nllen + + return nl, nllen + + def readline(self, size = -1): + """Read a line with approx. size. If size is negative, + read a whole line. + """ + if size < 0: + size = sys.maxint + elif size == 0: + return '' + + # check for a newline already in buffer + nl, nllen = self._checkfornewline() + + if nl >= 0: + # the next line was already in the buffer + nl = min(nl, size) + else: + # no line break in buffer - try to read more + size -= len(self.linebuffer) + while nl < 0 and size > 0: + buf = self.read(min(size, 100)) + if not buf: + break + self.linebuffer += buf + size -= len(buf) + + # check for a newline in buffer + nl, nllen = self._checkfornewline() + + # we either ran out of bytes in the file, or + # met the specified size limit without finding a newline, + # so return current buffer + if nl < 0: + s = self.linebuffer + self.linebuffer = '' + return s + + buf = self.linebuffer[:nl] + self.lastdiscard = self.linebuffer[nl:nl + nllen] + self.linebuffer = self.linebuffer[nl + nllen:] + + # line is always returned with \n as newline char (except possibly + # for a final incomplete line in the file, which is handled above). + return buf + "\n" + + def readlines(self, sizehint = -1): + """Return a list with all (following) lines. The sizehint parameter + is ignored in this implementation. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def read(self, size = None): + # act like file() obj and return empty string if size is 0 + if size == 0: + return '' + + # determine read size + bytesToRead = self.compress_size - self.bytes_read + + # adjust read size for encrypted files since the first 12 bytes + # are for the encryption/password information + if self.decrypter is not None: + bytesToRead -= 12 + + if size is not None and size >= 0: + if self.compress_type == ZIP_STORED: + lr = len(self.readbuffer) + bytesToRead = min(bytesToRead, size - lr) + elif self.compress_type == ZIP_DEFLATED: + if len(self.readbuffer) > size: + # the user has requested fewer bytes than we've already + # pulled through the decompressor; don't read any more + bytesToRead = 0 + else: + # user will use up the buffer, so read some more + lr = len(self.rawbuffer) + bytesToRead = min(bytesToRead, self.compreadsize - lr) + + # avoid reading past end of file contents + if bytesToRead + self.bytes_read > self.compress_size: + bytesToRead = self.compress_size - self.bytes_read + + # try to read from file (if necessary) + if bytesToRead > 0: + bytes = self.fileobj.read(bytesToRead) + self.bytes_read += len(bytes) + self.rawbuffer += bytes + + # handle contents of raw buffer + if self.rawbuffer: + newdata = self.rawbuffer + self.rawbuffer = '' + + # decrypt new data if we were given an object to handle that + if newdata and self.decrypter is not None: + newdata = ''.join(map(self.decrypter, newdata)) + + # decompress newly read data if necessary + if newdata and self.compress_type == ZIP_DEFLATED: + newdata = self.dc.decompress(newdata) + self.rawbuffer = self.dc.unconsumed_tail + if self.eof and len(self.rawbuffer) == 0: + # we're out of raw bytes (both from the file and + # the local buffer); flush just to make sure the + # decompressor is done + newdata += self.dc.flush() + # prevent decompressor from being used again + self.dc = None + + self.readbuffer += newdata + + + # return what the user asked for + if size is None or len(self.readbuffer) <= size: + bytes = self.readbuffer + self.readbuffer = '' + else: + bytes = self.readbuffer[:size] + self.readbuffer = self.readbuffer[size:] + + return bytes + + class ZipFile: """ Class with methods to open, read, write, close, list zip files. @@ -540,73 +734,75 @@ class ZipFile: def read(self, name, pwd=None): """Return file bytes (as a string) for name.""" - if self.mode not in ("r", "a"): - raise RuntimeError, 'read() requires mode "r" or "a"' + return self.open(name, "r", pwd).read() + + def open(self, name, mode="r", pwd=None): + """Return file-like object for 'name'.""" + if mode not in ("r", "U", "rU"): + raise RuntimeError, 'open() requires mode "r", "U", or "rU"' if not self.fp: raise RuntimeError, \ "Attempt to read ZIP archive that was already closed" + + # Only open a new file for instances where we were not + # given a file object in the constructor + if self._filePassed: + zef_file = self.fp + else: + zef_file = open(self.filename, 'rb') + + # Get info object for name zinfo = self.getinfo(name) - is_encrypted = zinfo.flag_bits & 0x1 - if is_encrypted: - if not pwd: - pwd = self.pwd - if not pwd: - raise RuntimeError, "File %s is encrypted, " \ - "password required for extraction" % name - filepos = self.fp.tell() - self.fp.seek(zinfo.header_offset, 0) + filepos = zef_file.tell() + + zef_file.seek(zinfo.header_offset, 0) # Skip the file header: - fheader = self.fp.read(30) + fheader = zef_file.read(30) if fheader[0:4] != stringFileHeader: raise BadZipfile, "Bad magic number for file header" fheader = struct.unpack(structFileHeader, fheader) - fname = self.fp.read(fheader[_FH_FILENAME_LENGTH]) + fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) if fheader[_FH_EXTRA_FIELD_LENGTH]: - self.fp.read(fheader[_FH_EXTRA_FIELD_LENGTH]) + zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH]) if fname != zinfo.orig_filename: raise BadZipfile, \ 'File name in directory "%s" and header "%s" differ.' % ( zinfo.orig_filename, fname) - bytes = self.fp.read(zinfo.compress_size) - # Go with decryption + # check for encrypted flag & handle password + is_encrypted = zinfo.flag_bits & 0x1 + zd = None if is_encrypted: + if not pwd: + pwd = self.pwd + if not pwd: + raise RuntimeError, "File %s is encrypted, " \ + "password required for extraction" % name + zd = _ZipDecrypter(pwd) # The first 12 bytes in the cypher stream is an encryption header # used to strengthen the algorithm. The first 11 bytes are # completely random, while the 12th contains the MSB of the CRC, # and is used to check the correctness of the password. + bytes = zef_file.read(12) h = map(zd, bytes[0:12]) if ord(h[11]) != ((zinfo.CRC>>24)&255): raise RuntimeError, "Bad password for file %s" % name - bytes = "".join(map(zd, bytes[12:])) - # Go with decompression - self.fp.seek(filepos, 0) - if zinfo.compress_type == ZIP_STORED: - pass - elif zinfo.compress_type == ZIP_DEFLATED: - if not zlib: - raise RuntimeError, \ - "De-compression requires the (missing) zlib module" - # zlib compress/decompress code by Jeremy Hylton of CNRI - dc = zlib.decompressobj(-15) - bytes = dc.decompress(bytes) - # need to feed in unused pad byte so that zlib won't choke - ex = dc.decompress('Z') + dc.flush() - if ex: - bytes = bytes + ex + + # build and return a ZipExtFile + if zd is None: + zef = ZipExtFile(zef_file, zinfo) else: - raise BadZipfile, \ - "Unsupported compression method %d for file %s" % \ - (zinfo.compress_type, name) - crc = binascii.crc32(bytes) - if crc != zinfo.CRC: - raise BadZipfile, "Bad CRC-32 for file %s" % name - return bytes + zef = ZipExtFile(zef_file, zinfo, zd) + + # set universal newlines on ZipExtFile if necessary + if "U" in mode: + zef.set_univ_newlines(True) + return zef def _writecheck(self, zinfo): """Check for errors before writing a file to the archive.""" @@ -29,12 +29,14 @@ John Aycock Donovan Baarda Attila Babo Alfonso Baciero +Dwayne Bailey Stig Bakken Greg Ball Luigi Ballabio Michael J. Barber Chris Barker Quentin Barnes +Richard Barran Cesar Eduardo Barros Des Barry Ulf Bartelt @@ -106,6 +108,7 @@ Tony Campbell Brett Cannon Mike Carlton Terry Carroll +Brian Leair Luke Kenneth Casson Leighton Donn Cave Per Cederqvist @@ -154,8 +157,10 @@ Ben Darnell Jonathan Dasteel John DeGood Vincent Delft +Erik Demaine Roger Dev Toby Dickenson +Mark Dickinson Yves Dionne Daniel Dittmar Walter Dörwald @@ -200,6 +205,7 @@ Mark Favas Niels Ferguson Sebastian Fernandez Vincent Fiack +Tomer Filiba Russell Finn Nils Fischbeck Frederik Fix @@ -216,6 +222,7 @@ Gyro Funch Peter Funk Geoff Furnish Lele Gaifax +Santiago Gala Yitzchak Gale Raymund Galvin Nitin Ganatra @@ -239,6 +246,7 @@ Hans de Graaff Eddy De Greef Duncan Grisby Dag Gruneau +Thomas Güttler Michael Guravage Lars Gustäbel Barry Haddow @@ -270,6 +278,7 @@ Chris Herborth Ivan Herman Jürgen Hermann Gary Herron +Thomas Herve Bernhard Herzog Magnus L. Hetland Raymond Hettinger @@ -324,6 +333,7 @@ Orjan Johansen Gregory K. Johnson Simon Johnston Evan Jones +Jeremy Jones Richard Jones Irmen de Jong Lucas de Jonge @@ -353,9 +363,11 @@ Kim Knapp Lenny Kneler Pat Knight Greg Kochanski +Damon Kohler Joseph Koshy Bob Kras Holger Krekel +Fabian Kreutz Hannu Krosing Andrew Kuchling Vladimir Kushnir @@ -383,6 +395,7 @@ Robert van Liere Martin Ligr Christopher Lindblad Eric Lindvall +Bjorn Lindqvist Per Lindqvist Nick Lockwood Stephanie Lockwood @@ -408,6 +421,7 @@ Vladimir Marangozov Doug Marien Alex Martelli Anthony Martin +Sébastien Martini Roger Masse Nick Mathewson Graham Matthews @@ -427,8 +441,10 @@ Mike Meyer Steven Miale Trent Mick Chad Miller +Damien Miller Roman Milner Dom Mitchell +Dustin J. Mitchell Doug Moen Paul Moore The Dragon De Monsyne @@ -468,6 +484,7 @@ Russel Owen Mike Pall Todd R. Palmer Jan Palus +Peter Parente Alexandre Parenteau Dan Parisien Harri Pasanen @@ -555,6 +572,7 @@ David Scherer Gregor Schmid Ralf Schmitt Peter Schneider-Kamp +Arvin Schnell Chad J. Schroeder Sam Schulenburg Stefan Schwarzer @@ -565,6 +583,7 @@ Nick Seidenman Žiga Seilnach Fred Sells Jiwon Seo +Jerry Seutter Denis Severson Ha Shao Bruce Sherwood @@ -689,6 +708,7 @@ Dan Wolfe Richard Wolff Gordon Worley Thomas Wouters +Heiko Wundram Doug Wyatt Ka-Ping Yee Bob Yodlowski @@ -18,7 +18,7 @@ TO DO - Unify range() and xrange(). -- Rework the standard I/O library to use bytes for binary files. +- Use io.py instead of C stdio everywhere. - Make strings all Unicode. @@ -28,6 +28,8 @@ TO DO Core and Builtins ----------------- +- Merged from (2.6) trunk at r54987. + - Patch #1660500: hide iteration variable in list comps, add set comps and use common code to handle compilation of iterative expressions diff --git a/Misc/build.sh b/Misc/build.sh index d5079ca..a6ba927 100755 --- a/Misc/build.sh +++ b/Misc/build.sh @@ -47,6 +47,7 @@ DIR=`dirname $DIR` FAILURE_SUBJECT="Python Regression Test Failures" #FAILURE_MAILTO="YOUR_ACCOUNT@gmail.com" FAILURE_MAILTO="python-checkins@python.org" +#FAILURE_CC="optional--uncomment and set to desired address" REMOTE_SYSTEM="neal@dinsdale.python.org" REMOTE_DIR="/data/ftp.python.org/pub/docs.python.org/dev/" @@ -91,7 +92,12 @@ update_status() { mail_on_failure() { if [ "$NUM_FAILURES" != "0" ]; then - mutt -s "$FAILURE_SUBJECT $1 ($NUM_FAILURES)" $FAILURE_MAILTO < $2 + dest=$FAILURE_MAILTO + # FAILURE_CC is optional. + if [ "$FAILURE_CC" != "" ]; then + dest="$dest -c $FAILURE_CC" + fi + mutt -s "$FAILURE_SUBJECT $1 ($NUM_FAILURES)" $dest < $2 fi } @@ -208,8 +214,19 @@ fi cd $DIR/Doc F="make-doc.out" start=`current_time` -make >& ../build/$F -err=$? +# Doc/commontex/boilerplate.tex is expected to always have an outstanding +# modification for the date. When a release is cut, a conflict occurs. +# This allows us to detect this problem and not try to build the docs +# which will definitely fail with a conflict. +CONFLICTED_FILE=commontex/boilerplate.tex +conflict_count=`grep -c "<<<" $CONFLICTED_FILE` +if [ $conflict_count != 0 ]; then + echo "Conflict detected in $CONFLICTED_FILE. Doc build skipped." > ../build/$F + err=1 +else + make >& ../build/$F + err=$? +fi update_status "Making doc" "$F" $start if [ $err != 0 ]; then NUM_FAILURES=1 diff --git a/Misc/developers.txt b/Misc/developers.txt index d325551..6c3128b 100644 --- a/Misc/developers.txt +++ b/Misc/developers.txt @@ -17,6 +17,18 @@ the format to accommodate documentation needs as they arise. Permissions History ------------------- +- Travis Oliphant was given SVN access on 17 Apr 2007 by MvL, + for implementing the extended buffer protocol. + +- Ziga Seilnacht was given SVN access on 09 Mar 2007 by MvL, + for general maintenance. + +- Pete Shinners was given SVN access on 04 Mar 2007 by NCN, + for PEP 3101 work in the sandbox. + +- Pat Maupin and Eric V. Smith were given SVN access on 28 Feb 2007 by NCN, + for PEP 3101 work in the sandbox. + - Steven Bethard (SF name "bediviere") added to the SourceForge Python project 26 Feb 2007, by NCN, as a tracker tech. @@ -151,3 +163,4 @@ NCN: Neal Norwitz RDH: Raymond Hettinger TGP: Tim Peters DJG: David Goodger +MvL: Martin v. Loewis diff --git a/Misc/python-config.in b/Misc/python-config.in index e0215a2..9ac4414 100644 --- a/Misc/python-config.in +++ b/Misc/python-config.in @@ -45,7 +45,9 @@ elif opt in ('--includes', '--cflags'): elif opt in ('--libs', '--ldflags'): libs = getvar('LIBS').split() + getvar('SYSLIBS').split() libs.append('-lpython'+pyver) - if opt == '--ldflags': + # add the prefix/lib/pythonX.Y/config dir, but only if there is no + # shared library in prefix/lib/. + if opt == '--ldflags' and not getvar('Py_ENABLE_SHARED'): libs.insert(0, '-L' + getvar('LIBPL')) print ' '.join(libs) diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c index b3e33c5..aa706e7 100644 --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -749,6 +749,24 @@ static void _addIntToDict(PyObject* dict, char *name, int value) Py_XDECREF(v); } + +/* The same, when the value is a time_t */ +static void _addTimeTToDict(PyObject* dict, char *name, time_t value) +{ + PyObject* v; + /* if the value fits in regular int, use that. */ +#ifdef HAVE_LONG_LONG + if (sizeof(time_t) > sizeof(long)) + v = PyLong_FromLongLong((PY_LONG_LONG) value); + else +#endif + v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(dict, name, v)) + PyErr_Clear(); + + Py_XDECREF(v); +} + #if (DBVER >= 43) /* add an db_seq_t to a dictionary using the given name as a key */ static void _addDb_seq_tToDict(PyObject* dict, char *name, db_seq_t value) @@ -4633,8 +4651,9 @@ DBEnv_txn_stat(DBEnvObject* self, PyObject* args) } #define MAKE_ENTRY(name) _addIntToDict(d, #name, sp->st_##name) +#define MAKE_TIME_T_ENTRY(name)_addTimeTToDict(d, #name, sp->st_##name) - MAKE_ENTRY(time_ckp); + MAKE_TIME_T_ENTRY(time_ckp); MAKE_ENTRY(last_txnid); MAKE_ENTRY(maxtxns); MAKE_ENTRY(nactive); @@ -4647,6 +4666,7 @@ DBEnv_txn_stat(DBEnvObject* self, PyObject* args) MAKE_ENTRY(region_nowait); #undef MAKE_ENTRY +#undef MAKE_TIME_T_ENTRY free(sp); return d; } diff --git a/Modules/collectionsmodule.c b/Modules/_collectionsmodule.c index fc402f9..2960665 100644 --- a/Modules/collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -846,8 +846,8 @@ static PyTypeObject deque_type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + /* tp_flags */ deque_doc, /* tp_doc */ (traverseproc)deque_traverse, /* tp_traverse */ (inquiry)deque_clear, /* tp_clear */ @@ -1075,7 +1075,7 @@ static PyTypeObject defdict_type; /* Forward */ PyDoc_STRVAR(defdict_missing_doc, "__missing__(key) # Called by __getitem__ for missing key; pseudo-code:\n\ - if self.default_factory is None: raise KeyError(key)\n\ + if self.default_factory is None: raise KeyError((key,))\n\ self[key] = value = self.default_factory()\n\ return value\n\ "); @@ -1087,7 +1087,11 @@ defdict_missing(defdictobject *dd, PyObject *key) PyObject *value; if (factory == NULL || factory == Py_None) { /* XXX Call dict.__missing__(key) */ - PyErr_SetObject(PyExc_KeyError, key); + PyObject *tup; + tup = PyTuple_Pack(1, key); + if (!tup) return NULL; + PyErr_SetObject(PyExc_KeyError, tup); + Py_DECREF(tup); return NULL; } value = PyEval_CallObject(factory, NULL); @@ -1140,7 +1144,6 @@ defdict_reduce(defdictobject *dd) */ PyObject *args; PyObject *items; - PyObject *iteritems; PyObject *result; if (dd->default_factory == NULL || dd->default_factory == Py_None) args = PyTuple_New(0); @@ -1153,15 +1156,9 @@ defdict_reduce(defdictobject *dd) Py_DECREF(args); return NULL; } - iteritems = PyObject_GetIter(items); - Py_DECREF(items); - if (iteritems == NULL) { - Py_DECREF(args); - return NULL; - } result = PyTuple_Pack(5, dd->dict.ob_type, args, - Py_None, Py_None, iteritems); - Py_DECREF(iteritems); + Py_None, Py_None, items); + Py_DECREF(items); Py_DECREF(args); return result; } @@ -1312,8 +1309,8 @@ static PyTypeObject defdict_type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + /* tp_flags */ defdict_doc, /* tp_doc */ defdict_traverse, /* tp_traverse */ (inquiry)defdict_tp_clear, /* tp_clear */ @@ -1344,11 +1341,11 @@ PyDoc_STRVAR(module_doc, "); PyMODINIT_FUNC -initcollections(void) +init_collections(void) { PyObject *m; - m = Py_InitModule3("collections", NULL, module_doc); + m = Py_InitModule3("_collections", NULL, module_doc); if (m == NULL) return; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b0768d6..e7effbe 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -339,24 +339,6 @@ CDataType_from_param(PyObject *type, PyObject *value) ((PyTypeObject *)type)->tp_name, ob_name); return NULL; } -#if 1 -/* XXX Remove this section ??? */ - /* tuple returned by byref: */ - /* ('i', addr, obj) */ - if (PyTuple_Check(value)) { - PyObject *ob; - StgDictObject *dict; - - dict = PyType_stgdict(type); - ob = PyTuple_GetItem(value, 2); - if (dict && ob && - 0 == PyObject_IsInstance(value, dict->proto)) { - Py_INCREF(value); - return value; - } - } -/* ... and leave the rest */ -#endif as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); if (as_parameter) { @@ -1020,6 +1002,12 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } itemsize = itemdict->size; + if (length * itemsize < 0) { + PyErr_SetString(PyExc_OverflowError, + "array too large"); + return NULL; + } + itemalign = itemdict->align; stgdict->size = itemsize * length; @@ -1119,7 +1107,7 @@ _type_ attribute. */ -static char *SIMPLE_TYPE_CHARS = "cbBhHiIlLdfuzZqQPXOv"; +static char *SIMPLE_TYPE_CHARS = "cbBhHiIlLdfuzZqQPXOvt"; static PyObject * c_wchar_p_from_param(PyObject *type, PyObject *value) @@ -2194,21 +2182,32 @@ PyTypeObject CData_Type = { 0, /* tp_free */ }; -static void CData_MallocBuffer(CDataObject *obj, StgDictObject *dict) +static int CData_MallocBuffer(CDataObject *obj, StgDictObject *dict) { 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; + /* The b_needsfree flag does not mean that we actually did + call PyMem_Malloc to allocate the memory block; instead it + means we are the *owner* of the memory and are responsible + for freeing resources associated with the memory. This is + also the reason that b_needsfree is exposed to Python. + */ obj->b_needsfree = 1; } else { /* 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 = (char *)PyMem_Malloc(dict->size); + if (obj->b_ptr == NULL) { + PyErr_NoMemory(); + return -1; + } obj->b_needsfree = 1; memset(obj->b_ptr, 0, dict->size); } obj->b_size = dict->size; + return 0; } PyObject * @@ -2240,7 +2239,10 @@ CData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) cmem->b_base = (CDataObject *)base; cmem->b_index = index; } else { /* copy contents of adr */ - CData_MallocBuffer(cmem, dict); + if (-1 == CData_MallocBuffer(cmem, dict)) { + return NULL; + Py_DECREF(cmem); + } memcpy(cmem->b_ptr, adr, dict->size); cmem->b_index = index; } @@ -2453,7 +2455,10 @@ GenericCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) obj->b_objects = NULL; obj->b_length = dict->length; - CData_MallocBuffer(obj, dict); + if (-1 == CData_MallocBuffer(obj, dict)) { + Py_DECREF(obj); + return NULL; + } return (PyObject *)obj; } /*****************************************************************/ @@ -4535,9 +4540,9 @@ create_comerror(void) #endif static PyObject * -string_at(const char *ptr, Py_ssize_t size) +string_at(const char *ptr, int size) { - if (size == 0) + if (size == -1) return PyString_FromString(ptr); return PyString_FromStringAndSize(ptr, size); } @@ -4622,7 +4627,7 @@ cast(void *ptr, PyObject *src, PyObject *ctype) static PyObject * wstring_at(const wchar_t *ptr, int size) { - if (size == 0) + if (size == -1) size = wcslen(ptr); return PyUnicode_FromWideChar(ptr, size); } diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 572a9fc..fb70963 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -64,6 +64,7 @@ #ifdef MS_WIN32 #include <windows.h> +#include <tchar.h> #else #include "ctypes_dlfcn.h" #endif @@ -97,9 +98,9 @@ static TCHAR *FormatError(DWORD code) 0, NULL); if (n) { - while (isspace(lpMsgBuf[n-1])) + while (_istspace(lpMsgBuf[n-1])) --n; - lpMsgBuf[n] = '\0'; /* rstrip() */ + lpMsgBuf[n] = _T('\0'); /* rstrip() */ } return lpMsgBuf; } diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 799b457..9b90882 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -725,6 +725,35 @@ vBOOL_get(void *ptr, unsigned size) } #endif +#ifdef HAVE_C99_BOOL +#define BOOL_TYPE _Bool +#else +#define BOOL_TYPE char +#undef SIZEOF__BOOL +#define SIZEOF__BOOL 1 +#endif + +static PyObject * +t_set(void *ptr, PyObject *value, unsigned size) +{ + switch (PyObject_IsTrue(value)) { + case -1: + return NULL; + case 0: + *(BOOL_TYPE *)ptr = 0; + _RET(value); + default: + *(BOOL_TYPE *)ptr = 1; + _RET(value); + } +} + +static PyObject * +t_get(void *ptr, unsigned size) +{ + return PyBool_FromLong((long)*(BOOL_TYPE *)ptr); +} + static PyObject * I_set(void *ptr, PyObject *value, unsigned size) { @@ -1432,19 +1461,10 @@ Z_get(void *ptr, unsigned size) #endif #ifdef MS_WIN32 -/* We cannot use SysFreeString as the PyCObject_FromVoidPtr - because of different calling convention -*/ -static void _my_SysFreeString(void *p) -{ - SysFreeString((BSTR)p); -} - static PyObject * BSTR_set(void *ptr, PyObject *value, unsigned size) { BSTR bstr; - PyObject *result; /* convert value into a PyUnicodeObject or NULL */ if (Py_None == value) { @@ -1472,19 +1492,15 @@ BSTR_set(void *ptr, PyObject *value, unsigned size) } else bstr = NULL; - if (bstr) { - result = PyCObject_FromVoidPtr((void *)bstr, _my_SysFreeString); - if (result == NULL) { - SysFreeString(bstr); - return NULL; - } - } else { - result = Py_None; - Py_INCREF(result); - } - + /* free the previous contents, if any */ + if (*(BSTR *)ptr) + SysFreeString(*(BSTR *)ptr); + + /* and store it */ *(BSTR *)ptr = bstr; - return result; + + /* We don't need to keep any other object */ + _RET(value); } @@ -1585,6 +1601,17 @@ static struct fielddesc formattable[] = { { 'X', BSTR_set, BSTR_get, &ffi_type_pointer}, { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #endif +#if SIZEOF__BOOL == 1 + { 't', t_set, t_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */ +#elif SIZEOF__BOOL == SIZEOF_SHORT + { 't', t_set, t_get, &ffi_type_ushort}, +#elif SIZEOF__BOOL == SIZEOF_INT + { 't', t_set, t_get, &ffi_type_uint, I_set_sw, I_get_sw}, +#elif SIZEOF__BOOL == SIZEOF_LONG + { 't', t_set, t_get, &ffi_type_ulong, L_set_sw, L_get_sw}, +#elif SIZEOF__BOOL == SIZEOF_LONG_LONG + { 't', t_set, t_get, &ffi_type_ulong, Q_set_sw, Q_get_sw}, +#endif /* SIZEOF__BOOL */ { 'O', O_set, O_get, &ffi_type_pointer}, { 0, NULL, NULL, NULL}, }; diff --git a/Modules/_ctypes/libffi/configure b/Modules/_ctypes/libffi/configure index 9808384..f65669e 100755 --- a/Modules/_ctypes/libffi/configure +++ b/Modules/_ctypes/libffi/configure @@ -934,7 +934,7 @@ esac else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi - cd "$ac_popdir" + cd $ac_popdir done fi @@ -1973,7 +1973,8 @@ 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=$? @@ -2031,7 +2032,8 @@ 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=$? @@ -2147,7 +2149,8 @@ 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=$? @@ -2201,7 +2204,8 @@ 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=$? @@ -2246,7 +2250,8 @@ 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=$? @@ -2290,7 +2295,8 @@ 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=$? @@ -2617,7 +2623,8 @@ 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=$? @@ -2787,7 +2794,8 @@ 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=$? @@ -2854,7 +2862,8 @@ 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=$? @@ -3038,7 +3047,8 @@ 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=$? @@ -3101,7 +3111,8 @@ 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=$? @@ -3279,7 +3290,8 @@ 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=$? @@ -3396,7 +3408,8 @@ 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=$? @@ -3569,7 +3582,8 @@ 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=$? @@ -3770,7 +3784,8 @@ 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=$? @@ -3833,7 +3848,8 @@ 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=$? @@ -3914,7 +3930,8 @@ 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=$? @@ -4055,7 +4072,8 @@ 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=$? @@ -4191,7 +4209,8 @@ 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=$? @@ -4253,7 +4272,8 @@ 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=$? @@ -4293,7 +4313,8 @@ 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=$? @@ -4349,7 +4370,8 @@ 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=$? @@ -4389,7 +4411,8 @@ 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=$? @@ -4453,7 +4476,8 @@ 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=$? @@ -4484,8 +4508,10 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { 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;} + { { 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;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -4597,7 +4623,8 @@ 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=$? @@ -4659,7 +4686,8 @@ 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=$? @@ -4699,7 +4727,8 @@ 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=$? @@ -4755,7 +4784,8 @@ 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=$? @@ -4795,7 +4825,8 @@ 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=$? @@ -4859,7 +4890,8 @@ 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=$? @@ -4890,8 +4922,10 @@ See \`config.log' for more details." >&2;} esac else if test "$cross_compiling" = yes; then - { { 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;} + { { 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;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF @@ -4986,6 +5020,479 @@ _ACEOF fi +echo "$as_me:$LINENO: checking for _Bool support" >&5 +echo $ECHO_N "checking for _Bool support... $ECHO_C" >&6 +have_c99_bool=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +_Bool x; x = (_Bool)0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_C99_BOOL 1 +_ACEOF + + have_c99_bool=yes + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $have_c99_bool" >&5 +echo "${ECHO_T}$have_c99_bool" >&6 +if test "$have_c99_bool" = yes ; then +echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6 +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((_Bool *) 0) + return 0; +if (sizeof (_Bool)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type__Bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6 + +echo "$as_me:$LINENO: checking size of _Bool" >&5 +echo $ECHO_N "checking size of _Bool... $ECHO_C" >&6 +if test "${ac_cv_sizeof__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type__Bool" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (_Bool))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (_Bool))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (_Bool))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (_Bool))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (_Bool))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + 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' + { (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); }; } && + { ac_try='test -s conftest.$ac_objext' + { (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 + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof__Bool=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (_Bool), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (_Bool), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +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;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (_Bool)); } +unsigned long ulongval () { return (long) (sizeof (_Bool)); } +#include <stdio.h> +#include <stdlib.h> +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (_Bool))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (_Bool)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (_Bool)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +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); } && { ac_try='./conftest$ac_exeext' + { (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 + ac_cv_sizeof__Bool=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (_Bool), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (_Bool), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof__Bool=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof__Bool" >&5 +echo "${ECHO_T}$ac_cv_sizeof__Bool" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF__BOOL $ac_cv_sizeof__Bool +_ACEOF + + +fi + echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 if test "${ac_cv_c_bigendian+set}" = set; then @@ -5021,7 +5528,8 @@ 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=$? @@ -5063,7 +5571,8 @@ 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=$? @@ -5120,7 +5629,8 @@ 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=$? @@ -5252,7 +5762,8 @@ 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=$? @@ -5318,7 +5829,8 @@ 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=$? @@ -6277,6 +6789,11 @@ 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. */ @@ -6315,12 +6832,6 @@ 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 diff --git a/Modules/_ctypes/libffi/configure.ac b/Modules/_ctypes/libffi/configure.ac index 1308034..8870fcb 100644 --- a/Modules/_ctypes/libffi/configure.ac +++ b/Modules/_ctypes/libffi/configure.ac @@ -106,6 +106,17 @@ if test $ac_cv_sizeof_double != $ac_cv_sizeof_long_double; then fi AC_SUBST(HAVE_LONG_DOUBLE) +AC_MSG_CHECKING(for _Bool support) +have_c99_bool=no +AC_TRY_COMPILE([], [_Bool x; x = (_Bool)0;], [ + AC_DEFINE(HAVE_C99_BOOL, 1, [Define this if you have the type _Bool.]) + have_c99_bool=yes +]) +AC_MSG_RESULT($have_c99_bool) +if test "$have_c99_bool" = yes ; then +AC_CHECK_SIZEOF(_Bool, 1) +fi + AC_C_BIGENDIAN AH_VERBATIM([WORDS_BIGENDIAN], [ diff --git a/Modules/_ctypes/libffi/fficonfig.h.in b/Modules/_ctypes/libffi/fficonfig.h.in index bcc5a58..ad2be4e 100644 --- a/Modules/_ctypes/libffi/fficonfig.h.in +++ b/Modules/_ctypes/libffi/fficonfig.h.in @@ -28,6 +28,9 @@ */ #undef HAVE_AS_SPARC_UA_PCREL +/* Define this if you have the type _Bool. */ +#undef HAVE_C99_BOOL + /* Define if __attribute__((visibility("hidden"))) is supported. */ #undef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE @@ -103,6 +106,9 @@ /* The size of a `long double', as computed by sizeof. */ #undef SIZEOF_LONG_DOUBLE +/* The size of a `_Bool', as computed by sizeof. */ +#undef SIZEOF__BOOL + /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 19c3b26..a6a6ba3 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -35,19 +35,22 @@ /* -A number of SysV or ncurses functions don't have wrappers yet; if you need -a given function, add it and send a patch. Here's a list of currently -unsupported functions: +A number of SysV or ncurses functions don't have wrappers yet; if you +need a given function, add it and send a patch. See +http://www.python.org/dev/patches/ for instructions on how to submit +patches to Python. - addchnstr addchstr chgat color_set define_key +Here's a list of currently unsupported functions: + + addchnstr addchstr color_set define_key del_curterm delscreen dupwin inchnstr inchstr innstr keyok - mcprint mvaddchnstr mvaddchstr mvchgat mvcur mvinchnstr - mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr mvwchgat + mcprint mvaddchnstr mvaddchstr mvcur mvinchnstr + mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr mvwinchnstr mvwinchstr mvwinnstr newterm restartterm ripoffline scr_dump scr_init scr_restore scr_set scrl set_curterm set_term setterm tgetent tgetflag tgetnum tgetstr tgoto timeout tputs - vidattr vidputs waddchnstr waddchstr wchgat + vidattr vidputs waddchnstr waddchstr wcolor_set winchnstr winchstr winnstr wmouse_trafo wscrl Low-priority: @@ -620,6 +623,56 @@ int py_mvwdelch(WINDOW *w, int y, int x) } #endif +/* chgat, added by Fabian Kreutz <fabian.kreutz at gmx.net> */ + +static PyObject * +PyCursesWindow_ChgAt(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + int num = -1; + short color; + attr_t attr = A_NORMAL; + int use_xy = FALSE; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"l;attr", &attr)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"il;n,attr", &num, &attr)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"iil;int,int,attr", &y, &x, &attr)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiil;int,int,n,attr", &y, &x, &num, &attr)) + return NULL; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "chgat requires 1 to 4 arguments"); + return NULL; + } + + color = (short)((attr >> 8) & 0xff); + attr = attr - (color << 8); + + if (use_xy == TRUE) { + rtn = mvwchgat(self->win,y,x,num,attr,color,NULL); + touchline(self->win,y,1); + } else { + getyx(self->win,y,x); + rtn = wchgat(self->win,num,attr,color,NULL); + touchline(self->win,y,1); + } + return PyCursesCheckERR(rtn, "chgat"); +} + static PyObject * PyCursesWindow_DelCh(PyCursesWindowObject *self, PyObject *args) @@ -1428,6 +1481,7 @@ static PyMethodDef PyCursesWindow_Methods[] = { {"attron", (PyCFunction)PyCursesWindow_wattron, METH_VARARGS}, {"attrset", (PyCFunction)PyCursesWindow_wattrset, METH_VARARGS}, {"bkgd", (PyCFunction)PyCursesWindow_Bkgd, METH_VARARGS}, + {"chgat", (PyCFunction)PyCursesWindow_ChgAt, METH_VARARGS}, {"bkgdset", (PyCFunction)PyCursesWindow_BkgdSet, METH_VARARGS}, {"border", (PyCFunction)PyCursesWindow_Border, METH_VARARGS}, {"box", (PyCFunction)PyCursesWindow_Box, METH_VARARGS}, @@ -2196,19 +2250,72 @@ PyCurses_QiFlush(PyObject *self, PyObject *args) } } +/* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES + * and _curses.COLS */ +static int +update_lines_cols(void) +{ + PyObject *o; + PyObject *m = PyImport_ImportModule("curses"); + + if (!m) + return 0; + + o = PyInt_FromLong(LINES); + if (!o) { + Py_DECREF(m); + return 0; + } + if (PyObject_SetAttrString(m, "LINES", o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + if (PyDict_SetItemString(ModDict, "LINES", o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + o = PyInt_FromLong(COLS); + if (!o) { + Py_DECREF(m); + return 0; + } + if (PyObject_SetAttrString(m, "COLS", o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + if (PyDict_SetItemString(ModDict, "COLS", o)) { + Py_DECREF(m); + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + Py_DECREF(m); + return 1; +} + #ifdef HAVE_CURSES_RESIZETERM static PyObject * PyCurses_ResizeTerm(PyObject *self, PyObject *args) { int lines; int columns; + PyObject *result; PyCursesInitialised if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) return NULL; - return PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); + result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; } #endif @@ -2220,12 +2327,19 @@ PyCurses_Resize_Term(PyObject *self, PyObject *args) int lines; int columns; + PyObject *result; + PyCursesInitialised if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) return NULL; - return PyCursesCheckERR(resize_term(lines, columns), "resize_term"); + result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); + if (!result) + return NULL; + if (!update_lines_cols()) + return NULL; + return result; } #endif /* HAVE_CURSES_RESIZE_TERM */ diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index abfca4e..02e9e53 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -360,7 +360,7 @@ PyLocale_strxfrm(PyObject* self, PyObject* args) buf = PyMem_Malloc(n1); if (!buf) return PyErr_NoMemory(); - n2 = strxfrm(buf, s, n1); + n2 = strxfrm(buf, s, n1) + 1; if (n2 > n1) { /* more space needed */ buf = PyMem_Realloc(buf, n2); diff --git a/Modules/_struct.c b/Modules/_struct.c index 5fc9991..a790e52 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -847,7 +847,7 @@ bu_longlong(const char *p, const formatdef *f) } while (--i > 0); /* Extend the sign bit. */ if (SIZEOF_LONG_LONG > f->size) - x |= -(x & (1L << ((8 * f->size) - 1))); + x |= -(x & ((PY_LONG_LONG)1 << ((8 * f->size) - 1))); if (x >= LONG_MIN && x <= LONG_MAX) return PyInt_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); return PyLong_FromLongLong(x); @@ -1083,7 +1083,7 @@ lu_longlong(const char *p, const formatdef *f) } while (i > 0); /* Extend the sign bit. */ if (SIZEOF_LONG_LONG > f->size) - x |= -(x & (1L << ((8 * f->size) - 1))); + x |= -(x & ((PY_LONG_LONG)1 << ((8 * f->size) - 1))); if (x >= LONG_MIN && x <= LONG_MAX) return PyInt_FromLong(Py_SAFE_DOWNCAST(x, PY_LONG_LONG, long)); return PyLong_FromLongLong(x); @@ -1532,21 +1532,35 @@ strings."); static PyObject * s_unpack(PyObject *self, PyObject *inputstr) { + char *start; + Py_ssize_t len; + PyObject *args=NULL, *result; PyStructObject *soself = (PyStructObject *)self; assert(PyStruct_Check(self)); assert(soself->s_codes != NULL); - if (inputstr != NULL && PyBytes_Check(inputstr) && - PyBytes_GET_SIZE(inputstr) == soself->s_size) { - return s_unpack_internal(soself, PyBytes_AS_STRING(inputstr)); + if (inputstr == NULL) + goto fail; + if (PyString_Check(inputstr) && + PyString_GET_SIZE(inputstr) == soself->s_size) { + return s_unpack_internal(soself, PyString_AS_STRING(inputstr)); } - if (inputstr == NULL || !PyString_Check(inputstr) || - PyString_GET_SIZE(inputstr) != soself->s_size) { - PyErr_Format(StructError, - "unpack requires a string argument of length %zd", - soself->s_size); + args = PyTuple_Pack(1, inputstr); + if (args == NULL) return NULL; - } - return s_unpack_internal(soself, PyString_AS_STRING(inputstr)); + if (!PyArg_ParseTuple(args, "s#:unpack", &start, &len)) + goto fail; + if (soself->s_size != len) + goto fail; + result = s_unpack_internal(soself, start); + Py_DECREF(args); + return result; + +fail: + Py_XDECREF(args); + PyErr_Format(StructError, + "unpack requires a string argument of length %zd", + soself->s_size); + return NULL; } PyDoc_STRVAR(s_unpack_from__doc__, diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 7fe0b26..626bdfc 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1147,12 +1147,19 @@ array_reduce(arrayobject *array) dict = Py_None; Py_INCREF(dict); } - result = Py_BuildValue("O(cs#)O", - array->ob_type, - array->ob_descr->typecode, - array->ob_item, - array->ob_size * array->ob_descr->itemsize, - dict); + if (array->ob_size > 0) { + result = Py_BuildValue("O(cs#)O", + array->ob_type, + array->ob_descr->typecode, + array->ob_item, + array->ob_size * array->ob_descr->itemsize, + dict); + } else { + result = Py_BuildValue("O(c)O", + array->ob_type, + array->ob_descr->typecode, + dict); + } Py_DECREF(dict); return result; } @@ -1762,6 +1769,8 @@ static PyMappingMethods array_as_mapping = { (objobjargproc)array_ass_subscr }; +static const void *emptybuf = ""; + static Py_ssize_t array_buffer_getreadbuf(arrayobject *self, Py_ssize_t index, const void **ptr) { @@ -1771,6 +1780,8 @@ array_buffer_getreadbuf(arrayobject *self, Py_ssize_t index, const void **ptr) return -1; } *ptr = (void *)self->ob_item; + if (*ptr == NULL) + *ptr = emptybuf; return self->ob_size*self->ob_descr->itemsize; } @@ -1783,6 +1794,8 @@ array_buffer_getwritebuf(arrayobject *self, Py_ssize_t index, const void **ptr) return -1; } *ptr = (void *)self->ob_item; + if (*ptr == NULL) + *ptr = emptybuf; return self->ob_size*self->ob_descr->itemsize; } diff --git a/Modules/binascii.c b/Modules/binascii.c index 4dee451..91309f6 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1160,12 +1160,14 @@ binascii_b2a_qp (PyObject *self, PyObject *args, PyObject *kwargs) if ((data[in] > 126) || (data[in] == '=') || (header && data[in] == '_') || - ((data[in] == '.') && (linelen == 1)) || + ((data[in] == '.') && (linelen == 0) && + (data[in+1] == '\n' || data[in+1] == '\r' || data[in+1] == 0)) || (!istext && ((data[in] == '\r') || (data[in] == '\n'))) || ((data[in] == '\t' || data[in] == ' ') && (in + 1 == datalen)) || ((data[in] < 33) && (data[in] != '\r') && (data[in] != '\n') && - (quotetabs && ((data[in] != '\t') || (data[in] != ' '))))) + (quotetabs || + (!quotetabs && ((data[in] != '\t') && (data[in] != ' ')))))) { if ((linelen + 3) >= MAXLINESIZE) { linelen = 0; @@ -1230,12 +1232,14 @@ binascii_b2a_qp (PyObject *self, PyObject *args, PyObject *kwargs) if ((data[in] > 126) || (data[in] == '=') || (header && data[in] == '_') || - ((data[in] == '.') && (linelen == 1)) || + ((data[in] == '.') && (linelen == 0) && + (data[in+1] == '\n' || data[in+1] == '\r' || data[in+1] == 0)) || (!istext && ((data[in] == '\r') || (data[in] == '\n'))) || ((data[in] == '\t' || data[in] == ' ') && (in + 1 == datalen)) || ((data[in] < 33) && (data[in] != '\r') && (data[in] != '\n') && - (quotetabs && ((data[in] != '\t') || (data[in] != ' '))))) + (quotetabs || + (!quotetabs && ((data[in] != '\t') && (data[in] != ' ')))))) { if ((linelen + 3 )>= MAXLINESIZE) { odata[out++] = '='; diff --git a/Modules/bz2module.c b/Modules/bz2module.c index 9b39442..a40334d 100644 --- a/Modules/bz2module.c +++ b/Modules/bz2module.c @@ -1553,6 +1553,8 @@ BZ2Comp_compress(BZ2CompObject *self, PyObject *args) Util_CatchBZ2Error(bzerror); goto error; } + if (bzs->avail_in == 0) + break; /* no more input data */ if (bzs->avail_out == 0) { bufsize = Util_NewBufferSize(bufsize); if (_PyString_Resize(&ret, bufsize) < 0) { @@ -1562,8 +1564,6 @@ BZ2Comp_compress(BZ2CompObject *self, PyObject *args) bzs->next_out = BUF(ret) + (BZS_TOTAL_OUT(bzs) - totalout); bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); - } else if (bzs->avail_in == 0) { - break; } } @@ -1845,6 +1845,8 @@ BZ2Decomp_decompress(BZ2DecompObject *self, PyObject *args) Util_CatchBZ2Error(bzerror); goto error; } + if (bzs->avail_in == 0) + break; /* no more input data */ if (bzs->avail_out == 0) { bufsize = Util_NewBufferSize(bufsize); if (_PyString_Resize(&ret, bufsize) < 0) { @@ -1855,8 +1857,6 @@ BZ2Decomp_decompress(BZ2DecompObject *self, PyObject *args) bzs->next_out = BUF(ret) + (BZS_TOTAL_OUT(bzs) - totalout); bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); - } else if (bzs->avail_in == 0) { - break; } } @@ -2134,6 +2134,13 @@ bz2_decompress(PyObject *self, PyObject *args) Py_DECREF(ret); return NULL; } + if (bzs->avail_in == 0) { + BZ2_bzDecompressEnd(bzs); + PyErr_SetString(PyExc_ValueError, + "couldn't find end of stream"); + Py_DECREF(ret); + return NULL; + } if (bzs->avail_out == 0) { bufsize = Util_NewBufferSize(bufsize); if (_PyString_Resize(&ret, bufsize) < 0) { @@ -2143,12 +2150,6 @@ bz2_decompress(PyObject *self, PyObject *args) } bzs->next_out = BUF(ret) + BZS_TOTAL_OUT(bzs); bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); - } else if (bzs->avail_in == 0) { - BZ2_bzDecompressEnd(bzs); - PyErr_SetString(PyExc_ValueError, - "couldn't find end of stream"); - Py_DECREF(ret); - return NULL; } } diff --git a/Modules/cPickle.c b/Modules/cPickle.c index ad2b91f..7b8ed66 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -533,11 +533,12 @@ read_file(Unpicklerobject *self, char **s, Py_ssize_t n) self->buf_size = size; } else if (n > self->buf_size) { - self->buf = (char *)realloc(self->buf, n); - if (!self->buf) { + char *newbuf = (char *)realloc(self->buf, n); + if (!newbuf) { PyErr_NoMemory(); return -1; } + self->buf = newbuf; self->buf_size = n; } @@ -576,6 +577,7 @@ readline_file(Unpicklerobject *self, char **s) i = 0; while (1) { int bigger; + char *newbuf; for (; i < (self->buf_size - 1); i++) { if (feof(self->fp) || (self->buf[i] = getc(self->fp)) == '\n') { @@ -589,11 +591,12 @@ readline_file(Unpicklerobject *self, char **s) PyErr_NoMemory(); return -1; } - self->buf = (char *)realloc(self->buf, bigger); - if (!self->buf) { + newbuf = (char *)realloc(self->buf, bigger); + if (!newbuf) { PyErr_NoMemory(); return -1; } + self->buf = newbuf; self->buf_size = bigger; } } @@ -4199,17 +4202,19 @@ load_mark(Unpicklerobject *self) */ if ((self->num_marks + 1) >= self->marks_size) { + int *marks; s=self->marks_size+20; if (s <= self->num_marks) s=self->num_marks + 1; if (self->marks == NULL) - self->marks=(int *)malloc(s * sizeof(int)); + marks=(int *)malloc(s * sizeof(int)); else - self->marks=(int *)realloc(self->marks, + marks=(int *)realloc(self->marks, s * sizeof(int)); - if (! self->marks) { + if (!marks) { PyErr_NoMemory(); return -1; } + self->marks = marks; self->marks_size = s; } diff --git a/Modules/cStringIO.c b/Modules/cStringIO.c index 2ed1485..42f2de2 100644 --- a/Modules/cStringIO.c +++ b/Modules/cStringIO.c @@ -348,13 +348,17 @@ O_seek(Oobject *self, PyObject *args) { } if (position > self->buf_size) { + char *newbuf; self->buf_size*=2; if (self->buf_size <= position) self->buf_size=position+1; - self->buf = (char*) realloc(self->buf,self->buf_size); - if (!self->buf) { + newbuf = (char*) realloc(self->buf,self->buf_size); + if (!newbuf) { + free(self->buf); + self->buf = 0; self->buf_size=self->pos=0; return PyErr_NoMemory(); } + self->buf = newbuf; } else if (position < 0) position=0; @@ -375,6 +379,7 @@ static int O_cwrite(PyObject *self, const char *c, Py_ssize_t l) { Py_ssize_t newl; Oobject *oself; + char *newbuf; if (!IO__opencheck(IOOOBJECT(self))) return -1; oself = (Oobject *)self; @@ -386,12 +391,15 @@ O_cwrite(PyObject *self, const char *c, Py_ssize_t l) { assert(newl + 1 < INT_MAX); oself->buf_size = (int)(newl+1); } - oself->buf = (char*)realloc(oself->buf, oself->buf_size); - if (!oself->buf) { + newbuf = (char*)realloc(oself->buf, oself->buf_size); + if (!newbuf) { PyErr_SetString(PyExc_MemoryError,"out of memory"); + free(oself->buf); + oself->buf = 0; oself->buf_size = oself->pos = 0; return -1; } + oself->buf = newbuf; } memcpy(oself->buf+oself->pos,c,l); diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 3b62f3d..96263f8 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -3644,6 +3644,12 @@ datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp, return NULL; fraction = timestamp - (double)timet; us = (int)round_to_long(fraction * 1e6); + if (us < 0) { + /* Truncation towards zero is not what we wanted + for negative numbers (Python's mod semantics) */ + timet -= 1; + us += 1000000; + } /* If timestamp is less than one microsecond smaller than a * full second, round up. Otherwise, ValueErrors are raised * for some floats. */ diff --git a/Modules/getpath.c b/Modules/getpath.c index 78bfaf9..09fbe10 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -26,7 +26,7 @@ * as best as is possible, but most imports will fail. * * Before any searches are done, the location of the executable is - * determined. If argv[0] has one or more slashs in it, it is used + * determined. If argv[0] has one or more slashes in it, it is used * unchanged. Otherwise, it must have been invoked from the shell's path, * so we search $PATH for the named executable and use that. If the * executable was not found on $PATH (or there was no $PATH environment diff --git a/Modules/main.c b/Modules/main.c index d604879..66dec8d 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -214,13 +214,11 @@ Py_Main(int argc, char **argv) char *module = NULL; FILE *fp = stdin; char *p; - int inspect = 0; int unbuffered = 0; int skipfirstline = 0; int stdin_is_interactive = 0; int help = 0; int version = 0; - int saw_inspect_flag = 0; int saw_unbuffered_flag = 0; PyCompilerFlags cf; @@ -268,8 +266,7 @@ Py_Main(int argc, char **argv) break; case 'i': - inspect++; - saw_inspect_flag = 1; + Py_InspectFlag++; Py_InteractiveFlag++; break; @@ -338,9 +335,9 @@ Py_Main(int argc, char **argv) return 0; } - if (!saw_inspect_flag && + if (!Py_InspectFlag && (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') - inspect = 1; + Py_InspectFlag = 1; if (!saw_unbuffered_flag && (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') unbuffered = 1; @@ -468,7 +465,7 @@ Py_Main(int argc, char **argv) PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); - if ((inspect || (command == NULL && filename == NULL && module == NULL)) && + if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) && isatty(fileno(stdin))) { PyObject *v; v = PyImport_ImportModule("readline"); @@ -487,6 +484,7 @@ Py_Main(int argc, char **argv) } else { if (filename == NULL && stdin_is_interactive) { + Py_InspectFlag = 0; /* do exit on SystemExit */ RunStartupFile(&cf); } /* XXX */ @@ -499,16 +497,18 @@ Py_Main(int argc, char **argv) /* Check this environment variable at the end, to give programs the * opportunity to set it from Python. */ - if (!saw_inspect_flag && + if (!Py_InspectFlag && (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') { - inspect = 1; + Py_InspectFlag = 1; } - if (inspect && stdin_is_interactive && - (filename != NULL || command != NULL || module != NULL)) + if (Py_InspectFlag && stdin_is_interactive && + (filename != NULL || command != NULL || module != NULL)) { + Py_InspectFlag = 0; /* XXX */ sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0; + } WaitForThreadShutdown(); diff --git a/Modules/operator.c b/Modules/operator.c index 90fac7961..fbc7470 100644 --- a/Modules/operator.c +++ b/Modules/operator.c @@ -164,43 +164,41 @@ static PyObject* op_getslice(PyObject *s, PyObject *a) { PyObject *a1; - int a2,a3; + Py_ssize_t a2, a3; - if (!PyArg_ParseTuple(a,"Oii:getslice",&a1,&a2,&a3)) + if (!PyArg_ParseTuple(a, "Onn:getslice", &a1, &a2, &a3)) return NULL; - return PySequence_GetSlice(a1,a2,a3); + return PySequence_GetSlice(a1, a2, a3); } static PyObject* op_setslice(PyObject *s, PyObject *a) { PyObject *a1, *a4; - int a2,a3; + Py_ssize_t a2, a3; - if (!PyArg_ParseTuple(a,"OiiO:setslice",&a1,&a2,&a3,&a4)) + if (!PyArg_ParseTuple(a, "OnnO:setslice", &a1, &a2, &a3, &a4)) return NULL; - if (-1 == PySequence_SetSlice(a1,a2,a3,a4)) + if (-1 == PySequence_SetSlice(a1, a2, a3, a4)) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject* op_delslice(PyObject *s, PyObject *a) { PyObject *a1; - int a2,a3; + Py_ssize_t a2, a3; - if(! PyArg_ParseTuple(a,"Oii:delslice",&a1,&a2,&a3)) + if (!PyArg_ParseTuple(a, "Onn:delslice", &a1, &a2, &a3)) return NULL; - if (-1 == PySequence_DelSlice(a1,a2,a3)) + if (-1 == PySequence_DelSlice(a1, a2, a3)) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #undef spam1 diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 01fcfb2..12b3472 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -844,14 +844,48 @@ check_gfax() *(FARPROC*)&gfaxw = GetProcAddress(hKernel32, "GetFileAttributesExW"); } +static BOOL +attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) +{ + HANDLE hFindFile; + WIN32_FIND_DATAA FileData; + hFindFile = FindFirstFileA(pszFile, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) + return FALSE; + FindClose(hFindFile); + pfad->dwFileAttributes = FileData.dwFileAttributes; + pfad->ftCreationTime = FileData.ftCreationTime; + pfad->ftLastAccessTime = FileData.ftLastAccessTime; + pfad->ftLastWriteTime = FileData.ftLastWriteTime; + pfad->nFileSizeHigh = FileData.nFileSizeHigh; + pfad->nFileSizeLow = FileData.nFileSizeLow; + return TRUE; +} + +static BOOL +attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad) +{ + HANDLE hFindFile; + WIN32_FIND_DATAW FileData; + hFindFile = FindFirstFileW(pszFile, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) + return FALSE; + FindClose(hFindFile); + pfad->dwFileAttributes = FileData.dwFileAttributes; + pfad->ftCreationTime = FileData.ftCreationTime; + pfad->ftLastAccessTime = FileData.ftLastAccessTime; + pfad->ftLastWriteTime = FileData.ftLastWriteTime; + pfad->nFileSizeHigh = FileData.nFileSizeHigh; + pfad->nFileSizeLow = FileData.nFileSizeLow; + return TRUE; +} + static BOOL WINAPI Py_GetFileAttributesExA(LPCSTR pszFile, GET_FILEEX_INFO_LEVELS level, LPVOID pv) { BOOL result; - HANDLE hFindFile; - WIN32_FIND_DATAA FileData; LPWIN32_FILE_ATTRIBUTE_DATA pfad = pv; /* First try to use the system's implementation, if that is available and either succeeds to gives an error other than @@ -873,17 +907,7 @@ Py_GetFileAttributesExA(LPCSTR pszFile, accept). */ if (GetFileAttributesA(pszFile) == 0xFFFFFFFF) return FALSE; - hFindFile = FindFirstFileA(pszFile, &FileData); - if (hFindFile == INVALID_HANDLE_VALUE) - return FALSE; - FindClose(hFindFile); - pfad->dwFileAttributes = FileData.dwFileAttributes; - pfad->ftCreationTime = FileData.ftCreationTime; - pfad->ftLastAccessTime = FileData.ftLastAccessTime; - pfad->ftLastWriteTime = FileData.ftLastWriteTime; - pfad->nFileSizeHigh = FileData.nFileSizeHigh; - pfad->nFileSizeLow = FileData.nFileSizeLow; - return TRUE; + return attributes_from_dir(pszFile, pfad); } static BOOL WINAPI @@ -892,8 +916,6 @@ Py_GetFileAttributesExW(LPCWSTR pszFile, LPVOID pv) { BOOL result; - HANDLE hFindFile; - WIN32_FIND_DATAW FileData; LPWIN32_FILE_ATTRIBUTE_DATA pfad = pv; /* First try to use the system's implementation, if that is available and either succeeds to gives an error other than @@ -915,17 +937,7 @@ Py_GetFileAttributesExW(LPCWSTR pszFile, accept). */ if (GetFileAttributesW(pszFile) == 0xFFFFFFFF) return FALSE; - hFindFile = FindFirstFileW(pszFile, &FileData); - if (hFindFile == INVALID_HANDLE_VALUE) - return FALSE; - FindClose(hFindFile); - pfad->dwFileAttributes = FileData.dwFileAttributes; - pfad->ftCreationTime = FileData.ftCreationTime; - pfad->ftLastAccessTime = FileData.ftLastAccessTime; - pfad->ftLastWriteTime = FileData.ftLastWriteTime; - pfad->nFileSizeHigh = FileData.nFileSizeHigh; - pfad->nFileSizeLow = FileData.nFileSizeLow; - return TRUE; + return attributes_from_dir_w(pszFile, pfad); } static int @@ -936,10 +948,20 @@ win32_stat(const char* path, struct win32_stat *result) char *dot; /* XXX not supported on Win95 and NT 3.x */ if (!Py_GetFileAttributesExA(path, GetFileExInfoStandard, &info)) { - /* Protocol violation: we explicitly clear errno, instead of - setting it to a POSIX error. Callers should use GetLastError. */ - errno = 0; - return -1; + if (GetLastError() != ERROR_SHARING_VIOLATION) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } else { + /* Could not get attributes on open file. Fall back to + reading the directory. */ + if (!attributes_from_dir(path, &info)) { + /* Very strange. This should not fail now */ + errno = 0; + return -1; + } + } } code = attribute_data_to_stat(&info, result); if (code != 0) @@ -964,10 +986,20 @@ win32_wstat(const wchar_t* path, struct win32_stat *result) WIN32_FILE_ATTRIBUTE_DATA info; /* XXX not supported on Win95 and NT 3.x */ if (!Py_GetFileAttributesExW(path, GetFileExInfoStandard, &info)) { - /* Protocol violation: we explicitly clear errno, instead of - setting it to a POSIX error. Callers should use GetLastError. */ - errno = 0; - return -1; + if (GetLastError() != ERROR_SHARING_VIOLATION) { + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + errno = 0; + return -1; + } else { + /* Could not get attributes on open file. Fall back to + reading the directory. */ + if (!attributes_from_dir_w(path, &info)) { + /* Very strange. This should not fail now */ + errno = 0; + return -1; + } + } } code = attribute_data_to_stat(&info, result); if (code < 0) @@ -4809,18 +4841,19 @@ _PyPopenCreateProcess(char *cmdstring, (sizeof(modulepath)/sizeof(modulepath[0])) -strlen(modulepath)); if (stat(modulepath, &statinfo) != 0) { + size_t mplen = sizeof(modulepath)/sizeof(modulepath[0]); /* Eeek - file-not-found - possibly an embedding situation - see if we can locate it in sys.prefix */ strncpy(modulepath, Py_GetExecPrefix(), - sizeof(modulepath)/sizeof(modulepath[0])); + mplen); + modulepath[mplen-1] = '\0'; if (modulepath[strlen(modulepath)-1] != '\\') strcat(modulepath, "\\"); strncat(modulepath, szConsoleSpawn, - (sizeof(modulepath)/sizeof(modulepath[0])) - -strlen(modulepath)); + mplen-strlen(modulepath)); /* No where else to look - raise an easily identifiable error, rather than leaving Windows to report "file not found" - as the user is probably blissfully @@ -6215,16 +6248,23 @@ static PyObject * posix_fdopen(PyObject *self, PyObject *args) { int fd; - char *mode = "r"; + char *orgmode = "r"; int bufsize = -1; FILE *fp; PyObject *f; - if (!PyArg_ParseTuple(args, "i|si", &fd, &mode, &bufsize)) + char *mode; + if (!PyArg_ParseTuple(args, "i|si", &fd, &orgmode, &bufsize)) return NULL; - if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') { - PyErr_Format(PyExc_ValueError, - "invalid file mode '%s'", mode); + /* Sanitize mode. See fileobject.c */ + mode = PyMem_MALLOC(strlen(orgmode)+3); + if (!mode) { + PyErr_NoMemory(); + return NULL; + } + strcpy(mode, orgmode); + if (_PyFile_SanitizeMode(mode)) { + PyMem_FREE(mode); return NULL; } Py_BEGIN_ALLOW_THREADS @@ -6245,10 +6285,11 @@ posix_fdopen(PyObject *self, PyObject *args) #else fp = fdopen(fd, mode); #endif + PyMem_FREE(mode); Py_END_ALLOW_THREADS if (fp == NULL) return posix_error(); - f = PyFile_FromFile(fp, "<fdopen>", mode, fclose); + f = PyFile_FromFile(fp, "<fdopen>", orgmode, fclose); if (f != NULL) PyFile_SetBufSize(f, bufsize); return f; diff --git a/Modules/readline.c b/Modules/readline.c index 853874b..fd800ff 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -34,6 +34,8 @@ #ifdef HAVE_RL_COMPLETION_MATCHES #define completion_matches(x, y) \ rl_completion_matches((x), ((rl_compentry_func_t *)(y))) +#else +extern char **completion_matches(char *, rl_compentry_func_t *); #endif @@ -632,7 +634,7 @@ on_pre_input_hook(void) /* C function to call the Python completer. */ static char * -on_completion(char *text, int state) +on_completion(const char *text, int state) { char *result = NULL; if (completer != NULL) { diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 91625a3..38ffba7 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -2384,14 +2384,14 @@ sock_recv_into(PySocketSockObject *s, PyObject *args, PyObject *kwds) int buflen; /* Get the buffer's memory */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "w#|ii:recv", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "w#|ii:recv_into", kwlist, &buf, &buflen, &recvlen, &flags)) return NULL; assert(buf != 0 && buflen > 0); if (recvlen < 0) { PyErr_SetString(PyExc_ValueError, - "negative buffersize in recv"); + "negative buffersize in recv_into"); return NULL; } if (recvlen == 0) { @@ -2507,6 +2507,12 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args) if (!PyArg_ParseTuple(args, "i|i:recvfrom", &recvlen, &flags)) return NULL; + if (recvlen < 0) { + PyErr_SetString(PyExc_ValueError, + "negative buffersize in recvfrom"); + return NULL; + } + buf = PyString_FromStringAndSize((char *) 0, recvlen); if (buf == NULL) return NULL; @@ -2553,14 +2559,15 @@ sock_recvfrom_into(PySocketSockObject *s, PyObject *args, PyObject* kwds) PyObject *addr = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "w#|ii:recvfrom", kwlist, - &buf, &buflen, &recvlen, &flags)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "w#|ii:recvfrom_into", + kwlist, &buf, &buflen, + &recvlen, &flags)) return NULL; assert(buf != 0 && buflen > 0); if (recvlen < 0) { PyErr_SetString(PyExc_ValueError, - "negative buffersize in recv"); + "negative buffersize in recvfrom_into"); return NULL; } if (recvlen == 0) { diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 13c0535..4e24299 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -175,7 +175,8 @@ time_clock(PyObject *self, PyObject *unused) if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { /* Unlikely to happen - this works on all intel machines at least! Revert to clock() */ - return PyFloat_FromDouble(clock()); + return PyFloat_FromDouble(((double)clock()) / + CLOCKS_PER_SEC); } divisor = (double)freq.QuadPart; } diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 8e1f4d7..82dd7c1 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -252,12 +252,51 @@ Py_complex PyComplex_AsCComplex(PyObject *op) { Py_complex cv; + PyObject *newop = NULL; + static PyObject *complex_str = NULL; + + assert(op); + /* If op is already of type PyComplex_Type, return its value */ if (PyComplex_Check(op)) { return ((PyComplexObject *)op)->cval; } + /* If not, use op's __complex__ method, if it exists */ + + /* return -1 on failure */ + cv.real = -1.; + cv.imag = 0.; + + { + PyObject *complexfunc; + if (!complex_str) { + if (!(complex_str = PyString_FromString("__complex__"))) + return cv; + } + complexfunc = _PyType_Lookup(op->ob_type, complex_str); + /* complexfunc is a borrowed reference */ + if (complexfunc) { + newop = PyObject_CallFunctionObjArgs(complexfunc, op, NULL); + if (!newop) + return cv; + } + } + + if (newop) { + if (!PyComplex_Check(newop)) { + PyErr_SetString(PyExc_TypeError, + "__complex__ should return a complex object"); + Py_DECREF(newop); + return cv; + } + cv = ((PyComplexObject *)newop)->cval; + Py_DECREF(newop); + return cv; + } + /* If neither of the above works, interpret op as a float giving the + real part of the result, and fill in the imaginary part as 0. */ else { + /* PyFloat_AsDouble will return -1 on failure */ cv.real = PyFloat_AsDouble(op); - cv.imag = 0.; return cv; } } @@ -512,7 +551,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) } else if (errno == ERANGE) { PyErr_SetString(PyExc_OverflowError, - "complex exponentiaion"); + "complex exponentiation"); return NULL; } return PyComplex_FromCComplex(p); @@ -652,7 +691,7 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) const char *s, *start; char *end; double x=0.0, y=0.0, z; - int got_re=0, got_im=0, done=0; + int got_re=0, got_im=0, got_bracket=0, done=0; int digit_or_dot; int sw_error=0; int sign; @@ -692,10 +731,17 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) start = s; while (*s && isspace(Py_CHARMASK(*s))) s++; - if (s[0] == '\0') { + if (s[0] == '\0') { PyErr_SetString(PyExc_ValueError, "complex() arg is an empty string"); return NULL; + } + if (s[0] == '(') { + /* Skip over possible bracket from repr(). */ + got_bracket = 1; + s++; + while (*s && isspace(Py_CHARMASK(*s))) + s++; } z = -1.0; @@ -714,13 +760,26 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) if(!done) sw_error=1; break; + case ')': + if (!got_bracket || !(got_re || got_im)) { + sw_error=1; + break; + } + got_bracket=0; + done=1; + s++; + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (*s) sw_error=1; + break; + case '-': sign = -1; /* Fallthrough */ case '+': if (done) sw_error=1; s++; - if ( *s=='\0'||*s=='+'||*s=='-' || + if ( *s=='\0'||*s=='+'||*s=='-'||*s==')'|| isspace(Py_CHARMASK(*s)) ) sw_error=1; break; @@ -746,7 +805,7 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) if (isspace(Py_CHARMASK(*s))) { while (*s && isspace(Py_CHARMASK(*s))) s++; - if (s[0] != '\0') + if (*s && *s != ')') sw_error=1; else done = 1; @@ -792,7 +851,7 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v) } while (s - start < len && !sw_error); - if (sw_error) { + if (sw_error || got_bracket) { PyErr_SetString(PyExc_ValueError, "complex() arg is a malformed string"); return NULL; @@ -817,12 +876,14 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) &r, &i)) return NULL; - /* Special-case for single argument that is already complex */ + /* Special-case for a single argument when type(arg) is complex. */ if (PyComplex_CheckExact(r) && i == NULL && type == &PyComplex_Type) { /* Note that we can't know whether it's safe to return a complex *subclass* instance as-is, hence the restriction - to exact complexes here. */ + to exact complexes here. If either the input or the + output is a complex subclass, it will be handled below + as a non-orthogonal vector. */ Py_INCREF(r); return r; } @@ -873,6 +934,14 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } return NULL; } + + /* If we get this far, then the "real" and "imag" parts should + both be treated as numbers, and the constructor should return a + complex number equal to (real + imag*1j). + + Note that we do NOT assume the input to already be in canonical + form; the "real" and "imag" parts might themselves be complex + numbers, which slightly complicates the code below. */ if (PyComplex_Check(r)) { /* Note that if r is of a complex subtype, we're only retaining its real & imag parts here, and the return @@ -883,8 +952,14 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } else { + /* The "real" part really is entirely real, and contributes + nothing in the imaginary direction. + Just treat it as a double. */ + cr.imag = 0.0; tmp = PyNumber_Float(r); if (own_r) { + /* r was a newly created complex number, rather + than the original "real" argument. */ Py_DECREF(r); } if (tmp == NULL) @@ -897,7 +972,6 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } cr.real = PyFloat_AsDouble(tmp); Py_DECREF(tmp); - cr.imag = 0.0; } if (i == NULL) { ci.real = 0.0; @@ -906,13 +980,19 @@ complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) else if (PyComplex_Check(i)) ci = ((PyComplexObject*)i)->cval; else { + /* The "imag" part really is entirely imaginary, and + contributes nothing in the real direction. + Just treat it as a double. */ + ci.imag = 0.0; tmp = (*nbi->nb_float)(i); if (tmp == NULL) return NULL; ci.real = PyFloat_AsDouble(tmp); Py_DECREF(tmp); - ci.imag = 0.; } + /* If the input was in canonical form, then the "real" and "imag" + parts are real numbers, so that ci.real and cr.imag are zero. + We need this correction in case they were not real numbers. */ cr.real -= ci.imag; cr.imag += ci.real; return complex_subtype_from_c_complex(type, cr); diff --git a/Objects/dictobject.c b/Objects/dictobject.c index d2a60c4..1da24f4 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1205,6 +1205,24 @@ dict_fromkeys(PyObject *cls, PyObject *args) if (d == NULL) return NULL; + if (PyDict_CheckExact(d) && PyAnySet_CheckExact(seq)) { + dictobject *mp = (dictobject *)d; + Py_ssize_t pos = 0; + PyObject *key; + long hash; + + if (dictresize(mp, PySet_GET_SIZE(seq))) + return NULL; + + while (_PySet_NextEntry(seq, &pos, &key, &hash)) { + Py_INCREF(key); + Py_INCREF(value); + if (insertdict(mp, key, hash, value)) + return NULL; + } + return d; + } + it = PyObject_GetIter(seq); if (it == NULL){ Py_DECREF(d); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 6832fd9..2b05cc5 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -24,6 +24,8 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyBaseExceptionObject *self; self = (PyBaseExceptionObject *)type->tp_alloc(type, 0); + if (!self) + return NULL; /* the dict is created on the fly in PyObject_GenericSetAttr */ self->message = self->dict = NULL; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 13f64fb..d12e132 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -138,17 +138,16 @@ fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, char *mode, ignore stuff they don't understand... write or append mode with universal newline support is expressly forbidden by PEP 278. Additionally, remove the 'U' from the mode string as platforms - won't know what it is. */ -/* zero return is kewl - one is un-kewl */ -static int -sanitize_the_mode(char *mode) + won't know what it is. Non-zero return signals an exception */ +int +_PyFile_SanitizeMode(char *mode) { char *upos; size_t len = strlen(mode); if (!len) { PyErr_SetString(PyExc_ValueError, "empty mode string"); - return 1; + return -1; } upos = strchr(mode, 'U'); @@ -159,7 +158,7 @@ sanitize_the_mode(char *mode) PyErr_Format(PyExc_ValueError, "universal newline " "mode can only be used with modes " "starting with 'r'"); - return 1; + return -1; } if (mode[0] != 'r') { @@ -174,7 +173,7 @@ sanitize_the_mode(char *mode) } else if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') { PyErr_Format(PyExc_ValueError, "mode string must begin with " "one of 'r', 'w', 'a' or 'U', not '%.200s'", mode); - return 1; + return -1; } return 0; @@ -203,7 +202,7 @@ open_the_file(PyFileObject *f, char *name, char *mode) } strcpy(newmode, mode); - if (sanitize_the_mode(newmode)) { + if (_PyFile_SanitizeMode(newmode)) { f = NULL; goto cleanup; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 83dacfd5..4f195ee 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -48,7 +48,7 @@ frame_getlineno(PyFrameObject *f, void *closure) } /* Setter for f_lineno - you can set f_lineno from within a trace function in - * order to jump to a given line of code, subject to some restrictions. Most + * order to jump to a given line of code, subject to some restrictions. Most * lines are OK to jump to because they don't make any assumptions about the * state of the stack (obvious because you could remove the line and the code * would still work without any stack errors), but there are some constructs @@ -68,7 +68,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) int new_lineno = 0; /* The new value of f_lineno */ int new_lasti = 0; /* The new value of f_lasti */ int new_iblock = 0; /* The new value of f_iblock */ - char *code = NULL; /* The bytecode for the frame... */ + unsigned char *code = NULL; /* The bytecode for the frame... */ Py_ssize_t code_len = 0; /* ...and its length */ char *lnotab = NULL; /* Iterating over co_lnotab */ Py_ssize_t lnotab_len = 0; /* (ditto) */ @@ -85,7 +85,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) int blockstack[CO_MAXBLOCKS]; /* Walking the 'finally' blocks */ int in_finally[CO_MAXBLOCKS]; /* (ditto) */ int blockstack_top = 0; /* (ditto) */ - int setup_op = 0; /* (ditto) */ + unsigned char setup_op = 0; /* (ditto) */ /* f_lineno must be an integer. */ if (!PyInt_CheckExact(p_new_lineno)) { @@ -137,7 +137,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) } /* We're now ready to look at the bytecode. */ - PyString_AsStringAndSize(f->f_code->co_code, &code, &code_len); + PyString_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len); min_addr = MIN(new_lasti, f->f_lasti); max_addr = MAX(new_lasti, f->f_lasti); @@ -159,7 +159,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) /* You can't jump into or out of a 'finally' block because the 'try' * block leaves something on the stack for the END_FINALLY to clean - * up. So we walk the bytecode, maintaining a simulated blockstack. + * up. So we walk the bytecode, maintaining a simulated blockstack. * When we reach the old or new address and it's in a 'finally' block * we note the address of the corresponding SETUP_FINALLY. The jump * is only legal if neither address is in a 'finally' block or @@ -383,7 +383,7 @@ static PyGetSetDef frame_getsetlist[] = { ob_type == &Frametype f_back next item on free list, or NULL f_stacksize size of value stack - ob_size size of localsplus + ob_size size of localsplus Note that the value and block stacks are preserved -- this can save another malloc() call or two (and two free() calls as well!). Also note that, unlike for integers, each frame object is a @@ -408,12 +408,12 @@ frame_dealloc(PyFrameObject *f) PyObject **p, **valuestack; PyCodeObject *co; - PyObject_GC_UnTrack(f); + PyObject_GC_UnTrack(f); Py_TRASHCAN_SAFE_BEGIN(f) /* Kill all local variables */ - valuestack = f->f_valuestack; - for (p = f->f_localsplus; p < valuestack; p++) - Py_CLEAR(*p); + valuestack = f->f_valuestack; + for (p = f->f_localsplus; p < valuestack; p++) + Py_CLEAR(*p); /* Free stack */ if (f->f_stacktop != NULL) { @@ -430,18 +430,18 @@ frame_dealloc(PyFrameObject *f) Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); - co = f->f_code; - if (co->co_zombieframe == NULL) - co->co_zombieframe = f; + co = f->f_code; + if (co->co_zombieframe == NULL) + co->co_zombieframe = f; else if (numfree < MAXFREELIST) { ++numfree; f->f_back = free_list; free_list = f; - } + } else PyObject_GC_Del(f); - Py_DECREF(co); + Py_DECREF(co); Py_TRASHCAN_SAFE_END(f) } @@ -482,12 +482,12 @@ frame_clear(PyFrameObject *f) int i, slots; /* 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. - */ + * 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; + f->f_stacktop = NULL; Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); @@ -514,10 +514,10 @@ PyTypeObject PyFrame_Type = { "frame", sizeof(PyFrameObject), sizeof(PyObject *), - (destructor)frame_dealloc, /* tp_dealloc */ + (destructor)frame_dealloc, /* tp_dealloc */ 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ @@ -530,8 +530,8 @@ PyTypeObject PyFrame_Type = { PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ - 0, /* tp_doc */ - (traverseproc)frame_traverse, /* tp_traverse */ + 0, /* tp_doc */ + (traverseproc)frame_traverse, /* tp_traverse */ (inquiry)frame_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -579,7 +579,7 @@ 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 || @@ -599,39 +599,39 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, Py_INCREF(builtins); } if (code->co_zombieframe != NULL) { - f = code->co_zombieframe; - code->co_zombieframe = NULL; - _Py_NewReference((PyObject *)f); - assert(f->f_code == code); + f = code->co_zombieframe; + code->co_zombieframe = NULL; + _Py_NewReference((PyObject *)f); + assert(f->f_code == code); } - else { - Py_ssize_t extras, ncells, nfrees; - ncells = PyTuple_GET_SIZE(code->co_cellvars); - nfrees = PyTuple_GET_SIZE(code->co_freevars); - extras = code->co_stacksize + code->co_nlocals + ncells + - nfrees; - if (free_list == NULL) { - f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, - extras); - if (f == NULL) { - Py_DECREF(builtins); - return NULL; - } - } - else { - assert(numfree > 0); - --numfree; - f = free_list; - free_list = free_list->f_back; - if (f->ob_size < extras) { - f = PyObject_GC_Resize(PyFrameObject, f, extras); - if (f == NULL) { - Py_DECREF(builtins); - return NULL; - } - } - _Py_NewReference((PyObject *)f); - } + else { + Py_ssize_t extras, ncells, nfrees; + ncells = PyTuple_GET_SIZE(code->co_cellvars); + nfrees = PyTuple_GET_SIZE(code->co_freevars); + extras = code->co_stacksize + code->co_nlocals + ncells + + nfrees; + if (free_list == NULL) { + f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, + extras); + if (f == NULL) { + Py_DECREF(builtins); + return NULL; + } + } + else { + assert(numfree > 0); + --numfree; + f = free_list; + free_list = free_list->f_back; + if (f->ob_size < extras) { + f = PyObject_GC_Resize(PyFrameObject, f, extras); + if (f == NULL) { + Py_DECREF(builtins); + return NULL; + } + } + _Py_NewReference((PyObject *)f); + } f->f_code = code; extras = code->co_nlocals + ncells + nfrees; @@ -640,7 +640,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, f->f_localsplus[i] = NULL; f->f_locals = NULL; f->f_trace = NULL; - f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; + f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_stacktop = f->f_valuestack; f->f_builtins = builtins; @@ -659,13 +659,13 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, Py_DECREF(f); return NULL; } - f->f_locals = locals; + f->f_locals = locals; } else { if (locals == NULL) locals = globals; Py_INCREF(locals); - f->f_locals = locals; + f->f_locals = locals; } f->f_tstate = tstate; @@ -701,18 +701,38 @@ PyFrame_BlockPop(PyFrameObject *f) return b; } -/* Convert between "fast" version of locals and dictionary version */ +/* Convert between "fast" version of locals and dictionary version. + + map and values are input arguments. map is a tuple of strings. + values is an array of PyObject*. At index i, map[i] is the name of + the variable with value values[i]. The function copies the first + nmap variable from map/values into dict. If values[i] is NULL, + the variable is deleted from dict. + + If deref is true, then the values being copied are cell variables + and the value is extracted from the cell variable before being put + in dict. + + Exceptions raised while modifying the dict are silently ignored, + because there is no good way to report them. + */ static void map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, - Py_ssize_t deref) + int deref) { Py_ssize_t j; + assert(PyTuple_Check(map)); + assert(PyDict_Check(dict)); + assert(PyTuple_Size(map) >= nmap); for (j = nmap; --j >= 0; ) { PyObject *key = PyTuple_GET_ITEM(map, j); PyObject *value = values[j]; - if (deref) + assert(PyString_Check(key)); + if (deref) { + assert(PyCell_Check(value)); value = PyCell_GET(value); + } if (value == NULL) { if (PyObject_DelItem(dict, key) != 0) PyErr_Clear(); @@ -724,29 +744,55 @@ map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, } } +/* Copy values from the "locals" dict into the fast locals. + + dict is an input argument containing string keys representing + variables names and arbitrary PyObject* as values. + + map and values are input arguments. map is a tuple of strings. + values is an array of PyObject*. At index i, map[i] is the name of + the variable with value values[i]. The function copies the first + nmap variable from map/values into dict. If values[i] is NULL, + the variable is deleted from dict. + + If deref is true, then the values being copied are cell variables + and the value is extracted from the cell variable before being put + in dict. If clear is true, then variables in map but not in dict + are set to NULL in map; if clear is false, variables missing in + dict are ignored. + + Exceptions raised while modifying the dict are silently ignored, + because there is no good way to report them. +*/ + static void dict_to_map(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values, - Py_ssize_t deref, int clear) + int deref, int clear) { Py_ssize_t j; + assert(PyTuple_Check(map)); + assert(PyDict_Check(dict)); + assert(PyTuple_Size(map) >= nmap); for (j = nmap; --j >= 0; ) { PyObject *key = PyTuple_GET_ITEM(map, j); PyObject *value = PyObject_GetItem(dict, key); - if (value == NULL) + assert(PyString_Check(key)); + /* We only care about NULLs if clear is true. */ + if (value == NULL) { PyErr_Clear(); + if (!clear) + continue; + } if (deref) { - if (value || clear) { - if (PyCell_GET(values[j]) != value) { - if (PyCell_Set(values[j], value) < 0) - PyErr_Clear(); - } - } - } else if (value != NULL || clear) { - if (values[j] != value) { - Py_XINCREF(value); - Py_XDECREF(values[j]); - values[j] = value; + assert(PyCell_Check(values[j])); + if (PyCell_GET(values[j]) != value) { + if (PyCell_Set(values[j], value) < 0) + PyErr_Clear(); } + } else if (values[j] != value) { + Py_XINCREF(value); + Py_XDECREF(values[j]); + values[j] = value; } Py_XDECREF(value); } @@ -761,7 +807,7 @@ PyFrame_FastToLocals(PyFrameObject *f) PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; Py_ssize_t j; - int ncells, nfreevars; + int ncells, nfreevars; if (f == NULL) return; locals = f->f_locals; @@ -788,8 +834,18 @@ PyFrame_FastToLocals(PyFrameObject *f) if (ncells || nfreevars) { map_to_dict(co->co_cellvars, ncells, locals, fast + co->co_nlocals, 1); - map_to_dict(co->co_freevars, nfreevars, - locals, fast + co->co_nlocals + ncells, 1); + /* If the namespace is unoptimized, then one of the + following cases applies: + 1. It does not contain free variables, because it + uses import * or is a top-level namespace. + 2. It is a class namespace. + We don't want to accidentally copy free variables + into the locals dict used by the class. + */ + if (co->co_flags & CO_OPTIMIZED) { + map_to_dict(co->co_freevars, nfreevars, + locals, fast + co->co_nlocals + ncells, 1); + } } PyErr_Restore(error_type, error_value, error_traceback); } @@ -827,7 +883,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) locals, fast + co->co_nlocals, 1, clear); dict_to_map(co->co_freevars, nfreevars, locals, fast + co->co_nlocals + ncells, 1, - clear); + clear); } PyErr_Restore(error_type, error_value, error_traceback); } diff --git a/Objects/longobject.c b/Objects/longobject.c index 3b4a675..95abfdd 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1985,6 +1985,8 @@ long_divrem(PyLongObject *a, PyLongObject *b, a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { /* |a| < |b|. */ *pdiv = (PyLongObject*)PyLong_FromLong(0); + if (*pdiv == NULL) + return -1; Py_INCREF(a); *prem = (PyLongObject *) a; return 0; @@ -1995,6 +1997,10 @@ long_divrem(PyLongObject *a, PyLongObject *b, if (z == NULL) return -1; *prem = (PyLongObject *) PyLong_FromLong((long)rem); + if (*prem == NULL) { + Py_DECREF(z); + return -1; + } } else { z = x_divrem(a, b, prem); @@ -3514,16 +3520,23 @@ long_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (base == -909) return PyNumber_Long(x); else if (PyString_Check(x)) { - char *s = PyString_AS_STRING(x); - char *end; - PyObject *r = PyLong_FromString(s, &end, base); - if (r != NULL && end != s + PyString_GET_SIZE(x)) { - PyErr_SetString(PyExc_ValueError, - "null byte in argument for int()"); - Py_DECREF(r); - r = NULL; + /* Since PyLong_FromString doesn't have a length parameter, + * check here for possible NULs in the string. */ + char *string = PyString_AS_STRING(x); + if (strlen(string) != PyString_Size(x)) { + /* create a repr() of the input string, + * just like PyLong_FromString does. */ + PyObject *srepr; + srepr = PyObject_Repr(x); + 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; } - return r; + return PyLong_FromString(PyString_AS_STRING(x), NULL, base); } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(x)) diff --git a/Objects/setobject.c b/Objects/setobject.c index 2210edf..65ca8b1 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -2154,7 +2154,7 @@ PySet_Add(PyObject *set, PyObject *key) } int -_PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry) +_PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **key) { setentry *entry_ptr; @@ -2164,7 +2164,23 @@ _PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **entry) } if (set_next((PySetObject *)set, pos, &entry_ptr) == 0) return 0; - *entry = entry_ptr->key; + *key = entry_ptr->key; + return 1; +} + +int +_PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, long *hash) +{ + setentry *entry; + + if (!PyAnySet_Check(set)) { + PyErr_BadInternalCall(); + return -1; + } + if (set_next((PySetObject *)set, pos, &entry) == 0) + return 0; + *key = entry->key; + *hash = entry->hash; return 1; } diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 0075a4e..d56d69b 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -274,9 +274,19 @@ indices, and the stride length of the extended slice described by\n\ S. Out of bounds indices are clipped in a manner consistent with the\n\ handling of normal slices."); +static PyObject * +slice_reduce(PySliceObject* self) +{ + return Py_BuildValue("O(OOO)", self->ob_type, self->start, self->stop, self->step); +} + +PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); + static PyMethodDef slice_methods[] = { {"indices", (PyCFunction)slice_indices, METH_O, slice_indices_doc}, + {"__reduce__", (PyCFunction)slice_reduce, + METH_NOARGS, reduce_doc}, {NULL, NULL} }; diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 94943f6..ee29c70 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -2346,10 +2346,10 @@ static PyObject * string_translate(PyStringObject *self, PyObject *args) { register char *input, *output; - register const char *table; + const char *table; register Py_ssize_t i, c, changed = 0; PyObject *input_obj = (PyObject*)self; - const char *table1, *output_start, *del_table=NULL; + const char *output_start, *del_table=NULL; Py_ssize_t inlen, tablen, dellen = 0; PyObject *result; int trans_table[256]; @@ -2360,9 +2360,13 @@ string_translate(PyStringObject *self, PyObject *args) return NULL; if (PyString_Check(tableobj)) { - table1 = PyString_AS_STRING(tableobj); + table = PyString_AS_STRING(tableobj); tablen = PyString_GET_SIZE(tableobj); } + else if (tableobj == Py_None) { + table = NULL; + tablen = 256; + } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(tableobj)) { /* Unicode .translate() does not support the deletechars @@ -2376,7 +2380,7 @@ string_translate(PyStringObject *self, PyObject *args) return PyUnicode_Translate((PyObject *)self, tableobj, NULL); } #endif - else if (PyObject_AsCharBuffer(tableobj, &table1, &tablen)) + else if (PyObject_AsCharBuffer(tableobj, &table, &tablen)) return NULL; if (tablen != 256) { @@ -2405,7 +2409,6 @@ string_translate(PyStringObject *self, PyObject *args) dellen = 0; } - table = table1; inlen = PyString_GET_SIZE(input_obj); result = PyString_FromStringAndSize((char *)NULL, inlen); if (result == NULL) @@ -2413,7 +2416,7 @@ string_translate(PyStringObject *self, PyObject *args) output_start = output = PyString_AsString(result); input = PyString_AS_STRING(input_obj); - if (dellen == 0) { + if (dellen == 0 && table != NULL) { /* If no deletions are required, use faster code */ for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); @@ -2427,8 +2430,13 @@ string_translate(PyStringObject *self, PyObject *args) return input_obj; } - for (i = 0; i < 256; i++) - trans_table[i] = Py_CHARMASK(table[i]); + if (table == NULL) { + for (i = 0; i < 256; i++) + trans_table[i] = Py_CHARMASK(i); + } else { + for (i = 0; i < 256; i++) + trans_table[i] = Py_CHARMASK(table[i]); + } for (i = 0; i < dellen; i++) trans_table[(int) Py_CHARMASK(del_table[i])] = -1; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c6091df..bf77bea 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -265,9 +265,10 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context) PyObject* mro; PyArg_UnpackTuple(PyList_GET_ITEM(temp, i), "", 2, 2, &cls, &mro); - Py_DECREF(cls->tp_mro); + Py_INCREF(mro); + ob = cls->tp_mro; cls->tp_mro = mro; - Py_INCREF(cls->tp_mro); + Py_DECREF(ob); } Py_DECREF(temp); goto bail; @@ -520,7 +521,7 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg) if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) /* For a heaptype, the instances count as references - to the type. Traverse the type so the collector + to the type. Traverse the type so the collector can find cycles involving this link. */ Py_VISIT(type); @@ -640,7 +641,7 @@ subtype_dealloc(PyObject *self) assert(base); } - /* If we added a weaklist, we clear it. Do this *before* calling + /* If we added a weaklist, we clear it. Do this *before* calling the finalizer (__del__), clearing slots, or clearing the instance dict. */ @@ -711,7 +712,7 @@ subtype_dealloc(PyObject *self) A. Read the comment titled "Trashcan mechanism" in object.h. For one, this explains why there must be a call to GC-untrack - before the trashcan begin macro. Without understanding the + before the trashcan begin macro. Without understanding the trashcan code, the answers to the following questions don't make sense. @@ -719,7 +720,7 @@ subtype_dealloc(PyObject *self) GC-track again afterward? A. In the case that the base class is GC-aware, the base class - probably GC-untracks the object. If it does that using the + probably GC-untracks the object. If it does that using the UNTRACK macro, this will crash when the object is already untracked. Because we don't know what the base class does, the only safe thing is to make sure the object is tracked when we @@ -727,19 +728,19 @@ subtype_dealloc(PyObject *self) requires that the object is *untracked* before it is called. So the dance becomes: - GC untrack + GC untrack trashcan begin GC track - Q. Why did the last question say "immediately GC-track again"? - It's nowhere near immediately. + Q. Why did the last question say "immediately GC-track again"? + It's nowhere near immediately. - A. Because the code *used* to re-track immediately. Bad Idea. - self has a refcount of 0, and if gc ever gets its hands on it - (which can happen if any weakref callback gets invoked), it - looks like trash to gc too, and gc also tries to delete self - then. But we're already deleting self. Double dealloction is - a subtle disaster. + A. Because the code *used* to re-track immediately. Bad Idea. + self has a refcount of 0, and if gc ever gets its hands on it + (which can happen if any weakref callback gets invoked), it + looks like trash to gc too, and gc also tries to delete self + then. But we're already deleting self. Double dealloction is + a subtle disaster. Q. Why the bizarre (net-zero) manipulation of _PyTrash_delete_nesting around the trashcan macros? @@ -752,17 +753,17 @@ subtype_dealloc(PyObject *self) - subtype_dealloc() is called - the trashcan limit is not yet reached, so the trashcan level - is incremented and the code between trashcan begin and end is - executed + is incremented and the code between trashcan begin and end is + executed - this destroys much of the object's contents, including its - slots and __dict__ + slots and __dict__ - basedealloc() is called; this is really list_dealloc(), or - some other type which also uses the trashcan macros + some other type which also uses the trashcan macros - the trashcan limit is now reached, so the object is put on the - trashcan's to-be-deleted-later list + trashcan's to-be-deleted-later list - basedealloc() returns @@ -771,13 +772,13 @@ subtype_dealloc(PyObject *self) - subtype_dealloc() returns - later, the trashcan code starts deleting the objects from its - to-be-deleted-later list + to-be-deleted-later list - subtype_dealloc() is called *AGAIN* for the same object - at the very least (if the destroyed slots and __dict__ don't - cause problems) the object's type gets decref'ed a second - time, which is *BAD*!!! + cause problems) the object's type gets decref'ed a second + time, which is *BAD*!!! The remedy is to make sure that if the code between trashcan begin and end in subtype_dealloc() is called, the code between @@ -789,7 +790,7 @@ subtype_dealloc(PyObject *self) But now it's possible that a chain of objects consisting solely of objects whose deallocator is subtype_dealloc() will defeat the trashcan mechanism completely: the decremented level means - that the effective level never reaches the limit. Therefore, we + that the effective level never reaches the limit. Therefore, we *increment* the level *before* entering the trashcan block, and matchingly decrement it after leaving. This means the trashcan code will trigger a little early, but that's no big deal. @@ -840,7 +841,7 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) /* Internal routines to do a method lookup in the type without looking in the instance dictionary (so we can't use PyObject_GetAttr) but still binding - it to the instance. The arguments are the object, + it to the instance. The arguments are the object, the method name as a C string, and the address of a static variable used to cache the interned Python string. @@ -883,7 +884,7 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj) } /* A variation of PyObject_CallMethod that uses lookup_method() - instead of PyObject_GetAttrString(). This uses the same convention + instead of PyObject_GetAttrString(). This uses the same convention as lookup_method to cache the interned name string object. */ static PyObject * @@ -1044,7 +1045,7 @@ check_duplicates(PyObject *list) It's hard to produce a good error message. In the absence of better insight into error reporting, report the classes that were candidates - to be put next into the MRO. There is some conflict between the + to be put next into the MRO. There is some conflict between the order in which they should be put in the MRO, but it's hard to diagnose what constraint can't be satisfied. */ @@ -1116,7 +1117,7 @@ pmerge(PyObject *acc, PyObject* to_merge) { if (remain[i] >= PyList_GET_SIZE(cur_list)) { empty_cnt++; continue; - } + } /* Choose next candidate for MRO. @@ -1193,7 +1194,7 @@ mro_implementation(PyTypeObject *type) if (parentMRO == NULL) { Py_DECREF(to_merge); return NULL; - } + } PyList_SET_ITEM(to_merge, i, parentMRO); } @@ -1510,32 +1511,69 @@ valid_identifier(PyObject *s) static PyObject * _unicode_to_string(PyObject *slots, Py_ssize_t nslots) { - PyObject *tmp = slots; - PyObject *o, *o1; + PyObject *tmp = NULL; + PyObject *slot_name, *new_name; Py_ssize_t i; - ssizessizeargfunc copy = slots->ob_type->tp_as_sequence->sq_slice; + for (i = 0; i < nslots; i++) { - if (PyUnicode_Check(o = PyTuple_GET_ITEM(tmp, i))) { - if (tmp == slots) { - tmp = copy(slots, 0, PyTuple_GET_SIZE(slots)); + if (PyUnicode_Check(slot_name = PyTuple_GET_ITEM(slots, i))) { + if (tmp == NULL) { + tmp = PySequence_List(slots); if (tmp == NULL) return NULL; } - o1 = _PyUnicode_AsDefaultEncodedString - (o, NULL); - if (o1 == NULL) { + new_name = _PyUnicode_AsDefaultEncodedString(slot_name, + NULL); + if (new_name == NULL) { Py_DECREF(tmp); - return 0; + return NULL; } - Py_INCREF(o1); - Py_DECREF(o); - PyTuple_SET_ITEM(tmp, i, o1); + Py_INCREF(new_name); + PyList_SET_ITEM(tmp, i, new_name); + Py_DECREF(slot_name); } } - return tmp; + if (tmp != NULL) { + slots = PyList_AsTuple(tmp); + Py_DECREF(tmp); + } + return slots; } #endif +/* Forward */ +static int +object_init(PyObject *self, PyObject *args, PyObject *kwds); + +static int +type_init(PyObject *cls, PyObject *args, PyObject *kwds) +{ + int res; + + assert(args != NULL && PyTuple_Check(args)); + assert(kwds == NULL || PyDict_Check(kwds)); + + if (kwds != NULL && PyDict_Check(kwds) && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "type.__init__() takes no keyword arguments"); + return -1; + } + + if (args != NULL && PyTuple_Check(args) && + (PyTuple_GET_SIZE(args) != 1 && PyTuple_GET_SIZE(args) != 3)) { + PyErr_SetString(PyExc_TypeError, + "type.__init__() takes 1 or 3 arguments"); + return -1; + } + + /* Call object.__init__(self) now. */ + /* XXX Could call super(type, cls).__init__() but what's the point? */ + args = PyTuple_GetSlice(args, 0, 0); + res = object_init(cls, args, NULL); + Py_DECREF(args); + return res; +} + static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { @@ -1652,7 +1690,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) /* Have slots */ /* Make it into a tuple */ - if (PyString_Check(slots)) + if (PyString_Check(slots) || PyUnicode_Check(slots)) slots = PyTuple_Pack(1, slots); else slots = PySequence_Tuple(slots); @@ -1677,12 +1715,12 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) #ifdef Py_USING_UNICODE tmp = _unicode_to_string(slots, nslots); + if (tmp == NULL) + goto bad_slots; if (tmp != slots) { Py_DECREF(slots); slots = tmp; } - if (!tmp) - return NULL; #endif /* Check for valid slot names and two special cases */ for (i = 0; i < nslots; i++) { @@ -1713,8 +1751,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } } - /* Copy slots into yet another tuple, demangling names */ - newslots = PyTuple_New(nslots - add_dict - add_weak); + /* Copy slots into a list, mangle names and sort them. + Sorted names are needed for __class__ assignment. + Convert them back to tuple at the end. + */ + newslots = PyList_New(nslots - add_dict - add_weak); if (newslots == NULL) goto bad_slots; for (i = j = 0; i < nslots; i++) { @@ -1725,15 +1766,25 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) (add_weak && strcmp(s, "__weakref__") == 0)) continue; tmp =_Py_Mangle(name, tmp); - if (!tmp) - goto bad_slots; - PyTuple_SET_ITEM(newslots, j, tmp); + if (!tmp) + goto bad_slots; + PyList_SET_ITEM(newslots, j, tmp); j++; } assert(j == nslots - add_dict - add_weak); nslots = j; Py_DECREF(slots); - slots = newslots; + if (PyList_Sort(newslots) == -1) { + Py_DECREF(bases); + Py_DECREF(newslots); + return NULL; + } + slots = PyList_AsTuple(newslots); + Py_DECREF(newslots); + if (slots == NULL) { + Py_DECREF(bases); + return NULL; + } /* Secondary bases may provide weakrefs or dict */ if (nbases > 1 && @@ -1824,13 +1875,13 @@ 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 = (char *)PyObject_MALLOC(n+1); + char *tp_doc = (char *)PyObject_MALLOC(n+1); if (tp_doc == NULL) { Py_DECREF(type); return NULL; } memcpy(tp_doc, PyString_AS_STRING(doc), n+1); - type->tp_doc = tp_doc; + type->tp_doc = tp_doc; } } @@ -1856,13 +1907,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) PyTuple_GET_ITEM(slots, i)); mp->type = T_OBJECT_EX; mp->offset = slotoffset; - if (base->tp_weaklistoffset == 0 && - strcmp(mp->name, "__weakref__") == 0) { - add_weak++; - mp->type = T_OBJECT; - mp->flags = READONLY; - type->tp_weaklistoffset = slotoffset; - } + + /* __dict__ and __weakref__ are already filtered out */ + assert(strcmp(mp->name, "__dict__") != 0); + assert(strcmp(mp->name, "__weakref__") != 0); + slotoffset += sizeof(PyObject *); } } @@ -2070,9 +2119,9 @@ type_dealloc(PyTypeObject *type) Py_XDECREF(type->tp_mro); Py_XDECREF(type->tp_cache); Py_XDECREF(type->tp_subclasses); - /* A type's tp_doc is heap allocated, unlike the tp_doc slots - * of most other objects. It's okay to cast it to char *. - */ + /* A type's tp_doc is heap allocated, unlike the tp_doc slots + * of most other objects. It's okay to cast it to char *. + */ PyObject_Free((char *)type->tp_doc); Py_XDECREF(et->ht_name); Py_XDECREF(et->ht_slots); @@ -2191,7 +2240,7 @@ PyTypeObject PyType_Type = { sizeof(PyMemberDef), /* tp_itemsize */ (destructor)type_dealloc, /* tp_dealloc */ 0, /* tp_print */ - 0, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)type_repr, /* tp_repr */ @@ -2221,37 +2270,113 @@ PyTypeObject PyType_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */ - 0, /* tp_init */ + type_init, /* tp_init */ 0, /* tp_alloc */ type_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ + PyObject_GC_Del, /* tp_free */ (inquiry)type_is_gc, /* tp_is_gc */ }; /* The base type of all types (eventually)... except itself. */ +/* You may wonder why object.__new__() only complains about arguments + when object.__init__() is not overridden, and vice versa. + + Consider the use cases: + + 1. When neither is overridden, we want to hear complaints about + excess (i.e., any) arguments, since their presence could + indicate there's a bug. + + 2. When defining an Immutable type, we are likely to override only + __new__(), since __init__() is called too late to initialize an + Immutable object. Since __new__() defines the signature for the + type, it would be a pain to have to override __init__() just to + stop it from complaining about excess arguments. + + 3. When defining a Mutable type, we are likely to override only + __init__(). So here the converse reasoning applies: we don't + want to have to override __new__() just to stop it from + complaining. + + 4. When __init__() is overridden, and the subclass __init__() calls + object.__init__(), the latter should complain about excess + arguments; ditto for __new__(). + + Use cases 2 and 3 make it unattractive to unconditionally check for + excess arguments. The best solution that addresses all four use + cases is as follows: __init__() complains about excess arguments + unless __new__() is overridden and __init__() is not overridden + (IOW, if __init__() is overridden or __new__() is not overridden); + symmetrically, __new__() complains about excess arguments unless + __init__() is overridden and __new__() is not overridden + (IOW, if __new__() is overridden or __init__() is not overridden). + + However, for backwards compatibility, this breaks too much code. + Therefore, in 2.6, we'll *warn* about excess arguments when both + methods are overridden; for all other cases we'll use the above + rules. + +*/ + +/* Forward */ +static PyObject * +object_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static int +excess_args(PyObject *args, PyObject *kwds) +{ + return PyTuple_GET_SIZE(args) || + (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)); +} + static int object_init(PyObject *self, PyObject *args, PyObject *kwds) { - return 0; + int err = 0; + if (excess_args(args, kwds)) { + PyTypeObject *type = self->ob_type; + if (type->tp_init != object_init && + type->tp_new != object_new) + { + err = PyErr_WarnEx(PyExc_DeprecationWarning, + "object.__init__() takes no parameters", + 1); + } + else if (type->tp_init != object_init || + type->tp_new == object_new) + { + PyErr_SetString(PyExc_TypeError, + "object.__init__() takes no parameters"); + err = -1; + } + } + return err; } -/* If we don't have a tp_new for a new-style class, new will use this one. - Therefore this should take no arguments/keywords. However, this new may - also be inherited by objects that define a tp_init but no tp_new. These - objects WILL pass argumets to tp_new, because it gets the same args as - tp_init. So only allow arguments if we aren't using the default init, in - which case we expect init to handle argument parsing. */ static PyObject * object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type->tp_init == object_init && (PyTuple_GET_SIZE(args) || - (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)))) { - PyErr_SetString(PyExc_TypeError, - "default __new__ takes no parameters"); - return NULL; + int err = 0; + if (excess_args(args, kwds)) { + if (type->tp_new != object_new && + type->tp_init != object_init) + { + err = PyErr_WarnEx(PyExc_DeprecationWarning, + "object.__new__() takes no parameters", + 1); + } + else if (type->tp_new != object_new || + type->tp_init == object_init) + { + PyErr_SetString(PyExc_TypeError, + "object.__new__() takes no parameters"); + err = -1; + } } + if (err < 0) + return NULL; return type->tp_alloc(type, 0); } @@ -2368,6 +2493,7 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) { PyTypeObject *base = a->tp_base; Py_ssize_t size; + PyObject *slots_a, *slots_b; if (base != b->tp_base) return 0; @@ -2378,6 +2504,15 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) size += sizeof(PyObject *); if (a->tp_weaklistoffset == size && b->tp_weaklistoffset == size) size += sizeof(PyObject *); + + /* Check slots compliance */ + slots_a = ((PyHeapTypeObject *)a)->ht_slots; + slots_b = ((PyHeapTypeObject *)b)->ht_slots; + if (slots_a && slots_b) { + if (PyObject_Compare(slots_a, slots_b) != 0) + return 0; + size += sizeof(PyObject *) * PyTuple_GET_SIZE(slots_a); + } return size == a->tp_basicsize && size == b->tp_basicsize; } @@ -2661,11 +2796,54 @@ reduce_2(PyObject *obj) return res; } +/* + * There were two problems when object.__reduce__ and object.__reduce_ex__ + * were implemented in the same function: + * - trying to pickle an object with a custom __reduce__ method that + * fell back to object.__reduce__ in certain circumstances led to + * infinite recursion at Python level and eventual RuntimeError. + * - Pickling objects that lied about their type by overwriting the + * __class__ descriptor could lead to infinite recursion at C level + * and eventual segfault. + * + * Because of backwards compatibility, the two methods still have to + * behave in the same way, even if this is not required by the pickle + * protocol. This common functionality was moved to the _common_reduce + * function. + */ +static PyObject * +_common_reduce(PyObject *self, int proto) +{ + PyObject *copy_reg, *res; + + if (proto >= 2) + return reduce_2(self); + + copy_reg = import_copy_reg(); + if (!copy_reg) + return NULL; + + res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto); + Py_DECREF(copy_reg); + + return res; +} + +static PyObject * +object_reduce(PyObject *self, PyObject *args) +{ + int proto = 0; + + if (!PyArg_ParseTuple(args, "|i:__reduce__", &proto)) + return NULL; + + return _common_reduce(self, proto); +} + static PyObject * object_reduce_ex(PyObject *self, PyObject *args) { - /* Call copy_reg._reduce_ex(self, proto) */ - PyObject *reduce, *copy_reg, *res; + PyObject *reduce, *res; int proto = 0; if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto)) @@ -2701,23 +2879,13 @@ object_reduce_ex(PyObject *self, PyObject *args) Py_DECREF(reduce); } - if (proto >= 2) - return reduce_2(self); - - copy_reg = import_copy_reg(); - if (!copy_reg) - return NULL; - - res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto); - Py_DECREF(copy_reg); - - return res; + return _common_reduce(self, proto); } static PyMethodDef object_methods[] = { {"__reduce_ex__", object_reduce_ex, METH_VARARGS, PyDoc_STR("helper for pickle")}, - {"__reduce__", object_reduce_ex, METH_VARARGS, + {"__reduce__", object_reduce, METH_VARARGS, PyDoc_STR("helper for pickle")}, {0} }; @@ -2725,13 +2893,13 @@ static PyMethodDef object_methods[] = { PyTypeObject PyBaseObject_Type = { PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ + 0, /* ob_size */ "object", /* tp_name */ sizeof(PyObject), /* tp_basicsize */ 0, /* tp_itemsize */ object_dealloc, /* tp_dealloc */ 0, /* tp_print */ - 0, /* tp_getattr */ + 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ object_repr, /* tp_repr */ @@ -2763,7 +2931,7 @@ PyTypeObject PyBaseObject_Type = { object_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ object_new, /* tp_new */ - PyObject_Del, /* tp_free */ + PyObject_Del, /* tp_free */ }; @@ -3136,9 +3304,9 @@ PyType_Ready(PyTypeObject *type) Py_INCREF(base); } - /* Now the only way base can still be NULL is if type is - * &PyBaseObject_Type. - */ + /* Now the only way base can still be NULL is if type is + * &PyBaseObject_Type. + */ /* Initialize the base class */ if (base != NULL && base->tp_dict == NULL) { @@ -3146,13 +3314,13 @@ PyType_Ready(PyTypeObject *type) goto error; } - /* Initialize ob_type if NULL. This means extensions that want to be + /* Initialize ob_type if NULL. This means extensions that want to be compilable separately on Windows can call PyType_Ready() instead of initializing the ob_type field of their type objects. */ - /* The test for base != NULL is really unnecessary, since base is only - NULL when type is &PyBaseObject_Type, and we know its ob_type is - not NULL (it's initialized to &PyType_Type). But coverity doesn't - know that. */ + /* The test for base != NULL is really unnecessary, since base is only + NULL when type is &PyBaseObject_Type, and we know its ob_type is + not NULL (it's initialized to &PyType_Type). But coverity doesn't + know that. */ if (type->ob_type == NULL && base != NULL) type->ob_type = base->ob_type; @@ -3216,9 +3384,9 @@ PyType_Ready(PyTypeObject *type) /* Sanity check for tp_free. */ if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && (type->tp_free == NULL || type->tp_free == PyObject_Del)) { - /* This base class needs to call tp_free, but doesn't have - * one, or its tp_free is for non-gc'ed objects. - */ + /* This base class needs to call tp_free, but doesn't have + * one, or its tp_free is for non-gc'ed objects. + */ PyErr_Format(PyExc_TypeError, "type '%.100s' participates in " "gc and is a base type but has inappropriate " "tp_free slot", @@ -3357,7 +3525,7 @@ check_num_args(PyObject *ob, int n) /* Generic wrappers for overloadable 'operators' such as __getitem__ */ /* There's a wrapper *function* for each distinct function typedef used - for type object slots (e.g. binaryfunc, ternaryfunc, etc.). There's a + for type object slots (e.g. binaryfunc, ternaryfunc, etc.). There's a wrapper *table* for each distinct operation (e.g. __len__, __add__). Most tables have only one entry; the tables for binary operators have two entries, one regular and one with reversed arguments. */ @@ -3692,8 +3860,8 @@ hackcheck(PyObject *self, setattrofunc func, char *what) PyTypeObject *type = self->ob_type; while (type && type->tp_flags & Py_TPFLAGS_HEAPTYPE) type = type->tp_base; - /* If type is NULL now, this is a really weird type. - In the spirit of backwards compatibility (?), just shut up. */ + /* If type is NULL now, this is a really weird type. + In the spirit of backwards compatibility (?), just shut up. */ if (type && type->tp_setattro != func) { PyErr_Format(PyExc_TypeError, "can't apply this %s to %s object", @@ -3909,8 +4077,8 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) staticbase = subtype; while (staticbase && (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE)) staticbase = staticbase->tp_base; - /* If staticbase is NULL now, it is a really weird type. - In the spirit of backwards compatibility (?), just shut up. */ + /* If staticbase is NULL now, it is a really weird type. + In the spirit of backwards compatibility (?), just shut up. */ if (staticbase && staticbase->tp_new != type->tp_new) { PyErr_Format(PyExc_TypeError, "%s.__new__(%s) is not safe, use %s.__new__()", @@ -3931,7 +4099,7 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) static struct PyMethodDef tp_new_methoddef[] = { {"__new__", (PyCFunction)tp_new_wrapper, METH_KEYWORDS, PyDoc_STR("T.__new__(S, ...) -> " - "a new object with type S, a subtype of T")}, + "a new object with type S, a subtype of T")}, {0} }; @@ -4869,17 +5037,17 @@ static slotdef slotdefs[] = { user-defined methods has unexpected side-effects, as shown by test_descr.notimplemented() */ SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, - "x.__add__(y) <==> x+y"), + "x.__add__(y) <==> x+y"), SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, - "x.__mul__(n) <==> x*n"), + "x.__mul__(n) <==> x*n"), SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, - "x.__rmul__(n) <==> n*x"), + "x.__rmul__(n) <==> n*x"), SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"), SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_ssizessizeargfunc, "x.__getslice__(i, j) <==> x[i:j]\n\ - \n\ - Use of negative indices is not supported."), + \n\ + Use of negative indices is not supported."), SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, "x.__setitem__(i, y) <==> x[i]=y"), SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, @@ -4887,18 +5055,18 @@ static slotdef slotdefs[] = { SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, wrap_ssizessizeobjargproc, "x.__setslice__(i, j, y) <==> x[i:j]=y\n\ - \n\ - Use of negative indices is not supported."), + \n\ + Use of negative indices is not supported."), SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, "x.__delslice__(i, j) <==> del x[i:j]\n\ - \n\ - Use of negative indices is not supported."), + \n\ + Use of negative indices is not supported."), SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, "x.__contains__(y) <==> y in x"), SQSLOT("__iadd__", sq_inplace_concat, NULL, - wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), + wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), SQSLOT("__imul__", sq_inplace_repeat, NULL, - wrap_indexargfunc, "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)"), @@ -5049,7 +5217,7 @@ static slotdef slotdefs[] = { }; /* Given a type pointer and an offset gotten from a slotdef entry, return a - pointer to the actual slot. This is not quite the same as simply adding + pointer to the actual slot. This is not quite the same as simply adding the offset to the type pointer, since it takes care to indirect through the proper indirection pointer (as_buffer, etc.); it returns NULL if the indirection pointer is NULL. */ @@ -5113,7 +5281,7 @@ resolve_slotdups(PyTypeObject *type, PyObject *name) } /* Look in all matching slots of the type; if exactly one of these has - a filled-in slot, return its value. Otherwise return NULL. */ + a filled-in slot, return its value. Otherwise return NULL. */ res = NULL; for (pp = ptrs; *pp; pp++) { ptr = slotptr(type, (*pp)->offset); @@ -5352,13 +5520,13 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name, dictionary with method descriptors for function slots. For each function slot (like tp_repr) that's defined in the type, one or more corresponding descriptors are added in the type's tp_dict dictionary - under the appropriate name (like __repr__). Some function slots + under the appropriate name (like __repr__). Some function slots cause more than one descriptor to be added (for example, the nb_add slot adds both __add__ and __radd__ descriptors) and some function slots compete for the same descriptor (for example both sq_item and mp_subscript generate a __getitem__ descriptor). - In the latter case, the first slotdef entry encoutered wins. Since + In the latter case, the first slotdef entry encoutered wins. Since slotdef entries are sorted by the offset of the slot in the PyHeapTypeObject, this gives us some control over disambiguating between competing slots: the members of PyHeapTypeObject are listed @@ -5530,7 +5698,7 @@ supercheck(PyTypeObject *type, PyObject *obj) obj can be a new-style class, or an instance of one: - - If it is a class, it must be a subclass of 'type'. This case is + - If it is a class, it must be a subclass of 'type'. This case is used for class methods; the return value is obj. - If it is an instance, it must be an instance of 'type'. This is @@ -5581,7 +5749,7 @@ supercheck(PyTypeObject *type, PyObject *obj) Py_DECREF(class_attr); } - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(PyExc_TypeError, "super(type, obj): " "obj must be an instance or subtype of type"); return NULL; @@ -5602,7 +5770,7 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type) /* If su is an instance of a (strict) subclass of super, call its type */ return PyObject_CallFunctionObjArgs((PyObject *)su->ob_type, - su->type, obj, NULL); + su->type, obj, NULL); else { /* Inline the common case */ PyTypeObject *obj_type = supercheck(su->type, obj); @@ -5655,7 +5823,7 @@ PyDoc_STRVAR(super_doc, "Typical use to call a cooperative superclass method:\n" "class C(B):\n" " def meth(self, arg):\n" -" super(C, self).meth(arg)"); +" super(C, self).meth(arg)"); static int super_traverse(PyObject *self, visitproc visit, void *arg) @@ -5676,7 +5844,7 @@ PyTypeObject PySuper_Type = { sizeof(superobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - super_dealloc, /* tp_dealloc */ + super_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -5684,7 +5852,7 @@ PyTypeObject PySuper_Type = { super_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -5693,9 +5861,9 @@ PyTypeObject PySuper_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ - super_doc, /* tp_doc */ - super_traverse, /* tp_traverse */ - 0, /* tp_clear */ + super_doc, /* tp_doc */ + super_traverse, /* tp_traverse */ + 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ @@ -5711,5 +5879,5 @@ PyTypeObject PySuper_Type = { super_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ - PyObject_GC_Del, /* tp_free */ + PyObject_GC_Del, /* tp_free */ }; diff --git a/PC/VC6/pcbuild.dsw b/PC/VC6/pcbuild.dsw index daf1535..bd3718d 100644 --- a/PC/VC6/pcbuild.dsw +++ b/PC/VC6/pcbuild.dsw @@ -26,6 +26,9 @@ Package=<5> Package=<4>
{{{
+ Begin Project Dependency
+ Project_Dep_Name pythoncore
+ End Project Dependency
}}}
###############################################################################
diff --git a/PC/_winreg.c b/PC/_winreg.c index bacb9dd..b981ee3 100644 --- a/PC/_winreg.c +++ b/PC/_winreg.c @@ -697,7 +697,7 @@ Py2Reg(PyObject *value, DWORD typ, BYTE **retDataBuf, DWORD *retDataSize) case REG_DWORD: if (value != Py_None && !PyInt_Check(value)) return FALSE; - *retDataBuf = (BYTE *)PyMem_NEW(DWORD, sizeof(DWORD)); + *retDataBuf = (BYTE *)PyMem_NEW(DWORD, 1); if (*retDataBuf==NULL){ PyErr_NoMemory(); return FALSE; diff --git a/PC/config.c b/PC/config.c index 0885cdc..e8cb1d2 100644 --- a/PC/config.c +++ b/PC/config.c @@ -43,7 +43,7 @@ extern void initxxsubtype(void); extern void initzipimport(void); extern void init_random(void); extern void inititertools(void); -extern void initcollections(void); +extern void init_collections(void); extern void init_heapq(void); extern void init_bisect(void); extern void init_symtable(void); @@ -126,7 +126,7 @@ struct _inittab _PyImport_Inittab[] = { {"_heapq", init_heapq}, {"_lsprof", init_lsprof}, {"itertools", inititertools}, - {"collections", initcollections}, + {"_collections", init_collections}, {"_symtable", init_symtable}, {"mmap", initmmap}, {"_csv", init_csv}, diff --git a/PC/getpathp.c b/PC/getpathp.c index 729d2e4..e62b936 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -650,7 +650,7 @@ calculate_path(void) start of the path in question - even if this is one character before the start of the buffer */ - while (*look != DELIM && look >= module_search_path) + while (look >= module_search_path && *look != DELIM) look--; nchars = lookEnd-look; strncpy(lookBuf, look+1, nchars); diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 3dcb24d..fb2d8df 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -461,7 +461,7 @@ RelativePath="..\Objects\codeobject.c"> </File> <File - RelativePath="..\Modules\collectionsmodule.c"> + RelativePath="..\Modules\_collectionsmodule.c"> </File> <File RelativePath="..\Python\compile.c"> diff --git a/PCbuild8/pythoncore.vcproj b/PCbuild8/pythoncore.vcproj index f335879..16a250e 100644 --- a/PCbuild8/pythoncore.vcproj +++ b/PCbuild8/pythoncore.vcproj @@ -1381,7 +1381,7 @@ > </File> <File - RelativePath="..\Modules\collectionsmodule.c" + RelativePath="..\Modules\_collectionsmodule.c" > </File> <File diff --git a/Python/ast.c b/Python/ast.c index f8bcdf2..8b91894 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -25,7 +25,8 @@ 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 *, expr_context_ty); +static asdl_seq *ast_for_exprlist(struct compiling *, const node *, + expr_context_ty); static expr_ty ast_for_testlist(struct compiling *, const node *); /* Note different signature for ast_for_call */ @@ -190,8 +191,8 @@ 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; + ast_error(n, "encoding declaration in Unicode string"); + goto error; } } else if (TYPE(n) == encoding_decl) { c.c_encoding = STR(n); @@ -206,7 +207,7 @@ PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename, case file_input: stmts = asdl_seq_new(num_stmts(n), arena); if (!stmts) - return NULL; + return NULL; for (i = 0; i < NCH(n) - 1; i++) { ch = CHILD(n, i); if (TYPE(ch) == NEWLINE) @@ -346,8 +347,8 @@ set_context(expr_ty e, expr_context_ty ctx, const node *n) switch (e->kind) { case Attribute_kind: if (ctx == Store && - !strcmp(PyString_AS_STRING(e->v.Attribute.attr), "None")) { - return ast_error(n, "assignment to None"); + !strcmp(PyString_AS_STRING(e->v.Attribute.attr), "None")) { + return ast_error(n, "assignment to None"); } e->v.Attribute.ctx = ctx; break; @@ -1075,34 +1076,34 @@ ast_for_ifexpr(struct compiling *c, const node *n) static int count_comp_fors(const node *n) { - int n_fors = 0; - node *ch = CHILD(n, 1); - - count_comp_for: - n_fors++; - REQ(ch, comp_for); - if (NCH(ch) == 5) - ch = CHILD(ch, 4); - else - return n_fors; - count_comp_iter: - REQ(ch, comp_iter); - ch = CHILD(ch, 0); - if (TYPE(ch) == comp_for) - goto count_comp_for; - else if (TYPE(ch) == comp_if) { - if (NCH(ch) == 3) { - ch = CHILD(ch, 2); - goto count_comp_iter; - } - else - return n_fors; + int n_fors = 0; + node *ch = CHILD(n, 1); + + count_comp_for: + n_fors++; + REQ(ch, comp_for); + if (NCH(ch) == 5) + ch = CHILD(ch, 4); + else + return n_fors; + count_comp_iter: + REQ(ch, comp_iter); + ch = CHILD(ch, 0); + if (TYPE(ch) == comp_for) + goto count_comp_for; + else if (TYPE(ch) == comp_if) { + if (NCH(ch) == 3) { + ch = CHILD(ch, 2); + goto count_comp_iter; } + else + return n_fors; + } - /* Should never be reached */ - PyErr_SetString(PyExc_SystemError, - "logic error in count_comp_fors"); - return -1; + /* Should never be reached */ + PyErr_SetString(PyExc_SystemError, + "logic error in count_comp_fors"); + return -1; } /* Count the number of 'if' statements in a comprehension. @@ -1113,19 +1114,19 @@ count_comp_fors(const node *n) static int count_comp_ifs(const node *n) { - int n_ifs = 0; + int n_ifs = 0; - while (1) { - REQ(n, comp_iter); - if (TYPE(CHILD(n, 0)) == comp_for) - return n_ifs; - n = CHILD(n, 0); - REQ(n, comp_if); - n_ifs++; - if (NCH(n) == 2) - return n_ifs; - n = CHILD(n, 2); - } + while (1) { + REQ(n, comp_iter); + if (TYPE(CHILD(n, 0)) == comp_for) + return n_ifs; + n = CHILD(n, 0); + REQ(n, comp_if); + n_ifs++; + if (NCH(n) == 2) + return n_ifs; + n = CHILD(n, 2); + } } static expr_ty @@ -1451,53 +1452,53 @@ ast_for_slice(struct compiling *c, const node *n) static expr_ty ast_for_binop(struct compiling *c, const node *n) { - /* Must account for a sequence of expressions. - How should A op B op C by represented? - BinOp(BinOp(A, op, B), op, C). - */ + /* Must account for a sequence of expressions. + How should A op B op C by represented? + BinOp(BinOp(A, op, B), op, C). + */ - int i, nops; - expr_ty expr1, expr2, result; - operator_ty newoperator; + int i, nops; + expr_ty expr1, expr2, result; + operator_ty newoperator; - expr1 = ast_for_expr(c, CHILD(n, 0)); - if (!expr1) - return NULL; + expr1 = ast_for_expr(c, CHILD(n, 0)); + if (!expr1) + return NULL; - expr2 = ast_for_expr(c, CHILD(n, 2)); - if (!expr2) - return NULL; + expr2 = ast_for_expr(c, CHILD(n, 2)); + if (!expr2) + return NULL; - newoperator = get_operator(CHILD(n, 1)); - if (!newoperator) - return NULL; + newoperator = get_operator(CHILD(n, 1)); + if (!newoperator) + return NULL; - result = BinOp(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, - c->c_arena); - if (!result) - return NULL; + result = BinOp(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, + c->c_arena); + if (!result) + return NULL; - nops = (NCH(n) - 1) / 2; - for (i = 1; i < nops; i++) { - expr_ty tmp_result, tmp; - const node* next_oper = CHILD(n, i * 2 + 1); + nops = (NCH(n) - 1) / 2; + for (i = 1; i < nops; i++) { + expr_ty tmp_result, tmp; + const node* next_oper = CHILD(n, i * 2 + 1); - newoperator = get_operator(next_oper); - if (!newoperator) - return NULL; + newoperator = get_operator(next_oper); + if (!newoperator) + return NULL; - tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); - if (!tmp) - return NULL; + tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); + if (!tmp) + return NULL; - tmp_result = BinOp(result, newoperator, tmp, - LINENO(next_oper), next_oper->n_col_offset, - c->c_arena); - if (!tmp) - return NULL; - result = tmp_result; - } - return result; + tmp_result = BinOp(result, newoperator, tmp, + LINENO(next_oper), next_oper->n_col_offset, + c->c_arena); + if (!tmp) + return NULL; + result = tmp_result; + } + return result; } static expr_ty @@ -2532,7 +2533,8 @@ ast_for_if_stmt(struct compiling *c, const node *n) if (!suite_seq) return NULL; - return If(expression, suite_seq, NULL, LINENO(n), n->n_col_offset, c->c_arena); + return If(expression, suite_seq, NULL, LINENO(n), n->n_col_offset, + c->c_arena); } s = STR(CHILD(n, 4)); @@ -2554,10 +2556,13 @@ ast_for_if_stmt(struct compiling *c, const node *n) if (!seq2) return NULL; - return If(expression, seq1, seq2, LINENO(n), n->n_col_offset, c->c_arena); + return If(expression, seq1, seq2, LINENO(n), n->n_col_offset, + c->c_arena); } else if (s[2] == 'i') { int i, n_elif, has_else = 0; + expr_ty expression; + asdl_seq *suite_seq; asdl_seq *orelse = NULL; n_elif = NCH(n) - 4; /* must reference the child n_elif+1 since 'else' token is third, @@ -2570,8 +2575,7 @@ ast_for_if_stmt(struct compiling *c, const node *n) n_elif /= 4; if (has_else) { - expr_ty expression; - asdl_seq *seq1, *seq2; + asdl_seq *suite_seq2; orelse = asdl_seq_new(1, c->c_arena); if (!orelse) @@ -2579,24 +2583,24 @@ ast_for_if_stmt(struct compiling *c, const node *n) expression = ast_for_expr(c, CHILD(n, NCH(n) - 6)); if (!expression) return NULL; - seq1 = ast_for_suite(c, CHILD(n, NCH(n) - 4)); - if (!seq1) + suite_seq = ast_for_suite(c, CHILD(n, NCH(n) - 4)); + if (!suite_seq) return NULL; - seq2 = ast_for_suite(c, CHILD(n, NCH(n) - 1)); - if (!seq2) + suite_seq2 = ast_for_suite(c, CHILD(n, NCH(n) - 1)); + if (!suite_seq2) return NULL; - asdl_seq_SET(orelse, 0, If(expression, seq1, seq2, - LINENO(CHILD(n, NCH(n) - 6)), CHILD(n, NCH(n) - 6)->n_col_offset, - c->c_arena)); + asdl_seq_SET(orelse, 0, + If(expression, suite_seq, suite_seq2, + LINENO(CHILD(n, NCH(n) - 6)), + CHILD(n, NCH(n) - 6)->n_col_offset, + c->c_arena)); /* the just-created orelse handled the last elif */ n_elif--; } for (i = 0; i < n_elif; i++) { int off = 5 + (n_elif - i - 1) * 4; - expr_ty expression; - asdl_seq *suite_seq; asdl_seq *newobj = asdl_seq_new(1, c->c_arena); if (!newobj) return NULL; @@ -2609,12 +2613,18 @@ ast_for_if_stmt(struct compiling *c, const node *n) asdl_seq_SET(newobj, 0, If(expression, suite_seq, orelse, - LINENO(CHILD(n, off)), CHILD(n, off)->n_col_offset, c->c_arena)); + LINENO(CHILD(n, off)), + CHILD(n, off)->n_col_offset, c->c_arena)); orelse = newobj; } - return If(ast_for_expr(c, CHILD(n, 1)), - ast_for_suite(c, CHILD(n, 3)), - orelse, LINENO(n), n->n_col_offset, c->c_arena); + expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, CHILD(n, 3)); + if (!suite_seq) + return NULL; + return If(expression, suite_seq, orelse, + LINENO(n), n->n_col_offset, c->c_arena); } PyErr_Format(PyExc_SystemError, @@ -2988,137 +2998,137 @@ ast_for_stmt(struct compiling *c, const node *n) static PyObject * parsenumber(const char *s) { - const char *end; - long x; - double dx; + const char *end; + long x; + double dx; #ifndef WITHOUT_COMPLEX - Py_complex c; - int imflag; + Py_complex c; + int imflag; #endif - errno = 0; - end = s + strlen(s) - 1; + errno = 0; + end = s + strlen(s) - 1; #ifndef WITHOUT_COMPLEX - imflag = *end == 'j' || *end == 'J'; + imflag = *end == 'j' || *end == 'J'; #endif - if (*end == 'l' || *end == 'L') - return PyLong_FromString((char *)s, (char **)0, 0); - if (s[0] == '0') { - x = (long) PyOS_strtoul((char *)s, (char **)&end, 0); - if (x < 0 && errno == 0) { - return PyLong_FromString((char *)s, - (char **)0, - 0); - } - } - else - x = PyOS_strtol((char *)s, (char **)&end, 0); - if (*end == '\0') { - if (errno != 0) - return PyLong_FromString((char *)s, (char **)0, 0); - return PyInt_FromLong(x); + if (*end == 'l' || *end == 'L') + return PyLong_FromString((char *)s, (char **)0, 0); + if (s[0] == '0') { + x = (long) PyOS_strtoul((char *)s, (char **)&end, 0); + if (x < 0 && errno == 0) { + return PyLong_FromString((char *)s, + (char **)0, + 0); } - /* XXX Huge floats may silently fail */ + } + else + x = PyOS_strtol((char *)s, (char **)&end, 0); + if (*end == '\0') { + if (errno != 0) + return PyLong_FromString((char *)s, (char **)0, 0); + return PyInt_FromLong(x); + } + /* XXX Huge floats may silently fail */ #ifndef WITHOUT_COMPLEX - if (imflag) { - c.real = 0.; - PyFPE_START_PROTECT("atof", return 0) - c.imag = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(c) - return PyComplex_FromCComplex(c); - } - else + if (imflag) { + c.real = 0.; + PyFPE_START_PROTECT("atof", return 0) + c.imag = PyOS_ascii_atof(s); + PyFPE_END_PROTECT(c) + return PyComplex_FromCComplex(c); + } + else #endif - { - PyFPE_START_PROTECT("atof", return 0) - dx = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(dx) - return PyFloat_FromDouble(dx); - } + { + PyFPE_START_PROTECT("atof", return 0) + dx = PyOS_ascii_atof(s); + PyFPE_END_PROTECT(dx) + return PyFloat_FromDouble(dx); + } } static PyObject * decode_utf8(const char **sPtr, const char *end, char* encoding) { #ifndef Py_USING_UNICODE - Py_FatalError("decode_utf8 should not be called in this build."); - return NULL; + Py_FatalError("decode_utf8 should not be called in this build."); + return NULL; #else - PyObject *u, *v; - char *s, *t; - t = s = (char *)*sPtr; - /* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ - while (s < end && (*s & 0x80)) s++; - *sPtr = s; - u = PyUnicode_DecodeUTF8(t, s - t, NULL); - if (u == NULL) - return NULL; - v = PyUnicode_AsEncodedString(u, encoding, NULL); - Py_DECREF(u); - return v; + PyObject *u, *v; + char *s, *t; + t = s = (char *)*sPtr; + /* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ + while (s < end && (*s & 0x80)) s++; + *sPtr = s; + u = PyUnicode_DecodeUTF8(t, s - t, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; #endif } static PyObject * decode_unicode(const char *s, size_t len, int rawmode, const char *encoding) { - PyObject *v, *u; - char *buf; - char *p; - const char *end; - if (encoding == NULL) { - buf = (char *)s; - u = NULL; - } else if (strcmp(encoding, "iso-8859-1") == 0) { - buf = (char *)s; - u = NULL; - } else { - /* "\XX" may become "\u005c\uHHLL" (12 bytes) */ - u = PyString_FromStringAndSize((char *)NULL, len * 4); - if (u == NULL) - return NULL; - p = buf = PyString_AsString(u); - end = s + len; - while (s < end) { - if (*s == '\\') { - *p++ = *s++; - if (*s & 0x80) { - strcpy(p, "u005c"); - p += 5; - } - } - if (*s & 0x80) { /* XXX inefficient */ - PyObject *w; - char *r; - Py_ssize_t rn, i; - w = decode_utf8(&s, end, "utf-16-be"); - if (w == NULL) { - Py_DECREF(u); - return NULL; - } - r = PyString_AsString(w); - rn = PyString_Size(w); - assert(rn % 2 == 0); - for (i = 0; i < rn; i += 2) { - sprintf(p, "\\u%02x%02x", - r[i + 0] & 0xFF, - r[i + 1] & 0xFF); - p += 6; - } - Py_DECREF(w); - } else { - *p++ = *s++; - } + PyObject *v, *u; + char *buf; + char *p; + const char *end; + if (encoding == NULL) { + buf = (char *)s; + u = NULL; + } else if (strcmp(encoding, "iso-8859-1") == 0) { + buf = (char *)s; + u = NULL; + } else { + /* "\XX" may become "\u005c\uHHLL" (12 bytes) */ + u = PyString_FromStringAndSize((char *)NULL, len * 4); + if (u == NULL) + return NULL; + p = buf = PyString_AsString(u); + end = s + len; + while (s < end) { + if (*s == '\\') { + *p++ = *s++; + if (*s & 0x80) { + strcpy(p, "u005c"); + p += 5; + } + } + if (*s & 0x80) { /* XXX inefficient */ + PyObject *w; + char *r; + Py_ssize_t rn, i; + w = decode_utf8(&s, end, "utf-16-be"); + if (w == NULL) { + Py_DECREF(u); + return NULL; } - len = p - buf; - s = buf; + r = PyString_AsString(w); + rn = PyString_Size(w); + assert(rn % 2 == 0); + for (i = 0; i < rn; i += 2) { + sprintf(p, "\\u%02x%02x", + r[i + 0] & 0xFF, + r[i + 1] & 0xFF); + p += 6; + } + Py_DECREF(w); + } else { + *p++ = *s++; + } } - if (rawmode) - v = PyUnicode_DecodeRawUnicodeEscape(s, len, NULL); - else - v = PyUnicode_DecodeUnicodeEscape(s, len, NULL); - Py_XDECREF(u); - return v; + len = p - buf; + s = buf; + } + if (rawmode) + v = PyUnicode_DecodeRawUnicodeEscape(s, len, NULL); + else + v = PyUnicode_DecodeUnicodeEscape(s, len, NULL); + Py_XDECREF(u); + return v; } /* s is a Python string literal, including the bracketing quote characters, @@ -3128,95 +3138,95 @@ decode_unicode(const char *s, size_t len, int rawmode, const char *encoding) static PyObject * parsestr(const node *n, const char *encoding, int *bytesmode) { - size_t len; - const char *s = STR(n); - int quote = Py_CHARMASK(*s); - int rawmode = 0; - int need_encoding; - int unicode = 0; - - if (isalpha(quote) || quote == '_') { - if (quote == 'u' || quote == 'U') { - quote = *++s; - unicode = 1; - } - if (quote == 'b' || quote == 'B') { - quote = *++s; - *bytesmode = 1; - } - if (quote == 'r' || quote == 'R') { - quote = *++s; - rawmode = 1; - } - } - if (quote != '\'' && quote != '\"') { - PyErr_BadInternalCall(); - return NULL; - } - if (unicode && *bytesmode) { - ast_error(n, "string cannot be both bytes and unicode"); - return NULL; - } - s++; - len = strlen(s); - if (len > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "string to parse is too long"); - return NULL; + size_t len; + const char *s = STR(n); + int quote = Py_CHARMASK(*s); + int rawmode = 0; + int need_encoding; + int unicode = 0; + + if (isalpha(quote) || quote == '_') { + if (quote == 'u' || quote == 'U') { + quote = *++s; + unicode = 1; } - if (s[--len] != quote) { - PyErr_BadInternalCall(); - return NULL; + if (quote == 'b' || quote == 'B') { + quote = *++s; + *bytesmode = 1; + } + if (quote == 'r' || quote == 'R') { + quote = *++s; + rawmode = 1; } - if (len >= 4 && s[0] == quote && s[1] == quote) { - s += 2; - len -= 2; - if (s[--len] != quote || s[--len] != quote) { - PyErr_BadInternalCall(); - return NULL; - } + } + if (quote != '\'' && quote != '\"') { + PyErr_BadInternalCall(); + return NULL; + } + if (unicode && *bytesmode) { + ast_error(n, "string cannot be both bytes and unicode"); + return NULL; + } + s++; + len = strlen(s); + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string to parse is too long"); + return NULL; + } + if (s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; + } + if (len >= 4 && s[0] == quote && s[1] == quote) { + s += 2; + len -= 2; + if (s[--len] != quote || s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; } + } #ifdef Py_USING_UNICODE - if (unicode || Py_UnicodeFlag) { - return decode_unicode(s, len, rawmode, encoding); - } + if (unicode || Py_UnicodeFlag) { + return decode_unicode(s, len, rawmode, encoding); + } #endif - if (*bytesmode) { - /* Disallow non-ascii characters (but not escapes) */ - const char *c; - for (c = s; *c; c++) { - if (Py_CHARMASK(*c) >= 0x80) { - ast_error(n, "bytes can only contain ASCII " - "literal characters."); - return NULL; - } - } + if (*bytesmode) { + /* Disallow non-ascii characters (but not escapes) */ + const char *c; + for (c = s; *c; c++) { + if (Py_CHARMASK(*c) >= 0x80) { + ast_error(n, "bytes can only contain ASCII " + "literal characters."); + return NULL; + } } - need_encoding = (!*bytesmode && encoding != NULL && - strcmp(encoding, "utf-8") != 0 && - strcmp(encoding, "iso-8859-1") != 0); - if (rawmode || strchr(s, '\\') == NULL) { - if (need_encoding) { + } + need_encoding = (!*bytesmode && encoding != NULL && + strcmp(encoding, "utf-8") != 0 && + strcmp(encoding, "iso-8859-1") != 0); + if (rawmode || strchr(s, '\\') == NULL) { + if (need_encoding) { #ifndef Py_USING_UNICODE - /* This should not happen - we never see any other - encoding. */ - Py_FatalError( - "cannot deal with encodings in this build."); + /* This should not happen - we never see any other + encoding. */ + Py_FatalError( + "cannot deal with encodings in this build."); #else - PyObject *v, *u = PyUnicode_DecodeUTF8(s, len, NULL); - if (u == NULL) - return NULL; - v = PyUnicode_AsEncodedString(u, encoding, NULL); - Py_DECREF(u); - return v; + PyObject *v, *u = PyUnicode_DecodeUTF8(s, len, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; #endif - } else { - return PyString_FromStringAndSize(s, len); - } + } else { + return PyString_FromStringAndSize(s, len); } + } - return PyString_DecodeEscape(s, len, NULL, unicode, - need_encoding ? encoding : NULL); + return PyString_DecodeEscape(s, len, NULL, unicode, + need_encoding ? encoding : NULL); } /* Build a Python string object out of a STRING atom. This takes care of @@ -3226,43 +3236,43 @@ parsestr(const node *n, const char *encoding, int *bytesmode) static PyObject * parsestrplus(struct compiling *c, const node *n, int *bytesmode) { - PyObject *v; - int i; - REQ(CHILD(n, 0), STRING); - v = parsestr(CHILD(n, 0), c->c_encoding, bytesmode); - if (v != NULL) { - /* String literal concatenation */ - for (i = 1; i < NCH(n); i++) { - PyObject *s; - int subbm = 0; - s = parsestr(CHILD(n, i), c->c_encoding, &subbm); - if (s == NULL) - goto onError; - if (*bytesmode != subbm) { - ast_error(n, "cannot mix bytes and nonbytes" - "literals"); - goto onError; - } - if (PyString_Check(v) && PyString_Check(s)) { - PyString_ConcatAndDel(&v, s); - if (v == NULL) - goto onError; - } + PyObject *v; + int i; + REQ(CHILD(n, 0), STRING); + v = parsestr(CHILD(n, 0), c->c_encoding, bytesmode); + if (v != NULL) { + /* String literal concatenation */ + for (i = 1; i < NCH(n); i++) { + PyObject *s; + int subbm = 0; + s = parsestr(CHILD(n, i), c->c_encoding, &subbm); + if (s == NULL) + goto onError; + if (*bytesmode != subbm) { + ast_error(n, "cannot mix bytes and nonbytes" + "literals"); + goto onError; + } + if (PyString_Check(v) && PyString_Check(s)) { + PyString_ConcatAndDel(&v, s); + if (v == NULL) + goto onError; + } #ifdef Py_USING_UNICODE - else { - PyObject *temp = PyUnicode_Concat(v, s); - Py_DECREF(s); - Py_DECREF(v); - v = temp; - if (v == NULL) - goto onError; - } + else { + PyObject *temp = PyUnicode_Concat(v, s); + Py_DECREF(s); + Py_DECREF(v); + v = temp; + if (v == NULL) + goto onError; + } #endif - } } - return v; + } + return v; - onError: - Py_XDECREF(v); - return NULL; + onError: + Py_XDECREF(v); + return NULL; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 8ce0e48..17cdb92 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -441,7 +441,7 @@ PyDoc_STRVAR(cmp_doc, Return negative if x<y, zero if x==y, positive if x>y."); static PyObject * -builtin_compile(PyObject *self, PyObject *args) +builtin_compile(PyObject *self, PyObject *args, PyObject *kwds) { char *str; char *filename; @@ -452,9 +452,12 @@ builtin_compile(PyObject *self, PyObject *args) PyCompilerFlags cf; PyObject *result = NULL, *cmd, *tmp = NULL; Py_ssize_t length; + static char *kwlist[] = {"source", "filename", "mode", "flags", + "dont_inherit", NULL}; - if (!PyArg_ParseTuple(args, "Oss|ii:compile", &cmd, &filename, - &startstr, &supplied_flags, &dont_inherit)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oss|ii:compile", + kwlist, &cmd, &filename, &startstr, + &supplied_flags, &dont_inherit)) return NULL; cf.cf_flags = supplied_flags; @@ -542,7 +545,7 @@ PyDoc_STRVAR(dir_doc, " for a module object: the module's attributes.\n" " for a class object: its attributes, and recursively the attributes\n" " of its bases.\n" -" for an other object: its attributes, its class's attributes, and\n" +" for any other object: its attributes, its class's attributes, and\n" " recursively the attributes of its class's base classes."); static PyObject * @@ -2269,7 +2272,7 @@ static PyMethodDef builtin_methods[] = { {"callable", builtin_callable, METH_O, callable_doc}, {"chr", builtin_chr, METH_VARARGS, chr_doc}, {"cmp", builtin_cmp, METH_VARARGS, cmp_doc}, - {"compile", builtin_compile, METH_VARARGS, compile_doc}, + {"compile", (PyCFunction)builtin_compile, METH_VARARGS | METH_KEYWORDS, compile_doc}, {"delattr", builtin_delattr, METH_VARARGS, delattr_doc}, {"dir", builtin_dir, METH_VARARGS, dir_doc}, {"divmod", builtin_divmod, METH_VARARGS, divmod_doc}, diff --git a/Python/ceval.c b/Python/ceval.c index d2cda84..fdee13d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -657,7 +657,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) #define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \ lltrace && prtrace(TOP(), "stackadj")); \ assert(STACK_LEVEL() <= co->co_stacksize); } -#define EXT_POP(STACK_POINTER) (lltrace && prtrace(*(STACK_POINTER), "ext_pop"), *--(STACK_POINTER)) +#define EXT_POP(STACK_POINTER) (lltrace && prtrace((STACK_POINTER)[-1], "ext_pop"), *--(STACK_POINTER)) #else #define PUSH(v) BASIC_PUSH(v) #define POP() BASIC_POP() @@ -1657,12 +1657,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PUSH(w); } } else if (unpack_iterable(v, oparg, - stack_pointer + oparg)) + stack_pointer + oparg)) { stack_pointer += oparg; - else { - if (PyErr_ExceptionMatches(PyExc_TypeError)) - PyErr_SetString(PyExc_TypeError, - "unpack non-sequence"); + } else { + /* unpack_iterable() raised an exception */ why = WHY_EXCEPTION; } Py_DECREF(v); @@ -3877,7 +3875,7 @@ assign_slice(PyObject *u, PyObject *v, PyObject *w, PyObject *x) PyTypeObject *tp = u->ob_type; PySequenceMethods *sq = tp->tp_as_sequence; - if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) { + if (sq && sq->sq_ass_slice && ISINDEX(v) && ISINDEX(w)) { Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX; if (!_PyEval_SliceIndex(v, &ilow)) return -1; diff --git a/Python/compile.c b/Python/compile.c index bee48da..ebb19f5 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8,7 +8,7 @@ * 2. Builds a symbol table. See symtable.c. * 3. Generate code for basic blocks. See compiler_mod() in this file. * 4. Assemble the basic blocks into final code. See assemble() in - * this file. + * this file. * 5. Optimize the byte code (peephole optimizations). See peephole.c * * Note that compiler_mod() suggests module, but the module ast type @@ -204,7 +204,17 @@ _Py_Mangle(PyObject *privateobj, PyObject *ident) } p = PyString_AsString(privateobj); nlen = strlen(name); - if (name[nlen-1] == '_' && name[nlen-2] == '_') { + /* Don't mangle __id__ or names with dots. + + The only time a name with a dot can occur is when + we are compiling an import statement that has a + package name. + + TODO(jhylton): Decide whether we want to support + mangling of the module name, e.g. __M.X. + */ + if ((name[nlen-1] == '_' && name[nlen-2] == '_') + || strchr(name, '.')) { Py_INCREF(ident); return ident; /* Don't mangle __whatever__ */ } @@ -439,7 +449,7 @@ compiler_enter_scope(struct compiler *c, identifier name, void *key, struct compiler_unit *u; u = (struct compiler_unit *)PyObject_Malloc(sizeof( - struct compiler_unit)); + struct compiler_unit)); if (!u) { PyErr_NoMemory(); return 0; @@ -608,7 +618,7 @@ compiler_next_instr(struct compiler *c, basicblock *b) assert(b != NULL); if (b->b_instr == NULL) { b->b_instr = (struct instr *)PyObject_Malloc( - sizeof(struct instr) * DEFAULT_BLOCK_SIZE); + sizeof(struct instr) * DEFAULT_BLOCK_SIZE); if (b->b_instr == NULL) { PyErr_NoMemory(); return -1; @@ -628,7 +638,7 @@ compiler_next_instr(struct compiler *c, basicblock *b) } b->b_ialloc <<= 1; tmp = (struct instr *)PyObject_Realloc( - (void *)b->b_instr, newsize); + (void *)b->b_instr, newsize); if (tmp == NULL) { PyErr_NoMemory(); return -1; @@ -1148,7 +1158,7 @@ compiler_mod(struct compiler *c, mod_ty mod) case Interactive_kind: c->c_interactive = 1; VISIT_SEQ_IN_SCOPE(c, stmt, - mod->v.Interactive.body); + mod->v.Interactive.body); break; case Expression_kind: VISIT_IN_SCOPE(c, expr, mod->v.Expression.body); @@ -1729,7 +1739,7 @@ compiler_if(struct compiler *c, stmt_ty s) compiler_use_next_block(c, next); ADDOP(c, POP_TOP); if (s->v.If.orelse) - VISIT_SEQ(c, stmt, s->v.If.orelse); + VISIT_SEQ(c, stmt, s->v.If.orelse); } compiler_use_next_block(c, end); return 1; @@ -1977,8 +1987,8 @@ compiler_try_except(struct compiler *c, stmt_ty s) 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 = 0; - c->u->u_lineno = handler->lineno; + c->u->u_lineno_set = 0; + c->u->u_lineno = handler->lineno; except = compiler_new_block(c); if (except == NULL) return 0; @@ -2321,7 +2331,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s) case Pass_kind: break; case Break_kind: - if (!compiler_in_loop(c)) + if (!compiler_in_loop(c)) return compiler_error(c, "'break' outside loop"); ADDOP(c, BREAK_LOOP); break; @@ -2459,7 +2469,7 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) return compiler_error(c, "can not assign to __debug__"); } - mangled = _Py_Mangle(c->u->u_private, name); +mangled = _Py_Mangle(c->u->u_private, name); if (!mangled) return 0; @@ -2645,20 +2655,20 @@ compiler_compare(struct compiler *c, expr_ty e) if (cleanup == NULL) return 0; VISIT(c, expr, - (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0)); + (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0)); } for (i = 1; i < n; i++) { ADDOP(c, DUP_TOP); ADDOP(c, ROT_THREE); ADDOP_I(c, COMPARE_OP, cmpop((cmpop_ty)(asdl_seq_GET( - e->v.Compare.ops, i - 1)))); + 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, - (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i)); + (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i)); } VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n - 1)); ADDOP_I(c, COMPARE_OP, @@ -2968,7 +2978,7 @@ expr_constant(expr_ty e) /* __debug__ is not assignable, so we can optimize * it away in if and while statements */ if (strcmp(PyString_AS_STRING(e->v.Name.id), - "__debug__") == 0) + "__debug__") == 0) return ! Py_OptimizeFlag; /* fall through */ default: @@ -3115,8 +3125,8 @@ 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. - */ + 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 = 0; @@ -3146,10 +3156,10 @@ compiler_visit_expr(struct compiler *c, expr_ty e) for (i = 0; i < n; i++) { ADDOP(c, DUP_TOP); VISIT(c, expr, - (expr_ty)asdl_seq_GET(e->v.Dict.values, i)); + (expr_ty)asdl_seq_GET(e->v.Dict.values, i)); ADDOP(c, ROT_TWO); VISIT(c, expr, - (expr_ty)asdl_seq_GET(e->v.Dict.keys, i)); + (expr_ty)asdl_seq_GET(e->v.Dict.keys, i)); ADDOP(c, STORE_SUBSCR); } break; @@ -3331,13 +3341,13 @@ compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b) static int compiler_in_loop(struct compiler *c) { - int i; - struct compiler_unit *u = c->u; - for (i = 0; i < u->u_nfblocks; ++i) { - if (u->u_fblock[i].fb_type == LOOP) - return 1; - } - return 0; + int i; + struct compiler_unit *u = c->u; + for (i = 0; i < u->u_nfblocks; ++i) { + if (u->u_fblock[i].fb_type == LOOP) + return 1; + } + return 0; } /* Raises a SyntaxError and returns 0. If something goes wrong, a different exception may be raised. @@ -3523,7 +3533,7 @@ compiler_visit_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) int i, n = asdl_seq_LEN(s->v.ExtSlice.dims); for (i = 0; i < n; i++) { slice_ty sub = (slice_ty)asdl_seq_GET( - s->v.ExtSlice.dims, i); + s->v.ExtSlice.dims, i); if (!compiler_visit_nested_slice(c, sub, ctx)) return 0; } @@ -3720,7 +3730,7 @@ the line # increment in each pair generated must be 0 until the remaining addr increment is < 256. So, in the example above, assemble_lnotab (it used to be called com_set_lineno) should not (as was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, - but to 255, 0, 45, 255, 0, 45. + but to 255, 0, 45, 255, 0, 45. */ static int diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 37d6d2e..1c3b3ab 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -13,16 +13,8 @@ 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 e00d3ec..443235a 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -599,8 +599,9 @@ PyErr_WriteUnraisable(PyObject *obj) PyFile_WriteString("Exception ", f); if (t) { PyObject* moduleName; - char* className = PyExceptionClass_Name(t); - + char* className; + assert(PyExceptionClass_Check(t)); + className = PyExceptionClass_Name(t); if (className != NULL) { char *dot = strrchr(className, '.'); if (dot != NULL) @@ -825,4 +826,3 @@ PyErr_ProgramText(const char *filename, int lineno) #ifdef __cplusplus } #endif - diff --git a/Python/fmod.c b/Python/fmod.c deleted file mode 100644 index 919c6cc..0000000 --- a/Python/fmod.c +++ /dev/null @@ -1,27 +0,0 @@ - -/* Portable fmod(x, y) implementation for systems that don't have it */ - -#include "pyconfig.h" - -#include "pyport.h" -#include <errno.h> - -double -fmod(double x, double y) -{ - double i, f; - - if (y == 0.0) { - errno = EDOM; - return 0.0; - } - - /* return f such that x = i*y + f for some integer i - such that |f| < |y| and f has the same sign as x */ - - i = floor(x/y); - f = x - i*y; - if ((x < 0.0) != (y < 0.0)) - f = f-y; - return f; -} diff --git a/Python/import.c b/Python/import.c index 6d65703..2350800 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4,6 +4,7 @@ #include "Python.h" #include "Python-ast.h" +#undef Yield /* undefine macro conflicting with winbase.h */ #include "pyarena.h" #include "pythonrun.h" #include "errcode.h" @@ -347,6 +348,14 @@ imp_release_lock(PyObject *self, PyObject *noargs) return Py_None; } +static void +imp_modules_reloading_clear(void) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->modules_reloading != NULL) + PyDict_Clear(interp->modules_reloading); +} + /* Helper for sys */ PyObject * @@ -506,6 +515,7 @@ PyImport_Cleanup(void) PyDict_Clear(modules); interp->modules = NULL; Py_DECREF(modules); + Py_CLEAR(interp->modules_reloading); } @@ -2408,13 +2418,21 @@ import_submodule(PyObject *mod, char *subname, char *fullname) PyObject * PyImport_ReloadModule(PyObject *m) { + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules_reloading = interp->modules_reloading; PyObject *modules = PyImport_GetModuleDict(); - PyObject *path = NULL, *loader = NULL; + PyObject *path = NULL, *loader = NULL, *existing_m = NULL; char *name, *subname; char buf[MAXPATHLEN+1]; struct filedescr *fdp; FILE *fp = NULL; PyObject *newm; + + if (modules_reloading == NULL) { + Py_FatalError("PyImport_ReloadModule: " + "no modules_reloading dictionary!"); + return NULL; + } if (m == NULL || !PyModule_Check(m)) { PyErr_SetString(PyExc_TypeError, @@ -2430,20 +2448,33 @@ PyImport_ReloadModule(PyObject *m) name); return NULL; } + existing_m = PyDict_GetItemString(modules_reloading, name); + if (existing_m != NULL) { + /* Due to a recursive reload, this module is already + being reloaded. */ + Py_INCREF(existing_m); + return existing_m; + } + if (PyDict_SetItemString(modules_reloading, name, m) < 0) + return NULL; + subname = strrchr(name, '.'); if (subname == NULL) subname = name; else { PyObject *parentname, *parent; parentname = PyString_FromStringAndSize(name, (subname-name)); - if (parentname == NULL) + if (parentname == NULL) { + imp_modules_reloading_clear(); return NULL; + } parent = PyDict_GetItem(modules, parentname); if (parent == NULL) { PyErr_Format(PyExc_ImportError, "reload(): parent %.200s not in sys.modules", PyString_AS_STRING(parentname)); Py_DECREF(parentname); + imp_modules_reloading_clear(); return NULL; } Py_DECREF(parentname); @@ -2458,6 +2489,7 @@ PyImport_ReloadModule(PyObject *m) if (fdp == NULL) { Py_XDECREF(loader); + imp_modules_reloading_clear(); return NULL; } @@ -2474,6 +2506,7 @@ PyImport_ReloadModule(PyObject *m) */ PyDict_SetItemString(modules, name, m); } + imp_modules_reloading_clear(); return newm; } @@ -2543,7 +2576,7 @@ PyImport_Import(PyObject *module_name) if (import == NULL) goto err; - /* Call the _import__ function with the proper argument list */ + /* Call the __import__ function with the proper argument list */ r = PyObject_CallFunctionObjArgs(import, module_name, globals, globals, silly_list, NULL); diff --git a/Python/peephole.c b/Python/peephole.c index f2e0c0b..7c4640c 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -1,4 +1,4 @@ -/* Peehole optimizations for bytecode compiler. */ +/* Peephole optimizations for bytecode compiler. */ #include "Python.h" @@ -380,13 +380,17 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, if (name == NULL || strcmp(name, "None") != 0) continue; for (j=0 ; j < PyList_GET_SIZE(consts) ; j++) { - if (PyList_GET_ITEM(consts, j) == Py_None) { - codestr[i] = LOAD_CONST; - SETARG(codestr, i, j); - cumlc = lastlc + 1; + if (PyList_GET_ITEM(consts, j) == Py_None) break; - } } + if (j == PyList_GET_SIZE(consts)) { + if (PyList_Append(consts, Py_None) == -1) + goto exitUnchanged; + } + assert(PyList_GET_ITEM(consts, j) == Py_None); + codestr[i] = LOAD_CONST; + SETARG(codestr, i, j); + cumlc = lastlc + 1; break; /* Skip over LOAD_CONST trueconst diff --git a/Python/pystate.c b/Python/pystate.c index cc25e3e..086789d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -68,6 +68,7 @@ PyInterpreterState_New(void) Py_FatalError("Can't initialize threads for interpreter"); #endif interp->modules = NULL; + interp->modules_reloading = NULL; interp->sysdict = NULL; interp->builtins = NULL; interp->tstate_head = NULL; @@ -107,6 +108,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) Py_CLEAR(interp->codec_search_cache); Py_CLEAR(interp->codec_error_registry); Py_CLEAR(interp->modules); + Py_CLEAR(interp->modules_reloading); Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 31c8329..8ff3629 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -4,6 +4,7 @@ #include "Python.h" #include "Python-ast.h" +#undef Yield /* undefine macro conflicting with winbase.h */ #include "grammar.h" #include "node.h" #include "token.h" @@ -71,6 +72,7 @@ extern void _PyGILState_Fini(void); int Py_DebugFlag; /* Needed by parser.c */ int Py_VerboseFlag; /* Needed by import.c */ int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */ +int Py_InspectFlag; /* Needed to determine whether to exit at SystemError */ int Py_NoSiteFlag; /* Suppress 'import site' */ int Py_UseClassExceptionsFlag = 1; /* Needed by bltinmodule.c: deprecated */ int Py_FrozenFlag; /* Needed by getpath.c */ @@ -194,6 +196,9 @@ Py_InitializeEx(int install_sigs) interp->modules = PyDict_New(); if (interp->modules == NULL) Py_FatalError("Py_Initialize: can't make modules dictionary"); + interp->modules_reloading = PyDict_New(); + if (interp->modules_reloading == NULL) + Py_FatalError("Py_Initialize: can't make modules_reloading dictionary"); #ifdef Py_USING_UNICODE /* Init Unicode implementation; relies on the codec registry */ @@ -531,6 +536,7 @@ Py_NewInterpreter(void) /* XXX The following is lax in error checking */ interp->modules = PyDict_New(); + interp->modules_reloading = PyDict_New(); bimod = _PyImport_FindExtension("__builtin__", "__builtin__"); if (bimod != NULL) { @@ -847,6 +853,7 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, { PyObject *m, *d, *v; const char *ext; + int set_file_name = 0, ret; m = PyImport_AddModule("__main__"); if (m == NULL) @@ -860,6 +867,7 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, Py_DECREF(f); return -1; } + set_file_name = 1; Py_DECREF(f); } ext = filename + strlen(filename) - 4; @@ -869,7 +877,8 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, fclose(fp); if ((fp = fopen(filename, "rb")) == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); - return -1; + ret = -1; + goto done; } /* Turn on optimization if a .pyo file is given */ if (strcmp(ext, ".pyo") == 0) @@ -881,10 +890,15 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, } if (v == NULL) { PyErr_Print(); - return -1; + ret = -1; + goto done; } Py_DECREF(v); - return 0; + ret = 0; + done: + if (set_file_name && PyDict_DelItemString(d, "__file__")) + PyErr_Clear(); + return ret; } int @@ -1014,6 +1028,11 @@ handle_system_exit(void) PyObject *exception, *value, *tb; int exitcode = 0; + if (Py_InspectFlag) + /* Don't exit if -i flag was given. This flag is set to 0 + * when entering interactive mode for inspecting. */ + return; + PyErr_Fetch(&exception, &value, &tb); fflush(stdout); if (value == NULL || value == Py_None) @@ -1201,8 +1220,8 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) err = PyFile_WriteObject(s, f, Py_PRINT_RAW); Py_XDECREF(s); } - if (err == 0) - err = PyFile_WriteString("\n", f); + /* try to write a newline in any case */ + err += PyFile_WriteString("\n", f); } Py_DECREF(value); /* If an error happened here, don't show it. @@ -1240,12 +1259,12 @@ PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, mod = PyParser_ASTFromFile(fp, filename, start, 0, 0, flags, NULL, arena); + if (closeit) + fclose(fp); if (mod == NULL) { PyArena_Free(arena); return NULL; } - if (closeit) - fclose(fp); ret = run_mod(mod, filename, globals, locals, flags, arena); PyArena_Free(arena); return ret; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d3c90bf..0d4a4e3 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1014,8 +1014,6 @@ svnversion_init(void) } else if (istag || strncmp(br_start, "branches", 8) == 0) { len = br_end2 - br_start; - assert(len >= 13); - assert(len < (sizeof(patchlevel_revision) - 13)); strncpy(branch, br_start, len); branch[len] = '\0'; @@ -1034,6 +1032,8 @@ svnversion_init(void) svn_revision = svnversion; else if (istag) { len = strlen(_patchlevel_revision); + assert(len >= 13); + assert(len < (sizeof(patchlevel_revision) + 13)); strncpy(patchlevel_revision, _patchlevel_revision + 11, len - 13); patchlevel_revision[len - 13] = '\0'; diff --git a/Python/thread_nt.h b/Python/thread_nt.h index 67f5ed5..27fca72 100644 --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -202,12 +202,12 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) * too many threads". */ dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n", - PyThread_get_thread_ident(), rv, errno)); + PyThread_get_thread_ident(), (void*)rv, errno)); obj.id = -1; } else { dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", - PyThread_get_thread_ident(), rv)); + PyThread_get_thread_ident(), (void*)rv)); /* wait for thread to initialize, so we can get its id */ WaitForSingleObject(obj.done, INFINITE); assert(obj.id != -1); @@ -333,7 +333,7 @@ PyThread_release_lock(PyThread_type_lock aLock) dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) - dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock, GetLastError())); + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); } /* minimum/maximum thread stack sizes supported */ @@ -578,9 +578,9 @@ Reliant UNIX: The thread support does not compile on Reliant UNIX, and MacOSX: The tests will crash on both 10.1 and 10.2 with SEGV in test_re and test_sre due to the small default stack size. If you set the stack size to 2048 before doing a "make test" the - failure can be avoided. If you're using the tcsh (the default - on OSX), or csh shells use "limit stacksize 2048" and for the - bash shell, use "ulimit -s 2048". + failure can be avoided. If you're using the tcsh or csh shells, + use "limit stacksize 2048" and for the bash shell (the default + as of OSX 10.3), use "ulimit -s 2048". On naked Darwin you may want to add the configure option "--disable-toolbox-glue" to disable the glue code for the Carbon diff --git a/Tools/pybench/pybench.py b/Tools/pybench/pybench.py index 89f6f9b..6c9d445 100755 --- a/Tools/pybench/pybench.py +++ b/Tools/pybench/pybench.py @@ -167,7 +167,7 @@ class Test: call of .run(). If you change a test in some way, don't forget to increase - it's version number. + its version number. """ diff --git a/configure.in b/configure.in index bececda..7004a06 100644 --- a/configure.in +++ b/configure.in @@ -1571,9 +1571,11 @@ if test -z "$CCSHARED" then case $ac_sys_system/$ac_sys_release in SunOS*) if test "$GCC" = yes; - then CCSHARED="-fPIC"; - else CCSHARED="-xcode=pic32"; - fi;; + then CCSHARED="-fPIC"; + elif test `uname -p` = sparc; + then CCSHARED="-xcode=pic32"; + else CCSHARED="-Kpic"; + fi;; hp*|HP*) if test "$GCC" = yes; then CCSHARED="-fPIC"; else CCSHARED="+z"; @@ -91,10 +91,14 @@ def find_module_file(module, dirlist): class PyBuildExt(build_ext): + def __init__(self, dist): + build_ext.__init__(self, dist) + self.failed = [] + def build_extensions(self): # Detect which modules should be compiled - self.detect_modules() + missing = self.detect_modules() # Remove modules that are present on the disabled list self.extensions = [ext for ext in self.extensions @@ -178,6 +182,31 @@ class PyBuildExt(build_ext): build_ext.build_extensions(self) + longest = max([len(e.name) for e in self.extensions]) + if self.failed: + longest = max(longest, max([len(name) for name in self.failed])) + + def print_three_column(lst): + lst.sort(key=str.lower) + # guarantee zip() doesn't drop anything + while len(lst) % 3: + lst.append("") + for e, f, g in zip(lst[::3], lst[1::3], lst[2::3]): + print("%-*s %-*s %-*s" % (longest, e, longest, f, + longest, g)) + print() + + if missing: + print() + print("Failed to find the necessary bits to build these modules:") + print_three_column(missing) + + if self.failed: + failed = self.failed[:] + print() + print("Failed to build these modules:") + print_three_column(failed) + def build_extension(self, ext): if ext.name == '_ctypes': @@ -189,6 +218,7 @@ class PyBuildExt(build_ext): except (CCompilerError, DistutilsError) as why: self.announce('WARNING: building of extension "%s" failed: %s' % (ext.name, sys.exc_info()[1])) + self.failed.append(ext.name) return # Workaround for Mac OS X: The Carbon-based modules cannot be # reliably imported into a command-line Python @@ -209,6 +239,7 @@ class PyBuildExt(build_ext): try: imp.load_dynamic(ext.name, ext_filename) except ImportError as why: + self.failed.append(ext.name) self.announce('*** WARNING: renaming "%s" since importing it' ' failed: %s' % (ext.name, why), level=3) assert not self.inplace @@ -234,6 +265,7 @@ class PyBuildExt(build_ext): self.announce('*** WARNING: importing extension "%s" ' 'failed with %s: %s' % (ext.name, exc_type, why), level=3) + self.failed.append(ext.name) def get_platform(self): # Get value of sys.platform @@ -299,6 +331,7 @@ class PyBuildExt(build_ext): ] inc_dirs = self.compiler.include_dirs + ['/usr/include'] exts = [] + missing = [] config_h = sysconfig.get_config_h_filename() config_h_vars = sysconfig.parse_config_h(open(config_h)) @@ -370,7 +403,7 @@ class PyBuildExt(build_ext): # fast iterator tools implemented in C exts.append( Extension("itertools", ["itertoolsmodule.c"]) ) # high-performance collections - exts.append( Extension("collections", ["collectionsmodule.c"]) ) + exts.append( Extension("_collections", ["_collectionsmodule.c"]) ) # bisect exts.append( Extension("_bisect", ["_bisectmodule.c"]) ) # heapq @@ -389,6 +422,8 @@ class PyBuildExt(build_ext): # static Unicode character database if have_unicode: exts.append( Extension('unicodedata', ['unicodedata.c']) ) + else: + missing.append('unicodedata') # access to ISO C locale support data = open('pyconfig.h').read() m = re.search(r"#s*define\s+WITH_LIBINTL\s+1\s*", data) @@ -421,6 +456,11 @@ class PyBuildExt(build_ext): if (config_h_vars.get('HAVE_GETSPNAM', False) or config_h_vars.get('HAVE_GETSPENT', False)): exts.append( Extension('spwd', ['spwdmodule.c']) ) + else: + missing.append('spwd') + else: + missing.extend(['pwd', 'grp', 'spwd']) + # select(2); not on ancient System V exts.append( Extension('select', ['selectmodule.c']) ) @@ -437,11 +477,15 @@ class PyBuildExt(build_ext): # Memory-mapped files (also works on Win32). if platform not in ['atheos', 'mac']: exts.append( Extension('mmap', ['mmapmodule.c']) ) + else: + missing.append('mmap') # Lance Ellinghaus's syslog module if platform not in ['mac']: # syslog daemon interface exts.append( Extension('syslog', ['syslogmodule.c']) ) + else: + missing.append('syslog') # George Neville-Neil's timing module: # Deprecated in PEP 4 http://www.python.org/peps/pep-0004.html @@ -468,6 +512,8 @@ class PyBuildExt(build_ext): exts.append( Extension('imageop', ['imageop.c']) ) # Read SGI RGB image files (but coded portably) exts.append( Extension('rgbimg', ['rgbimgmodule.c']) ) + else: + missing.extend(['imageop', 'rgbimg']) # readline do_readline = self.compiler.find_library_file(lib_dirs, 'readline') @@ -505,6 +551,9 @@ class PyBuildExt(build_ext): library_dirs=['/usr/lib/termcap'], extra_link_args=readline_extra_link_args, libraries=readline_libs) ) + else: + missing.append('readline') + if platform not in ['mac']: # crypt module. @@ -513,6 +562,8 @@ class PyBuildExt(build_ext): else: libs = [] exts.append( Extension('crypt', ['cryptmodule.c'], libraries=libs) ) + else: + missing.append('crypt') # CSV files exts.append( Extension('_csv', ['_csv.c']) ) @@ -545,6 +596,8 @@ class PyBuildExt(build_ext): library_dirs = ssl_libs, libraries = ['ssl', 'crypto'], depends = ['socketmodule.h']), ) + else: + missing.append('_ssl') # find out which version of OpenSSL we have openssl_ver = 0 @@ -578,6 +631,7 @@ class PyBuildExt(build_ext): include_dirs = ssl_incs, library_dirs = ssl_libs, libraries = ['ssl', 'crypto']) ) + missing.extend(['_sha', '_md5']) else: # The _sha module implements the SHA1 hash algorithm. exts.append( Extension('_sha', ['shamodule.c']) ) @@ -587,12 +641,14 @@ class PyBuildExt(build_ext): exts.append( Extension('_md5', sources = ['md5module.c', 'md5.c'], depends = ['md5.h']) ) + missing.append('_hashlib') if (openssl_ver < 0x00908000): # OpenSSL doesn't do these until 0.9.8 so we'll bring our own hash exts.append( Extension('_sha256', ['sha256module.c']) ) exts.append( Extension('_sha512', ['sha512module.c']) ) - + else: + missing.extend(['_sha256', '_sha512']) # Modules that provide persistent dictionary-like semantics. You will # probably want to arrange for at least one of them to be available on @@ -618,10 +674,11 @@ class PyBuildExt(build_ext): '/usr/include/db4', '/usr/local/include/db4', '/opt/sfw/include/db4', - '/sw/include/db4', '/usr/include/db3', '/usr/local/include/db3', '/opt/sfw/include/db3', + # Fink defaults (http://fink.sourceforge.net/) + '/sw/include/db4', '/sw/include/db3', ] # 4.x minor number specific paths @@ -632,6 +689,8 @@ class PyBuildExt(build_ext): db_inc_paths.append('/usr/local/include/db4%d' % x) db_inc_paths.append('/pkg/db-4.%d/include' % x) db_inc_paths.append('/opt/db-4.%d/include' % x) + # MacPorts default (http://www.macports.org/) + db_inc_paths.append('/opt/local/include/db4%d' % x) # 3.x minor number specific paths for x in (3,): db_inc_paths.append('/usr/include/db3%d' % x) @@ -656,7 +715,7 @@ class PyBuildExt(build_ext): std_variants.append(os.path.join(dn, "db3.%d"%x)) db_inc_paths = std_variants + db_inc_paths - + db_inc_paths = [p for p in db_inc_paths if os.path.exists(p)] db_ver_inc_map = {} @@ -679,7 +738,7 @@ class PyBuildExt(build_ext): if ( (db_ver not in db_ver_inc_map) and (db_ver <= max_db_ver and db_ver >= min_db_ver) ): # save the include directory with the db.h version - # (first occurrance only) + # (first occurrence only) db_ver_inc_map[db_ver] = d if db_setup_debug: print("db.h: found", db_ver, "in", d) @@ -688,7 +747,8 @@ class PyBuildExt(build_ext): if db_setup_debug: print("db.h: ignoring", d) else: # ignore this header, it didn't contain a version number - if db_setup_debug: print("db.h: unsupported version", db_ver, "in", d) + if db_setup_debug: + print("db.h: no version number version in", d) db_found_vers = sorted(db_ver_inc_map.keys()) @@ -698,10 +758,8 @@ class PyBuildExt(build_ext): # check lib directories parallel to the location of the header db_dirs_to_check = [ - os.path.join(db_incdir, '..', 'lib64'), - os.path.join(db_incdir, '..', 'lib'), - os.path.join(db_incdir, '..', '..', 'lib64'), - os.path.join(db_incdir, '..', '..', 'lib'), + db_incdir.replace("include", 'lib64'), + db_incdir.replace("include", 'lib'), ] db_dirs_to_check = filter(os.path.isdir, db_dirs_to_check) @@ -742,6 +800,7 @@ class PyBuildExt(build_ext): db_incs = None dblibs = [] dblib_dir = None + missing.append('_bsddb') # The sqlite interface sqlite_setup_debug = False # verbose debug prints from this script? @@ -834,6 +893,8 @@ class PyBuildExt(build_ext): runtime_library_dirs=sqlite_libdir, extra_link_args=sqlite_extra_link_args, libraries=["sqlite3",])) + else: + missing.append('_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 @@ -856,6 +917,10 @@ class PyBuildExt(build_ext): libraries=libraries)) else: exts.append(Extension('bsddb185', ['bsddbmodule.c'])) + else: + missing.append('bsddb185') + else: + missing.append('bsddb185') # The standard Unix dbm module: if platform not in ['cygwin']: @@ -881,11 +946,15 @@ class PyBuildExt(build_ext): define_macros=[('HAVE_BERKDB_H',None), ('DB_DBM_HSEARCH',None)], libraries=dblibs)) + else: + missing.append('dbm') # Anthony Baxter's gdbm module. GNU dbm(3) will require -lgdbm: if (self.compiler.find_library_file(lib_dirs, 'gdbm')): exts.append( Extension('gdbm', ['gdbmmodule.c'], libraries = ['gdbm'] ) ) + else: + missing.append('gdbm') # Unix-only modules if platform not in ['mac', 'win32']: @@ -894,6 +963,8 @@ class PyBuildExt(build_ext): # Jeremy Hylton's rlimit interface if platform not in ['atheos']: exts.append( Extension('resource', ['resource.c']) ) + else: + missing.append('resource') # Sun yellow pages. Some systems have the functions in libc. if platform not in ['cygwin', 'atheos']: @@ -903,6 +974,10 @@ class PyBuildExt(build_ext): libs = [] exts.append( Extension('nis', ['nismodule.c'], libraries = libs) ) + else: + missing.append('nis') + else: + missing.extend(['nis', 'resource', 'termios']) # Curses support, requiring the System V version of curses, often # provided by the ncurses library. @@ -931,13 +1006,16 @@ class PyBuildExt(build_ext): exts.append( Extension('_curses', ['_cursesmodule.c'], libraries = curses_libs) ) + else: + missing.append('_curses') # If the curses module is enabled, check for the panel module if (module_enabled(exts, '_curses') and self.compiler.find_library_file(lib_dirs, panel_library)): exts.append( Extension('_curses_panel', ['_curses_panel.c'], libraries = [panel_library] + curses_libs) ) - + else: + missing.append('_curses_panel') # Andrew Kuchling's zlib module. Note that some versions of zlib # 1.1.3 have security problems. See CERT Advisory CA-2002-07: @@ -973,6 +1051,12 @@ class PyBuildExt(build_ext): exts.append( Extension('zlib', ['zlibmodule.c'], libraries = ['z'], extra_link_args = zlib_extra_link_args)) + else: + missing.append('zlib') + else: + missing.append('zlib') + else: + missing.append('zlib') # Gustavo Niemeyer's bz2 module. if (self.compiler.find_library_file(lib_dirs, 'bz2')): @@ -983,6 +1067,8 @@ class PyBuildExt(build_ext): exts.append( Extension('bz2', ['bz2module.c'], libraries = ['bz2'], extra_link_args = bz2_extra_link_args) ) + else: + missing.append('bz2') # Interface to the Expat XML parser # @@ -1020,14 +1106,20 @@ class PyBuildExt(build_ext): include_dirs = [expatinc], sources = ['_elementtree.c'], )) + else: + missing.append('_elementtree') # Hye-Shik Chang's CJKCodecs modules. if have_unicode: exts.append(Extension('_multibytecodec', ['cjkcodecs/multibytecodec.c'])) for loc in ('kr', 'jp', 'cn', 'tw', 'hk', 'iso2022'): - exts.append(Extension('_codecs_' + loc, + exts.append(Extension('_codecs_%s' % loc, ['cjkcodecs/_codecs_%s.c' % loc])) + else: + missing.append('_multibytecodec') + for loc in ('kr', 'jp', 'cn', 'tw', 'hk', 'iso2022'): + missing.append('_codecs_%s' % loc) # Dynamic loading module if sys.maxint == 0x7fffffff: @@ -1035,6 +1127,10 @@ class PyBuildExt(build_ext): dl_inc = find_file('dlfcn.h', [], inc_dirs) if (dl_inc is not None) and (platform not in ['atheos']): exts.append( Extension('dl', ['dlmodule.c']) ) + else: + missing.append('dl') + else: + missing.append('dl') # Thomas Heller's _ctypes module self.detect_ctypes(inc_dirs, lib_dirs) @@ -1046,14 +1142,20 @@ class PyBuildExt(build_ext): if platform == 'linux2': # Linux-specific modules exts.append( Extension('linuxaudiodev', ['linuxaudiodev.c']) ) + else: + missing.append('linuxaudiodev') if platform in ('linux2', 'freebsd4', 'freebsd5', 'freebsd6', 'freebsd7'): exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) ) + else: + missing.append('ossaudiodev') if platform == 'sunos5': # SunOS specific modules exts.append( Extension('sunaudiodev', ['sunaudiodev.c']) ) + else: + missing.append('sunaudiodev') if platform == 'darwin' and ("--disable-toolbox-glue" not in sysconfig.get_config_var("CONFIG_ARGS")): @@ -1142,6 +1244,11 @@ class PyBuildExt(build_ext): # Call the method for detecting whether _tkinter can be compiled self.detect_tkinter(inc_dirs, lib_dirs) + if '_tkinter' not in [e.name for e in self.extensions]: + missing.append('_tkinter') + + return missing + def detect_tkinter_darwin(self, inc_dirs, lib_dirs): # The _tkinter module, using frameworks. Since frameworks are quite # different the UNIX search logic is not sharable. |