From python-checkins at python.org Thu Mar 1 00:31:22 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 00:31:22 +0100 (CET) Subject: [Python-checkins] r54046 - in python/branches/p3yk_no_args_on_exc: BROKEN Lib/socket.py Misc/NEWS Modules/_ssl.c Modules/socketmodule.c Python/errors.c Message-ID: <20070228233122.B7A9D1E400C@bag.python.org> Author: brett.cannon Date: Thu Mar 1 00:31:14 2007 New Revision: 54046 Modified: python/branches/p3yk_no_args_on_exc/BROKEN python/branches/p3yk_no_args_on_exc/Lib/socket.py python/branches/p3yk_no_args_on_exc/Misc/NEWS python/branches/p3yk_no_args_on_exc/Modules/_ssl.c python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c python/branches/p3yk_no_args_on_exc/Python/errors.c Log: Fix the BaseException.args removal. Required wrapping tuple arguments to PyErr_SetObject() within another tuple, else PyErr_Fetch() passes the tuple directly to the exception's init function which raising an exception itself since only one argument is allowed. Also had to tweak PyErr_SetFromErrno() to do the same thing. Modified: python/branches/p3yk_no_args_on_exc/BROKEN ============================================================================== --- python/branches/p3yk_no_args_on_exc/BROKEN (original) +++ python/branches/p3yk_no_args_on_exc/BROKEN Thu Mar 1 00:31:14 2007 @@ -1,10 +1,9 @@ -* Construction of exception at C level passing too many arguments? +* socket.error is passed two arguments but just subclasses PyExc_Exception, and + thus constructor gets passed two arguments (thanks to + PyErr_NormalizeException() passing any tuple given to it as a value as the + args tuple, but not when it is any other type of value). + test_bsddb - + test_socket - + test_socket_ssl - + test_timeout - + test_urllib2net - + test_urllibnet + + test_bsddb3 (?) * doctest oddness (need to change how SyntaxError is handled?) + test_xml_etree Modified: python/branches/p3yk_no_args_on_exc/Lib/socket.py ============================================================================== --- python/branches/p3yk_no_args_on_exc/Lib/socket.py (original) +++ python/branches/p3yk_no_args_on_exc/Lib/socket.py Thu Mar 1 00:31:14 2007 @@ -138,7 +138,7 @@ class _closedsocket(object): __slots__ = [] def _dummy(*args): - raise error(EBADF, 'Bad file descriptor') + raise error((EBADF, 'Bad file descriptor')) def close(self): pass # All _delegate_methods must also be initialized here. Modified: python/branches/p3yk_no_args_on_exc/Misc/NEWS ============================================================================== --- python/branches/p3yk_no_args_on_exc/Misc/NEWS (original) +++ python/branches/p3yk_no_args_on_exc/Misc/NEWS Thu Mar 1 00:31:14 2007 @@ -28,6 +28,9 @@ Core and Builtins ----------------- +- PyErr_SetFromErrno() now wraps its value argument in another tuple before + passing it to the exception type's init function. + - SyntaxError now enforces the requirement of having two arguments, second of which is a four-item tuple. Modified: python/branches/p3yk_no_args_on_exc/Modules/_ssl.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Modules/_ssl.c (original) +++ python/branches/p3yk_no_args_on_exc/Modules/_ssl.c Thu Mar 1 00:31:14 2007 @@ -161,19 +161,18 @@ n = PyInt_FromLong((long) p); if (n == NULL) return NULL; - v = PyTuple_New(2); - if (v == NULL) { - Py_DECREF(n); - return NULL; - } s = PyString_FromString(errstr); if (s == NULL) { - Py_DECREF(v); Py_DECREF(n); } - PyTuple_SET_ITEM(v, 0, n); - PyTuple_SET_ITEM(v, 1, s); + v = Py_BuildValue("((O, O))", n, s); + if (v == NULL) { + Py_DECREF(n); + Py_DECREF(s); + return NULL; + } + PyErr_SetObject(PySSLErrorObject, v); Py_DECREF(v); return NULL; Modified: python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c (original) +++ python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c Thu Mar 1 00:31:14 2007 @@ -531,7 +531,7 @@ } } - v = Py_BuildValue("(is)", err_no, msg); + v = Py_BuildValue("((is))", err_no, msg); if (v != NULL) { PyErr_SetObject(socket_error, v); Py_DECREF(v); @@ -567,7 +567,7 @@ *lastc-- = '\0'; } } - v = Py_BuildValue("(is)", myerrorcode, outbuf); + v = Py_BuildValue("((is))", myerrorcode, outbuf); if (v != NULL) { PyErr_SetObject(socket_error, v); Py_DECREF(v); @@ -580,7 +580,7 @@ #if defined(RISCOS) if (_inet_error.errnum != NULL) { PyObject *v; - v = Py_BuildValue("(is)", errno, _inet_err()); + v = Py_BuildValue("((is))", errno, _inet_err()); if (v != NULL) { PyErr_SetObject(socket_error, v); Py_DECREF(v); @@ -599,9 +599,9 @@ PyObject *v; #ifdef HAVE_HSTRERROR - v = Py_BuildValue("(is)", h_error, (char *)hstrerror(h_error)); + v = Py_BuildValue("((is))", h_error, (char *)hstrerror(h_error)); #else - v = Py_BuildValue("(is)", h_error, "host not found"); + v = Py_BuildValue("((is))", h_error, "host not found"); #endif if (v != NULL) { PyErr_SetObject(socket_herror, v); @@ -624,9 +624,9 @@ #endif #ifdef HAVE_GAI_STRERROR - v = Py_BuildValue("(is)", error, gai_strerror(error)); + v = Py_BuildValue("((is))", error, gai_strerror(error)); #else - v = Py_BuildValue("(is)", error, "getaddrinfo failed"); + v = Py_BuildValue("((is))", error, "getaddrinfo failed"); #endif if (v != NULL) { PyErr_SetObject(socket_gaierror, v); Modified: python/branches/p3yk_no_args_on_exc/Python/errors.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Python/errors.c (original) +++ python/branches/p3yk_no_args_on_exc/Python/errors.c Thu Mar 1 00:31:14 2007 @@ -335,9 +335,9 @@ #endif /* Unix/Windows */ #endif /* PLAN 9*/ if (filenameObject != NULL) - v = Py_BuildValue("(isO)", i, s, filenameObject); + v = Py_BuildValue("((isO))", i, s, filenameObject); else - v = Py_BuildValue("(is)", i, s); + v = Py_BuildValue("((is))", i, s); if (v != NULL) { PyErr_SetObject(exc, v); Py_DECREF(v); @@ -415,9 +415,9 @@ s[--len] = '\0'; } if (filenameObject != NULL) - v = Py_BuildValue("(isO)", err, s, filenameObject); + v = Py_BuildValue("((isO))", err, s, filenameObject); else - v = Py_BuildValue("(is)", err, s); + v = Py_BuildValue("((is))", err, s); if (v != NULL) { PyErr_SetObject(exc, v); Py_DECREF(v); From python-checkins at python.org Thu Mar 1 00:39:16 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 00:39:16 +0100 (CET) Subject: [Python-checkins] r54047 - in python/branches/p3yk_no_args_on_exc: BROKEN Modules/_bsddb.c Message-ID: <20070228233916.70AAD1E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 00:39:15 2007 New Revision: 54047 Modified: python/branches/p3yk_no_args_on_exc/BROKEN python/branches/p3yk_no_args_on_exc/Modules/_bsddb.c Log: Fix _bsddb for the BaseException.args removal. Modified: python/branches/p3yk_no_args_on_exc/BROKEN ============================================================================== --- python/branches/p3yk_no_args_on_exc/BROKEN (original) +++ python/branches/p3yk_no_args_on_exc/BROKEN Thu Mar 1 00:39:15 2007 @@ -2,7 +2,6 @@ thus constructor gets passed two arguments (thanks to PyErr_NormalizeException() passing any tuple given to it as a value as the args tuple, but not when it is any other type of value). - + test_bsddb + test_bsddb3 (?) * doctest oddness (need to change how SyntaxError is handled?) Modified: python/branches/p3yk_no_args_on_exc/Modules/_bsddb.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Modules/_bsddb.c (original) +++ python/branches/p3yk_no_args_on_exc/Modules/_bsddb.c Thu Mar 1 00:39:15 2007 @@ -323,7 +323,7 @@ #define _CHECK_OBJECT_NOT_CLOSED(nonNull, pyErrObj, name) \ if ((nonNull) == NULL) { \ PyObject *errTuple = NULL; \ - errTuple = Py_BuildValue("(is)", 0, #name " object has been closed"); \ + errTuple = Py_BuildValue("((is))", 0, #name " object has been closed"); \ PyErr_SetObject((pyErrObj), errTuple); \ Py_DECREF(errTuple); \ return NULL; \ @@ -602,7 +602,7 @@ } _db_errmsg[0] = 0; - errTuple = Py_BuildValue("(is)", err, errTxt); + errTuple = Py_BuildValue("((is))", err, errTxt); PyErr_SetObject(errObj, errTuple); Py_DECREF(errTuple); } @@ -1931,7 +1931,7 @@ #endif if (NULL == self->db) { - PyObject *t = Py_BuildValue("(is)", 0, + PyObject *t = Py_BuildValue("((is))", 0, "Cannot call open() twice for DB object"); PyErr_SetObject(DBError, t); Py_DECREF(t); @@ -2703,7 +2703,7 @@ DBObject* self = (DBObject*)_self; if (self->db == NULL) { - PyObject *t = Py_BuildValue("(is)", 0, "DB object has been closed"); + PyObject *t = Py_BuildValue("((is))", 0, "DB object has been closed"); PyErr_SetObject(DBError, t); Py_DECREF(t); return -1; @@ -2795,7 +2795,7 @@ int flags = 0; if (self->db == NULL) { - PyObject *t = Py_BuildValue("(is)", 0, "DB object has been closed"); + PyObject *t = Py_BuildValue("((is))", 0, "DB object has been closed"); PyErr_SetObject(DBError, t); Py_DECREF(t); return -1; @@ -4686,7 +4686,7 @@ return NULL; if (!self->txn) { - PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + PyObject *t = Py_BuildValue("((is))", 0, "DBTxn must not be used " "after txn_commit or txn_abort"); PyErr_SetObject(DBError, t); Py_DECREF(t); @@ -4723,7 +4723,7 @@ } if (!self->txn) { - PyObject *t = Py_BuildValue("(is)", 0,"DBTxn must not be used " + PyObject *t = Py_BuildValue("((is))", 0,"DBTxn must not be used " "after txn_commit or txn_abort"); PyErr_SetObject(DBError, t); Py_DECREF(t); @@ -4745,7 +4745,7 @@ return NULL; if (!self->txn) { - PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + PyObject *t = Py_BuildValue("((is))", 0, "DBTxn must not be used " "after txn_commit or txn_abort"); PyErr_SetObject(DBError, t); Py_DECREF(t); @@ -4770,7 +4770,7 @@ return NULL; if (!self->txn) { - PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + PyObject *t = Py_BuildValue("((is))", 0, "DBTxn must not be used " "after txn_commit or txn_abort"); PyErr_SetObject(DBError, t); Py_DECREF(t); @@ -4799,7 +4799,7 @@ return NULL; if (!self->txn) { - PyObject *t = Py_BuildValue("(is)", 0, "DBTxn must not be used " + PyObject *t = Py_BuildValue("((is))", 0, "DBTxn must not be used " "after txn_commit or txn_abort"); PyErr_SetObject(DBError, t); Py_DECREF(t); From python-checkins at python.org Thu Mar 1 00:51:13 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 00:51:13 +0100 (CET) Subject: [Python-checkins] r54048 - in python/branches/p3yk_no_args_on_exc: BROKEN Objects/exceptions.c Message-ID: <20070228235113.4101D1E4003@bag.python.org> Author: brett.cannon Date: Thu Mar 1 00:51:06 2007 New Revision: 54048 Modified: python/branches/p3yk_no_args_on_exc/BROKEN python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Log: Clean up the output from IOError. Allows test_xml_etree to pass after the BaseException.args removal. Modified: python/branches/p3yk_no_args_on_exc/BROKEN ============================================================================== --- python/branches/p3yk_no_args_on_exc/BROKEN (original) +++ python/branches/p3yk_no_args_on_exc/BROKEN Thu Mar 1 00:51:06 2007 @@ -3,6 +3,3 @@ PyErr_NormalizeException() passing any tuple given to it as a value as the args tuple, but not when it is any other type of value). + test_bsddb3 (?) - -* doctest oddness (need to change how SyntaxError is handled?) - + test_xml_etree Modified: python/branches/p3yk_no_args_on_exc/Objects/exceptions.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Objects/exceptions.c (original) +++ python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Thu Mar 1 00:51:06 2007 @@ -524,7 +524,7 @@ { PyObject *rtnval = NULL; - if (self->filename) { + if (self->filename != Py_None) { PyObject *fmt; PyObject *repr; PyObject *tuple; @@ -558,7 +558,7 @@ Py_DECREF(fmt); Py_DECREF(tuple); } - else if (self->myerrno && self->strerror) { + else if (self->myerrno != Py_None && self->strerror != Py_None) { PyObject *fmt; PyObject *tuple; From python-checkins at python.org Thu Mar 1 01:28:28 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 01:28:28 +0100 (CET) Subject: [Python-checkins] r54049 - in python/branches/p3yk_no_args_on_exc: BROKEN Lib/bsddb/dbtables.py Lib/bsddb/test/test_basics.py Lib/bsddb/test/test_recno.py Python/errors.c Message-ID: <20070301002828.D70721E4004@bag.python.org> Author: brett.cannon Date: Thu Mar 1 01:28:20 2007 New Revision: 54049 Modified: python/branches/p3yk_no_args_on_exc/BROKEN python/branches/p3yk_no_args_on_exc/Lib/bsddb/dbtables.py python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_basics.py python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_recno.py python/branches/p3yk_no_args_on_exc/Python/errors.c Log: Fix test_bsddb and test_bsddb3 (still one failure, but it doesn't look like it is from this work). Had to revert part of r54046 where PyErr_SetFromErrno() was being wrapped in another tuple since it messed up IOError. This means that socket.error needs to be rewritten so that it takes up to two arguments and does the right thing when calling Exception.__init__. See _bsddb for possible inspiration on how to do this in Python code within a C extension module. Modified: python/branches/p3yk_no_args_on_exc/BROKEN ============================================================================== --- python/branches/p3yk_no_args_on_exc/BROKEN (original) +++ python/branches/p3yk_no_args_on_exc/BROKEN Thu Mar 1 01:28:20 2007 @@ -2,4 +2,8 @@ thus constructor gets passed two arguments (thanks to PyErr_NormalizeException() passing any tuple given to it as a value as the args tuple, but not when it is any other type of value). - + test_bsddb3 (?) + + test_socket + + test_socket_ssl + + test_timeout + + test_urllib2net + + test_urllibnet Modified: python/branches/p3yk_no_args_on_exc/Lib/bsddb/dbtables.py ============================================================================== --- python/branches/p3yk_no_args_on_exc/Lib/bsddb/dbtables.py (original) +++ python/branches/p3yk_no_args_on_exc/Lib/bsddb/dbtables.py Thu Mar 1 01:28:20 2007 @@ -244,7 +244,7 @@ columnlist_key = _columns_key(table) if self.db.has_key(columnlist_key): - raise TableAlreadyExists, "table already exists" + raise TableAlreadyExists("table already exists") txn = self.env.txn_begin() # store the table's column info @@ -263,7 +263,7 @@ except DBError as dberror: if txn: txn.abort() - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) def ListTableColumns(self, table): @@ -272,7 +272,7 @@ """ assert isinstance(table, StringType) if contains_metastrings(table): - raise ValueError, "bad table name: contains reserved metastrings" + raise ValueError("bad table name: contains reserved metastrings") columnlist_key = _columns_key(table) if not self.db.has_key(columnlist_key): @@ -341,7 +341,7 @@ except DBError as dberror: if txn: txn.abort() - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) def __load_column_info(self, table) : @@ -350,9 +350,9 @@ try: tcolpickles = self.db.get(_columns_key(table)) except DBNotFoundError: - raise TableDBError, "unknown table: %r" % (table,) + raise TableDBError("unknown table: %r" % (table,)) if not tcolpickles: - raise TableDBError, "unknown table: %r" % (table,) + raise TableDBError("unknown table: %r" % (table,)) self.__tablecolumns[table] = pickle.loads(tcolpickles) def __new_rowid(self, table, txn) : @@ -416,7 +416,7 @@ if txn: txn.abort() self.db.delete(_rowid_key(table, rowid)) - raise TableDBError, dberror[1], info[2] + raise TableDBError((dberror.message[1], info[2])) def Modify(self, table, conditions={}, mappings={}): @@ -467,7 +467,7 @@ raise except DBError as dberror: - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) def Delete(self, table, conditions={}): """Delete(table, conditions) - Delete items matching the given @@ -507,7 +507,7 @@ txn.abort() raise except DBError as dberror: - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) def Select(self, table, columns, conditions={}): @@ -527,7 +527,7 @@ columns = self.__tablecolumns[table] matching_rowids = self.__Select(table, columns, conditions) except DBError as dberror: - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) # return the matches as a list of dictionaries return matching_rowids.values() @@ -617,7 +617,7 @@ key, data = cur.next() except DBError as dberror: - if dberror[0] != DB_NOTFOUND: + if dberror.message != DB_NOTFOUND: raise continue @@ -703,4 +703,4 @@ except DBError as dberror: if txn: txn.abort() - raise TableDBError, dberror[1] + raise TableDBError(dberror.message[1]) Modified: python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_basics.py ============================================================================== --- python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_basics.py (original) +++ python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_basics.py Thu Mar 1 01:28:20 2007 @@ -163,7 +163,7 @@ try: d.delete('abcd') except db.DBNotFoundError as val: - assert val[0] == db.DB_NOTFOUND + assert val.message[0] == db.DB_NOTFOUND if verbose: print(val) else: self.fail("expected exception") @@ -182,7 +182,7 @@ try: d.put('abcd', 'this should fail', flags=db.DB_NOOVERWRITE) except db.DBKeyExistError as val: - assert val[0] == db.DB_KEYEXIST + assert val.message[0] == db.DB_KEYEXIST if verbose: print(val) else: self.fail("expected exception") @@ -315,7 +315,7 @@ rec = c.next() except db.DBNotFoundError as val: if get_raises_error: - assert val[0] == db.DB_NOTFOUND + assert val.message[0] == db.DB_NOTFOUND if verbose: print(val) rec = None else: @@ -335,7 +335,7 @@ rec = c.prev() except db.DBNotFoundError as val: if get_raises_error: - assert val[0] == db.DB_NOTFOUND + assert val.message[0] == db.DB_NOTFOUND if verbose: print(val) rec = None else: @@ -358,7 +358,7 @@ try: n = c.set('bad key') except db.DBNotFoundError as val: - assert val[0] == db.DB_NOTFOUND + assert val.message[0] == db.DB_NOTFOUND if verbose: print(val) else: if set_raises_error: @@ -372,7 +372,7 @@ try: n = c.get_both('0404', 'bad data') except db.DBNotFoundError as val: - assert val[0] == db.DB_NOTFOUND + assert val.message[0] == db.DB_NOTFOUND if verbose: print(val) else: if get_raises_error: @@ -401,7 +401,7 @@ rec = c.current() except db.DBKeyEmptyError as val: if get_raises_error: - assert val[0] == db.DB_KEYEMPTY + assert val.message[0] == db.DB_KEYEMPTY if verbose: print(val) else: self.fail("unexpected DBKeyEmptyError") @@ -446,7 +446,7 @@ # a bug may cause a NULL pointer dereference... getattr(c, method)(*args) except db.DBError as val: - assert val[0] == 0 + assert val.message[0] == 0 if verbose: print(val) else: self.fail("no exception raised when using a buggy cursor's" Modified: python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_recno.py ============================================================================== --- python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_recno.py (original) +++ python/branches/p3yk_no_args_on_exc/Lib/bsddb/test/test_recno.py Thu Mar 1 01:28:20 2007 @@ -64,7 +64,7 @@ try: data = d[0] # This should raise a KeyError!?!?! except db.DBInvalidArgError as val: - assert val[0] == db.EINVAL + assert val.message[0] == db.EINVAL if verbose: print(val) else: self.fail("expected exception") @@ -268,7 +268,7 @@ try: # this one will fail d.append('bad' * 20) except db.DBInvalidArgError as val: - assert val[0] == db.EINVAL + assert val.message[0] == db.EINVAL if verbose: print(val) else: self.fail("expected exception") Modified: python/branches/p3yk_no_args_on_exc/Python/errors.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Python/errors.c (original) +++ python/branches/p3yk_no_args_on_exc/Python/errors.c Thu Mar 1 01:28:20 2007 @@ -335,9 +335,9 @@ #endif /* Unix/Windows */ #endif /* PLAN 9*/ if (filenameObject != NULL) - v = Py_BuildValue("((isO))", i, s, filenameObject); + v = Py_BuildValue("(isO)", i, s, filenameObject); else - v = Py_BuildValue("((is))", i, s); + v = Py_BuildValue("(is)", i, s); if (v != NULL) { PyErr_SetObject(exc, v); Py_DECREF(v); @@ -415,9 +415,9 @@ s[--len] = '\0'; } if (filenameObject != NULL) - v = Py_BuildValue("((isO))", err, s, filenameObject); + v = Py_BuildValue("(isO)", err, s, filenameObject); else - v = Py_BuildValue("((is))", err, s); + v = Py_BuildValue("(is)", err, s); if (v != NULL) { PyErr_SetObject(exc, v); Py_DECREF(v); From python-checkins at python.org Thu Mar 1 04:33:20 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 04:33:20 +0100 (CET) Subject: [Python-checkins] r54050 - python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c Message-ID: <20070301033320.2789B1E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 04:33:15 2007 New Revision: 54050 Modified: python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c Log: Re-implement socket.error to add support for taking two arguments. Fixes (again) all of the failing networking tests. Modified: python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c (original) +++ python/branches/p3yk_no_args_on_exc/Modules/socketmodule.c Thu Mar 1 04:33:15 2007 @@ -4240,6 +4240,7 @@ init_socket(void) { PyObject *m, *has_ipv6; + PyObject *module_dict; if (!os_init()) return; @@ -4251,30 +4252,23 @@ if (m == NULL) return; - socket_error = PyErr_NewException("socket.error", NULL, NULL); - if (socket_error == NULL) - return; - PySocketModuleAPI.error = socket_error; - Py_INCREF(socket_error); - PyModule_AddObject(m, "error", socket_error); - socket_herror = PyErr_NewException("socket.herror", - socket_error, NULL); - if (socket_herror == NULL) - return; - Py_INCREF(socket_herror); - PyModule_AddObject(m, "herror", socket_herror); - socket_gaierror = PyErr_NewException("socket.gaierror", socket_error, - NULL); - if (socket_gaierror == NULL) - return; - Py_INCREF(socket_gaierror); - PyModule_AddObject(m, "gaierror", socket_gaierror); - socket_timeout = PyErr_NewException("socket.timeout", - socket_error, NULL); - if (socket_timeout == NULL) - return; - Py_INCREF(socket_timeout); - PyModule_AddObject(m, "timeout", socket_timeout); + module_dict = PyModule_GetDict(m); + PyDict_SetItemString(module_dict, "Exception", PyExc_Exception); + + PyRun_String("class error(Exception):\n" + " def __init__(self, message='', errno=None):\n" + " Exception.__init__(self, message)\n" + " if errno:\n" + " self.errno = errno\n" + "class herror(error): pass\n" + "class gaierror(error): pass\n" + "class timeout(error): pass\n", + Py_file_input, module_dict, module_dict); + socket_error = PyDict_GetItemString(module_dict, "error"); + socket_herror = PyDict_GetItemString(module_dict, "herror"); + socket_gaierror = PyDict_GetItemString(module_dict, "gaierror"); + socket_timeout = PyDict_GetItemString(module_dict, "timeout"); + Py_INCREF((PyObject *)&sock_type); if (PyModule_AddObject(m, "SocketType", (PyObject *)&sock_type) != 0) From python-checkins at python.org Thu Mar 1 04:34:43 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 04:34:43 +0100 (CET) Subject: [Python-checkins] r54051 - in python/branches/p3yk_no_args_on_exc: BROKEN Lib/test/test_compiler.py Message-ID: <20070301033443.1DBF41E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 04:34:39 2007 New Revision: 54051 Modified: python/branches/p3yk_no_args_on_exc/BROKEN python/branches/p3yk_no_args_on_exc/Lib/test/test_compiler.py Log: Make test_compiler not fail because of str concatentation. Test still fails, though under -uall execution. Modified: python/branches/p3yk_no_args_on_exc/BROKEN ============================================================================== --- python/branches/p3yk_no_args_on_exc/BROKEN (original) +++ python/branches/p3yk_no_args_on_exc/BROKEN Thu Mar 1 04:34:39 2007 @@ -1,9 +1 @@ -* socket.error is passed two arguments but just subclasses PyExc_Exception, and - thus constructor gets passed two arguments (thanks to - PyErr_NormalizeException() passing any tuple given to it as a value as the - args tuple, but not when it is any other type of value). - + test_socket - + test_socket_ssl - + test_timeout - + test_urllib2net - + test_urllibnet +test_bsddb3 test_compiler (with -uall) Modified: python/branches/p3yk_no_args_on_exc/Lib/test/test_compiler.py ============================================================================== --- python/branches/p3yk_no_args_on_exc/Lib/test/test_compiler.py (original) +++ python/branches/p3yk_no_args_on_exc/Lib/test/test_compiler.py Thu Mar 1 04:34:39 2007 @@ -50,7 +50,7 @@ try: compiler.compile(buf, basename, "exec") except Exception as e: - e.message += "[in file %s]" % basename + e.message = str(e.message) + " [in file %s]" % basename raise def testNewClassSyntax(self): From python-checkins at python.org Thu Mar 1 04:41:05 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 04:41:05 +0100 (CET) Subject: [Python-checkins] r54052 - python/branches/p3yk_no_args_on_exc/Misc/NEWS Message-ID: <20070301034105.73E961E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 04:41:02 2007 New Revision: 54052 Modified: python/branches/p3yk_no_args_on_exc/Misc/NEWS Log: Add info about things that have changed. Modified: python/branches/p3yk_no_args_on_exc/Misc/NEWS ============================================================================== --- python/branches/p3yk_no_args_on_exc/Misc/NEWS (original) +++ python/branches/p3yk_no_args_on_exc/Misc/NEWS Thu Mar 1 04:41:02 2007 @@ -150,6 +150,10 @@ Extension Modules ----------------- +- socket.error binds its second argument to 'errno'. + +- bsddb.DBError binds all arguments as a tuple to 'message'. + Library ------- From python-checkins at python.org Thu Mar 1 07:16:45 2007 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 1 Mar 2007 07:16:45 +0100 (CET) Subject: [Python-checkins] r54053 - in python/trunk: Doc/lib/libcollections.tex Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20070301061645.26AE41E4009@bag.python.org> Author: raymond.hettinger Date: Thu Mar 1 07:16:43 2007 New Revision: 54053 Added: python/trunk/Lib/test/test_collections.py Modified: python/trunk/Doc/lib/libcollections.tex python/trunk/Lib/collections.py python/trunk/Misc/NEWS Log: Add collections.NamedTuple Modified: python/trunk/Doc/lib/libcollections.tex ============================================================================== --- python/trunk/Doc/lib/libcollections.tex (original) +++ python/trunk/Doc/lib/libcollections.tex Thu Mar 1 07:16:43 2007 @@ -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 + 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. @@ -339,3 +341,50 @@ >>> 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. 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 deparment paygrade') + for tup in csv.reader(open("employees.csv", "rb")): + print EmployeeRecord(*tup) + \end{verbatim} + +\end{funcdesc} Modified: python/trunk/Lib/collections.py ============================================================================== --- python/trunk/Lib/collections.py (original) +++ python/trunk/Lib/collections.py Thu Mar 1 07:16:43 2007 @@ -1,3 +1,62 @@ -__all__ = ['deque', 'defaultdict'] +__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, 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()) Added: python/trunk/Lib/test/test_collections.py ============================================================================== --- (empty file) +++ python/trunk/Lib/test/test_collections.py Thu Mar 1 07:16:43 2007 @@ -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) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Thu Mar 1 07:16:43 2007 @@ -138,6 +138,8 @@ - Added heapq.merge() for merging sorted input streams. +- Added collections.NamedTuple() for assigning field names to tuples. + - Added itertools.izip_longest(). - Have the encoding package's search function dynamically import using absolute From python-checkins at python.org Thu Mar 1 08:04:42 2007 From: python-checkins at python.org (neal.norwitz) Date: Thu, 1 Mar 2007 08:04:42 +0100 (CET) Subject: [Python-checkins] r54054 - python/trunk/Misc/developers.txt Message-ID: <20070301070442.735CB1E4002@bag.python.org> Author: neal.norwitz Date: Thu Mar 1 08:04:41 2007 New Revision: 54054 Modified: python/trunk/Misc/developers.txt Log: Add Pat and Eric for work on PEP 3101 in the sandbox Modified: python/trunk/Misc/developers.txt ============================================================================== --- python/trunk/Misc/developers.txt (original) +++ python/trunk/Misc/developers.txt Thu Mar 1 08:04:41 2007 @@ -17,6 +17,9 @@ Permissions History ------------------- +- 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. From python-checkins at python.org Thu Mar 1 09:00:05 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 09:00:05 +0100 (CET) Subject: [Python-checkins] r54055 - sandbox/trunk/pep3101 Message-ID: <20070301080005.B15161E4003@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 09:00:03 2007 New Revision: 54055 Added: sandbox/trunk/pep3101/ Log: Added directory for development of PEP3101 and related code From python-checkins at python.org Thu Mar 1 09:22:28 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 09:22:28 +0100 (CET) Subject: [Python-checkins] r54056 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/pep3101.h sandbox/trunk/pep3101/setup.py sandbox/trunk/pep3101/stringformat.c sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301082228.55D7B1E4002@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 09:22:25 2007 New Revision: 54056 Added: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/pep3101.h sandbox/trunk/pep3101/setup.py sandbox/trunk/pep3101/stringformat.c sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Initial version of pep3101 sandbox code committed Added: sandbox/trunk/pep3101/README.txt ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/README.txt Thu Mar 1 09:22:25 2007 @@ -0,0 +1,70 @@ + +PEP 3101 + +This directory is where sample code to support PEP 3101 and do +related things is being developed. + +Current developers: + + Patrick Maupin (pmaupin at gmail.com) + Eric V. Smith + Pete Shinner + +The code is only half-baked at present +(development was started at PyCon 2007 and is in progress). + +Although the PEP3101 goal is a (unicode) string format method, since +this is a sandbox, we might do a few more ambitious things as well, +to see if people like them. + +The current plan of record is to make a pep3101 extension module. +It will have at least the following features: + + - contains a format function which takes a string and parameters, + (but which is easily portable to a string method for Py3k) + - can be compiled against 2.4, 2.5, and Py3K + - Works with the string object as well as unicode + +The current code has a module which is progressing nicely, and some +unittests for the current version. + +Files: + + - unicodeformat.c is designed to be easily added to Python + as a method of the unicode object. + - stringformat.c is a wrapper around unicodeformat.c, which + "templatizes" the entire file to make it easy to add to Python + as a method of the string object. + - pep3101.h contains definitions for the functions in stringformat + and unicodeformat + - pep3101.c contains a module implementation which can be linked + with these method files for testing and/or use with earlier + Python versions. + - setup.py -- Use "build" option to make the extension module + - test_simpleformat.py -- initial unittests + +Todo: + + - finish up format specifier handling + - document differences between PEP and implementation + - Add docstrings to module + - print string offset information on certain errors + - Add _flags options + - Play with possible implementations for formatting + strings against dictionaries as well as the format + (dangerous) + - Play with possible implementations for exposing + lowest level format specifier handler for use in + compatible template systems. + - Play with possible options for specifying additional + escape syntaxes + +_flags options to consider adding: + + - useall=1 means all arguments should be used + - allow_leading_under means leading underbars allowed + - syntax=0,1,2,3 -- different syntaxes + - hook=object -- callback hook as described in PEP + - informational mode to dump exceptions into string + (as described in pep) + - max_recursion=xxx (default 4) Added: sandbox/trunk/pep3101/pep3101.c ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/pep3101.c Thu Mar 1 09:22:25 2007 @@ -0,0 +1,64 @@ +#include "Python.h" + +/* XXX -- remove this include if integrated into Python.h ??? */ +#include "pep3101.h" + + +/* ----------------------------------------------------- */ + +static char pep3101_format__doc__[] = +"" +; + +static PyObject * +pep3101_format(PyObject *self, PyObject *args, PyObject *keywords) +{ + PyObject *newargs, *newself, *result; + newself = PyTuple_GetItem(args, 0); /* borrowed reference */ + if (newself == NULL) + return NULL; + newargs = PyTuple_GetSlice(args, 1, PyTuple_Size(args) + 1); + if (newargs == NULL) + return NULL; + if (PyUnicode_Check(newself)) + result = PyUnicode_FormatMethod(newself, newargs, keywords); + else if (PyString_Check(newself)) + result = PyString_FormatMethod(newself, newargs, keywords); + else { + result = NULL; + PyErr_SetString(PyExc_TypeError, + "First parameter to format must be string or unicode object"); + } + Py_DECREF(newargs); + return result; +} + +/* List of methods defined in the module */ + +static struct PyMethodDef pep3101_methods[] = { + {"format", (PyCFunction)pep3101_format, METH_VARARGS | METH_KEYWORDS, pep3101_format__doc__}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called initpep3101) */ + +static char pep3101_module_documentation[] = +"" +; + +void +initpep3101(void) +{ + PyObject *m; + + /* Create the module and add the functions */ + m = Py_InitModule4("pep3101", pep3101_methods, + pep3101_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module pep3101"); +} Added: sandbox/trunk/pep3101/pep3101.h ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/pep3101.h Thu Mar 1 09:22:25 2007 @@ -0,0 +1,12 @@ +#ifndef Py_PEP3101_H +#define Py_PEP3101_H + +/* XXX -- need #ifdefs to remove Unicode if not using it, and remove String on Py3K */ + +PyObject * +PyString_FormatMethod(PyObject *self, PyObject *args, PyObject *keywords); + +PyObject * +PyUnicode_FormatMethod(PyObject *self, PyObject *args, PyObject *keywords); + +#endif Added: sandbox/trunk/pep3101/setup.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/setup.py Thu Mar 1 09:22:25 2007 @@ -0,0 +1,11 @@ +from distutils.core import setup, Extension + +module1 = Extension('pep3101', + sources = ['pep3101.c', 'unicodeformat.c', 'stringformat.c'], + depends = ['unicodeformat.c'], # Force rebuild of stringobject when this changes + ) + +setup (name = 'pep3101', + version = '1.0', + description = 'Extension module to implement features of PEP 3101', + ext_modules = [module1]) Added: sandbox/trunk/pep3101/stringformat.c ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/stringformat.c Thu Mar 1 09:22:25 2007 @@ -0,0 +1,4 @@ +#include "Python.h" +#define COMPILED_FROM_INSIDE_STRINGFORMAT +#define C_UNICODE 0 +#include "unicodeformat.c" Added: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/test_simpleformat.py Thu Mar 1 09:22:25 2007 @@ -0,0 +1,115 @@ + +import unittest +from test import test_support + +import pep3101 + + +# The test implementation does not allow an argument +# index or keyword name to be used more than once. The +# PEP doesn't make any specification on this, but it +# seems too useful to leave as is. + + +class FormatTest(unittest.TestCase): + # All tests run through these functions. They can be + # overridden to change the class of string being tested + # and the function being used. + def formatEquals(self, result, text, *args, **kwargs): + text = str(text) + result = str(result) + val = pep3101.format(text, *args, **kwargs) + self.assertEquals(val, result) + + def formatRaises(self, exc, text, *args, **kwargs): + exc = exc or Exception #StringFormat.FormatError + text = str(text) + #prevState = StringFormat.strict_format_errors + #StringFormat.strict_format_errors = True + try: + self.assertRaises(exc, + lambda: pep3101.format( + text, *args, **kwargs)) + finally: + pass + #StringFormat.strict_format_errors = prevState + + + def test_basic(self): + # directly from the pep intro + self.formatEquals( + "My name is Fred", + "My name is {0}", "Fred") + self.formatEquals( + "My name is Fred :-{}", + "My name is {0} :-{{}}", "Fred") + self.formatEquals("abc", "{0:}", "abc") # is this allowed? + + def test_missingargs(self): + #self.formatRaises(None, "Doesn't use all {0} args", 42, 24) + self.formatRaises(IndexError, "There is no {4} arg", 42, 24) + self.formatRaises(KeyError, "There question is {when}", who=True) + + def test_attributes(self): + class Container(object): + one, _two, four4 = 1, 2, 4 + def __getattr__(self, name): + if name == "five": return 5 + raise TypeError("Never do this") + self.formatEquals( + "Count with me; 1 2 4", + "Count with me; {0.one} {item._two} {1.four4}", + Container, Container, item=Container) + self.formatEquals( + "Five is 5", "Five is {c.five}", c=Container()) + self.formatRaises(AttributeError, + "Missing {0.rabbit} lookup", Container) + self.formatRaises(TypeError, + "Forbidden {0.secret} lookup", Container()) + + def test_items(self): + d = dict(val="value", sum=1) + t = tuple(("apple", "ball", "cat")) + self.formatEquals( + "The value of apple", + "The {0[val]} of {t[0]}", d, t=t) + # Decided against negative indices for now + #self.formatEquals( + # "The shiny red ball", + # "The shiny red {0[-2]}", t) + + def test_formatlookup(self): + self.formatEquals("32_0>4d", "{0:{1}}", 32, "0>4d") + self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d") + + def test_specifiers(self): + self.formatEquals("97_c", "{0:c}", ord("a")) + self.formatEquals("8_08b", "{0:08b}", 8) + self.formatEquals("8_ >3d", "{0: >3d}", 8) + self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) + + def test_custom_format(self): + class Custom(object): + def __format__(self, specifiers): + return specifiers + custom = Custom() + self.formatEquals("magic", "{0:magic}", custom) + self.formatEquals("custom", "{0:{1}}", custom, "custom") + + def test_syntaxerror(self): + self.assertRaises(Exception, "}{", True) + self.assertRaises(Exception, "{0", True) + self.assertRaises(Exception, "{0.[]}", True) + self.assertRaises(Exception, "{0[0}", True) + self.assertRaises(Exception, "{0[0:foo}", True) + self.assertRaises(Exception, "{c]}", True) + self.assertRaises(Exception, "{{1}}", True, 0) + self.assertRaises(Exception, "{{ {{{0}}", True) + self.assertRaises(Exception, "{0}}", True) + + +def test_main(): + test_support.run_unittest(FormatTest) + +if __name__ == "__main__": + test_main() Added: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 09:22:25 2007 @@ -0,0 +1,1091 @@ +#define DUMMY_FORMATTING 1 + +/* + unicodeformat.c -- implementation of PEP 3101 + + PEP 3101 and example Python implementation written by Talin + + This module designed and written by Patrick Maupin and Eric V Smith + + This module is designed to be compiled standalone, or from inside stringformat.c, + to support both unicode and traditional strings. +*/ + +/* + XXX -- todo: insert a fragment of the source string into error messages +*/ + +#ifndef COMPILED_FROM_INSIDE_STRINGFORMAT +#include "Python.h" +#define C_UNICODE 1 +#endif + +#if C_UNICODE +#define CH_TYPE Py_UNICODE +#define CH_TYPE_ISDECIMAL Py_UNICODE_ISDECIMAL +#define CH_TYPE_TODECIMAL Py_UNICODE_TODECIMAL +#define STROBJ_AS_PTR PyUnicode_AS_UNICODE +#define STROBJ_GET_SIZE PyUnicode_GET_SIZE +#define STROBJ_NEW PyUnicode_FromUnicode +#define STROBJ_RESIZE PyUnicode_Resize +#define STROBJ_CHECK PyUnicode_Check +#define STROBJ_FORMAT PyUnicode_FormatMethod +#define STROBJ_STR PyObject_Unicode +#else +#define CH_TYPE char +#define CH_TYPE_ISDECIMAL(x) ((x >= '0') && (x <= '9')) +#define CH_TYPE_TODECIMAL(x) (CH_TYPE_ISDECIMAL(x) ? (x - '0') : -1) +#define STROBJ_AS_PTR PyString_AS_STRING +#define STROBJ_GET_SIZE PyString_GET_SIZE +#define STROBJ_NEW PyString_FromStringAndSize +#define STROBJ_RESIZE _PyString_Resize +#define STROBJ_CHECK PyString_Check +#define STROBJ_FORMAT PyString_FormatMethod +#define STROBJ_STR PyObject_Str +#endif + +/* Try to support older versions of Python*/ +#if PYTHON_API_VERSION < 1013 +typedef int Py_ssize_t; +#define Py_LOCAL_INLINE(x) static x +#endif + +/* Defines for more efficiently reallocating the string buffer */ +#define INITIAL_SIZE_INCREMENT 100 +#define SIZE_MULTIPLIER 2 +#define MAX_SIZE_INCREMENT 3200 + +#ifdef __cplusplus +extern "C" { +#endif + +/* XXX -- remove this include if integrated into Python.h ??? */ +#include "pep3101.h" + +/* + A SubString is a string between two unicode pointers. +*/ +typedef struct { + CH_TYPE *ptr; + CH_TYPE *end; +} SubString; + +/* + A SubStringObj is like a SubString, but also has an associated + object (which may be null). +*/ +typedef struct { + CH_TYPE *ptr; + CH_TYPE *end; + PyObject * obj; +} SubStringObj; + +/* + If this were written in C++, FmtState would be the class, + and most of the functions inside this file would be members + of this class. +*/ +typedef struct { + /* args passed to PyString_FormatMethod or PyUnicode_FormatMethod */ + PyObject *args; + /* keywords passed to PyString_FormatMethod or PyUnicode_FormatMethod */ + PyObject *keywords; + /* current position and end of the 'self' string passed to FormatMethod */ + SubString fmtstr; + /* Output string we are constructing, including current and end pointers*/ + SubStringObj outstr; + /* Field Specifier, after the colon in {1:{2}} + This may or may not have a valid object (the field specifier might + just be a substring of the fmtstr. If it does not have its own + object, the .obj struct member will be NULL */ + SubStringObj fieldspec; + /* size_increment is used for optimizing string growth */ + int size_increment; + /* max_recursion is used to limit the ability of a malicious string + to damage the stack. Default value is 4 */ + int max_recursion; + /* By default, leading underscores are not allowed for security reasons */ + int allow_leading_under; + /* positional_arg_set contains one bit for every positional argument + that we still expect to be used. This implementation only checks + that the first 32 positional arguments are actually used. If they + want more than that, they probably really need the check, but on + the other hand they are probably beyond help, so the check would + be necessary but not sufficient :) */ + int positional_arg_set; + /* Keyword arguments can be checked as well */ + PyObject *keyword_arg_set; + /* For some interface functions, we could have a list or tuple of + dictionaries to search, e.g. locals()/globals(). */ + int keywords_is_tuple; +} FmtState; + +/* + Our internal conversion functions have this signature. + + returns the number of characters written, or -1 if error +*/ +/* XXX obviously wrong, but just a placeholder currently */ +typedef Py_ssize_t (*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +/* + Forward declarations for our conversion functions +*/ +static Py_ssize_t convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + + +/* Some forward declarations for recursion */ +static PyObject * +get_field_object(FmtState *fs); + +static PyObject * +recurse_format(FmtState *fs); + +/* + Most of our errors are value errors, because to Python, the + format string is a "value". Also, it's convenient to return + a NULL when we are erroring out. +*/ +static void * +SetError(const char *s) +{ + PyErr_SetString(PyExc_ValueError, s); + return NULL; +} + +/* + check_fmtstr returns True if we still have characters + left in the format string. +*/ +Py_LOCAL_INLINE(int) +check_fmtstr(FmtState *fs) +{ + return (fs->fmtstr.ptr < fs->fmtstr.end) || + SetError("Invalid format string"); +} + +/* + end_identifier returns true if a character marks + the end of an identifier string. + + Although the PEP specifies that identifiers are + numbers or valid Python identifiers, we just let + getattr/getitem handle that, so the implementation + is more flexible than the PEP would indicate. +*/ +Py_LOCAL_INLINE(int) +end_identifier(CH_TYPE c) +{ + switch (c) { + case '.': case '[': case ']': case '}': case ':': + return 1; + default: + return 0; + } +} + + +/* returns true if this character is a specifier alignment token */ +Py_LOCAL_INLINE(int) +alignment_token(CH_TYPE c) +{ + switch (c) { + case '<': case '>': case '=': case '^': + return 1; + default: + return 0; + } +} + +/* returns true if this character is a sign element */ +Py_LOCAL_INLINE(int) +sign_element(CH_TYPE c) +{ + switch (c) { + case ' ': case '+': case '-': case '(': + return 1; + default: + return 0; + } +} + + + +/* returns a pointer to our conversion function, or NULL if invalid */ +Py_LOCAL_INLINE(ConversionFunction) +conversion_function(CH_TYPE c) +{ + switch (c) { + case 'b': return convert_binary; /* base-2 */ + case 'c': return convert_char; /* as character */ + case 'd': return convert_decimal; /* decimal integer */ + case 'e': return convert_exponent; /* exponential notation */ + case 'E': return convert_exponentUC; /* exponential notation with uppercase 'E' */ + case 'f': return convert_fixed; /* fixed-point */ + case 'F': return convert_fixedUC; /* fixed-point with uppercase */ + case 'g': return convert_general; /* general number notation */ + case 'G': return convert_generalUC; /* general number notation with uppercase 'E' */ + case 'n': return convert_number; /* number in locale-specific format */ + case 'o': return convert_octal; /* octal */ + case 'r': return convert_repr; /* in repr() format */ + case 's': return convert_string; /* convert using str() */ + case 'x': return convert_hex; /* base 16 */ + case 'X': return convert_hexUC; /* base 16 uppercase */ + case '%': return convert_percentage; /* as percentage */ + default: + return NULL; + } +} + +/* Fill in a SubStringObj from a Python string */ +Py_LOCAL_INLINE(SubStringObj) +make_substrobj(PyObject *obj) +{ + SubStringObj s; + s.obj = obj; + s.ptr = STROBJ_AS_PTR(obj); + s.end = STROBJ_GET_SIZE(obj) + s.ptr; + return s; +} + +#if PYTHON_API_VERSION < 1013 +static void +PySet_Discard(PyObject *myset, PyObject *mykey) +{ + /* XXX --- Need to add the right code here */ +} +#endif + +/* XXX -- similar function elsewhere ???? */ +/* + output_data dumps characters into our output string + buffer. + + In some cases, it has to reallocate the string. + + It returns a status: 0 for a failed reallocation, + 1 for success. +*/ +static int +output_data(FmtState *fs, const CH_TYPE *s, Py_ssize_t count) +{ + Py_ssize_t room = fs->outstr.end - fs->outstr.ptr; + if (count > room) { + CH_TYPE *startptr; + Py_ssize_t curlen, maxlen; + startptr = STROBJ_AS_PTR(fs->outstr.obj); + curlen = fs->outstr.ptr - startptr; + maxlen = curlen + count + fs->size_increment; + if (STROBJ_RESIZE(&fs->outstr.obj, maxlen) < 0) + return 0; + startptr = STROBJ_AS_PTR(fs->outstr.obj); + fs->outstr.ptr = startptr + curlen; + fs->outstr.end = startptr + maxlen; + if (fs->size_increment < MAX_SIZE_INCREMENT) + fs->size_increment *= SIZE_MULTIPLIER; + } + memcpy(fs->outstr.ptr, s, count * sizeof(CH_TYPE)); + fs->outstr.ptr += count; + return 1; +} + +/* + get_python_identifier is a bit of a misnomer. It returns + a value for use with getattr or getindex. This value + will usually be a string value, but we allow {} inside the + text, so it could really be any arbitrary python object, + as retrieved from the method arguments. +*/ +static PyObject * +get_python_identifier(FmtState *fs, int isargument) +{ + CH_TYPE *startptr; + PyObject *result; + if (!check_fmtstr(fs)) + return NULL; + if (*fs->fmtstr.ptr == '{') { + /* This little bit of mutual recursion allows nested dictionary + lookups and computed attribute names + */ + if (--fs->max_recursion < 0) + return SetError("Max string recursion exceeded"); + result = get_field_object(fs); + fs->max_recursion++; + if (result && (*fs->fmtstr.ptr++ != '}')) + result = SetError("Expected closing }"); + return result; + } + if (end_identifier(*fs->fmtstr.ptr)) + return SetError("Expected attribute or index"); + if ((*fs->fmtstr.ptr == '_') && !fs->allow_leading_under) + return SetError("Index/attribute leading underscores disallowed"); + + for (startptr = fs->fmtstr.ptr; + !end_identifier(*fs->fmtstr.ptr); + fs->fmtstr.ptr++) { + if (!check_fmtstr(fs)) + return NULL; + } + result = STROBJ_NEW(startptr, fs->fmtstr.ptr - startptr); + if (result == NULL) + return NULL; + if (isargument && (fs->keyword_arg_set != NULL)) + PySet_Discard(fs->keyword_arg_set, result); + /* + We might want to add code here to check for invalid Python + identifiers. All identifiers are eventually passed to getattr + or getitem, so there is a check when used. However, we might + want to remove (or not) the ability to have strings like + "a/b" or " ab" or "-1" (which is not parsed as a number). + For now, this is left as an exercise for the first disgruntled + user... + + if (XXX -- need check function) { + Py_DECREF(result); + PyErr_SetString(PyExc_ValueError, "Invalid embedded Python identifier"); + return NULL; + } + */ + return result; +} + +/* + If keywords are supplied as a sequence of dictionaries + (e.g. locals/globals) then name_mapper will do multiple + lookups until it finds the right information. This + should not be called (keywords_is_tuple should not be + set) unless fs->keywords is a tuple. +*/ +static PyObject * +name_mapper(PyObject *keywords, PyObject *key) +{ + PyObject *result; + int index; + int lastindex = PyTuple_GET_SIZE(keywords)-1; + + for (index=0;; index++) { + result = PyObject_GetItem(PyTuple_GET_ITEM(keywords, index), key); + if (result != NULL) { + Py_INCREF(result); + return result; + } + if (index >= lastindex) + return NULL; + PyErr_Clear(); + } +} + +/* + get_integer_index consumes 0 or more decimal digit characters + from a format string, updates *result with the corresponding + positive integer, and returns the number of digits consumed. + + if the isargument parameter is true, it will remove the + integer from the arguments bitset. +*/ +static int +get_integer_index(FmtState *fs, Py_ssize_t *result) +{ + Py_ssize_t accumulator, digitval, oldaccumulator; + int numdigits; + accumulator = numdigits = 0; + for (;;fs->fmtstr.ptr++, numdigits++) { + if (fs->fmtstr.ptr >= fs->fmtstr.end) + break; + digitval = CH_TYPE_TODECIMAL(*fs->fmtstr.ptr); + if (digitval < 0) + break; + /* + This trick was copied from old Unicode format code. It's cute, + but would really suck on an old machine with a slow divide + implementation. Fortunately, in the normal case we do not + expect too many digits. + */ + oldaccumulator = accumulator; + accumulator *= 10; + if ((accumulator+10)/10 != oldaccumulator+1) + return (int)SetError("field width or index value too large"); + accumulator += digitval; + } + *result = accumulator; + return numdigits; +} + +/* + get_specifier retrieves the part of the format string + between the colon and trailing }. +*/ +static int +get_specifier(FmtState *fs) +{ + CH_TYPE c; + + int curlycount, gotcurly; + + curlycount = 1; + gotcurly = 0; + + fs->fieldspec.ptr = fs->fmtstr.ptr; + for (;;) { + if (!check_fmtstr(fs)) + return 0; + c = *fs->fmtstr.ptr++; + if (c == '{') { + gotcurly = 1; + curlycount++; + } + else if (c == '}') { + curlycount--; + if (curlycount <= 0) + break; + } + } + fs->fieldspec.end = fs->fmtstr.ptr - 1; + if (gotcurly) { + PyObject *myobject; + SubString savefmt = fs->fmtstr; + fs->fmtstr.ptr = fs->fieldspec.ptr; + fs->fmtstr.end = fs->fieldspec.end; + myobject = recurse_format(fs); + if (myobject == NULL) + return 0; + fs->fieldspec = make_substrobj(myobject); + fs->fmtstr = savefmt; + } + return 1; +} + +/* + get_field_object returns the object inside {} + It handles getindex and getattr lookups and consumes + the format string up to but not including the trailing + } or the optional : format specifier separator. +*/ +static PyObject * +get_field_object(FmtState *fs) +{ + PyObject *myobj, *subobj, *newobj; + CH_TYPE c; + Py_ssize_t index; + int isindex, expectclose, isnumeric, isargument; + + if (!check_fmtstr(fs)) + return NULL; + isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); + myobj = isnumeric ? fs->args : fs->keywords; + Py_INCREF(myobj); + + for (isindex=1, expectclose=0, isargument=1;;) { + if (!check_fmtstr(fs)) + break; + if (!isindex) { + if ((subobj = get_python_identifier(fs, isargument)) == NULL) + break; + newobj = (isargument && fs->keywords_is_tuple) + ? name_mapper(myobj, subobj) + : PyObject_GetAttr(myobj, subobj); + Py_DECREF(subobj); + } + else { + isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); + if (isnumeric) { + get_integer_index(fs, &index); + if (isargument) + fs->positional_arg_set &= ~(1 << index); + } + if (isnumeric && PySequence_Check(myobj)) + newobj = PySequence_GetItem(myobj, index); + else { + /* XXX -- do we need PyLong_FromLongLong? Using ssizet, not int... */ + subobj = isnumeric ? + PyInt_FromLong(index) : + get_python_identifier(fs, isargument); + if (subobj == NULL) + break; + newobj = PyObject_GetItem(myobj, subobj); + Py_DECREF(subobj); + } + } + Py_DECREF(myobj); + myobj = newobj; + if (expectclose) + if ((!check_fmtstr(fs)) || (*fs->fmtstr.ptr++ != ']')) { + SetError("Expected ]"); + break; + } + if (!check_fmtstr(fs)) + break; + c = *fs->fmtstr.ptr; + if ((c == '}') || (c == ':')) + return myobj; + fs->fmtstr.ptr++; + isargument = 0; + isindex = expectclose = (c == '['); + if (!isindex && (c != '.')) { + SetError("Expected ., [, :, or }"); + break; + } + } + Py_DECREF(myobj); + return NULL; +} +/* + get_field_and_spec calls subfunctions to retrieve the + field object and optional specification string. +*/ +static PyObject * +get_field_and_spec(FmtState *fs) +{ + PyObject *myobj; + CH_TYPE c; + + fs->fieldspec.ptr = fs->fieldspec.end = fs->fmtstr.ptr; + fs->fieldspec.obj = NULL; + + myobj = get_field_object(fs); + if (myobj != NULL) { + if (check_fmtstr(fs)) { + c = *fs->fmtstr.ptr++; + if ((c == '}') || ((c == ':') && (get_specifier(fs)))) + return myobj; + } + Py_DECREF(myobj); + } + return NULL; +} + +/* + user_format is invoked to format an object with a defined __format__ + attribute. +*/ +static int +user_format(FmtState *fs, PyObject *__format__) +{ + PyObject *myobj; + int ok; + + myobj = fs->fieldspec.obj; + if (myobj == NULL) { + myobj = STROBJ_NEW(fs->fieldspec.ptr, + fs->fieldspec.end - fs->fieldspec.ptr); + if (myobj == NULL) + return 0; + fs->fieldspec.obj = myobj; /* Owned by our caller now */ + } + /* XXX -- possible optimization to CallFunctionWithArgs */ + myobj = PyObject_CallFunction(__format__, "(O)", myobj); + if (myobj == NULL) + return 0; + ok = STROBJ_CHECK(myobj); + if (!ok) + SetError("__format__ method did not return correct string type"); + else + ok = output_data(fs, STROBJ_AS_PTR(myobj), + STROBJ_GET_SIZE(myobj)); + Py_DECREF(myobj); + return ok; +} + +typedef struct { + CH_TYPE fill_char; + CH_TYPE align; + CH_TYPE sign; + Py_ssize_t width; + Py_ssize_t precision; + CH_TYPE type; +} DefaultFormat; + +/* + parse the default specification +*/ + +static int +parse_default_format(FmtState *fs, DefaultFormat *format) +{ + Py_ssize_t index = 0; + Py_ssize_t specified_width; + Py_ssize_t remaining; + SubString *spec = &fs->fmtstr; + + format->fill_char = '\0'; + format->align = '\0'; + format->sign = '\0'; + format->width = -1; + format->precision = -1; + format->type = '\0'; + + /* cache the length, since that's convenient */ + Py_ssize_t spec_len = spec->end - spec->ptr; + + /* If the second char is an alignment token, + then parse the fill char */ + if (spec_len >= 2 && alignment_token(spec->ptr[1])) { + format->align = spec->ptr[1]; + format->fill_char = spec->ptr[0]; + index = 2; + } else if (spec_len >= 1 && alignment_token(spec->ptr[0])) { + format->align = spec->ptr[0]; + index = 1; + } + + /* Parse the various sign options */ + if (index < spec_len && sign_element(spec->ptr[index])) { + format->sign = spec->ptr[index]; + index++; + if (index < spec_len && spec->ptr[index] == ')') { + index++; + } + } + + /* The special case for 0-padding (backwards compat) */ + if (format->fill_char == '\0' && index < spec_len && spec->ptr[index] == '0') { + format->fill_char = '0'; + if (format->align == '\0') { + format->align = '='; + } + index++; + } + + specified_width = get_integer_index(fs, &format->width); + + /* recalculate the length, since the pointers may have just changed */ + spec_len = spec->end - spec->ptr; + + /* if specified_width is 0, we didn't consume any characters for the width. + in that case, reset the width to -1, because get_integer_index() will + have set it to zero */ + if (specified_width <= 0) { + format->width = -1; + } + + /* Parse field precision */ + if (index < spec_len && spec->ptr[index] == '.') { + index++; + + specified_width = get_integer_index(fs, &format->precision); + + /* recalculate the length, since the pointers may have just changed */ + spec_len = spec->end - spec->ptr; + + /* again, check if any characters specified */ + if (specified_width <= 0) { + format->precision = -1; + } + } + + /* Finally, parse the type field */ + + remaining = spec_len - index; + if (remaining > 1) { + /* invalid conversion spec */ + SetError("Invalid conversion specification"); + return 0; + } + + if (remaining == 1) { + format->type = spec->ptr[index]; + } + + return 1; +} + + +/* conversion functions */ +static Py_ssize_t +convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + int ok; + PyObject *s; + PyObject *r; + Py_ssize_t len; + + r = PyObject_Repr(fieldobj); + if (r == NULL) + return 0; + + s = STROBJ_STR(r); + Py_DECREF(r); + + len = STROBJ_GET_SIZE(s); + ok = output_data(fs, STROBJ_AS_PTR(s), len); + Py_DECREF(s); + + return ok ? len : -1; +} + +static Py_ssize_t +convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + PyObject *myobj; + Py_ssize_t ok; + Py_ssize_t len; + + myobj = STROBJ_STR(fieldobj); + if (myobj == NULL) + return -1; + + len = STROBJ_GET_SIZE(myobj); + + ok = output_data(fs, STROBJ_AS_PTR(myobj), len); + Py_DECREF(myobj); + + return ok ? len : -1; +} + +static Py_ssize_t +convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +static Py_ssize_t +convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +{ + return -1; +} + +/* + default_format -- "Then a miracle occurs" +*/ +static int default_format(FmtState *fs, PyObject *fieldobj) +{ +#if DUMMY_FORMATTING == 1 + PyObject *myobj; + int ok; + + /* Test implementation, only at top level */ + CH_TYPE under = '_'; + + myobj = STROBJ_STR(fieldobj); + if (myobj == NULL) + return 0; + ok = output_data(fs, STROBJ_AS_PTR(myobj), + STROBJ_GET_SIZE(myobj)); + Py_DECREF(myobj); + if (!ok) + return 0; + if (fs->fieldspec.ptr != fs->fieldspec.end) { + if (!output_data(fs, &under, 1)) + return 0; + if (!output_data(fs, fs->fieldspec.ptr, + fs->fieldspec.end - fs->fieldspec.ptr)) + return 0; + } +#else + + Py_ssize_t len; + Py_ssize_t padding; + DefaultFormat format; + ConversionFunction conversion; + CH_TYPE sign = '\0'; + CH_TYPE prefix; + CH_TYPE suffix; + + if (!parse_default_format(fs, &format)) { + return 0; + } + + /* if no type character was specified, look up the default type character, based on the type of our object */ + if (format.type == '\0') { + if (PyInt_Check(fieldobj) || PyLong_Check(fieldobj)) { + format.type = 'd'; + } else if (PyFloat_Check(fieldobj)) { + format.type = 'g'; + } else { + format.type = 's'; + } + } + + /* handle conversion functions that logically map to other conversion functions? */ + + conversion = conversion_function(format.type); + if (conversion == NULL) { + SetError("Invalid conversion character"); + return 0; + } + + /* convert to a string first */ + /* get the length written so that we can fixup inside the buffer, as needed */ + len = conversion(fieldobj, fs, &sign); + if (len < 0) + return 0; + + /* we wrote "len" bytes. see what fixups need to be done */ + + /* Handle the sign logic */ + prefix = '\0'; + suffix = '\0'; + if (sign == '-') { + if (format.sign == '(') { + prefix = '('; + suffix = ')'; + } else { + prefix = '-'; + } + } else if (sign == '+') { + if (format.sign == '+') { + prefix = '+'; + } else if (format.sign == ' ') { + prefix = ' '; + } + } + + /* Handle the padding logic */ + if (format.width != -1) { + padding = format.width - len - (prefix == '\0' ? 0 : 1) - (suffix == '\0' ? 0 : 1); + if (padding > 0) { +#if 0 + if align == '>' or align == '^': + return fill_char * padding + prefix + result + suffix + elif align == '=' + return prefix + fill_char * padding + result + suffix + else: + return prefix + result + suffix + fill_char * padding + +#endif + } + } + +#endif + return 1; +} + +/* + renderfield determines if the field object has a defined __format__ + method, and dispatches to the appropriate subfunction. +*/ +static int +renderfield(FmtState *fs, PyObject *fieldobj) +{ + int result; + SubString savefmt; + + PyObject *__format__ = PyObject_GetAttrString(fieldobj, "__format__"); + if (__format__ != NULL) { + result = user_format(fs, __format__); + Py_DECREF(__format__); + } + else { + /* XXX -- saw other things just do this, but Guido mentioned + that maybe we should check whether the error was + an AttributeError or not. Figure out if this is + necessary -- if so, and not AttributeError, propagate + the error up the stack. + */ + PyErr_Clear(); /* For GetAttr __format__ */ + + savefmt = fs->fmtstr; + fs->fmtstr.ptr = fs->fieldspec.ptr; + fs->fmtstr.end = fs->fieldspec.end; + result = default_format(fs, fieldobj); + fs->fmtstr = savefmt; + } + return result; +} + +/* + do_format is the main program loop. It rummages through + the format string, looking for escapes to markup, and + calls other functions to move non-markup text to the output, + and to perform the markup to the output. +*/ +static PyObject * +do_format(FmtState *fs) +{ + PyObject *myobj; + CH_TYPE c, *start; + Py_ssize_t count, total; + SubString fmtstr; + int doubled, ok; + + fmtstr = fs->fmtstr; + count = fmtstr.end - fmtstr.ptr; + myobj = STROBJ_NEW(NULL, count + INITIAL_SIZE_INCREMENT); + if (myobj == NULL) + return NULL; + fs->outstr = make_substrobj(myobj); + fs->size_increment = INITIAL_SIZE_INCREMENT; + + ok = 1; + c = '\0'; /* Avoid compiler warning */ + while (fmtstr.ptr < fmtstr.end) { + start = fmtstr.ptr; + count = total = fmtstr.end - start; + while (count && ((c = *fmtstr.ptr) != '{') && (c != '}')) { + fmtstr.ptr++; + count--; + } + count = total - count; + total -= count; + doubled = (total > 1) && (fmtstr.ptr[1] == c); + if (doubled) { + output_data(fs, start, count+1); + fmtstr.ptr += 2; + continue; + } else if (count) + output_data(fs, start, count); + if (total < 2) { + ok = !total || + (int)SetError("Invalid format string -- { or } at end"); + break; + } + if (c == '}') { + SetError("Invalid format string -- single } encountered"); + ok = 0; + break; + } + fs->fmtstr.ptr = fmtstr.ptr + 1; + myobj = get_field_and_spec(fs); + ok = (myobj != NULL) && renderfield(fs, myobj); + Py_XDECREF(fs->fieldspec.obj); + Py_XDECREF(myobj); + if (!ok) + break; + fmtstr.ptr = fs->fmtstr.ptr; + } + myobj = fs->outstr.obj; + if (ok) { + count = fs->outstr.ptr - STROBJ_AS_PTR(myobj); + if (STROBJ_RESIZE(&myobj, count) >= 0) + return myobj; + } + Py_XDECREF(myobj); + return NULL; +} + +/* + recurse_format is called for nested format specifiers, + e.g. {1:{2}}. It saves off the current information, + and recursively calls do_format. +*/ +static PyObject * +recurse_format(FmtState *fs) +{ + PyObject *result; + SubStringObj saveoutstr = fs->outstr; + int saveincrement = fs->size_increment; + if (--(fs->max_recursion) < 0) + return SetError("Max string recursion exceeded"); + result = do_format(fs); + fs->max_recursion++; + fs->outstr = saveoutstr; + fs->size_increment = saveincrement; + return result; +} + +/* + STROBJ_FORMAT (actually PyUnicode_FormatMethod or PyString_FormatMethod) + is the public interface to the module. + + XXX -- do we need to check input types here, or are we guaranteed + they are right???? +*/ +PyObject * +STROBJ_FORMAT(PyObject *self, PyObject *args, PyObject *keywords) +{ + FmtState fs; + + fs.max_recursion = 4; + fs.allow_leading_under = 1; + fs.positional_arg_set = 0; + fs.keyword_arg_set = NULL; + fs.keywords_is_tuple = 0; + + fs.fmtstr.ptr = STROBJ_AS_PTR(self); + fs.fmtstr.end = fs.fmtstr.ptr + STROBJ_GET_SIZE(self); + fs.args = args; + fs.keywords = keywords; + + return do_format(&fs); +} + +#ifdef __cplusplus +} +#endif From python-checkins at python.org Thu Mar 1 09:43:11 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 09:43:11 +0100 (CET) Subject: [Python-checkins] r54057 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301084311.E49911E4002@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 09:43:11 2007 New Revision: 54057 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Reduced size of all lines below 80 characters Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 09:43:11 2007 @@ -7,8 +7,8 @@ This module designed and written by Patrick Maupin and Eric V Smith - This module is designed to be compiled standalone, or from inside stringformat.c, - to support both unicode and traditional strings. + This module is designed to be compiled standalone, or from inside + stringformat.c, to support both unicode and traditional strings. */ /* @@ -126,27 +126,59 @@ returns the number of characters written, or -1 if error */ /* XXX obviously wrong, but just a placeholder currently */ -typedef Py_ssize_t (*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +typedef Py_ssize_t +(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); /* Forward declarations for our conversion functions */ -static Py_ssize_t convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); -static Py_ssize_t convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +static Py_ssize_t +convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + +static Py_ssize_t +convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); /* Some forward declarations for recursion */ @@ -235,12 +267,15 @@ case 'c': return convert_char; /* as character */ case 'd': return convert_decimal; /* decimal integer */ case 'e': return convert_exponent; /* exponential notation */ - case 'E': return convert_exponentUC; /* exponential notation with uppercase 'E' */ + case 'E': return convert_exponentUC; /* exponential notation + with uppercase 'E' */ case 'f': return convert_fixed; /* fixed-point */ case 'F': return convert_fixedUC; /* fixed-point with uppercase */ case 'g': return convert_general; /* general number notation */ - case 'G': return convert_generalUC; /* general number notation with uppercase 'E' */ - case 'n': return convert_number; /* number in locale-specific format */ + case 'G': return convert_generalUC; /* general number notation + with uppercase 'E' */ + case 'n': return convert_number; /* number in locale-specific + format */ case 'o': return convert_octal; /* octal */ case 'r': return convert_repr; /* in repr() format */ case 's': return convert_string; /* convert using str() */ @@ -357,7 +392,8 @@ if (XXX -- need check function) { Py_DECREF(result); - PyErr_SetString(PyExc_ValueError, "Invalid embedded Python identifier"); + PyErr_SetString(PyExc_ValueError, + "Invalid embedded Python identifier"); return NULL; } */ @@ -511,7 +547,8 @@ if (isnumeric && PySequence_Check(myobj)) newobj = PySequence_GetItem(myobj, index); else { - /* XXX -- do we need PyLong_FromLongLong? Using ssizet, not int... */ + /* XXX -- do we need PyLong_FromLongLong? + Using ssizet, not int... */ subobj = isnumeric ? PyInt_FromLong(index) : get_python_identifier(fs, isargument); @@ -653,7 +690,8 @@ } /* The special case for 0-padding (backwards compat) */ - if (format->fill_char == '\0' && index < spec_len && spec->ptr[index] == '0') { + if (format->fill_char == '\0' && + index < spec_len && spec->ptr[index] == '0') { format->fill_char = '0'; if (format->align == '\0') { format->align = '='; @@ -666,9 +704,9 @@ /* recalculate the length, since the pointers may have just changed */ spec_len = spec->end - spec->ptr; - /* if specified_width is 0, we didn't consume any characters for the width. - in that case, reset the width to -1, because get_integer_index() will - have set it to zero */ + /* if specified_width is 0, we didn't consume any characters for + the width. in that case, reset the width to -1, because + get_integer_index() will have set it to zero */ if (specified_width <= 0) { format->width = -1; } @@ -872,7 +910,8 @@ return 0; } - /* if no type character was specified, look up the default type character, based on the type of our object */ + /* if no type character was specified, look up the default + type character, based on the type of our object */ if (format.type == '\0') { if (PyInt_Check(fieldobj) || PyLong_Check(fieldobj)) { format.type = 'd'; @@ -883,7 +922,8 @@ } } - /* handle conversion functions that logically map to other conversion functions? */ + /* handle conversion functions that logically map to + other conversion functions? */ conversion = conversion_function(format.type); if (conversion == NULL) { @@ -892,7 +932,8 @@ } /* convert to a string first */ - /* get the length written so that we can fixup inside the buffer, as needed */ + /* get the length written so that we can fixup + inside the buffer, as needed */ len = conversion(fieldobj, fs, &sign); if (len < 0) return 0; @@ -919,7 +960,8 @@ /* Handle the padding logic */ if (format.width != -1) { - padding = format.width - len - (prefix == '\0' ? 0 : 1) - (suffix == '\0' ? 0 : 1); + padding = format.width - len - (prefix == '\0' ? 0 : 1) - + (suffix == '\0' ? 0 : 1); if (padding > 0) { #if 0 if align == '>' or align == '^': From python-checkins at python.org Thu Mar 1 14:24:10 2007 From: python-checkins at python.org (eric.smith) Date: Thu, 1 Mar 2007 14:24:10 +0100 (CET) Subject: [Python-checkins] r54058 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301132410.E3F2A1E4002@bag.python.org> Author: eric.smith Date: Thu Mar 1 14:24:08 2007 New Revision: 54058 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Moved conversion functions so that forward declarations are no longer needed. It's one less thing to maintain. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 14:24:08 2007 @@ -120,67 +120,6 @@ int keywords_is_tuple; } FmtState; -/* - Our internal conversion functions have this signature. - - returns the number of characters written, or -1 if error -*/ -/* XXX obviously wrong, but just a placeholder currently */ -typedef Py_ssize_t -(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -/* - Forward declarations for our conversion functions -*/ -static Py_ssize_t -convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - -static Py_ssize_t -convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); - - /* Some forward declarations for recursion */ static PyObject * get_field_object(FmtState *fs); @@ -258,35 +197,6 @@ -/* returns a pointer to our conversion function, or NULL if invalid */ -Py_LOCAL_INLINE(ConversionFunction) -conversion_function(CH_TYPE c) -{ - switch (c) { - case 'b': return convert_binary; /* base-2 */ - case 'c': return convert_char; /* as character */ - case 'd': return convert_decimal; /* decimal integer */ - case 'e': return convert_exponent; /* exponential notation */ - case 'E': return convert_exponentUC; /* exponential notation - with uppercase 'E' */ - case 'f': return convert_fixed; /* fixed-point */ - case 'F': return convert_fixedUC; /* fixed-point with uppercase */ - case 'g': return convert_general; /* general number notation */ - case 'G': return convert_generalUC; /* general number notation - with uppercase 'E' */ - case 'n': return convert_number; /* number in locale-specific - format */ - case 'o': return convert_octal; /* octal */ - case 'r': return convert_repr; /* in repr() format */ - case 's': return convert_string; /* convert using str() */ - case 'x': return convert_hex; /* base 16 */ - case 'X': return convert_hexUC; /* base 16 uppercase */ - case '%': return convert_percentage; /* as percentage */ - default: - return NULL; - } -} - /* Fill in a SubStringObj from a Python string */ Py_LOCAL_INLINE(SubStringObj) make_substrobj(PyObject *obj) @@ -743,6 +653,15 @@ } +/* + Our internal conversion functions have this signature. + + returns the number of characters written, or -1 if error +*/ +/* XXX obviously wrong, but just a placeholder currently */ +typedef Py_ssize_t +(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); + /* conversion functions */ static Py_ssize_t convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) @@ -869,6 +788,35 @@ return -1; } +/* returns a pointer to our conversion function, or NULL if invalid */ +Py_LOCAL_INLINE(ConversionFunction) +conversion_function(CH_TYPE c) +{ + switch (c) { + case 'b': return convert_binary; /* base-2 */ + case 'c': return convert_char; /* as character */ + case 'd': return convert_decimal; /* decimal integer */ + case 'e': return convert_exponent; /* exponential notation */ + case 'E': return convert_exponentUC; /* exponential notation + with uppercase 'E' */ + case 'f': return convert_fixed; /* fixed-point */ + case 'F': return convert_fixedUC; /* fixed-point with uppercase */ + case 'g': return convert_general; /* general number notation */ + case 'G': return convert_generalUC; /* general number notation + with uppercase 'E' */ + case 'n': return convert_number; /* number in locale-specific + format */ + case 'o': return convert_octal; /* octal */ + case 'r': return convert_repr; /* in repr() format */ + case 's': return convert_string; /* convert using str() */ + case 'x': return convert_hex; /* base 16 */ + case 'X': return convert_hexUC; /* base 16 uppercase */ + case '%': return convert_percentage; /* as percentage */ + default: + return NULL; + } +} + /* default_format -- "Then a miracle occurs" */ From python-checkins at python.org Thu Mar 1 14:33:35 2007 From: python-checkins at python.org (eric.smith) Date: Thu, 1 Mar 2007 14:33:35 +0100 (CET) Subject: [Python-checkins] r54059 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301133335.5A7DA1E4008@bag.python.org> Author: eric.smith Date: Thu Mar 1 14:33:32 2007 New Revision: 54059 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Removed alignment character '^'. It was in Talin's sample python implementation, but not in the PEP. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 14:33:32 2007 @@ -176,7 +176,7 @@ alignment_token(CH_TYPE c) { switch (c) { - case '<': case '>': case '=': case '^': + case '<': case '>': case '=': return 1; default: return 0; @@ -912,7 +912,7 @@ (suffix == '\0' ? 0 : 1); if (padding > 0) { #if 0 - if align == '>' or align == '^': + if align == '>': return fill_char * padding + prefix + result + suffix elif align == '=' return prefix + fill_char * padding + result + suffix From python-checkins at python.org Thu Mar 1 15:07:21 2007 From: python-checkins at python.org (andrew.kuchling) Date: Thu, 1 Mar 2007 15:07:21 +0100 (CET) Subject: [Python-checkins] r54060 - python/branches/release25-maint/Doc/lib/libcollections.tex Message-ID: <20070301140721.58BB11E4002@bag.python.org> Author: andrew.kuchling Date: Thu Mar 1 15:07:19 2007 New Revision: 54060 Modified: python/branches/release25-maint/Doc/lib/libcollections.tex Log: Fix typo (noticed in Raymond's r54053 commit adding NamedTuple Modified: python/branches/release25-maint/Doc/lib/libcollections.tex ============================================================================== --- python/branches/release25-maint/Doc/lib/libcollections.tex (original) +++ python/branches/release25-maint/Doc/lib/libcollections.tex Thu Mar 1 15:07:19 2007 @@ -16,7 +16,7 @@ \subsection{\class{deque} objects \label{deque-objects}} \begin{funcdesc}{deque}{\optional{iterable}} - Returns a new deque objected initialized left-to-right (using + 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. From python-checkins at python.org Thu Mar 1 15:36:13 2007 From: python-checkins at python.org (andrew.kuchling) Date: Thu, 1 Mar 2007 15:36:13 +0100 (CET) Subject: [Python-checkins] r54061 - python/trunk/Doc/whatsnew/whatsnew26.tex Message-ID: <20070301143613.839E41E4002@bag.python.org> Author: andrew.kuchling Date: Thu Mar 1 15:36:12 2007 New Revision: 54061 Modified: python/trunk/Doc/whatsnew/whatsnew26.tex Log: Add NamedTuple Modified: python/trunk/Doc/whatsnew/whatsnew26.tex ============================================================================== --- python/trunk/Doc/whatsnew/whatsnew26.tex (original) +++ python/trunk/Doc/whatsnew/whatsnew26.tex Thu Mar 1 15:36:12 2007 @@ -76,6 +76,22 @@ \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 function in the \module{heapq} module: \function{merge(iter1, iter2, ...)} takes any number of iterables that return data From python-checkins at python.org Thu Mar 1 17:31:59 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 17:31:59 +0100 (CET) Subject: [Python-checkins] r54062 - python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Message-ID: <20070301163159.6A2771E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 17:31:57 2007 New Revision: 54062 Modified: python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Log: Add BaseException's name to its PyArg_ParseTuple() call. Modified: python/branches/p3yk_no_args_on_exc/Objects/exceptions.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Objects/exceptions.c (original) +++ python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Thu Mar 1 17:31:57 2007 @@ -42,7 +42,7 @@ if (!_PyArg_NoKeywords(self->ob_type->tp_name, kwds)) return -1; - if (!PyArg_ParseTuple(args, "|O", &message)) + if (!PyArg_ParseTuple(args, "|O:BaseException", &message)) return -1; if (message) { From python-checkins at python.org Thu Mar 1 18:53:29 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 18:53:29 +0100 (CET) Subject: [Python-checkins] r54063 - sandbox/trunk/pep3101/loadpep.py sandbox/trunk/pep3101/makefile sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301175329.E6B201E400B@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 18:53:24 2007 New Revision: 54063 Added: sandbox/trunk/pep3101/loadpep.py sandbox/trunk/pep3101/makefile Modified: sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/unicodeformat.c Log: Generalized the dispatch mechanism, added test/build infrastructure Added: sandbox/trunk/pep3101/loadpep.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/loadpep.py Thu Mar 1 18:53:24 2007 @@ -0,0 +1,16 @@ + +import sys + +platdict = dict( + linux2='build/lib.linux-i686-%s.%s', +) + +platstr = platdict.get(sys.platform) + +if platstr is None: + raise ValueError("Unknown platform") + +sys.path.append(platstr % sys.version_info[:2]) + +import test_simpleformat +test_simpleformat.test_main() Added: sandbox/trunk/pep3101/makefile ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/makefile Thu Mar 1 18:53:24 2007 @@ -0,0 +1,29 @@ + +SRCS = pep3101.c stringformat.c unicodeformat.c pep3101.h setup.py + +PY30 = ../bin/python +PY24 = python2.4 + +test: test24 test30 + +all: 24 30 + +24: build/lib.linux-i686-2.4/pep3101.so + +30: build/lib.linux-i686-3.0/pep3101.so + +build/lib.linux-i686-3.0/pep3101.so: $(SRCS) + $(PY30) setup.py build + +build/lib.linux-i686-2.4/pep3101.so: $(SRCS) + $(PY24) setup.py build + +test24: 24 + $(PY24) loadpep.py + +test30: 30 + $(PY24) loadpep.py + +clean: + rm -Rf build *.pyc + Modified: sandbox/trunk/pep3101/pep3101.c ============================================================================== --- sandbox/trunk/pep3101/pep3101.c (original) +++ sandbox/trunk/pep3101/pep3101.c Thu Mar 1 18:53:24 2007 @@ -11,26 +11,35 @@ ; static PyObject * -pep3101_format(PyObject *self, PyObject *args, PyObject *keywords) +StringDispatch(ternaryfunc *table, PyObject *self, PyObject *args, PyObject *keywords) { - PyObject *newargs, *newself, *result; - newself = PyTuple_GetItem(args, 0); /* borrowed reference */ - if (newself == NULL) - return NULL; - newargs = PyTuple_GetSlice(args, 1, PyTuple_Size(args) + 1); - if (newargs == NULL) - return NULL; - if (PyUnicode_Check(newself)) - result = PyUnicode_FormatMethod(newself, newargs, keywords); - else if (PyString_Check(newself)) - result = PyString_FormatMethod(newself, newargs, keywords); + PyObject *myobj = self; + + if (self == NULL) { + if (!PyTuple_GET_SIZE(args)) { + PyErr_SetString(PyExc_TypeError, + "Function expects at least one argument"); + return NULL; + } + myobj = PyTuple_GET_ITEM(args, 0); + } + if (PyUnicode_Check(myobj)) + return table[0](self, args, keywords); + else if (PyString_Check(self)) + return table[1](self, args, keywords); else { - result = NULL; PyErr_SetString(PyExc_TypeError, - "First parameter to format must be string or unicode object"); + "First parameter must be string or unicode object"); + return NULL; } - Py_DECREF(newargs); - return result; +} + +static PyObject * +pep3101_format(PyObject *self, PyObject *args, PyObject *keywords) +{ + static ternaryfunc table[2] = + {PyUnicode_FormatMethod, PyString_FormatMethod}; + return StringDispatch(table, self, args, keywords); } /* List of methods defined in the module */ Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 18:53:24 2007 @@ -81,15 +81,30 @@ } SubStringObj; /* + A MarkupEscapeHandler allows us to handle different escape + mechanisms between regular text and markup. This is useful + both for providing alternate markup syntax, and for + providing an eval() type function where there is no regular + text. +*/ +typedef struct FmtState *FSPTR; +typedef int +(*MarkupEscapeHandler)(FSPTR fs); + +/* If this were written in C++, FmtState would be the class, and most of the functions inside this file would be members of this class. */ -typedef struct { +typedef struct FmtState { /* args passed to PyString_FormatMethod or PyUnicode_FormatMethod */ PyObject *args; /* keywords passed to PyString_FormatMethod or PyUnicode_FormatMethod */ PyObject *keywords; + /* arg_param_offset is 1 if not called as a method */ + int arg_param_offset; + /* Function to call to perform markup */ + MarkupEscapeHandler do_markup; /* current position and end of the 'self' string passed to FormatMethod */ SubString fmtstr; /* Output string we are constructing, including current and end pointers*/ @@ -212,7 +227,7 @@ static void PySet_Discard(PyObject *myset, PyObject *mykey) { - /* XXX --- Need to add the right code here */ + Py_XDECREF(PyObject_CallMethod(myset, "discard", "(O)", mykey)); } #endif @@ -430,6 +445,7 @@ Py_ssize_t index; int isindex, expectclose, isnumeric, isargument; + index = 0; /* Just to shut up the compiler warning */ if (!check_fmtstr(fs)) return NULL; isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); @@ -454,6 +470,7 @@ if (isargument) fs->positional_arg_set &= ~(1 << index); } + if (isnumeric && PySequence_Check(myobj)) newobj = PySequence_GetItem(myobj, index); else { @@ -961,13 +978,13 @@ } /* - do_format is the main program loop. It rummages through + do_markup is the main program loop. It rummages through the format string, looking for escapes to markup, and calls other functions to move non-markup text to the output, and to perform the markup to the output. */ -static PyObject * -do_format(FmtState *fs) +static int +do_markup(FmtState *fs) { PyObject *myobj; CH_TYPE c, *start; @@ -976,13 +993,6 @@ int doubled, ok; fmtstr = fs->fmtstr; - count = fmtstr.end - fmtstr.ptr; - myobj = STROBJ_NEW(NULL, count + INITIAL_SIZE_INCREMENT); - if (myobj == NULL) - return NULL; - fs->outstr = make_substrobj(myobj); - fs->size_increment = INITIAL_SIZE_INCREMENT; - ok = 1; c = '\0'; /* Avoid compiler warning */ while (fmtstr.ptr < fmtstr.end) { @@ -1020,9 +1030,31 @@ break; fmtstr.ptr = fs->fmtstr.ptr; } - myobj = fs->outstr.obj; + return ok; +} + +/* + do_format allocates the output string and then + calls a markup handler to do the dirty work. +*/ +static PyObject * +do_format(FmtState *fs) +{ + PyObject *myobj; + int ok; + + myobj = STROBJ_NEW(NULL, + fs->fmtstr.end - fs->fmtstr.ptr + INITIAL_SIZE_INCREMENT); + if (myobj == NULL) + return NULL; + fs->outstr = make_substrobj(myobj); + fs->size_increment = INITIAL_SIZE_INCREMENT; + + ok = fs->do_markup(fs); + + myobj = fs->outstr.obj; /* Might have been reallocated */ if (ok) { - count = fs->outstr.ptr - STROBJ_AS_PTR(myobj); + int count = fs->outstr.ptr - STROBJ_AS_PTR(myobj); if (STROBJ_RESIZE(&myobj, count) >= 0) return myobj; } @@ -1050,6 +1082,7 @@ return result; } + /* STROBJ_FORMAT (actually PyUnicode_FormatMethod or PyString_FormatMethod) is the public interface to the module. @@ -1062,12 +1095,25 @@ { FmtState fs; + /* This function can be called as a python function or as a method */ + if (self == NULL) { + if (!PyTuple_GET_SIZE(args)) { + PyErr_SetString(PyExc_TypeError, + "function expects at least one argument"); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + fs.arg_param_offset = 1; + } + else + fs.arg_param_offset = 0; + fs.max_recursion = 4; fs.allow_leading_under = 1; fs.positional_arg_set = 0; fs.keyword_arg_set = NULL; fs.keywords_is_tuple = 0; - + fs.do_markup = do_markup; fs.fmtstr.ptr = STROBJ_AS_PTR(self); fs.fmtstr.end = fs.fmtstr.ptr + STROBJ_GET_SIZE(self); fs.args = args; From python-checkins at python.org Thu Mar 1 19:10:15 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 19:10:15 +0100 (CET) Subject: [Python-checkins] r54064 - sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301181015.7525E1E4002@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 19:10:12 2007 New Revision: 54064 Modified: sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/unicodeformat.c Log: Fixed bugs introduced when my import was picking up old .so file Modified: sandbox/trunk/pep3101/pep3101.c ============================================================================== --- sandbox/trunk/pep3101/pep3101.c (original) +++ sandbox/trunk/pep3101/pep3101.c Thu Mar 1 19:10:12 2007 @@ -25,7 +25,7 @@ } if (PyUnicode_Check(myobj)) return table[0](self, args, keywords); - else if (PyString_Check(self)) + else if (PyString_Check(myobj)) return table[1](self, args, keywords); else { PyErr_SetString(PyExc_TypeError, Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 19:10:12 2007 @@ -467,8 +467,10 @@ isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); if (isnumeric) { get_integer_index(fs, &index); - if (isargument) + if (isargument) { fs->positional_arg_set &= ~(1 << index); + index += fs->arg_param_offset; + } } if (isnumeric && PySequence_Check(myobj)) From python-checkins at python.org Thu Mar 1 19:54:07 2007 From: python-checkins at python.org (brett.cannon) Date: Thu, 1 Mar 2007 19:54:07 +0100 (CET) Subject: [Python-checkins] r54065 - python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Message-ID: <20070301185407.CA1901E4002@bag.python.org> Author: brett.cannon Date: Thu Mar 1 19:54:06 2007 New Revision: 54065 Modified: python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Log: Swith to PyArg_UnpackTuple() for BaseException_init. Modified: python/branches/p3yk_no_args_on_exc/Objects/exceptions.c ============================================================================== --- python/branches/p3yk_no_args_on_exc/Objects/exceptions.c (original) +++ python/branches/p3yk_no_args_on_exc/Objects/exceptions.c Thu Mar 1 19:54:06 2007 @@ -42,7 +42,7 @@ if (!_PyArg_NoKeywords(self->ob_type->tp_name, kwds)) return -1; - if (!PyArg_ParseTuple(args, "|O:BaseException", &message)) + if (!PyArg_UnpackTuple(args, "BaseException", 0, 1, &message)) return -1; if (message) { From python-checkins at python.org Thu Mar 1 21:54:13 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 1 Mar 2007 21:54:13 +0100 (CET) Subject: [Python-checkins] r54067 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070301205413.94D2F1E4002@bag.python.org> Author: patrick.maupin Date: Thu Mar 1 21:54:10 2007 New Revision: 54067 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Added method option parsing code to format func Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 1 21:54:10 2007 @@ -102,6 +102,7 @@ /* keywords passed to PyString_FormatMethod or PyUnicode_FormatMethod */ PyObject *keywords; /* arg_param_offset is 1 if not called as a method */ + int num_args; int arg_param_offset; /* Function to call to perform markup */ MarkupEscapeHandler do_markup; @@ -224,11 +225,9 @@ } #if PYTHON_API_VERSION < 1013 -static void -PySet_Discard(PyObject *myset, PyObject *mykey) -{ - Py_XDECREF(PyObject_CallMethod(myset, "discard", "(O)", mykey)); -} +#define PySet_Discard PyDict_DelItem +#define PySet_New PyDict_Copy +#define PySet_GET_SIZE PyDict_Size #endif /* XXX -- similar function elsewhere ???? */ @@ -1084,6 +1083,94 @@ return result; } +static int +get_options(PyObject *keywords, FmtState *fs) +{ + static char* keys[3] = { + "useall", "allow_leading_under", NULL + }; + int values[2]; + int index; + + PyObject *flags, *myobj; + + fs->max_recursion = 4; + fs->allow_leading_under = 1; /* XXX- SHould set to 0, but breaks */ + fs->positional_arg_set = 0; + fs->keyword_arg_set = NULL; + fs->keywords_is_tuple = 0; + fs->do_markup = do_markup; + fs->keywords = keywords; + + flags = PyDict_GetItemString(keywords, "_dict"); + if (flags == NULL) + return 1; + + for (index=0; keys[index] != NULL; index++) { + myobj = PyDict_GetItemString(flags, keys[index]); + if (myobj == NULL) + values[index] = 0; + else if (PyInt_Check(myobj)) + values[index] = PyInt_AS_LONG(myobj); + else { + PyErr_SetString(PyExc_TypeError, + "All flags values must be integers"); + return 0; + } + } + if (values[0]) { + fs->positional_arg_set = (1 << fs->num_args) - 1; + fs->keyword_arg_set = PySet_New(keywords); + if (!fs->keyword_arg_set) + return 0; + } + fs->allow_leading_under = values[1]; + return 1; +} + +static int +get_self_args(PyObject *self, PyObject *args, FmtState *fs) +{ + fs->num_args = PyTuple_GET_SIZE(args); + if (self == NULL) { + if (!fs->num_args) { + PyErr_SetString(PyExc_TypeError, + "Function expects at least one argument"); + return 0; + } + self = PyTuple_GET_ITEM(args, 0); + if (!STROBJ_CHECK(self)) { + PyErr_SetString(PyExc_TypeError, + "Not valid format string"); + return 0; + } + fs->arg_param_offset = 1; + fs->num_args -= 1; + } + else + fs->arg_param_offset = 0; + fs->args = args; + fs->fmtstr.ptr = STROBJ_AS_PTR(self); + fs->fmtstr.end = fs->fmtstr.ptr + STROBJ_GET_SIZE(self); + return 1; +} + +static PyObject * +fs_cleanup(PyObject *result, FmtState *fs) +{ + PyObject *used; + int ok = result != NULL; + used = fs->keyword_arg_set; + if (ok && used) { + ok = (PySet_GET_SIZE(used) <= 1) && !fs->positional_arg_set; + if (!ok) { + Py_DECREF(result); + result = SetError("Not all arguments consumed"); + } + } + Py_XDECREF(used); + return result; +} /* STROBJ_FORMAT (actually PyUnicode_FormatMethod or PyString_FormatMethod) @@ -1098,30 +1185,11 @@ FmtState fs; /* This function can be called as a python function or as a method */ - if (self == NULL) { - if (!PyTuple_GET_SIZE(args)) { - PyErr_SetString(PyExc_TypeError, - "function expects at least one argument"); - return NULL; - } - self = PyTuple_GET_ITEM(args, 0); - fs.arg_param_offset = 1; - } - else - fs.arg_param_offset = 0; - - fs.max_recursion = 4; - fs.allow_leading_under = 1; - fs.positional_arg_set = 0; - fs.keyword_arg_set = NULL; - fs.keywords_is_tuple = 0; - fs.do_markup = do_markup; - fs.fmtstr.ptr = STROBJ_AS_PTR(self); - fs.fmtstr.end = fs.fmtstr.ptr + STROBJ_GET_SIZE(self); - fs.args = args; - fs.keywords = keywords; + if (!get_self_args(self, args, &fs) || + !get_options(keywords, &fs)) + return NULL; - return do_format(&fs); + return fs_cleanup(do_format(&fs), &fs); } #ifdef __cplusplus From python-checkins at python.org Thu Mar 1 23:39:01 2007 From: python-checkins at python.org (jack.diederich) Date: Thu, 1 Mar 2007 23:39:01 +0100 (CET) Subject: [Python-checkins] r54070 - peps/trunk/pep-0306.txt Message-ID: <20070301223901.556281E4003@bag.python.org> Author: jack.diederich Date: Thu Mar 1 23:39:00 2007 New Revision: 54070 Modified: peps/trunk/pep-0306.txt Log: * update the Checklist to reflect the current ast/compiler Modified: peps/trunk/pep-0306.txt ============================================================================== --- peps/trunk/pep-0306.txt (original) +++ peps/trunk/pep-0306.txt Thu Mar 1 23:39:00 2007 @@ -2,7 +2,7 @@ Title: How to Change Python's Grammar Version: $Revision$ Last-Modified: $Date$ -Author: Michael Hudson +Author: Michael Hudson , Jack Diederich , Nick Coghlan Status: Active Type: Informational Content-Type: text/plain @@ -33,13 +33,26 @@ Checklist - __ Grammar/Grammar: OK, you'd probably worked this one out :) + __ Grammar/Grammar: OK, you'd probably worked this one out :) - __ Python/compile.c: you'll most likely need to edit both the - actual compiler and the symtable builder (which is to say both - the com_foo and the sym_foo functions). + __ Parser/Python.asdl may need changes to match the Grammar. + Use Parser/asdl_c.py to regenerate Include/Python-ast.h - __ You may need to regenerate Lib/symbol.py and/or Lib/token.py. + __ Python/Python-ast.c may need changes to create the AST + objects involved with the Grammar change. Lib/compiler/ast.py + will need matching changes to the pure-python AST objects. + + __ Parser/pgen needs to be rerun to regenerate Include/graminit.h + and Python/graminit.c + + __ Python/symbtable.c: This handles the symbol collection pass + that happens immediately before the compilation pass + + __ Python/compile.c: You will need to create or modify the + compiler_* functions for your productions. + + __ You may need to regenerate Lib/symbol.py and/or Lib/token.py + and/or Lib/keyword.py __ The parser module. Add some of your new syntax to test_parser, bang on parsermodule.c until it passes. @@ -47,22 +60,17 @@ __ The compiler package. A good test is to compile the standard library and test suite with the compiler package and then check it runs. You did add some of your new syntax to the test - suite, didn't you? There's a script in Tools/compiler that - does this. + suite, didn't you? __ If you've gone so far as to change the token structure of - Python, then the tokenizer library module will need to be - changed. + Python, then the Lib/tokenizer.py library module will need to + be changed. __ Certain changes may require tweaks to the library module pyclbr. - __ Jython too will need work, but I don't know what. Explanations - gratefully received. - __ Documentation must be written! - References [1] SF Bug #676521, parser module validation failure From python-checkins at python.org Fri Mar 2 02:22:28 2007 From: python-checkins at python.org (phillip.eby) Date: Fri, 2 Mar 2007 02:22:28 +0100 (CET) Subject: [Python-checkins] r54071 - sandbox/trunk/setuptools/setuptools/command/test.py Message-ID: <20070302012228.997511E4004@bag.python.org> Author: phillip.eby Date: Fri Mar 2 02:22:27 2007 New Revision: 54071 Modified: sandbox/trunk/setuptools/setuptools/command/test.py Log: Fix problem activating dependencies for tests Modified: sandbox/trunk/setuptools/setuptools/command/test.py ============================================================================== --- sandbox/trunk/setuptools/setuptools/command/test.py (original) +++ sandbox/trunk/setuptools/setuptools/command/test.py Fri Mar 2 02:22:27 2007 @@ -96,6 +96,7 @@ try: sys.path.insert(0, normalize_path(ei_cmd.egg_base)) working_set.__init__() + add_activation_listener(lambda dist: dist.activate()) require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) func() finally: @@ -120,7 +121,6 @@ - def run_tests(self): import unittest loader_ep = EntryPoint.parse("x="+self.test_loader) From python-checkins at python.org Fri Mar 2 02:23:32 2007 From: python-checkins at python.org (phillip.eby) Date: Fri, 2 Mar 2007 02:23:32 +0100 (CET) Subject: [Python-checkins] r54072 - sandbox/branches/setuptools-0.6/setuptools/command/test.py Message-ID: <20070302012332.B26B51E400A@bag.python.org> Author: phillip.eby Date: Fri Mar 2 02:23:32 2007 New Revision: 54072 Modified: sandbox/branches/setuptools-0.6/setuptools/command/test.py Log: Fix problem activating dependencies for tests (backport from trunk) Modified: sandbox/branches/setuptools-0.6/setuptools/command/test.py ============================================================================== --- sandbox/branches/setuptools-0.6/setuptools/command/test.py (original) +++ sandbox/branches/setuptools-0.6/setuptools/command/test.py Fri Mar 2 02:23:32 2007 @@ -96,6 +96,7 @@ try: sys.path.insert(0, normalize_path(ei_cmd.egg_base)) working_set.__init__() + add_activation_listener(lambda dist: dist.activate()) require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) func() finally: @@ -120,7 +121,6 @@ - def run_tests(self): import unittest loader_ep = EntryPoint.parse("x="+self.test_loader) From python-checkins at python.org Fri Mar 2 02:38:44 2007 From: python-checkins at python.org (patrick.maupin) Date: Fri, 2 Mar 2007 02:38:44 +0100 (CET) Subject: [Python-checkins] r54073 - sandbox/trunk/pep3101/loadpep.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302013844.1D99F1E4004@bag.python.org> Author: patrick.maupin Date: Fri Mar 2 02:38:38 2007 New Revision: 54073 Modified: sandbox/trunk/pep3101/loadpep.py sandbox/trunk/pep3101/unicodeformat.c Log: Cope with null keyword arguments (need testcase for this) Modified: sandbox/trunk/pep3101/loadpep.py ============================================================================== --- sandbox/trunk/pep3101/loadpep.py (original) +++ sandbox/trunk/pep3101/loadpep.py Fri Mar 2 02:38:38 2007 @@ -12,5 +12,8 @@ sys.path.append(platstr % sys.version_info[:2]) -import test_simpleformat -test_simpleformat.test_main() +if __name__ == '__main__': + import test_simpleformat + test_simpleformat.test_main() + from pep3101 import format as f + f('Hi there') Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 02:38:38 2007 @@ -449,6 +449,8 @@ return NULL; isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); myobj = isnumeric ? fs->args : fs->keywords; + if (myobj == NULL) + return SetError("No keyword arguments passed"); Py_INCREF(myobj); for (isindex=1, expectclose=0, isargument=1;;) { @@ -1102,6 +1104,9 @@ fs->do_markup = do_markup; fs->keywords = keywords; + if (keywords == NULL) + return 1; + flags = PyDict_GetItemString(keywords, "_dict"); if (flags == NULL) return 1; From python-checkins at python.org Fri Mar 2 03:03:19 2007 From: python-checkins at python.org (patrick.maupin) Date: Fri, 2 Mar 2007 03:03:19 +0100 (CET) Subject: [Python-checkins] r54074 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302020319.0F84F1E4004@bag.python.org> Author: patrick.maupin Date: Fri Mar 2 03:03:11 2007 New Revision: 54074 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Disallow leading underscores by default, fix _flags and tests Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Fri Mar 2 03:03:11 2007 @@ -57,10 +57,17 @@ if name == "five": return 5 raise TypeError("Never do this") self.formatEquals( - "Count with me; 1 2 4", + "Count with me; 1 4", + "Count with me; {0.one} {1.four4}", + Container, Container, item=Container) + self.formatRaises(ValueError, "Count with me; {0.one} {item._two} {1.four4}", Container, Container, item=Container) self.formatEquals( + "Count with me; 1 2 4", + "Count with me; {0.one} {item._two} {1.four4}", + Container, Container, item=Container, _flags=dict(allow_leading_under=1)) + self.formatEquals( "Five is 5", "Five is {c.five}", c=Container()) self.formatRaises(AttributeError, "Missing {0.rabbit} lookup", Container) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 03:03:11 2007 @@ -1097,7 +1097,7 @@ PyObject *flags, *myobj; fs->max_recursion = 4; - fs->allow_leading_under = 1; /* XXX- SHould set to 0, but breaks */ + fs->allow_leading_under = 0; fs->positional_arg_set = 0; fs->keyword_arg_set = NULL; fs->keywords_is_tuple = 0; @@ -1107,7 +1107,7 @@ if (keywords == NULL) return 1; - flags = PyDict_GetItemString(keywords, "_dict"); + flags = PyDict_GetItemString(keywords, "_flags"); if (flags == NULL) return 1; From python-checkins at python.org Fri Mar 2 04:19:11 2007 From: python-checkins at python.org (eric.smith) Date: Fri, 2 Mar 2007 04:19:11 +0100 (CET) Subject: [Python-checkins] r54075 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302031911.16D581E4004@bag.python.org> Author: eric.smith Date: Fri Mar 2 04:19:03 2007 New Revision: 54075 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Added output_allocate() so I can allocate memory separately from copying into it. Modified signature of built-in conversion functions. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 04:19:03 2007 @@ -230,18 +230,19 @@ #define PySet_GET_SIZE PyDict_Size #endif -/* XXX -- similar function elsewhere ???? */ /* - output_data dumps characters into our output string - buffer. + output_allocate reserves space in our output string buffer In some cases, it has to reallocate the string. It returns a status: 0 for a failed reallocation, 1 for success. + + On success, it sets *ptr to point to the allocated memory. */ + static int -output_data(FmtState *fs, const CH_TYPE *s, Py_ssize_t count) +output_allocate(FmtState *fs, Py_ssize_t count, CH_TYPE **ptr) { Py_ssize_t room = fs->outstr.end - fs->outstr.ptr; if (count > room) { @@ -258,11 +259,31 @@ if (fs->size_increment < MAX_SIZE_INCREMENT) fs->size_increment *= SIZE_MULTIPLIER; } - memcpy(fs->outstr.ptr, s, count * sizeof(CH_TYPE)); + *ptr = fs->outstr.ptr; fs->outstr.ptr += count; return 1; } +/* XXX -- similar function elsewhere ???? */ +/* + output_data dumps characters into our output string + buffer. + + In some cases, it has to reallocate the string. + + It returns a status: 0 for a failed reallocation, + 1 for success. +*/ +static int +output_data(FmtState *fs, const CH_TYPE *s, Py_ssize_t count) +{ + CH_TYPE *dst; + if (output_allocate(fs, count, &dst) == 0) + return 0; + memcpy(dst, s, count * sizeof(CH_TYPE)); + return 1; +} + /* get_python_identifier is a bit of a misnomer. It returns a value for use with getattr or getindex. This value @@ -477,7 +498,7 @@ if (isnumeric && PySequence_Check(myobj)) newobj = PySequence_GetItem(myobj, index); else { - /* XXX -- do we need PyLong_FromLongLong? + /* XXX -- do we need PyLong_FromLongLong? Using ssizet, not int... */ subobj = isnumeric ? PyInt_FromLong(index) : @@ -680,77 +701,77 @@ */ /* XXX obviously wrong, but just a placeholder currently */ typedef Py_ssize_t -(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign); +(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format); /* conversion functions */ static Py_ssize_t -convert_binary(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_binary(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_char(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_char(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_decimal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_decimal(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_exponent(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_exponent(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_exponentUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_exponentUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_fixed(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_fixed(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_fixedUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_fixedUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_general(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_general(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_generalUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_generalUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_number(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_number(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_octal(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_octal(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_repr(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_repr(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { int ok; PyObject *s; @@ -772,11 +793,12 @@ } static Py_ssize_t -convert_string(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_string(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { PyObject *myobj; Py_ssize_t ok; Py_ssize_t len; + CH_TYPE *dst; myobj = STROBJ_STR(fieldobj); if (myobj == NULL) @@ -784,26 +806,30 @@ len = STROBJ_GET_SIZE(myobj); - ok = output_data(fs, STROBJ_AS_PTR(myobj), len); + /* reserve all the space we'll need */ + ok = output_allocate(fs, len, &dst); + if (ok) { + memcpy(dst, STROBJ_AS_PTR(myobj), len * sizeof(CH_TYPE)); + } Py_DECREF(myobj); return ok ? len : -1; } static Py_ssize_t -convert_hex(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_hex(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_hexUC(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_hexUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } static Py_ssize_t -convert_percentage(PyObject *fieldobj, FmtState *fs, CH_TYPE *sign) +convert_percentage(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) { return -1; } @@ -902,7 +928,7 @@ /* convert to a string first */ /* get the length written so that we can fixup inside the buffer, as needed */ - len = conversion(fieldobj, fs, &sign); + len = conversion(fieldobj, fs, &format); if (len < 0) return 0; From python-checkins at python.org Fri Mar 2 06:13:40 2007 From: python-checkins at python.org (patrick.maupin) Date: Fri, 2 Mar 2007 06:13:40 +0100 (CET) Subject: [Python-checkins] r54076 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302051340.9FB1E1E4004@bag.python.org> Author: patrick.maupin Date: Fri Mar 2 06:13:36 2007 New Revision: 54076 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Reordered code to group into logical sections. Added format string location to exception messages. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 06:13:36 2007 @@ -11,9 +11,11 @@ stringformat.c, to support both unicode and traditional strings. */ -/* - XXX -- todo: insert a fragment of the source string into error messages -*/ +/************************************************************************/ +/*********** Macros to encapsulate build differences ****************/ +/************************************************************************/ + +/* We can build for several Python versions, and for Unicode or strings */ #ifndef COMPILED_FROM_INSIDE_STRINGFORMAT #include "Python.h" @@ -55,6 +57,16 @@ #define SIZE_MULTIPLIER 2 #define MAX_SIZE_INCREMENT 3200 +#if PYTHON_API_VERSION < 1013 +#define PySet_Discard PyDict_DelItem +#define PySet_New PyDict_Copy +#define PySet_GET_SIZE PyDict_Size +#endif + +/************************************************************************/ +/*********** Global data structures and forward declarations *********/ +/************************************************************************/ + #ifdef __cplusplus extern "C" { #endif @@ -108,6 +120,8 @@ MarkupEscapeHandler do_markup; /* current position and end of the 'self' string passed to FormatMethod */ SubString fmtstr; + /* Used for error reporting */ + CH_TYPE *fmtstart; /* Output string we are constructing, including current and end pointers*/ SubStringObj outstr; /* Field Specifier, after the colon in {1:{2}} @@ -143,15 +157,27 @@ static PyObject * recurse_format(FmtState *fs); +/************************************************************************/ +/*********** Error handling and exception generation **************/ +/************************************************************************/ + /* Most of our errors are value errors, because to Python, the format string is a "value". Also, it's convenient to return a NULL when we are erroring out. */ static void * -SetError(const char *s) +SetError(FmtState *fs, const char *s) { - PyErr_SetString(PyExc_ValueError, s); + if (fs->fmtstr.ptr == fs->fmtstr.end) + PyErr_Format(PyExc_ValueError, "%s at end of format_string", s); + else if ((fs->fmtstr.ptr >= fs->fmtstart) && + (fs->fmtstr.ptr < fs->fmtstr.end)) + PyErr_Format(PyExc_ValueError, "%s at format_string[%d]", + s, fs->fmtstr.ptr - fs->fmtstart); + else + PyErr_Format(PyExc_ValueError, + "%s (apparently in computed format specifier)", s); return NULL; } @@ -163,55 +189,12 @@ check_fmtstr(FmtState *fs) { return (fs->fmtstr.ptr < fs->fmtstr.end) || - SetError("Invalid format string"); -} - -/* - end_identifier returns true if a character marks - the end of an identifier string. - - Although the PEP specifies that identifiers are - numbers or valid Python identifiers, we just let - getattr/getitem handle that, so the implementation - is more flexible than the PEP would indicate. -*/ -Py_LOCAL_INLINE(int) -end_identifier(CH_TYPE c) -{ - switch (c) { - case '.': case '[': case ']': case '}': case ':': - return 1; - default: - return 0; - } + SetError(fs, "Unexpected end of format_string"); } - -/* returns true if this character is a specifier alignment token */ -Py_LOCAL_INLINE(int) -alignment_token(CH_TYPE c) -{ - switch (c) { - case '<': case '>': case '=': - return 1; - default: - return 0; - } -} - -/* returns true if this character is a sign element */ -Py_LOCAL_INLINE(int) -sign_element(CH_TYPE c) -{ - switch (c) { - case ' ': case '+': case '-': case '(': - return 1; - default: - return 0; - } -} - - +/************************************************************************/ +/*********** Output string management functions ****************/ +/************************************************************************/ /* Fill in a SubStringObj from a Python string */ Py_LOCAL_INLINE(SubStringObj) @@ -224,12 +207,6 @@ return s; } -#if PYTHON_API_VERSION < 1013 -#define PySet_Discard PyDict_DelItem -#define PySet_New PyDict_Copy -#define PySet_GET_SIZE PyDict_Size -#endif - /* output_allocate reserves space in our output string buffer @@ -264,7 +241,6 @@ return 1; } -/* XXX -- similar function elsewhere ???? */ /* output_data dumps characters into our output string buffer. @@ -284,6 +260,66 @@ return 1; } +/************************************************************************/ +/*********** Format string parsing -- integers and identifiers *********/ +/************************************************************************/ + +/* + end_identifier returns true if a character marks + the end of an identifier string. + + Although the PEP specifies that identifiers are + numbers or valid Python identifiers, we just let + getattr/getitem handle that, so the implementation + is more flexible than the PEP would indicate. +*/ +Py_LOCAL_INLINE(int) +end_identifier(CH_TYPE c) +{ + switch (c) { + case '.': case '[': case ']': case '}': case ':': + return 1; + default: + return 0; + } +} + +/* + get_integer_index consumes 0 or more decimal digit characters + from a format string, updates *result with the corresponding + positive integer, and returns the number of digits consumed. + + if the isargument parameter is true, it will remove the + integer from the arguments bitset. +*/ +static int +get_integer_index(FmtState *fs, Py_ssize_t *result) +{ + Py_ssize_t accumulator, digitval, oldaccumulator; + int numdigits; + accumulator = numdigits = 0; + for (;;fs->fmtstr.ptr++, numdigits++) { + if (fs->fmtstr.ptr >= fs->fmtstr.end) + break; + digitval = CH_TYPE_TODECIMAL(*fs->fmtstr.ptr); + if (digitval < 0) + break; + /* + This trick was copied from old Unicode format code. It's cute, + but would really suck on an old machine with a slow divide + implementation. Fortunately, in the normal case we do not + expect too many digits. + */ + oldaccumulator = accumulator; + accumulator *= 10; + if ((accumulator+10)/10 != oldaccumulator+1) + return (int)SetError(fs, "Too many digits"); + accumulator += digitval; + } + *result = accumulator; + return numdigits; +} + /* get_python_identifier is a bit of a misnomer. It returns a value for use with getattr or getindex. This value @@ -303,17 +339,18 @@ lookups and computed attribute names */ if (--fs->max_recursion < 0) - return SetError("Max string recursion exceeded"); + return SetError(fs, "Maximum string recursion limit exceeded"); result = get_field_object(fs); fs->max_recursion++; if (result && (*fs->fmtstr.ptr++ != '}')) - result = SetError("Expected closing }"); + result = SetError(fs, "Expected closing }"); return result; } if (end_identifier(*fs->fmtstr.ptr)) - return SetError("Expected attribute or index"); + return SetError(fs, "Expected attribute or index"); if ((*fs->fmtstr.ptr == '_') && !fs->allow_leading_under) - return SetError("Index/attribute leading underscores disallowed"); + return SetError(fs, + "Leading underscores not allowed in attribute/index strings"); for (startptr = fs->fmtstr.ptr; !end_identifier(*fs->fmtstr.ptr); @@ -345,6 +382,15 @@ return result; } +/************************************************************************/ +/******** Functions to get field objects and specification strings ******/ +/************************************************************************/ + +/* get_field_and_spec is the main function in this section. It parses + the format string well enough to return a field object to render along + with a field specification string. +*/ + /* If keywords are supplied as a sequence of dictionaries (e.g. locals/globals) then name_mapper will do multiple @@ -372,42 +418,6 @@ } /* - get_integer_index consumes 0 or more decimal digit characters - from a format string, updates *result with the corresponding - positive integer, and returns the number of digits consumed. - - if the isargument parameter is true, it will remove the - integer from the arguments bitset. -*/ -static int -get_integer_index(FmtState *fs, Py_ssize_t *result) -{ - Py_ssize_t accumulator, digitval, oldaccumulator; - int numdigits; - accumulator = numdigits = 0; - for (;;fs->fmtstr.ptr++, numdigits++) { - if (fs->fmtstr.ptr >= fs->fmtstr.end) - break; - digitval = CH_TYPE_TODECIMAL(*fs->fmtstr.ptr); - if (digitval < 0) - break; - /* - This trick was copied from old Unicode format code. It's cute, - but would really suck on an old machine with a slow divide - implementation. Fortunately, in the normal case we do not - expect too many digits. - */ - oldaccumulator = accumulator; - accumulator *= 10; - if ((accumulator+10)/10 != oldaccumulator+1) - return (int)SetError("field width or index value too large"); - accumulator += digitval; - } - *result = accumulator; - return numdigits; -} - -/* get_specifier retrieves the part of the format string between the colon and trailing }. */ @@ -471,7 +481,7 @@ isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); myobj = isnumeric ? fs->args : fs->keywords; if (myobj == NULL) - return SetError("No keyword arguments passed"); + return SetError(fs, "Keyword not specified"); Py_INCREF(myobj); for (isindex=1, expectclose=0, isargument=1;;) { @@ -513,7 +523,7 @@ myobj = newobj; if (expectclose) if ((!check_fmtstr(fs)) || (*fs->fmtstr.ptr++ != ']')) { - SetError("Expected ]"); + SetError(fs, "Expected ]"); break; } if (!check_fmtstr(fs)) @@ -525,13 +535,14 @@ isargument = 0; isindex = expectclose = (c == '['); if (!isindex && (c != '.')) { - SetError("Expected ., [, :, or }"); + SetError(fs, "Expected ., [, :, or }"); break; } } Py_DECREF(myobj); return NULL; } + /* get_field_and_spec calls subfunctions to retrieve the field object and optional specification string. @@ -557,37 +568,21 @@ return NULL; } +/************************************************************************/ +/***************** Field rendering functions **************************/ +/************************************************************************/ + /* - user_format is invoked to format an object with a defined __format__ - attribute. + render_field is the main function in this section. It takes the field + object and field specification string generated by get_field_and_spec, + and renders the field into the output string. + + The two main subfunctions of render_field are caller_render (which + calls the object-supplied __format__ hook), and internal_render, which + renders objects which don't have format hoohs. */ -static int -user_format(FmtState *fs, PyObject *__format__) -{ - PyObject *myobj; - int ok; - myobj = fs->fieldspec.obj; - if (myobj == NULL) { - myobj = STROBJ_NEW(fs->fieldspec.ptr, - fs->fieldspec.end - fs->fieldspec.ptr); - if (myobj == NULL) - return 0; - fs->fieldspec.obj = myobj; /* Owned by our caller now */ - } - /* XXX -- possible optimization to CallFunctionWithArgs */ - myobj = PyObject_CallFunction(__format__, "(O)", myobj); - if (myobj == NULL) - return 0; - ok = STROBJ_CHECK(myobj); - if (!ok) - SetError("__format__ method did not return correct string type"); - else - ok = output_data(fs, STROBJ_AS_PTR(myobj), - STROBJ_GET_SIZE(myobj)); - Py_DECREF(myobj); - return ok; -} +#if !DUMMY_FORMATTING typedef struct { CH_TYPE fill_char; @@ -598,12 +593,36 @@ CH_TYPE type; } DefaultFormat; +/* returns true if this character is a specifier alignment token */ +Py_LOCAL_INLINE(int) +alignment_token(CH_TYPE c) +{ + switch (c) { + case '<': case '>': case '=': + return 1; + default: + return 0; + } +} + +/* returns true if this character is a sign element */ +Py_LOCAL_INLINE(int) +sign_element(CH_TYPE c) +{ + switch (c) { + case ' ': case '+': case '-': case '(': + return 1; + default: + return 0; + } +} + /* parse the default specification */ static int -parse_default_format(FmtState *fs, DefaultFormat *format) +parse_internal_render(FmtState *fs, DefaultFormat *format) { Py_ssize_t index = 0; Py_ssize_t specified_width; @@ -682,7 +701,7 @@ remaining = spec_len - index; if (remaining > 1) { /* invalid conversion spec */ - SetError("Invalid conversion specification"); + SetError(fs, "Invalid conversion specification"); return 0; } @@ -862,11 +881,12 @@ return NULL; } } +#endif /* - default_format -- "Then a miracle occurs" + internal_render -- "Then a miracle occurs" */ -static int default_format(FmtState *fs, PyObject *fieldobj) +static int internal_render(FmtState *fs, PyObject *fieldobj) { #if DUMMY_FORMATTING == 1 PyObject *myobj; @@ -900,7 +920,7 @@ CH_TYPE prefix; CH_TYPE suffix; - if (!parse_default_format(fs, &format)) { + if (!parse_internal_render(fs, &format)) { return 0; } @@ -921,7 +941,7 @@ conversion = conversion_function(format.type); if (conversion == NULL) { - SetError("Invalid conversion character"); + SetError(fs, "Invalid conversion character"); return 0; } @@ -974,18 +994,50 @@ } /* - renderfield determines if the field object has a defined __format__ + caller_render is invoked to format an object with a defined __format__ + attribute. +*/ +static int +caller_render(FmtState *fs, PyObject *__format__) +{ + PyObject *myobj; + int ok; + + myobj = fs->fieldspec.obj; + if (myobj == NULL) { + myobj = STROBJ_NEW(fs->fieldspec.ptr, + fs->fieldspec.end - fs->fieldspec.ptr); + if (myobj == NULL) + return 0; + fs->fieldspec.obj = myobj; /* Owned by our caller now */ + } + /* XXX -- possible optimization to CallFunctionWithArgs */ + myobj = PyObject_CallFunction(__format__, "(O)", myobj); + if (myobj == NULL) + return 0; + ok = STROBJ_CHECK(myobj); + if (!ok) + SetError(fs, "__format__ method did not return correct string type"); + else + ok = output_data(fs, STROBJ_AS_PTR(myobj), + STROBJ_GET_SIZE(myobj)); + Py_DECREF(myobj); + return ok; +} + +/* + render_field determines if the field object has a defined __format__ method, and dispatches to the appropriate subfunction. */ static int -renderfield(FmtState *fs, PyObject *fieldobj) +render_field(FmtState *fs, PyObject *fieldobj) { int result; SubString savefmt; PyObject *__format__ = PyObject_GetAttrString(fieldobj, "__format__"); if (__format__ != NULL) { - result = user_format(fs, __format__); + result = caller_render(fs, __format__); Py_DECREF(__format__); } else { @@ -1000,12 +1052,16 @@ savefmt = fs->fmtstr; fs->fmtstr.ptr = fs->fieldspec.ptr; fs->fmtstr.end = fs->fieldspec.end; - result = default_format(fs, fieldobj); + result = internal_render(fs, fieldobj); fs->fmtstr = savefmt; } return result; } +/************************************************************************/ +/******* Output string allocation and escape-to-markup processing ******/ +/************************************************************************/ + /* do_markup is the main program loop. It rummages through the format string, looking for escapes to markup, and @@ -1031,28 +1087,29 @@ fmtstr.ptr++; count--; } + fmtstr.ptr++; count = total - count; total -= count; - doubled = (total > 1) && (fmtstr.ptr[1] == c); + doubled = (total > 1) && (*fmtstr.ptr == c); if (doubled) { output_data(fs, start, count+1); - fmtstr.ptr += 2; + fmtstr.ptr++; continue; } else if (count) output_data(fs, start, count); - if (total < 2) { - ok = !total || - (int)SetError("Invalid format string -- { or } at end"); - break; - } + fs->fmtstr.ptr = fmtstr.ptr; if (c == '}') { - SetError("Invalid format string -- single } encountered"); + SetError(fs, "Single } encountered"); ok = 0; break; } - fs->fmtstr.ptr = fmtstr.ptr + 1; + if (total < 2) { + ok = !total || + (int)SetError(fs, "Single { encountered"); + break; + } myobj = get_field_and_spec(fs); - ok = (myobj != NULL) && renderfield(fs, myobj); + ok = (myobj != NULL) && render_field(fs, myobj); Py_XDECREF(fs->fieldspec.obj); Py_XDECREF(myobj); if (!ok) @@ -1103,7 +1160,7 @@ SubStringObj saveoutstr = fs->outstr; int saveincrement = fs->size_increment; if (--(fs->max_recursion) < 0) - return SetError("Max string recursion exceeded"); + return SetError(fs, "Max string recursion exceeded"); result = do_format(fs); fs->max_recursion++; fs->outstr = saveoutstr; @@ -1111,6 +1168,10 @@ return result; } +/************************************************************************/ +/*********** Main function, option processing, setup and teardown ******/ +/************************************************************************/ + static int get_options(PyObject *keywords, FmtState *fs) { @@ -1181,7 +1242,7 @@ else fs->arg_param_offset = 0; fs->args = args; - fs->fmtstr.ptr = STROBJ_AS_PTR(self); + fs->fmtstr.ptr = fs->fmtstart = STROBJ_AS_PTR(self); fs->fmtstr.end = fs->fmtstr.ptr + STROBJ_GET_SIZE(self); return 1; } @@ -1196,7 +1257,7 @@ ok = (PySet_GET_SIZE(used) <= 1) && !fs->positional_arg_set; if (!ok) { Py_DECREF(result); - result = SetError("Not all arguments consumed"); + result = SetError(fs, "Not all arguments consumed"); } } Py_XDECREF(used); From python-checkins at python.org Fri Mar 2 07:28:11 2007 From: python-checkins at python.org (patrick.maupin) Date: Fri, 2 Mar 2007 07:28:11 +0100 (CET) Subject: [Python-checkins] r54077 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302062811.B57E31E4004@bag.python.org> Author: patrick.maupin Date: Fri Mar 2 07:28:09 2007 New Revision: 54077 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Better error detection and exception reporting for bad/missing arguments and object index/attribute errors. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Fri Mar 2 07:28:09 2007 @@ -47,8 +47,8 @@ def test_missingargs(self): #self.formatRaises(None, "Doesn't use all {0} args", 42, 24) - self.formatRaises(IndexError, "There is no {4} arg", 42, 24) - self.formatRaises(KeyError, "There question is {when}", who=True) + self.formatRaises(ValueError, "There is no {4} arg", 42, 24) + self.formatRaises(ValueError, "There question is {when}", who=True) def test_attributes(self): class Container(object): Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 07:28:09 2007 @@ -121,7 +121,7 @@ /* current position and end of the 'self' string passed to FormatMethod */ SubString fmtstr; /* Used for error reporting */ - CH_TYPE *fmtstart; + CH_TYPE *fmtstart, *fieldstart; /* Output string we are constructing, including current and end pointers*/ SubStringObj outstr; /* Field Specifier, after the colon in {1:{2}} @@ -185,11 +185,18 @@ check_fmtstr returns True if we still have characters left in the format string. */ +static void * +check_fmtstr_helper(FmtState *fs) +{ + fs->fmtstr.ptr = fs->fieldstart; + return SetError(fs, "Unterminated replacement field"); +} + Py_LOCAL_INLINE(int) check_fmtstr(FmtState *fs) { return (fs->fmtstr.ptr < fs->fmtstr.end) || - SetError(fs, "Unexpected end of format_string"); + check_fmtstr_helper(fs); } /************************************************************************/ @@ -478,13 +485,14 @@ index = 0; /* Just to shut up the compiler warning */ if (!check_fmtstr(fs)) return NULL; + isargument=1; isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); myobj = isnumeric ? fs->args : fs->keywords; if (myobj == NULL) - return SetError(fs, "Keyword not specified"); + goto ERROR; Py_INCREF(myobj); - for (isindex=1, expectclose=0, isargument=1;;) { + for (isindex=1, expectclose=0;;) { if (!check_fmtstr(fs)) break; if (!isindex) { @@ -521,6 +529,8 @@ } Py_DECREF(myobj); myobj = newobj; + if (myobj == NULL) + break; if (expectclose) if ((!check_fmtstr(fs)) || (*fs->fmtstr.ptr++ != ']')) { SetError(fs, "Expected ]"); @@ -539,7 +549,16 @@ break; } } - Py_DECREF(myobj); +ERROR: + if ((myobj == NULL) && isargument) + { + PyErr_Clear(); + fs->fmtstr.ptr = fs->fieldstart; + return SetError(fs, isnumeric + ? "Not enough positional arguments" + : "Keyword argument not found"); + } + Py_XDECREF(myobj); return NULL; } @@ -1087,7 +1106,7 @@ fmtstr.ptr++; count--; } - fmtstr.ptr++; + fs->fieldstart = fmtstr.ptr++; count = total - count; total -= count; doubled = (total > 1) && (*fmtstr.ptr == c); From python-checkins at python.org Fri Mar 2 12:42:10 2007 From: python-checkins at python.org (eric.smith) Date: Fri, 2 Mar 2007 12:42:10 +0100 (CET) Subject: [Python-checkins] r54078 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/StringFormat.py Message-ID: <20070302114210.095291E4004@bag.python.org> Author: eric.smith Date: Fri Mar 2 12:42:08 2007 New Revision: 54078 Added: sandbox/trunk/pep3101/StringFormat.py (contents, props changed) Modified: sandbox/trunk/pep3101/README.txt Log: Added StringFormat.py, which is Talin's original pure Python implementation. Only for historical interest. Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Fri Mar 2 12:42:08 2007 @@ -42,6 +42,9 @@ Python versions. - setup.py -- Use "build" option to make the extension module - test_simpleformat.py -- initial unittests + - StringFormat.py -- Talin's original implementation in Python. + This is only for historical interest: it doesn't exactly match + the PEP or C implementation. Todo: Added: sandbox/trunk/pep3101/StringFormat.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/StringFormat.py Fri Mar 2 12:42:08 2007 @@ -0,0 +1,442 @@ +# Python string formatting + +from math import log +try: + import locale +except: + locale = None +try: + import fpformat +except: + fpformat = None + +# Except for errors in the format string. +class FormatError(StandardError): + pass + +strict_format_errors = False + +class ConversionTypes: + Binary = 'b' # Base-2 + Character = 'c' # Print as character + Decimal = 'd' # Decimal integer + Exponent = 'e' # Exponential notation + ExponentUC = 'E' # Exponential notation with upper case 'E' + Fixed = 'f' # Fixed-point + FixedUC = 'F' # Fixed-point with upper case + General = 'g' # General number notation + GeneralUC = 'G' # General number notation with upper case 'E' + Number = 'n' # Number in locale-specific format + Octal = 'o' # Octal + Repr = 'r' # In repr() format + String = 's' # Convert using str() + Hex = 'x' # Base 16 + HexUC = 'X' # Base 16 upper case + Percentage = '%' # As percentage + +ConversionTypes.All = set(ConversionTypes.__dict__.values()) + +# Parse the standard conversion spec. Note that I don't use +# regex here because I'm trying to eliminate external dependencies +# as much as possible. +def parse_std_conversion(spec): + length = None + precision = None + ctype = None + align = None + fill_char = None + sign = None + + index = 0 + spec_len = len(spec) + + # If the second char is an alignment token, + # then parse the fill char + if spec_len >=2 and spec[ 1 ] in '<>=^': + fill_char = spec[ 0 ] + align = spec[ 1 ] + index = 2 + # Otherwise, parse the alignment token + elif spec_len >= 1 and spec[ 0 ] in '<>=^': + align = spec[ 0 ] + index = 1 + + # Parse the various sign options + if index < spec_len and spec[ index ] in ' +-(': + sign = spec_len[ index ] + index += 1 + if index < spec_len and spec[ index ] == ')': + index += 1 + + # The special case for 0-padding (backwards compat) + if fill_char == None and index < spec_len and spec[ index ] == '0': + fill_char = '0' + if align == None: + align = '=' + index += 1 + + # Parse field width + saveindex = index + while index < spec_len and spec[index].isdigit(): + index += 1 + + if index > saveindex: + length = int(spec[saveindex : index]) + + # Parse field precision + if index < spec_len and spec[index] == '.': + index += 1 + saveindex = index + while index < spec_len and spec[index].isdigit(): + index += 1 + if index > saveindex: + precision = int(spec[saveindex:index]) + + # Finally, parse the type field + remaining = spec_len - index + if remaining > 1: + return None # Invalid conversion spec + + if remaining == 1: + ctype = spec[index] + if ctype not in ConversionTypes.All: + return None + + return (fill_char, align, sign, length, precision, ctype) + +# Convert to int, and split into sign part and magnitude part +def to_int(val): + val = int(val) + if val < 0: return '-', -val + return '+', val + +# Convert to float, and split into sign part and magnitude part +def to_float(val): + val = float(val) + if val < 0: return '-', -val + return '+', val + +# Pure python implementation of the C printf 'e' format specificer +def sci(val,precision,letter='e'): + # Split into sign and magnitude (not really needed for formatting + # since we already did this part. Mainly here in case 'sci' + # ever gets split out as an independent function.) + sign = '' + if val < 0: + sign = '-' + val = -val + + # Calculate the exponent + exp = int(floor(log(val,10))) + + # Normalize the value + val *= 10**-exp + + # If the value is exactly an integer, then we don't want to + # print *any* decimal digits, regardless of precision + if val == floor(val): + val = int(val) + else: + # Otherwise, round it based on precision + val = round(val,precision) + # The rounding operation might have increased the + # number to where it is no longer normalized, if so + # then adjust the exponent. + if val >= 10.0: + exp += 1 + val = val * 0.1 + + # Convert the exponent to a string using only str(). + # The existing C printf always prints at least 2 digits. + esign = '+' + if exp < 0: + exp = -exp + esign = '-' + if exp < 10: exp = '0' + str(exp) + else: exp = str(exp) + + # The final result + return sign + str(val) + letter + esign + exp + +# The standard formatter +def format_builtin_type(value, spec): + + # Parse the conversion spec + conversion = parse_std_conversion(spec) + if conversion is None: + raise FormatError("Invalid conversion spec: " + spec) + + # Unpack the conversion spec + fill_char, align, sign_char, length, precision, ctype = conversion + + # Choose a default conversion type + if ctype == None: + if isinstance(value, int) or isinstance(value, long): + ctype = ConversionTypes.Decimal + elif isinstance(value, float): + ctype = ConversionTypes.General + else: + ctype = ConversionTypes.String + + sign = None + + # Conversion types that resolve to other types + if ctype == ConversionTypes.Percentage: + ctype = ConversionTypes.Fixed + value = float(value) * 100.0 + + if ctype == ConversionTypes.Binary: + result = '' + sign, value = to_int(value) + while value: + if value & 1: result = '1' + result + else: result = '0' + result + value >>= 1 + if len(result) == 0: + result = '0' + elif ctype == ConversionTypes.Octal: + sign, value = to_int(value) + result = oct(value) + elif ctype == ConversionTypes.Hex: + sign, value = to_int(value) + result = hex(value) + elif ctype == ConversionTypes.HexUC: + sign, value = to_int(value) + result = hex(value).upper() + elif ctype == ConversionTypes.Character: + result = chr(int( value) ) + elif ctype == ConversionTypes.Decimal: + sign, value = to_int(value) + result = str(value) + elif ctype == ConversionTypes.Fixed or ctype == ConversionTypes.FixedUC: + sign, value = to_float(value) + if fpformat and precision is not None: + result = fpformat.fix(value, precision) + else: + result = str(value) + elif ctype == ConversionTypes.General or ctype == ConversionTypes.GeneralUC: + #Same as "e" if exponent is less than -4 or greater than precision, "f" otherwise. + sign, value = to_float(value) + if fpformat and precision is not None: + if value < 0.0001 or value > 10**precision: + result = fpformat.sci(value, precision) + else: + result = fpformat.fix(value, precision) + if ctype == ConversionTypes.GeneralUC: + result = result.upper() + else: + result = str(value) + elif ctype == ConversionTypes.Exponent or ctype == ConversionTypes.ExponentUC: + sign, value = to_float(value) + if precision is None: precision = 5 # Duh, I dunno + result = sci(value, precision, ctype) + elif ctype == ConversionTypes.Number: + sign, value = to_float(value) + if locale: + # For some reason, this is not working the way I would + # expect + result = locale.format("%f", float( value) ) + else: + result = str(value) + elif ctype == ConversionTypes.String: + result = str(value) + elif ctype == ConversionTypes.Repr: + result = repr(value) + + # Handle the sign logic + prefix = '' + suffix = '' + if sign == '-': + if sign_char == '(': prefix, suffix = '(', ')' + else: prefix = '-' + elif sign == '+': + if sign_char == '+': prefix = '+' + elif sign_char == ' ': prefix = ' ' + + # Handle the padding logic + if length is not None: + padding = length - len(result) - len(prefix) - len(suffix) + if padding > 0: + if align == '>' or align == '^': + return fill_char * padding + prefix + result + suffix + elif align == '=' + return prefix + fill_char * padding + result + suffix + else: + return prefix + result + suffix + fill_char * padding + + return prefix + result + suffix + +def cformat(template, format_hook, args, kwargs): + # Using array types since we're going to be growing + # a lot. + from array import array + array_type = 'c' + + # Use unicode array if the original string is unicode. + if isinstance(template, unicode): array_type = 'u' + buffer = array(array_type) + + # Track which arguments actuallly got used + unused_args = set(kwargs.keys()) + unused_args.update(range(0, len(args))) + + # Inner function to format a field from a value and + # conversion spec. Most details missing. + def format_field(value, cspec): + + # See if there's a hook + if format_hook: + v = format_hook(value, cspec) + if v is not None: + return str(v) + + # See if there's a __format__ method + elif hasattr(value, '__format__'): + return value.__format__(cspec) + + # Default formatting + return format_builtin_type(value, cspec) + + # Parse a field specification. Returns True if it was a valid + # field, False if it was merely an escaped brace. (We do it + # this way to avoid lookahead.) + def parse_field(buffer): + + # A separate array for the field spec. + fieldspec = array(array_type) + + # Consume from the template iterator. + for index, ch in template_iter: + # A sub-field. We just interpret it like a normal field, + # and append to the fieldspec. + if ch == '{': + # If the very first character is an open brace, then + # assume its an escaped (doubled) brace. + if len(fieldspec) == 0: + return False + + # Here's where we catch that doubled brace + if not parse_field(fieldspec): + buffer.extend('{') + return True + + # End of field. Now interpret it. + elif ch == '}': + # Convert the array to string or uni + if array_type == 'u': + fieldspec = fieldspec.tosunicode() + else: + fieldspec = fieldspec.tostring() + + # Check for conversion spec + name = fieldspec + conversion = '' + parts = fieldspec.split(':', 1) + if len(parts) > 1: + name, conversion = parts + + try: + first_time = True + # Split the field name into subfields + for namepart in name.split('.'): + # Split that part by open bracket chars + keyparts = namepart.split('[') + # The first part is just a bare name + key = keyparts[0] + + # Empty strings are not allowed as field names + if key == '': + raise FormatError("empty field name at char " + str(index)) + + # The first name in the sequence is used to index + # the args/kwargs arrays. Subsequent names are used + # on the result of the previous operation. + if first_time: + first_time = False + + # Attempt to coerce key to integer + try: + key = int(key) + value = args[key] + except ValueError: + # Keyword args are strings, not uni (so far) + value = kwargs[key] + + # If we got no exception, then remove from + # unused args + unused_args.remove(key) + else: + # This is not the first time, so get + # an attribute + value = getattr(value, key) + + # Now process any bracket expressions which followed + # the first part. + for key in keyparts[1:]: + endbracket = key.find(']') + if endbracket < 0 or endbracket != len(key) - 1: + raise FormatError("Invalid field syntax at position " + str(index)) + + # Strip off the closing bracket and try to coerce to int + key = key[:-1] + try: + key = int(key) + except ValueError: + pass + + # Get the attribute + value = value[key] + + except (AttributeError,KeyError,IndexError), e: + if strict_format_errors: raise + buffer.extend('?' + e.__class__.__name__ + '?') + return True + + buffer.extend(format_field(value, conversion)) + return True + else: + fieldspec.append(ch) + + raise FormatError("unmatched open brace at position " + str(index)) + + # Construct an iterator from the template + template_iter = enumerate(template) + prev = None + for index, ch in template_iter: + if prev == '}': + if ch != '}': + raise FormatError("unmatched close brace") + else: + buffer.append('}') + prev = None + continue + + if ch == '{': + # It's a field + if not parse_field(buffer): + buffer.extend('{') + elif ch != '}': + buffer.append(ch) + prev = ch + + if prev == '}': + raise FormatError("unmatched close brace") + + # Complain about unused args + if unused_args and strict_format_errors: + raise FormatError( + "Unused arguments: " + + ",".join(str(x) for x in unused_args)) + + # Convert the array to its proper type + if isinstance(template, unicode): + return buffer.tounicode() + else: + return buffer.tostring() + +def format(template, *args, **kwargs): + return cformat(template, None, args, kwargs) + +if __name__ == '__main__': + print format("This is a test of }} {0:x} {x} {y[3]} {2[2]} {1:5n}{{", + 1000, 200000, 'grag', x='hex', y=[1,2,3]); From python-checkins at python.org Fri Mar 2 13:07:41 2007 From: python-checkins at python.org (eric.smith) Date: Fri, 2 Mar 2007 13:07:41 +0100 (CET) Subject: [Python-checkins] r54079 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070302120741.5E1EA1E4004@bag.python.org> Author: eric.smith Date: Fri Mar 2 13:07:39 2007 New Revision: 54079 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Completed implementation for string and repr formatting. Removed 'DUMMY_FORMATTING' define, so that we can incrementally add and test formatting for various types. Fixed parsing of internal format specifiers, it now no longer caches the length. Added unit tests for strings and repr. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Fri Mar 2 13:07:39 2007 @@ -1,4 +1,3 @@ - import unittest from test import test_support @@ -12,111 +11,134 @@ class FormatTest(unittest.TestCase): - # All tests run through these functions. They can be - # overridden to change the class of string being tested - # and the function being used. - def formatEquals(self, result, text, *args, **kwargs): - text = str(text) - result = str(result) - val = pep3101.format(text, *args, **kwargs) - self.assertEquals(val, result) - - def formatRaises(self, exc, text, *args, **kwargs): - exc = exc or Exception #StringFormat.FormatError - text = str(text) - #prevState = StringFormat.strict_format_errors - #StringFormat.strict_format_errors = True - try: - self.assertRaises(exc, - lambda: pep3101.format( - text, *args, **kwargs)) - finally: - pass - #StringFormat.strict_format_errors = prevState - - - def test_basic(self): - # directly from the pep intro - self.formatEquals( + # All tests run through these functions. They can be + # overridden to change the class of string being tested + # and the function being used. + def formatEquals(self, result, text, *args, **kwargs): + text = str(text) + result = str(result) + val = pep3101.format(text, *args, **kwargs) + self.assertEquals(val, result) + + def formatRaises(self, exc, text, *args, **kwargs): + exc = exc or Exception #StringFormat.FormatError + text = str(text) + #prevState = StringFormat.strict_format_errors + #StringFormat.strict_format_errors = True + try: + self.assertRaises(exc, + lambda: pep3101.format( + text, *args, **kwargs)) + finally: + pass + #StringFormat.strict_format_errors = prevState + + + def test_basic(self): + # directly from the pep intro + self.formatEquals( "My name is Fred", "My name is {0}", "Fred") - self.formatEquals( + self.formatEquals( "My name is Fred :-{}", "My name is {0} :-{{}}", "Fred") - self.formatEquals("abc", "{0:}", "abc") # is this allowed? + self.formatEquals("abc", "{0:}", "abc") # is this allowed? - def test_missingargs(self): - #self.formatRaises(None, "Doesn't use all {0} args", 42, 24) - self.formatRaises(ValueError, "There is no {4} arg", 42, 24) - self.formatRaises(ValueError, "There question is {when}", who=True) - - def test_attributes(self): - class Container(object): - one, _two, four4 = 1, 2, 4 - def __getattr__(self, name): - if name == "five": return 5 - raise TypeError("Never do this") - self.formatEquals( + def test_missingargs(self): + #self.formatRaises(None, "Doesn't use all {0} args", 42, 24) + self.formatRaises(ValueError, "There is no {4} arg", 42, 24) + self.formatRaises(ValueError, "There question is {when}", who=True) + + def test_attributes(self): + class Container(object): + one, _two, four4 = 1, 2, 4 + def __getattr__(self, name): + if name == "five": return 5 + raise TypeError("Never do this") + self.formatEquals( "Count with me; 1 4", "Count with me; {0.one} {1.four4}", Container, Container, item=Container) - self.formatRaises(ValueError, + self.formatRaises(ValueError, "Count with me; {0.one} {item._two} {1.four4}", Container, Container, item=Container) - self.formatEquals( + self.formatEquals( "Count with me; 1 2 4", "Count with me; {0.one} {item._two} {1.four4}", Container, Container, item=Container, _flags=dict(allow_leading_under=1)) - self.formatEquals( + self.formatEquals( "Five is 5", "Five is {c.five}", c=Container()) - self.formatRaises(AttributeError, + self.formatRaises(AttributeError, "Missing {0.rabbit} lookup", Container) - self.formatRaises(TypeError, + self.formatRaises(TypeError, "Forbidden {0.secret} lookup", Container()) - def test_items(self): - d = dict(val="value", sum=1) - t = tuple(("apple", "ball", "cat")) - self.formatEquals( + def test_items(self): + d = dict(val="value", sum=1) + t = tuple(("apple", "ball", "cat")) + self.formatEquals( "The value of apple", "The {0[val]} of {t[0]}", d, t=t) - # Decided against negative indices for now - #self.formatEquals( - # "The shiny red ball", - # "The shiny red {0[-2]}", t) - - def test_formatlookup(self): - self.formatEquals("32_0>4d", "{0:{1}}", 32, "0>4d") - self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d") - - def test_specifiers(self): - self.formatEquals("97_c", "{0:c}", ord("a")) - self.formatEquals("8_08b", "{0:08b}", 8) - self.formatEquals("8_ >3d", "{0: >3d}", 8) - self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) - - def test_custom_format(self): - class Custom(object): - def __format__(self, specifiers): - return specifiers - custom = Custom() - self.formatEquals("magic", "{0:magic}", custom) - self.formatEquals("custom", "{0:{1}}", custom, "custom") - - def test_syntaxerror(self): - self.assertRaises(Exception, "}{", True) - self.assertRaises(Exception, "{0", True) - self.assertRaises(Exception, "{0.[]}", True) - self.assertRaises(Exception, "{0[0}", True) - self.assertRaises(Exception, "{0[0:foo}", True) - self.assertRaises(Exception, "{c]}", True) - self.assertRaises(Exception, "{{1}}", True, 0) - self.assertRaises(Exception, "{{ {{{0}}", True) - self.assertRaises(Exception, "{0}}", True) + # Decided against negative indices for now + #self.formatEquals( + # "The shiny red ball", + # "The shiny red {0[-2]}", t) + + def test_formatlookup(self): + self.formatEquals("32_0>4d", "{0:{1}}", 32, "0>4d") + self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d") + + def test_specifiers(self): + self.formatEquals("97_c", "{0:c}", ord("a")) + self.formatEquals("8_08b", "{0:08b}", 8) + self.formatEquals("8_ >3d", "{0: >3d}", 8) + self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) + + def test_string_specifiers(self): + self.formatEquals("abc", "{0:.3s}", "abc") + + self.formatEquals("ab", "{0:.3s}", "ab") + + self.formatEquals("abc", "{0:.3s}", "abcdef") + self.formatEquals("resultx", "{0:x<7s}", "result") + self.formatEquals("result ", "{0: <7s}", "result") + self.formatEquals("result ", "{0:<7s}", "result") + self.formatEquals(" result", "{0:>7s}", "result") + + def test_repr_specifiers(self): + self.formatEquals("3", "{0:r}", 3) + self.formatEquals("3.141", "{0:5r}", 3.141592654) + + # I'm not sure this is a good test, since the quoting might change + self.formatEquals("'abcdefg'", "{0:r}", "abcdefg") + self.formatEquals("'abcdefg", "{0:8r}", "abcdefg") + + def test_missing_type_specifier(self): + # make sure floats use 'g', ints and longs 'd', and everything else 's' + pass + + def test_custom_format(self): + class Custom(object): + def __format__(self, specifiers): + return specifiers + custom = Custom() + self.formatEquals("magic", "{0:magic}", custom) + self.formatEquals("custom", "{0:{1}}", custom, "custom") + + def test_syntaxerror(self): + self.assertRaises(Exception, "}{", True) + self.assertRaises(Exception, "{0", True) + self.assertRaises(Exception, "{0.[]}", True) + self.assertRaises(Exception, "{0[0}", True) + self.assertRaises(Exception, "{0[0:foo}", True) + self.assertRaises(Exception, "{c]}", True) + self.assertRaises(Exception, "{{1}}", True, 0) + self.assertRaises(Exception, "{{ {{{0}}", True) + self.assertRaises(Exception, "{0}}", True) def test_main(): - test_support.run_unittest(FormatTest) + test_support.run_unittest(FormatTest) if __name__ == "__main__": - test_main() + test_main() Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Fri Mar 2 13:07:39 2007 @@ -158,6 +158,22 @@ recurse_format(FmtState *fs); /************************************************************************/ +/************************** Utility functions ************************/ +/************************************************************************/ + +/* XXX probably a better way to do this. can't use memset, though, + because of Unicode and char* support */ +Py_LOCAL_INLINE(void) +charset(CH_TYPE* dst, CH_TYPE ch, Py_ssize_t len) +{ + CH_TYPE* end = dst + len; + for(; dst < end; dst++) { + *dst = ch; + } +} + + +/************************************************************************/ /*********** Error handling and exception generation **************/ /************************************************************************/ @@ -295,9 +311,6 @@ get_integer_index consumes 0 or more decimal digit characters from a format string, updates *result with the corresponding positive integer, and returns the number of digits consumed. - - if the isargument parameter is true, it will remove the - integer from the arguments bitset. */ static int get_integer_index(FmtState *fs, Py_ssize_t *result) @@ -598,11 +611,9 @@ The two main subfunctions of render_field are caller_render (which calls the object-supplied __format__ hook), and internal_render, which - renders objects which don't have format hoohs. + renders objects which don't have format hooks. */ -#if !DUMMY_FORMATTING - typedef struct { CH_TYPE fill_char; CH_TYPE align; @@ -610,7 +621,7 @@ Py_ssize_t width; Py_ssize_t precision; CH_TYPE type; -} DefaultFormat; +} InternalFormatSpec; /* returns true if this character is a specifier alignment token */ Py_LOCAL_INLINE(int) @@ -636,16 +647,22 @@ } } + +/* a convenience function to return the length of a substring */ +Py_LOCAL_INLINE(int) +spec_len(SubString* spec) +{ + return spec->end - spec->ptr; +} + /* parse the default specification */ static int -parse_internal_render(FmtState *fs, DefaultFormat *format) +parse_internal_render_format_spec(FmtState *fs, InternalFormatSpec *format) { - Py_ssize_t index = 0; Py_ssize_t specified_width; - Py_ssize_t remaining; SubString *spec = &fs->fmtstr; format->fill_char = '\0'; @@ -655,44 +672,38 @@ format->precision = -1; format->type = '\0'; - /* cache the length, since that's convenient */ - Py_ssize_t spec_len = spec->end - spec->ptr; - /* If the second char is an alignment token, then parse the fill char */ - if (spec_len >= 2 && alignment_token(spec->ptr[1])) { + if (spec_len(spec) >= 2 && alignment_token(spec->ptr[1])) { format->align = spec->ptr[1]; format->fill_char = spec->ptr[0]; - index = 2; - } else if (spec_len >= 1 && alignment_token(spec->ptr[0])) { + spec->ptr += 2; + } else if (spec_len(spec) >= 1 && alignment_token(spec->ptr[0])) { format->align = spec->ptr[0]; - index = 1; + spec->ptr++; } /* Parse the various sign options */ - if (index < spec_len && sign_element(spec->ptr[index])) { - format->sign = spec->ptr[index]; - index++; - if (index < spec_len && spec->ptr[index] == ')') { - index++; + if (spec_len(spec) >= 1 && sign_element(spec->ptr[0])) { + format->sign = spec->ptr[0]; + spec->ptr++; + if (spec_len(spec) >= 1 && spec->ptr[0] == ')') { + spec->ptr++; } } /* The special case for 0-padding (backwards compat) */ if (format->fill_char == '\0' && - index < spec_len && spec->ptr[index] == '0') { + spec_len(spec) >= 1 && spec->ptr[0] == '0') { format->fill_char = '0'; if (format->align == '\0') { format->align = '='; } - index++; + spec->ptr++; } specified_width = get_integer_index(fs, &format->width); - /* recalculate the length, since the pointers may have just changed */ - spec_len = spec->end - spec->ptr; - /* if specified_width is 0, we didn't consume any characters for the width. in that case, reset the width to -1, because get_integer_index() will have set it to zero */ @@ -701,33 +712,86 @@ } /* Parse field precision */ - if (index < spec_len && spec->ptr[index] == '.') { - index++; + if (spec_len(spec) && spec->ptr[0] == '.') { + spec->ptr++; specified_width = get_integer_index(fs, &format->precision); - /* recalculate the length, since the pointers may have just changed */ - spec_len = spec->end - spec->ptr; - - /* again, check if any characters specified */ + /* not having a precision after a dot is an error */ if (specified_width <= 0) { - format->precision = -1; + SetError(fs, "Missing precision"); + return 0; } + } /* Finally, parse the type field */ - remaining = spec_len - index; - if (remaining > 1) { + if (spec_len(spec) > 1) { /* invalid conversion spec */ SetError(fs, "Invalid conversion specification"); return 0; } - if (remaining == 1) { - format->type = spec->ptr[index]; + if (spec_len(spec) == 1) { + format->type = spec->ptr[0]; + spec->ptr++; } + /* XXX do some consistency checking, like "=" can only be on + signed numbers. or leave that to the individual converers? */ + + return 1; +} + + +/* used to output simple strings, only supports a maximum width and + total field alignment */ +static int +output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len, const InternalFormatSpec *format) +{ + Py_ssize_t ok; + Py_ssize_t width; /* total field width */ + CH_TYPE *dst; + + /* if precision is specified, output no more that format.precision + characters */ + if (format->precision >= 0 && len >= format->precision) { + len = format->precision; + } + + if (format->width >= 0) { + width = format->width; + } else { + /* not specified, use all of the chars and no more */ + width = len; + } + + /* reserve all the space we'll need */ + if (output_allocate(fs, width, &dst) == 0) + return 0; + + /* now write into that space */ + + /* if right aligning, increment the destination allow space on the left */ + memcpy(dst + (format->align == '>' ? (width - len) : 0), src, len * sizeof(CH_TYPE)); + + /* do any padding */ + if (len != width) { + CH_TYPE fill_char = format->fill_char; + if (fill_char == '\0') { + /* use the default, if not specified */ + fill_char = ' '; + } + + if (format->align == '>') { + /* right align, pad on left */ + charset(dst, fill_char, width - len); + } else { + /* left align, pad on right */ + charset(dst + len, fill_char, width - len); + } + } return 1; } @@ -735,141 +799,190 @@ /* Our internal conversion functions have this signature. - returns the number of characters written, or -1 if error + returns 0 on failure, else 1 */ -/* XXX obviously wrong, but just a placeholder currently */ -typedef Py_ssize_t -(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format); +typedef int +(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format); + +/* XXX delete this when all internal conversions are implemented */ +static int +convert_DUMMY(PyObject *fieldobj, FmtState *fs) +{ + PyObject *myobj; + int ok; + + /* Test implementation, only at top level */ + CH_TYPE under = '_'; + + myobj = STROBJ_STR(fieldobj); + if (myobj == NULL) + return 0; + ok = output_data(fs, STROBJ_AS_PTR(myobj), + STROBJ_GET_SIZE(myobj)); + Py_DECREF(myobj); + if (!ok) + return 0; + if (fs->fieldspec.ptr != fs->fieldspec.end) { + if (!output_data(fs, &under, 1)) + return 0; + if (!output_data(fs, fs->fieldspec.ptr, + fs->fieldspec.end - fs->fieldspec.ptr)) + return 0; + } + return 1; +} /* conversion functions */ -static Py_ssize_t -convert_binary(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_binary(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_char(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_char(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_decimal(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); +#if 0 + PyObject *intobj; + PyObject *strobj; + CH_TYPE* src; + Py_ssize_t len; + int negative = 0; + int ok; + + + intobj = PyIntObject(fieldobj); + if (intobj == NULL) + return 0; + + strobj = STROBJ_STR(intobj); + Py_DECREF(intobj); + + /* see if we're negative. we know src must point to at least one + character, so skip that check */ + src = STROBJ_AS_PTR(strobj); + len = STROBJ_GET_SIZE(strobj); + if (src[0] == '-') { + /* remember that we're negative, and skip the char */ + negative = 1; + src++; + len--; + } + + ok = output_string_chars(fs, src, len, format); + Py_DECREF(strobj); + + return ok; +#endif } -static Py_ssize_t -convert_exponent(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_exponent(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_exponentUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_exponentUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_fixed(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_fixed(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_fixedUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_fixedUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_general(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_general(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_generalUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_generalUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_number(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_octal(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_octal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_repr(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_repr(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { int ok; - PyObject *s; - PyObject *r; - Py_ssize_t len; + PyObject *strobj; + PyObject *reprobj; - r = PyObject_Repr(fieldobj); - if (r == NULL) + reprobj = PyObject_Repr(fieldobj); + if (reprobj == NULL) return 0; - s = STROBJ_STR(r); - Py_DECREF(r); + strobj = STROBJ_STR(reprobj); + Py_DECREF(reprobj); - len = STROBJ_GET_SIZE(s); - ok = output_data(fs, STROBJ_AS_PTR(s), len); - Py_DECREF(s); + ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format); + Py_DECREF(strobj); - return ok ? len : -1; + return ok; } -static Py_ssize_t -convert_string(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_string(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - PyObject *myobj; - Py_ssize_t ok; - Py_ssize_t len; - CH_TYPE *dst; + PyObject *strobj; + int ok; - myobj = STROBJ_STR(fieldobj); - if (myobj == NULL) - return -1; + strobj = STROBJ_STR(fieldobj); + if (strobj == NULL) + return 0; - len = STROBJ_GET_SIZE(myobj); + ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format); + Py_DECREF(strobj); - /* reserve all the space we'll need */ - ok = output_allocate(fs, len, &dst); - if (ok) { - memcpy(dst, STROBJ_AS_PTR(myobj), len * sizeof(CH_TYPE)); - } - Py_DECREF(myobj); - - return ok ? len : -1; + return ok; } -static Py_ssize_t -convert_hex(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_hex(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_hexUC(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_hexUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } -static Py_ssize_t -convert_percentage(PyObject *fieldobj, FmtState *fs, const DefaultFormat *format) +static int +convert_percentage(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return -1; + return convert_DUMMY(fieldobj, fs); } /* returns a pointer to our conversion function, or NULL if invalid */ @@ -900,46 +1013,16 @@ return NULL; } } -#endif /* internal_render -- "Then a miracle occurs" */ static int internal_render(FmtState *fs, PyObject *fieldobj) { -#if DUMMY_FORMATTING == 1 - PyObject *myobj; - int ok; - - /* Test implementation, only at top level */ - CH_TYPE under = '_'; - - myobj = STROBJ_STR(fieldobj); - if (myobj == NULL) - return 0; - ok = output_data(fs, STROBJ_AS_PTR(myobj), - STROBJ_GET_SIZE(myobj)); - Py_DECREF(myobj); - if (!ok) - return 0; - if (fs->fieldspec.ptr != fs->fieldspec.end) { - if (!output_data(fs, &under, 1)) - return 0; - if (!output_data(fs, fs->fieldspec.ptr, - fs->fieldspec.end - fs->fieldspec.ptr)) - return 0; - } -#else - - Py_ssize_t len; - Py_ssize_t padding; - DefaultFormat format; + InternalFormatSpec format; ConversionFunction conversion; - CH_TYPE sign = '\0'; - CH_TYPE prefix; - CH_TYPE suffix; - if (!parse_internal_render(fs, &format)) { + if (!parse_internal_render_format_spec(fs, &format)) { return 0; } @@ -955,8 +1038,9 @@ } } - /* handle conversion functions that logically map to - other conversion functions? */ + /* XXX handle conversion functions that logically map to + other conversion functions? percent is the only one, and I'm not wild + about having percent at all*/ conversion = conversion_function(format.type); if (conversion == NULL) { @@ -964,14 +1048,10 @@ return 0; } - /* convert to a string first */ - /* get the length written so that we can fixup - inside the buffer, as needed */ - len = conversion(fieldobj, fs, &format); - if (len < 0) - return 0; + /* do the conversion, writing into the output string */ + return conversion(fieldobj, fs, &format); - /* we wrote "len" bytes. see what fixups need to be done */ +#if 0 /* Handle the sign logic */ prefix = '\0'; @@ -1008,8 +1088,8 @@ } } -#endif return 1; +#endif } /* From python-checkins at python.org Fri Mar 2 15:37:15 2007 From: python-checkins at python.org (georg.brandl) Date: Fri, 2 Mar 2007 15:37:15 +0100 (CET) Subject: [Python-checkins] r54080 - python/trunk/Lib/pydoc.py Message-ID: <20070302143715.B6DC61E4005@bag.python.org> Author: georg.brandl Date: Fri Mar 2 15:37:12 2007 New Revision: 54080 Modified: python/trunk/Lib/pydoc.py Log: Bug #1628895: some better tries to find HTML documentation in pydoc. Modified: python/trunk/Lib/pydoc.py ============================================================================== --- python/trunk/Lib/pydoc.py (original) +++ python/trunk/Lib/pydoc.py Fri Mar 2 15:37:12 2007 @@ -1627,16 +1627,21 @@ 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-' + split(sys.version)[0], - '/usr/doc/python-' + split(sys.version)[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-' + split(sys.version)[0]), + join(sys.prefix, 'doc/python-' + split(sys.version)[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] == '?': From buildbot at python.org Fri Mar 2 15:44:57 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 14:44:57 +0000 Subject: [Python-checkins] buildbot failure in x86 XP trunk Message-ID: <20070302144457.52B3D1E4005@bag.python.org> The Buildbot has detected a new failure of x86 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%2520trunk/builds/231 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling,georg.brandl,neal.norwitz BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 16:01:53 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 15:01:53 +0000 Subject: [Python-checkins] buildbot warnings in amd64 gentoo trunk Message-ID: <20070302150153.C767F1E4005@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%2520gentoo%2520trunk/builds/1878 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling,georg.brandl,neal.norwitz Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_ctypes ====================================================================== ERROR: test_gl (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory ====================================================================== ERROR: test_glu (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory ====================================================================== ERROR: test_glut (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/trunk.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Mar 2 20:19:06 2007 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 2 Mar 2007 20:19:06 +0100 (CET) Subject: [Python-checkins] r54085 - in python/branches/release25-maint: Lib/test/test_peepholer.py Misc/NEWS Python/compile.c Message-ID: <20070302191906.BC7351E4005@bag.python.org> Author: raymond.hettinger Date: Fri Mar 2 20:19:05 2007 New Revision: 54085 Modified: python/branches/release25-maint/Lib/test/test_peepholer.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Python/compile.c Log: Fix constantification of None. Modified: python/branches/release25-maint/Lib/test/test_peepholer.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_peepholer.py (original) +++ python/branches/release25-maint/Lib/test/test_peepholer.py Fri Mar 2 20:19:05 2007 @@ -49,6 +49,11 @@ 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 Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Fri Mar 2 20:19:05 2007 @@ -15,6 +15,9 @@ - Bug #1669182: prevent crash when trying to print an unraisable error from a string exception. +- The peephole optimizer left None as a global in functions with a docstring + and an explicit return value. + - Bug #1653736: Properly discard third argument to slot_nb_inplace_power. - SF #151204: enumerate() now raises an Overflow error at sys.maxint items. Modified: python/branches/release25-maint/Python/compile.c ============================================================================== --- python/branches/release25-maint/Python/compile.c (original) +++ python/branches/release25-maint/Python/compile.c Fri Mar 2 20:19:05 2007 @@ -773,13 +773,17 @@ 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 From python-checkins at python.org Fri Mar 2 20:20:47 2007 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 2 Mar 2007 20:20:47 +0100 (CET) Subject: [Python-checkins] r54086 - in python/trunk: Lib/test/test_peepholer.py Python/peephole.c Message-ID: <20070302192047.611C71E4005@bag.python.org> Author: raymond.hettinger Date: Fri Mar 2 20:20:46 2007 New Revision: 54086 Modified: python/trunk/Lib/test/test_peepholer.py python/trunk/Python/peephole.c Log: Fix embarrassing typo and fix constantification of None Modified: python/trunk/Lib/test/test_peepholer.py ============================================================================== --- python/trunk/Lib/test/test_peepholer.py (original) +++ python/trunk/Lib/test/test_peepholer.py Fri Mar 2 20:20:46 2007 @@ -49,6 +49,11 @@ 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 Modified: python/trunk/Python/peephole.c ============================================================================== --- python/trunk/Python/peephole.c (original) +++ python/trunk/Python/peephole.c Fri Mar 2 20:20:46 2007 @@ -1,4 +1,4 @@ -/* Peehole optimizations for bytecode compiler. */ +/* Peephole optimizations for bytecode compiler. */ #include "Python.h" @@ -386,13 +386,17 @@ 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 From buildbot at python.org Fri Mar 2 20:43:17 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 19:43:17 +0000 Subject: [Python-checkins] buildbot warnings in amd64 gentoo 2.5 Message-ID: <20070302194318.057DD1E4005@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%2520gentoo%25202.5/builds/249 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: andrew.kuchling,raymond.hettinger Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_ctypes ====================================================================== ERROR: test_gl (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory ====================================================================== ERROR: test_glu (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory ====================================================================== ERROR: test_glut (ctypes.test.test_find.Test_OpenGL_libs) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/test/test_find.py", line 38, in setUp self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) File "/home/buildslave/buildslave/python/2.5.norwitz-amd64/build/Lib/ctypes/__init__.py", line 340, in __init__ self._handle = _dlopen(self._name, mode) OSError: libGL.so.1: cannot open shared object file: No such file or directory make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 21:15:25 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 20:15:25 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.5 Message-ID: <20070302201526.67C121E400D@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%25202.5/builds/181 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: andrew.kuchling,raymond.hettinger Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket ====================================================================== FAIL: testInterruptedTimeout (test.test_socket.TCPTimeoutTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/net/ringneck/scratch1/nnorwitz/python/2.5.norwitz-tru64/build/Lib/test/test_socket.py", line 872, in testInterruptedTimeout self.fail("got Alarm in wrong place") AssertionError: got Alarm in wrong place sincerely, -The Buildbot From python-checkins at python.org Fri Mar 2 21:29:30 2007 From: python-checkins at python.org (collin.winter) Date: Fri, 2 Mar 2007 21:29:30 +0100 (CET) Subject: [Python-checkins] r54087 - sandbox/trunk/2to3/tests/test_grammar.py Message-ID: <20070302202930.E282B1E4005@bag.python.org> Author: collin.winter Date: Fri Mar 2 21:29:28 2007 New Revision: 54087 Added: sandbox/trunk/2to3/tests/test_grammar.py (contents, props changed) Log: Add a test suite for grammar changes. Added: sandbox/trunk/2to3/tests/test_grammar.py ============================================================================== --- (empty file) +++ sandbox/trunk/2to3/tests/test_grammar.py Fri Mar 2 21:29:28 2007 @@ -0,0 +1,100 @@ +#!/usr/bin/env python2.5 +""" Test suite for Grammar.txt. This is the place to add tests for +changes to 2to3's grammar, such as those merging the grammars for +Python 2 and 3. """ +# Author: Collin Winter + +# Testing imports +import support +if __name__ == '__main__': + support.adjust_path() + +# Python imports +import os.path + +# Local imports +import pytree +from pgen2 import driver +from pgen2.parse import ParseError + + +grammar_path = os.path.join(os.path.dirname(__file__), "..", "Grammar.txt") +grammar = driver.load_grammar(grammar_path) +driver = driver.Driver(grammar, convert=pytree.convert) + +class GrammarTest(support.TestCase): + def validate(self, code): + driver.parse_string(support.reformat(code), debug=True) + + def invalid_syntax(self, code): + try: + self.validate(code) + except ParseError: + pass + + +# Adapated from Python 3's Lib/test/test_grammar.py:GrammarTests.testFuncdef +class TestFunctionAnnotations(GrammarTest): + def test_1(self): + self.validate("""def f(x) -> list: pass""") + + def test_2(self): + self.validate("""def f(x:int): pass""") + + def test_3(self): + self.validate("""def f(*x:str): pass""") + + def test_4(self): + self.validate("""def f(**x:float): pass""") + + def test_5(self): + self.validate("""def f(x, y:1+2): pass""") + + def test_6(self): + self.validate("""def f(a, (b:1, c:2, d)): pass""") + + def test_7(self): + self.validate("""def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6): pass""") + + def test_8(self): + s = """def f(a, (b:1, c:2, d), e:3=4, f=5, + *g:6, h:7, i=8, j:9=10, **k:11) -> 12: pass""" + self.validate(s) + + +class TestExcept(GrammarTest): + def test_new(self): + s = """ + try: + x + except E as N: + y""" + self.validate(s) + + def test_old(self): + s = """ + try: + x + except E, N: + y""" + self.validate(s) + + +# Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testAtoms +class TestSetLiteral(GrammarTest): + def test_1(self): + self.validate("""x = {'one'}""") + + def test_2(self): + self.validate("""x = {'one', 1,}""") + + def test_3(self): + self.validate("""x = {'one', 'two', 'three'}""") + + def test_4(self): + self.validate("""x = {2, 3, 4,}""") + + +if __name__ == "__main__": + import __main__ + support.run_all_tests(__main__) From python-checkins at python.org Fri Mar 2 21:30:15 2007 From: python-checkins at python.org (georg.brandl) Date: Fri, 2 Mar 2007 21:30:15 +0100 (CET) Subject: [Python-checkins] r54088 - python/trunk/Doc/api/memory.tex Message-ID: <20070302203015.843851E400C@bag.python.org> Author: georg.brandl Date: Fri Mar 2 21:30:14 2007 New Revision: 54088 Modified: python/trunk/Doc/api/memory.tex Log: Bugs #1668032, #1668036, #1669304: clarify behavior of PyMem_Realloc and _Resize. Modified: python/trunk/Doc/api/memory.tex ============================================================================== --- python/trunk/Doc/api/memory.tex (original) +++ python/trunk/Doc/api/memory.tex Fri Mar 2 21:30:14 2007 @@ -100,7 +100,9 @@ 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 @@ \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} From python-checkins at python.org Fri Mar 2 21:30:19 2007 From: python-checkins at python.org (georg.brandl) Date: Fri, 2 Mar 2007 21:30:19 +0100 (CET) Subject: [Python-checkins] r54089 - python/branches/release25-maint/Doc/api/memory.tex Message-ID: <20070302203019.C497E1E4014@bag.python.org> Author: georg.brandl Date: Fri Mar 2 21:30:19 2007 New Revision: 54089 Modified: python/branches/release25-maint/Doc/api/memory.tex Log: Bugs #1668032, #1668036, #1669304: clarify behavior of PyMem_Realloc and _Resize. (backport from rev. 54088) Modified: python/branches/release25-maint/Doc/api/memory.tex ============================================================================== --- python/branches/release25-maint/Doc/api/memory.tex (original) +++ python/branches/release25-maint/Doc/api/memory.tex Fri Mar 2 21:30:19 2007 @@ -100,7 +100,9 @@ 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 @@ \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} From python-checkins at python.org Fri Mar 2 21:33:02 2007 From: python-checkins at python.org (collin.winter) Date: Fri, 2 Mar 2007 21:33:02 +0100 (CET) Subject: [Python-checkins] r54090 - in sandbox/trunk/2to3: Grammar.txt tests/test_grammar.py Message-ID: <20070302203302.E46D21E4011@bag.python.org> Author: collin.winter Date: Fri Mar 2 21:33:02 2007 New Revision: 54090 Modified: sandbox/trunk/2to3/Grammar.txt sandbox/trunk/2to3/tests/test_grammar.py Log: Add support for parsing Py3k's 'raise ... from ...' statement. Modified: sandbox/trunk/2to3/Grammar.txt ============================================================================== --- sandbox/trunk/2to3/Grammar.txt (original) +++ sandbox/trunk/2to3/Grammar.txt Fri Mar 2 21:33:02 2007 @@ -66,7 +66,7 @@ continue_stmt: 'continue' return_stmt: 'return' [testlist] yield_stmt: yield_expr -raise_stmt: 'raise' [test [',' test [',' test]]] +raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]] import_stmt: import_name | import_from import_name: 'import' dotted_as_names import_from: ('from' ('.'* dotted_name | '.'+) Modified: sandbox/trunk/2to3/tests/test_grammar.py ============================================================================== --- sandbox/trunk/2to3/tests/test_grammar.py (original) +++ sandbox/trunk/2to3/tests/test_grammar.py Fri Mar 2 21:33:02 2007 @@ -33,6 +33,35 @@ pass +class TestRaiseChanges(GrammarTest): + def test_2x_style_1(self): + self.validate("raise") + + def test_2x_style_2(self): + self.validate("raise E, V") + + def test_2x_style_3(self): + self.validate("raise E, V, T") + + def test_2x_style_invalid_1(self): + self.invalid_syntax("raise E, V, T, Z") + + def test_3x_style(self): + self.validate("raise E1 from E2") + + def test_3x_style_invalid_1(self): + self.invalid_syntax("raise E, V from E1") + + def test_3x_style_invalid_2(self): + self.invalid_syntax("raise E from E1, E2") + + def test_3x_style_invalid_3(self): + self.invalid_syntax("raise from E1, E2") + + def test_3x_style_invalid_4(self): + self.invalid_syntax("raise E from") + + # Adapated from Python 3's Lib/test/test_grammar.py:GrammarTests.testFuncdef class TestFunctionAnnotations(GrammarTest): def test_1(self): From python-checkins at python.org Fri Mar 2 21:43:50 2007 From: python-checkins at python.org (collin.winter) Date: Fri, 2 Mar 2007 21:43:50 +0100 (CET) Subject: [Python-checkins] r54091 - in sandbox/trunk/2to3/tests: data data/py2_test_grammar.py data/py3_test_grammar.py test_grammar.py Message-ID: <20070302204350.BB3DE1E4005@bag.python.org> Author: collin.winter Date: Fri Mar 2 21:43:48 2007 New Revision: 54091 Added: sandbox/trunk/2to3/tests/data/ sandbox/trunk/2to3/tests/data/py2_test_grammar.py (contents, props changed) sandbox/trunk/2to3/tests/data/py3_test_grammar.py (contents, props changed) Modified: sandbox/trunk/2to3/tests/test_grammar.py Log: Add two tests that try to parse the Lib/test/test_grammar.py files from Python 2.5 and Python 3.0. Added: sandbox/trunk/2to3/tests/data/py2_test_grammar.py ============================================================================== --- (empty file) +++ sandbox/trunk/2to3/tests/data/py2_test_grammar.py Fri Mar 2 21:43:48 2007 @@ -0,0 +1,922 @@ +# Python 2's Lib/test/test_grammar.py (r54061) + +# Python test set -- part 1, grammar. +# This just tests whether the parser accepts them all. + +# NOTE: When you run this test as a script from the command line, you +# get warnings about certain hex/oct constants. Since those are +# issued by the parser, you can't suppress them by adding a +# filterwarnings() call to this module. Therefore, to shut up the +# regression test, the filterwarnings() call has been added to +# regrtest.py. + +from test.test_support import run_unittest, check_syntax_error +import unittest +import sys +# testing import * +from sys import * + +class TokenTests(unittest.TestCase): + + def testBackslash(self): + # Backslash means line continuation: + x = 1 \ + + 1 + self.assertEquals(x, 2, 'backslash for line continuation') + + # Backslash does not means continuation in comments :\ + x = 0 + self.assertEquals(x, 0, 'backslash ending comment') + + def testPlainIntegers(self): + self.assertEquals(0xff, 255) + self.assertEquals(0377, 255) + self.assertEquals(2147483647, 017777777777) + from sys import maxint + if maxint == 2147483647: + self.assertEquals(-2147483647-1, -020000000000) + # XXX -2147483648 + self.assert_(037777777777 > 0) + self.assert_(0xffffffff > 0) + for s in '2147483648', '040000000000', '0x100000000': + try: + x = eval(s) + except OverflowError: + self.fail("OverflowError on huge integer literal %r" % s) + elif maxint == 9223372036854775807: + self.assertEquals(-9223372036854775807-1, -01000000000000000000000) + self.assert_(01777777777777777777777 > 0) + self.assert_(0xffffffffffffffff > 0) + for s in '9223372036854775808', '02000000000000000000000', \ + '0x10000000000000000': + try: + x = eval(s) + except OverflowError: + self.fail("OverflowError on huge integer literal %r" % s) + else: + self.fail('Weird maxint value %r' % maxint) + + def testLongIntegers(self): + x = 0L + x = 0l + x = 0xffffffffffffffffL + x = 0xffffffffffffffffl + x = 077777777777777777L + x = 077777777777777777l + x = 123456789012345678901234567890L + x = 123456789012345678901234567890l + + def testFloats(self): + x = 3.14 + x = 314. + x = 0.314 + # XXX x = 000.314 + x = .314 + x = 3e14 + x = 3E14 + x = 3e-14 + x = 3e+14 + x = 3.e14 + x = .3e14 + x = 3.1e4 + + def testStringLiterals(self): + x = ''; y = ""; self.assert_(len(x) == 0 and x == y) + x = '\''; y = "'"; self.assert_(len(x) == 1 and x == y and ord(x) == 39) + x = '"'; y = "\""; self.assert_(len(x) == 1 and x == y and ord(x) == 34) + x = "doesn't \"shrink\" does it" + y = 'doesn\'t "shrink" does it' + self.assert_(len(x) == 24 and x == y) + x = "does \"shrink\" doesn't it" + y = 'does "shrink" doesn\'t it' + self.assert_(len(x) == 24 and x == y) + x = """ +The "quick" +brown fox +jumps over +the 'lazy' dog. +""" + y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n' + self.assertEquals(x, y) + y = ''' +The "quick" +brown fox +jumps over +the 'lazy' dog. +''' + self.assertEquals(x, y) + y = "\n\ +The \"quick\"\n\ +brown fox\n\ +jumps over\n\ +the 'lazy' dog.\n\ +" + self.assertEquals(x, y) + y = '\n\ +The \"quick\"\n\ +brown fox\n\ +jumps over\n\ +the \'lazy\' dog.\n\ +' + self.assertEquals(x, y) + + +class GrammarTests(unittest.TestCase): + + # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE + # XXX can't test in a script -- this rule is only used when interactive + + # file_input: (NEWLINE | stmt)* ENDMARKER + # Being tested as this very moment this very module + + # expr_input: testlist NEWLINE + # XXX Hard to test -- used only in calls to input() + + def testEvalInput(self): + # testlist ENDMARKER + x = eval('1, 0 or 1') + + def testFuncdef(self): + ### 'def' NAME parameters ':' suite + ### parameters: '(' [varargslist] ')' + ### varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' ('**'|'*' '*') NAME] + ### | ('**'|'*' '*') NAME) + ### | fpdef ['=' test] (',' fpdef ['=' test])* [','] + ### fpdef: NAME | '(' fplist ')' + ### fplist: fpdef (',' fpdef)* [','] + ### arglist: (argument ',')* (argument | *' test [',' '**' test] | '**' test) + ### argument: [test '='] test # Really [keyword '='] test + def f1(): pass + f1() + f1(*()) + f1(*(), **{}) + def f2(one_argument): pass + def f3(two, arguments): pass + def f4(two, (compound, (argument, list))): pass + def f5((compound, first), two): pass + self.assertEquals(f2.func_code.co_varnames, ('one_argument',)) + self.assertEquals(f3.func_code.co_varnames, ('two', 'arguments')) + if sys.platform.startswith('java'): + self.assertEquals(f4.func_code.co_varnames, + ('two', '(compound, (argument, list))', 'compound', 'argument', + 'list',)) + self.assertEquals(f5.func_code.co_varnames, + ('(compound, first)', 'two', 'compound', 'first')) + else: + self.assertEquals(f4.func_code.co_varnames, + ('two', '.1', 'compound', 'argument', 'list')) + self.assertEquals(f5.func_code.co_varnames, + ('.0', 'two', 'compound', 'first')) + def a1(one_arg,): pass + def a2(two, args,): pass + def v0(*rest): pass + def v1(a, *rest): pass + def v2(a, b, *rest): pass + def v3(a, (b, c), *rest): return a, b, c, rest + + f1() + f2(1) + f2(1,) + f3(1, 2) + f3(1, 2,) + f4(1, (2, (3, 4))) + v0() + v0(1) + v0(1,) + v0(1,2) + v0(1,2,3,4,5,6,7,8,9,0) + v1(1) + v1(1,) + v1(1,2) + v1(1,2,3) + v1(1,2,3,4,5,6,7,8,9,0) + v2(1,2) + v2(1,2,3) + v2(1,2,3,4) + v2(1,2,3,4,5,6,7,8,9,0) + v3(1,(2,3)) + v3(1,(2,3),4) + v3(1,(2,3),4,5,6,7,8,9,0) + + # ceval unpacks the formal arguments into the first argcount names; + # thus, the names nested inside tuples must appear after these names. + if sys.platform.startswith('java'): + self.assertEquals(v3.func_code.co_varnames, ('a', '(b, c)', 'rest', 'b', 'c')) + else: + self.assertEquals(v3.func_code.co_varnames, ('a', '.1', 'rest', 'b', 'c')) + self.assertEquals(v3(1, (2, 3), 4), (1, 2, 3, (4,))) + def d01(a=1): pass + d01() + d01(1) + d01(*(1,)) + d01(**{'a':2}) + def d11(a, b=1): pass + d11(1) + d11(1, 2) + d11(1, **{'b':2}) + def d21(a, b, c=1): pass + d21(1, 2) + d21(1, 2, 3) + d21(*(1, 2, 3)) + d21(1, *(2, 3)) + d21(1, 2, *(3,)) + d21(1, 2, **{'c':3}) + def d02(a=1, b=2): pass + d02() + d02(1) + d02(1, 2) + d02(*(1, 2)) + d02(1, *(2,)) + d02(1, **{'b':2}) + d02(**{'a': 1, 'b': 2}) + def d12(a, b=1, c=2): pass + d12(1) + d12(1, 2) + d12(1, 2, 3) + def d22(a, b, c=1, d=2): pass + d22(1, 2) + d22(1, 2, 3) + d22(1, 2, 3, 4) + def d01v(a=1, *rest): pass + d01v() + d01v(1) + d01v(1, 2) + d01v(*(1, 2, 3, 4)) + d01v(*(1,)) + d01v(**{'a':2}) + def d11v(a, b=1, *rest): pass + d11v(1) + d11v(1, 2) + d11v(1, 2, 3) + def d21v(a, b, c=1, *rest): pass + d21v(1, 2) + d21v(1, 2, 3) + d21v(1, 2, 3, 4) + d21v(*(1, 2, 3, 4)) + d21v(1, 2, **{'c': 3}) + def d02v(a=1, b=2, *rest): pass + d02v() + d02v(1) + d02v(1, 2) + d02v(1, 2, 3) + d02v(1, *(2, 3, 4)) + d02v(**{'a': 1, 'b': 2}) + def d12v(a, b=1, c=2, *rest): pass + d12v(1) + d12v(1, 2) + d12v(1, 2, 3) + d12v(1, 2, 3, 4) + d12v(*(1, 2, 3, 4)) + d12v(1, 2, *(3, 4, 5)) + d12v(1, *(2,), **{'c': 3}) + def d22v(a, b, c=1, d=2, *rest): pass + d22v(1, 2) + d22v(1, 2, 3) + d22v(1, 2, 3, 4) + d22v(1, 2, 3, 4, 5) + d22v(*(1, 2, 3, 4)) + d22v(1, 2, *(3, 4, 5)) + d22v(1, *(2, 3), **{'d': 4}) + def d31v((x)): pass + d31v(1) + def d32v((x,)): pass + d32v((1,)) + + def testLambdef(self): + ### lambdef: 'lambda' [varargslist] ':' test + l1 = lambda : 0 + self.assertEquals(l1(), 0) + l2 = lambda : a[d] # XXX just testing the expression + l3 = lambda : [2 < x for x in [-1, 3, 0L]] + self.assertEquals(l3(), [0, 1, 0]) + l4 = lambda x = lambda y = lambda z=1 : z : y() : x() + self.assertEquals(l4(), 1) + l5 = lambda x, y, z=2: x + y + z + self.assertEquals(l5(1, 2), 5) + self.assertEquals(l5(1, 2, 3), 6) + check_syntax_error(self, "lambda x: x = 2") + + ### stmt: simple_stmt | compound_stmt + # Tested below + + def testSimpleStmt(self): + ### simple_stmt: small_stmt (';' small_stmt)* [';'] + x = 1; pass; del x + def foo(): + # verify statments that end with semi-colons + x = 1; pass; del x; + foo() + + ### small_stmt: expr_stmt | print_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt | exec_stmt + # Tested below + + def testExprStmt(self): + # (exprlist '=')* exprlist + 1 + 1, 2, 3 + x = 1 + x = 1, 2, 3 + x = y = z = 1, 2, 3 + x, y, z = 1, 2, 3 + abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4) + + check_syntax_error(self, "x + 1 = 1") + check_syntax_error(self, "a + 1 = b + 2") + + def testPrintStmt(self): + # 'print' (test ',')* [test] + import StringIO + + # Can't test printing to real stdout without comparing output + # which is not available in unittest. + save_stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + print 1, 2, 3 + print 1, 2, 3, + print + print 0 or 1, 0 or 1, + print 0 or 1 + + # 'print' '>>' test ',' + print >> sys.stdout, 1, 2, 3 + print >> sys.stdout, 1, 2, 3, + print >> sys.stdout + print >> sys.stdout, 0 or 1, 0 or 1, + print >> sys.stdout, 0 or 1 + + # test printing to an instance + class Gulp: + def write(self, msg): pass + + gulp = Gulp() + print >> gulp, 1, 2, 3 + print >> gulp, 1, 2, 3, + print >> gulp + print >> gulp, 0 or 1, 0 or 1, + print >> gulp, 0 or 1 + + # test print >> None + def driver(): + oldstdout = sys.stdout + sys.stdout = Gulp() + try: + tellme(Gulp()) + tellme() + finally: + sys.stdout = oldstdout + + # we should see this once + def tellme(file=sys.stdout): + print >> file, 'hello world' + + driver() + + # we should not see this at all + def tellme(file=None): + print >> file, 'goodbye universe' + + driver() + + self.assertEqual(sys.stdout.getvalue(), '''\ +1 2 3 +1 2 3 +1 1 1 +1 2 3 +1 2 3 +1 1 1 +hello world +''') + sys.stdout = save_stdout + + # syntax errors + check_syntax_error(self, 'print ,') + check_syntax_error(self, 'print >> x,') + + def testDelStmt(self): + # 'del' exprlist + abc = [1,2,3] + x, y, z = abc + xyz = x, y, z + + del abc + del x, y, (z, xyz) + + def testPassStmt(self): + # 'pass' + pass + + # flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt + # Tested below + + def testBreakStmt(self): + # 'break' + while 1: break + + def testContinueStmt(self): + # 'continue' + i = 1 + while i: i = 0; continue + + msg = "" + while not msg: + msg = "ok" + try: + continue + msg = "continue failed to continue inside try" + except: + msg = "continue inside try called except block" + if msg != "ok": + self.fail(msg) + + msg = "" + while not msg: + msg = "finally block not called" + try: + continue + finally: + msg = "ok" + if msg != "ok": + self.fail(msg) + + def test_break_continue_loop(self): + # This test warrants an explanation. It is a test specifically for SF bugs + # #463359 and #462937. The bug is that a 'break' statement executed or + # exception raised inside a try/except inside a loop, *after* a continue + # statement has been executed in that loop, will cause the wrong number of + # arguments to be popped off the stack and the instruction pointer reset to + # a very small number (usually 0.) Because of this, the following test + # *must* written as a function, and the tracking vars *must* be function + # arguments with default values. Otherwise, the test will loop and loop. + + def test_inner(extra_burning_oil = 1, count=0): + big_hippo = 2 + while big_hippo: + count += 1 + try: + if extra_burning_oil and big_hippo == 1: + extra_burning_oil -= 1 + break + big_hippo -= 1 + continue + except: + raise + if count > 2 or big_hippo <> 1: + self.fail("continue then break in try/except in loop broken!") + test_inner() + + def testReturn(self): + # 'return' [testlist] + def g1(): return + def g2(): return 1 + g1() + x = g2() + check_syntax_error(self, "class foo:return 1") + + def testYield(self): + check_syntax_error(self, "class foo:yield 1") + + def testRaise(self): + # 'raise' test [',' test] + try: raise RuntimeError, 'just testing' + except RuntimeError: pass + try: raise KeyboardInterrupt + except KeyboardInterrupt: pass + + def testImport(self): + # 'import' dotted_as_names + import sys + import time, sys + # 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names) + from time import time + from time import (time) + # not testable inside a function, but already done at top of the module + # from sys import * + from sys import path, argv + from sys import (path, argv) + from sys import (path, argv,) + + def testGlobal(self): + # 'global' NAME (',' NAME)* + global a + global a, b + global one, two, three, four, five, six, seven, eight, nine, ten + + def testExec(self): + # 'exec' expr ['in' expr [',' expr]] + z = None + del z + exec 'z=1+1\n' + if z != 2: self.fail('exec \'z=1+1\'\\n') + del z + exec 'z=1+1' + if z != 2: self.fail('exec \'z=1+1\'') + z = None + del z + import types + if hasattr(types, "UnicodeType"): + exec r"""if 1: + exec u'z=1+1\n' + if z != 2: self.fail('exec u\'z=1+1\'\\n') + del z + exec u'z=1+1' + if z != 2: self.fail('exec u\'z=1+1\'')""" + g = {} + exec 'z = 1' in g + if g.has_key('__builtins__'): del g['__builtins__'] + if g != {'z': 1}: self.fail('exec \'z = 1\' in g') + g = {} + l = {} + + import warnings + warnings.filterwarnings("ignore", "global statement", module="") + exec 'global a; a = 1; b = 2' in g, l + if g.has_key('__builtins__'): del g['__builtins__'] + if l.has_key('__builtins__'): del l['__builtins__'] + if (g, l) != ({'a':1}, {'b':2}): + self.fail('exec ... in g (%s), l (%s)' %(g,l)) + + def testAssert(self): + # assert_stmt: 'assert' test [',' test] + assert 1 + assert 1, 1 + assert lambda x:x + assert 1, lambda x:x+1 + try: + assert 0, "msg" + except AssertionError, e: + self.assertEquals(e.args[0], "msg") + else: + if __debug__: + self.fail("AssertionError not raised by assert 0") + + ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef + # Tested below + + def testIf(self): + # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] + if 1: pass + if 1: pass + else: pass + if 0: pass + elif 0: pass + if 0: pass + elif 0: pass + elif 0: pass + elif 0: pass + else: pass + + def testWhile(self): + # 'while' test ':' suite ['else' ':' suite] + while 0: pass + while 0: pass + else: pass + + def testFor(self): + # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] + for i in 1, 2, 3: pass + for i, j, k in (): pass + else: pass + class Squares: + def __init__(self, max): + self.max = max + self.sofar = [] + def __len__(self): return len(self.sofar) + def __getitem__(self, i): + if not 0 <= i < self.max: raise IndexError + n = len(self.sofar) + while n <= i: + self.sofar.append(n*n) + n = n+1 + return self.sofar[i] + n = 0 + for x in Squares(10): n = n+x + if n != 285: + self.fail('for over growing sequence') + + result = [] + for x, in [(1,), (2,), (3,)]: + result.append(x) + self.assertEqual(result, [1, 2, 3]) + + def testTry(self): + ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] + ### | 'try' ':' suite 'finally' ':' suite + ### except_clause: 'except' [expr [',' expr]] + try: + 1/0 + except ZeroDivisionError: + pass + else: + pass + try: 1/0 + except EOFError: pass + except TypeError, msg: pass + except RuntimeError, msg: pass + except: pass + else: pass + try: 1/0 + except (EOFError, TypeError, ZeroDivisionError): pass + try: 1/0 + except (EOFError, TypeError, ZeroDivisionError), msg: pass + try: pass + finally: pass + + def testSuite(self): + # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT + if 1: pass + if 1: + pass + if 1: + # + # + # + pass + pass + # + pass + # + + def testTest(self): + ### and_test ('or' and_test)* + ### and_test: not_test ('and' not_test)* + ### not_test: 'not' not_test | comparison + if not 1: pass + if 1 and 1: pass + if 1 or 1: pass + if not not not 1: pass + if not 1 and 1 and 1: pass + if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass + + def testComparison(self): + ### comparison: expr (comp_op expr)* + ### comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' + if 1: pass + x = (1 == 1) + if 1 == 1: pass + if 1 != 1: pass + if 1 <> 1: pass + if 1 < 1: pass + if 1 > 1: pass + if 1 <= 1: pass + if 1 >= 1: pass + if 1 is 1: pass + if 1 is not 1: pass + if 1 in (): pass + if 1 not in (): pass + if 1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1: pass + + def testBinaryMaskOps(self): + x = 1 & 1 + x = 1 ^ 1 + x = 1 | 1 + + def testShiftOps(self): + x = 1 << 1 + x = 1 >> 1 + x = 1 << 1 >> 1 + + def testAdditiveOps(self): + x = 1 + x = 1 + 1 + x = 1 - 1 - 1 + x = 1 - 1 + 1 - 1 + 1 + + def testMultiplicativeOps(self): + x = 1 * 1 + x = 1 / 1 + x = 1 % 1 + x = 1 / 1 * 1 % 1 + + def testUnaryOps(self): + x = +1 + x = -1 + x = ~1 + x = ~1 ^ 1 & 1 | 1 & 1 ^ -1 + x = -1*1/1 + 1*1 - ---1*1 + + def testSelectors(self): + ### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME + ### subscript: expr | [expr] ':' [expr] + + import sys, time + c = sys.path[0] + x = time.time() + x = sys.modules['time'].time() + a = '01234' + c = a[0] + c = a[-1] + s = a[0:5] + s = a[:5] + s = a[0:] + s = a[:] + s = a[-5:] + s = a[:-1] + s = a[-4:-3] + # A rough test of SF bug 1333982. http://python.org/sf/1333982 + # The testing here is fairly incomplete. + # Test cases should include: commas with 1 and 2 colons + d = {} + d[1] = 1 + d[1,] = 2 + d[1,2] = 3 + d[1,2,3] = 4 + L = list(d) + L.sort() + self.assertEquals(str(L), '[1, (1,), (1, 2), (1, 2, 3)]') + + def testAtoms(self): + ### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING + ### dictmaker: test ':' test (',' test ':' test)* [','] + + x = (1) + x = (1 or 2 or 3) + x = (1 or 2 or 3, 2, 3) + + x = [] + x = [1] + x = [1 or 2 or 3] + x = [1 or 2 or 3, 2, 3] + x = [] + + x = {} + x = {'one': 1} + x = {'one': 1,} + x = {'one' or 'two': 1 or 2} + x = {'one': 1, 'two': 2} + x = {'one': 1, 'two': 2,} + x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6} + + x = `x` + x = `1 or 2 or 3` + self.assertEqual(`1,2`, '(1, 2)') + + x = x + x = 'x' + x = 123 + + ### exprlist: expr (',' expr)* [','] + ### testlist: test (',' test)* [','] + # These have been exercised enough above + + def testClassdef(self): + # 'class' NAME ['(' [testlist] ')'] ':' suite + class B: pass + class B2(): pass + class C1(B): pass + class C2(B): pass + class D(C1, C2, B): pass + class C: + def meth1(self): pass + def meth2(self, arg): pass + def meth3(self, a1, a2): pass + + def testListcomps(self): + # list comprehension tests + nums = [1, 2, 3, 4, 5] + strs = ["Apple", "Banana", "Coconut"] + spcs = [" Apple", " Banana ", "Coco nut "] + + self.assertEqual([s.strip() for s in spcs], ['Apple', 'Banana', 'Coco nut']) + self.assertEqual([3 * x for x in nums], [3, 6, 9, 12, 15]) + self.assertEqual([x for x in nums if x > 2], [3, 4, 5]) + self.assertEqual([(i, s) for i in nums for s in strs], + [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'), + (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'), + (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'), + (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'), + (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')]) + self.assertEqual([(i, s) for i in nums for s in [f for f in strs if "n" in f]], + [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'), + (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'), + (5, 'Banana'), (5, 'Coconut')]) + self.assertEqual([(lambda a:[a**i for i in range(a+1)])(j) for j in range(5)], + [[1], [1, 1], [1, 2, 4], [1, 3, 9, 27], [1, 4, 16, 64, 256]]) + + def test_in_func(l): + return [None < x < 3 for x in l if x > 2] + + self.assertEqual(test_in_func(nums), [False, False, False]) + + def test_nested_front(): + self.assertEqual([[y for y in [x, x + 1]] for x in [1,3,5]], + [[1, 2], [3, 4], [5, 6]]) + + test_nested_front() + + check_syntax_error(self, "[i, s for i in nums for s in strs]") + check_syntax_error(self, "[x if y]") + + suppliers = [ + (1, "Boeing"), + (2, "Ford"), + (3, "Macdonalds") + ] + + parts = [ + (10, "Airliner"), + (20, "Engine"), + (30, "Cheeseburger") + ] + + suppart = [ + (1, 10), (1, 20), (2, 20), (3, 30) + ] + + x = [ + (sname, pname) + for (sno, sname) in suppliers + for (pno, pname) in parts + for (sp_sno, sp_pno) in suppart + if sno == sp_sno and pno == sp_pno + ] + + self.assertEqual(x, [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'), + ('Macdonalds', 'Cheeseburger')]) + + def testGenexps(self): + # generator expression tests + g = ([x for x in range(10)] for x in range(1)) + self.assertEqual(g.next(), [x for x in range(10)]) + try: + g.next() + self.fail('should produce StopIteration exception') + except StopIteration: + pass + + a = 1 + try: + g = (a for d in a) + g.next() + self.fail('should produce TypeError') + except TypeError: + pass + + self.assertEqual(list((x, y) for x in 'abcd' for y in 'abcd'), [(x, y) for x in 'abcd' for y in 'abcd']) + self.assertEqual(list((x, y) for x in 'ab' for y in 'xy'), [(x, y) for x in 'ab' for y in 'xy']) + + a = [x for x in range(10)] + b = (x for x in (y for y in a)) + self.assertEqual(sum(b), sum([x for x in range(10)])) + + self.assertEqual(sum(x**2 for x in range(10)), sum([x**2 for x in range(10)])) + self.assertEqual(sum(x*x for x in range(10) if x%2), sum([x*x for x in range(10) if x%2])) + self.assertEqual(sum(x for x in (y for y in range(10))), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10)))), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in [y for y in (z for z in range(10))]), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True)) if True), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True) if False) if True), 0) + check_syntax_error(self, "foo(x for x in range(10), 100)") + check_syntax_error(self, "foo(100, x for x in range(10))") + + def testComprehensionSpecials(self): + # test for outmost iterable precomputation + x = 10; g = (i for i in range(x)); x = 5 + self.assertEqual(len(list(g)), 10) + + # This should hold, since we're only precomputing outmost iterable. + x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x)) + x = 5; t = True; + self.assertEqual([(i,j) for i in range(10) for j in range(5)], list(g)) + + # Grammar allows multiple adjacent 'if's in listcomps and genexps, + # even though it's silly. Make sure it works (ifelse broke this.) + self.assertEqual([ x for x in range(10) if x % 2 if x % 3 ], [1, 5, 7]) + self.assertEqual(list(x for x in range(10) if x % 2 if x % 3), [1, 5, 7]) + + # verify unpacking single element tuples in listcomp/genexp. + self.assertEqual([x for x, in [(4,), (5,), (6,)]], [4, 5, 6]) + self.assertEqual(list(x for x, in [(7,), (8,), (9,)]), [7, 8, 9]) + + def testIfElseExpr(self): + # Test ifelse expressions in various cases + def _checkeval(msg, ret): + "helper to check that evaluation of expressions is done correctly" + print x + return ret + + self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True]) + self.assertEqual([ x() for x in (lambda: True, lambda: False) if x() ], [True]) + self.assertEqual([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ], [True]) + self.assertEqual((5 if 1 else _checkeval("check 1", 0)), 5) + self.assertEqual((_checkeval("check 2", 0) if 0 else 5), 5) + self.assertEqual((5 and 6 if 0 else 1), 1) + self.assertEqual(((5 and 6) if 0 else 1), 1) + self.assertEqual((5 and (6 if 1 else 1)), 6) + self.assertEqual((0 or _checkeval("check 3", 2) if 0 else 3), 3) + self.assertEqual((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)), 1) + self.assertEqual((0 or 5 if 1 else _checkeval("check 6", 3)), 5) + self.assertEqual((not 5 if 1 else 1), False) + self.assertEqual((not 5 if 0 else 1), 1) + self.assertEqual((6 + 1 if 1 else 2), 7) + self.assertEqual((6 - 1 if 1 else 2), 5) + self.assertEqual((6 * 2 if 1 else 4), 12) + self.assertEqual((6 / 2 if 1 else 3), 3) + self.assertEqual((6 < 4 if 0 else 2), 2) + + +def test_main(): + run_unittest(TokenTests, GrammarTests) + +if __name__ == '__main__': + test_main() Added: sandbox/trunk/2to3/tests/data/py3_test_grammar.py ============================================================================== --- (empty file) +++ sandbox/trunk/2to3/tests/data/py3_test_grammar.py Fri Mar 2 21:43:48 2007 @@ -0,0 +1,864 @@ +# Python 3's Lib/test/test_grammar.py (r53781) + +# Python test set -- part 1, grammar. +# This just tests whether the parser accepts them all. + +# NOTE: When you run this test as a script from the command line, you +# get warnings about certain hex/oct constants. Since those are +# issued by the parser, you can't suppress them by adding a +# filterwarnings() call to this module. Therefore, to shut up the +# regression test, the filterwarnings() call has been added to +# regrtest.py. + +from test.test_support import run_unittest, check_syntax_error +import unittest +import sys +# testing import * +from sys import * + +class TokenTests(unittest.TestCase): + + def testBackslash(self): + # Backslash means line continuation: + x = 1 \ + + 1 + self.assertEquals(x, 2, 'backslash for line continuation') + + # Backslash does not means continuation in comments :\ + x = 0 + self.assertEquals(x, 0, 'backslash ending comment') + + def testPlainIntegers(self): + self.assertEquals(0xff, 255) + self.assertEquals(0377, 255) + self.assertEquals(2147483647, 017777777777) + from sys import maxint + if maxint == 2147483647: + self.assertEquals(-2147483647-1, -020000000000) + # XXX -2147483648 + self.assert_(037777777777 > 0) + self.assert_(0xffffffff > 0) + for s in '2147483648', '040000000000', '0x100000000': + try: + x = eval(s) + except OverflowError: + self.fail("OverflowError on huge integer literal %r" % s) + elif maxint == 9223372036854775807: + self.assertEquals(-9223372036854775807-1, -01000000000000000000000) + self.assert_(01777777777777777777777 > 0) + self.assert_(0xffffffffffffffff > 0) + for s in '9223372036854775808', '02000000000000000000000', \ + '0x10000000000000000': + try: + x = eval(s) + except OverflowError: + self.fail("OverflowError on huge integer literal %r" % s) + else: + self.fail('Weird maxint value %r' % maxint) + + def testLongIntegers(self): + x = 0 + x = 0 + x = 0xffffffffffffffff + x = 0xffffffffffffffff + x = 077777777777777777 + x = 077777777777777777 + x = 123456789012345678901234567890 + x = 123456789012345678901234567890 + + def testFloats(self): + x = 3.14 + x = 314. + x = 0.314 + # XXX x = 000.314 + x = .314 + x = 3e14 + x = 3E14 + x = 3e-14 + x = 3e+14 + x = 3.e14 + x = .3e14 + x = 3.1e4 + + def testStringLiterals(self): + x = ''; y = ""; self.assert_(len(x) == 0 and x == y) + x = '\''; y = "'"; self.assert_(len(x) == 1 and x == y and ord(x) == 39) + x = '"'; y = "\""; self.assert_(len(x) == 1 and x == y and ord(x) == 34) + x = "doesn't \"shrink\" does it" + y = 'doesn\'t "shrink" does it' + self.assert_(len(x) == 24 and x == y) + x = "does \"shrink\" doesn't it" + y = 'does "shrink" doesn\'t it' + self.assert_(len(x) == 24 and x == y) + x = """ +The "quick" +brown fox +jumps over +the 'lazy' dog. +""" + y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n' + self.assertEquals(x, y) + y = ''' +The "quick" +brown fox +jumps over +the 'lazy' dog. +''' + self.assertEquals(x, y) + y = "\n\ +The \"quick\"\n\ +brown fox\n\ +jumps over\n\ +the 'lazy' dog.\n\ +" + self.assertEquals(x, y) + y = '\n\ +The \"quick\"\n\ +brown fox\n\ +jumps over\n\ +the \'lazy\' dog.\n\ +' + self.assertEquals(x, y) + + def testEllipsis(self): + x = ... + self.assert_(x is Ellipsis) + +class GrammarTests(unittest.TestCase): + + # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE + # XXX can't test in a script -- this rule is only used when interactive + + # file_input: (NEWLINE | stmt)* ENDMARKER + # Being tested as this very moment this very module + + # expr_input: testlist NEWLINE + # XXX Hard to test -- used only in calls to input() + + def testEvalInput(self): + # testlist ENDMARKER + x = eval('1, 0 or 1') + + def testFuncdef(self): + ### [decorators] 'def' NAME parameters ['->' test] ':' suite + ### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE + ### decorators: decorator+ + ### parameters: '(' [typedargslist] ')' + ### typedargslist: ((tfpdef ['=' test] ',')* + ### ('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname) + ### | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) + ### tname: NAME [':' test] + ### tfpdef: tname | '(' tfplist ')' + ### tfplist: tfpdef (',' tfpdef)* [','] + ### varargslist: ((vfpdef ['=' test] ',')* + ### ('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname) + ### | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + ### vname: NAME + ### vfpdef: vname | '(' vfplist ')' + ### vfplist: vfpdef (',' vfpdef)* [','] + def f1(): pass + f1() + f1(*()) + f1(*(), **{}) + def f2(one_argument): pass + def f3(two, arguments): pass + def f4(two, (compound, (argument, list))): pass + def f5((compound, first), two): pass + self.assertEquals(f2.func_code.co_varnames, ('one_argument',)) + self.assertEquals(f3.func_code.co_varnames, ('two', 'arguments')) + if sys.platform.startswith('java'): + self.assertEquals(f4.func_code.co_varnames, + ('two', '(compound, (argument, list))', 'compound', 'argument', + 'list',)) + self.assertEquals(f5.func_code.co_varnames, + ('(compound, first)', 'two', 'compound', 'first')) + else: + self.assertEquals(f4.func_code.co_varnames, + ('two', '.1', 'compound', 'argument', 'list')) + self.assertEquals(f5.func_code.co_varnames, + ('.0', 'two', 'compound', 'first')) + def a1(one_arg,): pass + def a2(two, args,): pass + def v0(*rest): pass + def v1(a, *rest): pass + def v2(a, b, *rest): pass + def v3(a, (b, c), *rest): return a, b, c, rest + + f1() + f2(1) + f2(1,) + f3(1, 2) + f3(1, 2,) + f4(1, (2, (3, 4))) + v0() + v0(1) + v0(1,) + v0(1,2) + v0(1,2,3,4,5,6,7,8,9,0) + v1(1) + v1(1,) + v1(1,2) + v1(1,2,3) + v1(1,2,3,4,5,6,7,8,9,0) + v2(1,2) + v2(1,2,3) + v2(1,2,3,4) + v2(1,2,3,4,5,6,7,8,9,0) + v3(1,(2,3)) + v3(1,(2,3),4) + v3(1,(2,3),4,5,6,7,8,9,0) + + # ceval unpacks the formal arguments into the first argcount names; + # thus, the names nested inside tuples must appear after these names. + if sys.platform.startswith('java'): + self.assertEquals(v3.func_code.co_varnames, ('a', '(b, c)', 'rest', 'b', 'c')) + else: + self.assertEquals(v3.func_code.co_varnames, ('a', '.1', 'rest', 'b', 'c')) + self.assertEquals(v3(1, (2, 3), 4), (1, 2, 3, (4,))) + def d01(a=1): pass + d01() + d01(1) + d01(*(1,)) + d01(**{'a':2}) + def d11(a, b=1): pass + d11(1) + d11(1, 2) + d11(1, **{'b':2}) + def d21(a, b, c=1): pass + d21(1, 2) + d21(1, 2, 3) + d21(*(1, 2, 3)) + d21(1, *(2, 3)) + d21(1, 2, *(3,)) + d21(1, 2, **{'c':3}) + def d02(a=1, b=2): pass + d02() + d02(1) + d02(1, 2) + d02(*(1, 2)) + d02(1, *(2,)) + d02(1, **{'b':2}) + d02(**{'a': 1, 'b': 2}) + def d12(a, b=1, c=2): pass + d12(1) + d12(1, 2) + d12(1, 2, 3) + def d22(a, b, c=1, d=2): pass + d22(1, 2) + d22(1, 2, 3) + d22(1, 2, 3, 4) + def d01v(a=1, *rest): pass + d01v() + d01v(1) + d01v(1, 2) + d01v(*(1, 2, 3, 4)) + d01v(*(1,)) + d01v(**{'a':2}) + def d11v(a, b=1, *rest): pass + d11v(1) + d11v(1, 2) + d11v(1, 2, 3) + def d21v(a, b, c=1, *rest): pass + d21v(1, 2) + d21v(1, 2, 3) + d21v(1, 2, 3, 4) + d21v(*(1, 2, 3, 4)) + d21v(1, 2, **{'c': 3}) + def d02v(a=1, b=2, *rest): pass + d02v() + d02v(1) + d02v(1, 2) + d02v(1, 2, 3) + d02v(1, *(2, 3, 4)) + d02v(**{'a': 1, 'b': 2}) + def d12v(a, b=1, c=2, *rest): pass + d12v(1) + d12v(1, 2) + d12v(1, 2, 3) + d12v(1, 2, 3, 4) + d12v(*(1, 2, 3, 4)) + d12v(1, 2, *(3, 4, 5)) + d12v(1, *(2,), **{'c': 3}) + def d22v(a, b, c=1, d=2, *rest): pass + d22v(1, 2) + d22v(1, 2, 3) + d22v(1, 2, 3, 4) + d22v(1, 2, 3, 4, 5) + d22v(*(1, 2, 3, 4)) + d22v(1, 2, *(3, 4, 5)) + d22v(1, *(2, 3), **{'d': 4}) + def d31v((x)): pass + d31v(1) + def d32v((x,)): pass + d32v((1,)) + # keyword only argument tests + def pos0key1(*, key): return key + pos0key1(key=100) + def pos2key2(p1, p2, *, k1, k2=100): return p1,p2,k1,k2 + pos2key2(1, 2, k1=100) + pos2key2(1, 2, k1=100, k2=200) + pos2key2(1, 2, k2=100, k1=200) + def pos2key2dict(p1, p2, *, k1=100, k2, **kwarg): return p1,p2,k1,k2,kwarg + pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200) + pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100) + + # argument annotation tests + def f(x) -> list: pass + self.assertEquals(f.func_annotations, {'return': list}) + def f(x:int): pass + self.assertEquals(f.func_annotations, {'x': int}) + def f(*x:str): pass + self.assertEquals(f.func_annotations, {'x': str}) + def f(**x:float): pass + self.assertEquals(f.func_annotations, {'x': float}) + def f(x, y:1+2): pass + self.assertEquals(f.func_annotations, {'y': 3}) + def f(a, (b:1, c:2, d)): pass + self.assertEquals(f.func_annotations, {'b': 1, 'c': 2}) + def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6): pass + self.assertEquals(f.func_annotations, + {'b': 1, 'c': 2, 'e': 3, 'g': 6}) + def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6, h:7, i=8, j:9=10, + **k:11) -> 12: pass + self.assertEquals(f.func_annotations, + {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, + 'k': 11, 'return': 12}) + + def testLambdef(self): + ### lambdef: 'lambda' [varargslist] ':' test + l1 = lambda : 0 + self.assertEquals(l1(), 0) + l2 = lambda : a[d] # XXX just testing the expression + l3 = lambda : [2 < x for x in [-1, 3, 0]] + self.assertEquals(l3(), [0, 1, 0]) + l4 = lambda x = lambda y = lambda z=1 : z : y() : x() + self.assertEquals(l4(), 1) + l5 = lambda x, y, z=2: x + y + z + self.assertEquals(l5(1, 2), 5) + self.assertEquals(l5(1, 2, 3), 6) + check_syntax_error(self, "lambda x: x = 2") + l6 = lambda x, y, *, k=20: x+y+k + self.assertEquals(l6(1,2), 1+2+20) + self.assertEquals(l6(1,2,k=10), 1+2+10) + + + ### stmt: simple_stmt | compound_stmt + # Tested below + + def testSimpleStmt(self): + ### simple_stmt: small_stmt (';' small_stmt)* [';'] + x = 1; pass; del x + def foo(): + # verify statments that end with semi-colons + x = 1; pass; del x; + foo() + + ### small_stmt: expr_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt + # Tested below + + def testExprStmt(self): + # (exprlist '=')* exprlist + 1 + 1, 2, 3 + x = 1 + x = 1, 2, 3 + x = y = z = 1, 2, 3 + x, y, z = 1, 2, 3 + abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4) + + check_syntax_error(self, "x + 1 = 1") + check_syntax_error(self, "a + 1 = b + 2") + + def testDelStmt(self): + # 'del' exprlist + abc = [1,2,3] + x, y, z = abc + xyz = x, y, z + + del abc + del x, y, (z, xyz) + + def testPassStmt(self): + # 'pass' + pass + + # flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt + # Tested below + + def testBreakStmt(self): + # 'break' + while 1: break + + def testContinueStmt(self): + # 'continue' + i = 1 + while i: i = 0; continue + + msg = "" + while not msg: + msg = "ok" + try: + continue + msg = "continue failed to continue inside try" + except: + msg = "continue inside try called except block" + if msg != "ok": + self.fail(msg) + + msg = "" + while not msg: + msg = "finally block not called" + try: + continue + finally: + msg = "ok" + if msg != "ok": + self.fail(msg) + + def test_break_continue_loop(self): + # This test warrants an explanation. It is a test specifically for SF bugs + # #463359 and #462937. The bug is that a 'break' statement executed or + # exception raised inside a try/except inside a loop, *after* a continue + # statement has been executed in that loop, will cause the wrong number of + # arguments to be popped off the stack and the instruction pointer reset to + # a very small number (usually 0.) Because of this, the following test + # *must* written as a function, and the tracking vars *must* be function + # arguments with default values. Otherwise, the test will loop and loop. + + def test_inner(extra_burning_oil = 1, count=0): + big_hippo = 2 + while big_hippo: + count += 1 + try: + if extra_burning_oil and big_hippo == 1: + extra_burning_oil -= 1 + break + big_hippo -= 1 + continue + except: + raise + if count > 2 or big_hippo != 1: + self.fail("continue then break in try/except in loop broken!") + test_inner() + + def testReturn(self): + # 'return' [testlist] + def g1(): return + def g2(): return 1 + g1() + x = g2() + check_syntax_error(self, "class foo:return 1") + + def testYield(self): + check_syntax_error(self, "class foo:yield 1") + + def testRaise(self): + # 'raise' test [',' test] + try: raise RuntimeError, 'just testing' + except RuntimeError: pass + try: raise KeyboardInterrupt + except KeyboardInterrupt: pass + + def testImport(self): + # 'import' dotted_as_names + import sys + import time, sys + # 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names) + from time import time + from time import (time) + # not testable inside a function, but already done at top of the module + # from sys import * + from sys import path, argv + from sys import (path, argv) + from sys import (path, argv,) + + def testGlobal(self): + # 'global' NAME (',' NAME)* + global a + global a, b + global one, two, three, four, five, six, seven, eight, nine, ten + + def testAssert(self): + # assert_stmt: 'assert' test [',' test] + assert 1 + assert 1, 1 + assert lambda x:x + assert 1, lambda x:x+1 + try: + assert 0, "msg" + except AssertionError as e: + self.assertEquals(e.args[0], "msg") + else: + if __debug__: + self.fail("AssertionError not raised by assert 0") + + ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef + # Tested below + + def testIf(self): + # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] + if 1: pass + if 1: pass + else: pass + if 0: pass + elif 0: pass + if 0: pass + elif 0: pass + elif 0: pass + elif 0: pass + else: pass + + def testWhile(self): + # 'while' test ':' suite ['else' ':' suite] + while 0: pass + while 0: pass + else: pass + + def testFor(self): + # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] + for i in 1, 2, 3: pass + for i, j, k in (): pass + else: pass + class Squares: + def __init__(self, max): + self.max = max + self.sofar = [] + def __len__(self): return len(self.sofar) + def __getitem__(self, i): + if not 0 <= i < self.max: raise IndexError + n = len(self.sofar) + while n <= i: + self.sofar.append(n*n) + n = n+1 + return self.sofar[i] + n = 0 + for x in Squares(10): n = n+x + if n != 285: + self.fail('for over growing sequence') + + result = [] + for x, in [(1,), (2,), (3,)]: + result.append(x) + self.assertEqual(result, [1, 2, 3]) + + def testTry(self): + ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] + ### | 'try' ':' suite 'finally' ':' suite + ### except_clause: 'except' [expr ['as' expr]] + try: + 1/0 + except ZeroDivisionError: + pass + else: + pass + try: 1/0 + except EOFError: pass + except TypeError as msg: pass + except RuntimeError as msg: pass + except: pass + else: pass + try: 1/0 + except (EOFError, TypeError, ZeroDivisionError): pass + try: 1/0 + except (EOFError, TypeError, ZeroDivisionError) as msg: pass + try: pass + finally: pass + + def testSuite(self): + # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT + if 1: pass + if 1: + pass + if 1: + # + # + # + pass + pass + # + pass + # + + def testTest(self): + ### and_test ('or' and_test)* + ### and_test: not_test ('and' not_test)* + ### not_test: 'not' not_test | comparison + if not 1: pass + if 1 and 1: pass + if 1 or 1: pass + if not not not 1: pass + if not 1 and 1 and 1: pass + if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass + + def testComparison(self): + ### comparison: expr (comp_op expr)* + ### comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not' + if 1: pass + x = (1 == 1) + if 1 == 1: pass + if 1 != 1: pass + if 1 < 1: pass + if 1 > 1: pass + if 1 <= 1: pass + if 1 >= 1: pass + if 1 is 1: pass + if 1 is not 1: pass + if 1 in (): pass + if 1 not in (): pass + if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in 1 is 1 is not 1: pass + + def testBinaryMaskOps(self): + x = 1 & 1 + x = 1 ^ 1 + x = 1 | 1 + + def testShiftOps(self): + x = 1 << 1 + x = 1 >> 1 + x = 1 << 1 >> 1 + + def testAdditiveOps(self): + x = 1 + x = 1 + 1 + x = 1 - 1 - 1 + x = 1 - 1 + 1 - 1 + 1 + + def testMultiplicativeOps(self): + x = 1 * 1 + x = 1 / 1 + x = 1 % 1 + x = 1 / 1 * 1 % 1 + + def testUnaryOps(self): + x = +1 + x = -1 + x = ~1 + x = ~1 ^ 1 & 1 | 1 & 1 ^ -1 + x = -1*1/1 + 1*1 - ---1*1 + + def testSelectors(self): + ### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME + ### subscript: expr | [expr] ':' [expr] + + import sys, time + c = sys.path[0] + x = time.time() + x = sys.modules['time'].time() + a = '01234' + c = a[0] + c = a[-1] + s = a[0:5] + s = a[:5] + s = a[0:] + s = a[:] + s = a[-5:] + s = a[:-1] + s = a[-4:-3] + # A rough test of SF bug 1333982. http://python.org/sf/1333982 + # The testing here is fairly incomplete. + # Test cases should include: commas with 1 and 2 colons + d = {} + d[1] = 1 + d[1,] = 2 + d[1,2] = 3 + d[1,2,3] = 4 + L = list(d) + L.sort(key=lambda x: x if isinstance(x, tuple) else ()) + self.assertEquals(str(L), '[1, (1,), (1, 2), (1, 2, 3)]') + + def testAtoms(self): + ### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictsetmaker] '}' | NAME | NUMBER | STRING + ### dictsetmaker: (test ':' test (',' test ':' test)* [',']) | (test (',' test)* [',']) + + x = (1) + x = (1 or 2 or 3) + x = (1 or 2 or 3, 2, 3) + + x = [] + x = [1] + x = [1 or 2 or 3] + x = [1 or 2 or 3, 2, 3] + x = [] + + x = {} + x = {'one': 1} + x = {'one': 1,} + x = {'one' or 'two': 1 or 2} + x = {'one': 1, 'two': 2} + x = {'one': 1, 'two': 2,} + x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6} + + x = {'one'} + x = {'one', 1,} + x = {'one', 'two', 'three'} + x = {2, 3, 4,} + + x = x + x = 'x' + x = 123 + + ### exprlist: expr (',' expr)* [','] + ### testlist: test (',' test)* [','] + # These have been exercised enough above + + def testClassdef(self): + # 'class' NAME ['(' [testlist] ')'] ':' suite + class B: pass + class B2(): pass + class C1(B): pass + class C2(B): pass + class D(C1, C2, B): pass + class C: + def meth1(self): pass + def meth2(self, arg): pass + def meth3(self, a1, a2): pass + + def testListcomps(self): + # list comprehension tests + nums = [1, 2, 3, 4, 5] + strs = ["Apple", "Banana", "Coconut"] + spcs = [" Apple", " Banana ", "Coco nut "] + + self.assertEqual([s.strip() for s in spcs], ['Apple', 'Banana', 'Coco nut']) + self.assertEqual([3 * x for x in nums], [3, 6, 9, 12, 15]) + self.assertEqual([x for x in nums if x > 2], [3, 4, 5]) + self.assertEqual([(i, s) for i in nums for s in strs], + [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'), + (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'), + (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'), + (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'), + (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')]) + self.assertEqual([(i, s) for i in nums for s in [f for f in strs if "n" in f]], + [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'), + (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'), + (5, 'Banana'), (5, 'Coconut')]) + self.assertEqual([(lambda a:[a**i for i in range(a+1)])(j) for j in range(5)], + [[1], [1, 1], [1, 2, 4], [1, 3, 9, 27], [1, 4, 16, 64, 256]]) + + def test_in_func(l): + return [0 < x < 3 for x in l if x > 2] + + self.assertEqual(test_in_func(nums), [False, False, False]) + + def test_nested_front(): + self.assertEqual([[y for y in [x, x + 1]] for x in [1,3,5]], + [[1, 2], [3, 4], [5, 6]]) + + test_nested_front() + + check_syntax_error(self, "[i, s for i in nums for s in strs]") + check_syntax_error(self, "[x if y]") + + suppliers = [ + (1, "Boeing"), + (2, "Ford"), + (3, "Macdonalds") + ] + + parts = [ + (10, "Airliner"), + (20, "Engine"), + (30, "Cheeseburger") + ] + + suppart = [ + (1, 10), (1, 20), (2, 20), (3, 30) + ] + + x = [ + (sname, pname) + for (sno, sname) in suppliers + for (pno, pname) in parts + for (sp_sno, sp_pno) in suppart + if sno == sp_sno and pno == sp_pno + ] + + self.assertEqual(x, [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'), + ('Macdonalds', 'Cheeseburger')]) + + def testGenexps(self): + # generator expression tests + g = ([x for x in range(10)] for x in range(1)) + self.assertEqual(g.next(), [x for x in range(10)]) + try: + g.next() + self.fail('should produce StopIteration exception') + except StopIteration: + pass + + a = 1 + try: + g = (a for d in a) + g.next() + self.fail('should produce TypeError') + except TypeError: + pass + + self.assertEqual(list((x, y) for x in 'abcd' for y in 'abcd'), [(x, y) for x in 'abcd' for y in 'abcd']) + self.assertEqual(list((x, y) for x in 'ab' for y in 'xy'), [(x, y) for x in 'ab' for y in 'xy']) + + a = [x for x in range(10)] + b = (x for x in (y for y in a)) + self.assertEqual(sum(b), sum([x for x in range(10)])) + + self.assertEqual(sum(x**2 for x in range(10)), sum([x**2 for x in range(10)])) + self.assertEqual(sum(x*x for x in range(10) if x%2), sum([x*x for x in range(10) if x%2])) + self.assertEqual(sum(x for x in (y for y in range(10))), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10)))), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in [y for y in (z for z in range(10))]), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True)) if True), sum([x for x in range(10)])) + self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True) if False) if True), 0) + check_syntax_error(self, "foo(x for x in range(10), 100)") + check_syntax_error(self, "foo(100, x for x in range(10))") + + def testComprehensionSpecials(self): + # test for outmost iterable precomputation + x = 10; g = (i for i in range(x)); x = 5 + self.assertEqual(len(list(g)), 10) + + # This should hold, since we're only precomputing outmost iterable. + x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x)) + x = 5; t = True; + self.assertEqual([(i,j) for i in range(10) for j in range(5)], list(g)) + + # Grammar allows multiple adjacent 'if's in listcomps and genexps, + # even though it's silly. Make sure it works (ifelse broke this.) + self.assertEqual([ x for x in range(10) if x % 2 if x % 3 ], [1, 5, 7]) + self.assertEqual(list(x for x in range(10) if x % 2 if x % 3), [1, 5, 7]) + + # verify unpacking single element tuples in listcomp/genexp. + self.assertEqual([x for x, in [(4,), (5,), (6,)]], [4, 5, 6]) + self.assertEqual(list(x for x, in [(7,), (8,), (9,)]), [7, 8, 9]) + + def testIfElseExpr(self): + # Test ifelse expressions in various cases + def _checkeval(msg, ret): + "helper to check that evaluation of expressions is done correctly" + print(x) + return ret + + self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True]) + self.assertEqual([ x() for x in (lambda: True, lambda: False) if x() ], [True]) + self.assertEqual([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ], [True]) + self.assertEqual((5 if 1 else _checkeval("check 1", 0)), 5) + self.assertEqual((_checkeval("check 2", 0) if 0 else 5), 5) + self.assertEqual((5 and 6 if 0 else 1), 1) + self.assertEqual(((5 and 6) if 0 else 1), 1) + self.assertEqual((5 and (6 if 1 else 1)), 6) + self.assertEqual((0 or _checkeval("check 3", 2) if 0 else 3), 3) + self.assertEqual((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)), 1) + self.assertEqual((0 or 5 if 1 else _checkeval("check 6", 3)), 5) + self.assertEqual((not 5 if 1 else 1), False) + self.assertEqual((not 5 if 0 else 1), 1) + self.assertEqual((6 + 1 if 1 else 2), 7) + self.assertEqual((6 - 1 if 1 else 2), 5) + self.assertEqual((6 * 2 if 1 else 4), 12) + self.assertEqual((6 / 2 if 1 else 3), 3) + self.assertEqual((6 < 4 if 0 else 2), 2) + + +def test_main(): + run_unittest(TokenTests, GrammarTests) + +if __name__ == '__main__': + test_main() Modified: sandbox/trunk/2to3/tests/test_grammar.py ============================================================================== --- sandbox/trunk/2to3/tests/test_grammar.py (original) +++ sandbox/trunk/2to3/tests/test_grammar.py Fri Mar 2 21:43:48 2007 @@ -1,7 +1,11 @@ #!/usr/bin/env python2.5 """ Test suite for Grammar.txt. This is the place to add tests for changes to 2to3's grammar, such as those merging the grammars for -Python 2 and 3. """ +Python 2 and 3. + +In addition to specific tests for parts of the grammar we've changed, +TestGrammarFiles also attempts to process the test_grammar.py files +from Python 2 and Python 3. """ # Author: Collin Winter # Testing imports @@ -17,8 +21,8 @@ from pgen2 import driver from pgen2.parse import ParseError - -grammar_path = os.path.join(os.path.dirname(__file__), "..", "Grammar.txt") +test_dir = os.path.dirname(__file__) +grammar_path = os.path.join(test_dir, "..", "Grammar.txt") grammar = driver.load_grammar(grammar_path) driver = driver.Driver(grammar, convert=pytree.convert) @@ -122,6 +126,16 @@ def test_4(self): self.validate("""x = {2, 3, 4,}""") + + +class TestGrammarFiles(GrammarTest): + def test_python2(self): + f = os.path.join(test_dir, "data", "py2_test_grammar.py") + driver.parse_file(f) + + def test_python3(self): + f = os.path.join(test_dir, "data", "py3_test_grammar.py") + driver.parse_file(f) if __name__ == "__main__": From buildbot at python.org Fri Mar 2 23:32:01 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 22:32:01 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 2.5 Message-ID: <20070302223201.F03B91E4005@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%25202.5/builds/236 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Cary': Sullivan Build Source Stamp: [branch Mitchel] Cortney Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 23:34:33 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 22:34:33 +0000 Subject: [Python-checkins] buildbot failure in sparc solaris10 gcc 2.5 Message-ID: <20070302223433.DB6D81E4005@bag.python.org> The Buildbot has detected a new failure of sparc solaris10 gcc 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/sparc%2520solaris10%2520gcc%25202.5/builds/245 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Dillon': Kerry Build Source Stamp: [branch Andreas] Marcos Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 23:35:02 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 22:35:02 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo 2.5 Message-ID: <20070302223502.45B1A1E4008@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%25202.5/builds/250 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Dallin': Van Build Source Stamp: [branch Zachery] Gianni Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 23:43:07 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 22:43:07 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 2.5 Message-ID: <20070302224307.42B231E4005@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%2520gentoo%25202.5/builds/250 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Jonah': Dashawn Build Source Stamp: [branch Ibrahim] Mikhail Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Fri Mar 2 23:49:12 2007 From: buildbot at python.org (buildbot at python.org) Date: Fri, 02 Mar 2007 22:49:12 +0000 Subject: [Python-checkins] buildbot failure in x86 XP 2.5 Message-ID: <20070302224912.285AF1E4005@bag.python.org> The Buildbot has detected a new failure of x86 XP 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%25202.5/builds/136 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Adam': Colten Build Source Stamp: [branch Jean] Spenser Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Sat Mar 3 01:30:41 2007 From: python-checkins at python.org (eric.smith) Date: Sat, 3 Mar 2007 01:30:41 +0100 (CET) Subject: [Python-checkins] r54100 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070303003041.052AB1E400D@bag.python.org> Author: eric.smith Date: Sat Mar 3 01:30:36 2007 New Revision: 54100 Modified: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added character formatter, code cleanup, added to do items in README. Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Sat Mar 3 01:30:36 2007 @@ -7,7 +7,7 @@ Current developers: Patrick Maupin (pmaupin at gmail.com) - Eric V. Smith + Eric V. Smith (eric at trueblade.com) Pete Shinner The code is only half-baked at present @@ -61,6 +61,10 @@ compatible template systems. - Play with possible options for specifying additional escape syntaxes + - Should we have stricter checking on format strings? For example + type "s" doesn't allow a sign character. Should specifying one + be an error? + - Test suite needs to check for specific exceptions. _flags options to consider adding: Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Sat Mar 3 01:30:36 2007 @@ -89,7 +89,7 @@ self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d") def test_specifiers(self): - self.formatEquals("97_c", "{0:c}", ord("a")) + self.formatEquals("a", "{0:c}", ord("a")) self.formatEquals("8_08b", "{0:08b}", 8) self.formatEquals("8_ >3d", "{0: >3d}", 8) self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) @@ -101,9 +101,11 @@ self.formatEquals("abc", "{0:.3s}", "abcdef") self.formatEquals("resultx", "{0:x<7s}", "result") + self.formatEquals("resultxx", "{0:x<8s}", "result") self.formatEquals("result ", "{0: <7s}", "result") self.formatEquals("result ", "{0:<7s}", "result") self.formatEquals(" result", "{0:>7s}", "result") + self.formatEquals(" result", "{0:>8s}", "result") def test_repr_specifiers(self): self.formatEquals("3", "{0:r}", 3) @@ -113,6 +115,30 @@ self.formatEquals("'abcdefg'", "{0:r}", "abcdefg") self.formatEquals("'abcdefg", "{0:8r}", "abcdefg") + def test_decimal_specifiers(self): + pass +# self.assertRaises(Exception, "{0:d}", "non-number") + +# self.formatEquals("0", "{0:d}", 0) +# self.formatEquals("1" + "0" * 100, "{0:d}", 10**100) + + def test_char_specifiers(self): + self.formatEquals("A", "{0:c}", "A") + self.formatEquals("8", "{0:c}", "8") + self.formatEquals(";", "{0:c}", ";") + self.formatEquals(";", "{0:c}", long(ord(";"))) + + self.formatRaises(TypeError, "{0:c}", "abcd") + + # XXX not sure why this doesn't raise + #self.formatRaises(TypeError, "{0:c}", [1, 2, 3]) + + # XXX not sure why this doesn't raise + #self.formatRaises(TypeError, "{0:c}", 1j) + + # XXX this should raise, but instead gives a DeprecationWarning + #self.formatRaises(TypeError, "{0:c}", 3.14) + def test_missing_type_specifier(self): # make sure floats use 'g', ints and longs 'd', and everything else 's' pass Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Sat Mar 3 01:30:36 2007 @@ -1,5 +1,3 @@ -#define DUMMY_FORMATTING 1 - /* unicodeformat.c -- implementation of PEP 3101 @@ -26,6 +24,7 @@ #define CH_TYPE Py_UNICODE #define CH_TYPE_ISDECIMAL Py_UNICODE_ISDECIMAL #define CH_TYPE_TODECIMAL Py_UNICODE_TODECIMAL +#define CH_TYPE_FILL Py_UNICODE_FILL #define STROBJ_AS_PTR PyUnicode_AS_UNICODE #define STROBJ_GET_SIZE PyUnicode_GET_SIZE #define STROBJ_NEW PyUnicode_FromUnicode @@ -37,6 +36,7 @@ #define CH_TYPE char #define CH_TYPE_ISDECIMAL(x) ((x >= '0') && (x <= '9')) #define CH_TYPE_TODECIMAL(x) (CH_TYPE_ISDECIMAL(x) ? (x - '0') : -1) +#define CH_TYPE_FILL Py_UNICODE_FILL #define STROBJ_AS_PTR PyString_AS_STRING #define STROBJ_GET_SIZE PyString_GET_SIZE #define STROBJ_NEW PyString_FromStringAndSize @@ -67,6 +67,19 @@ /*********** Global data structures and forward declarations *********/ /************************************************************************/ +/* FORMATBUFLEN is taken from stringobject.c, it should probably be + factored out */ +/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) + + FORMATBUFLEN is the length of the buffer in which the floats, ints, & + chars are formatted. XXX This is a magic number. Each formatting + routine does bounds checking to ensure no overflow, but a better + solution may be to malloc a buffer of appropriate size for each + format. For now, the current solution is sufficient. +*/ +#define FORMATBUFLEN (size_t)120 + + #ifdef __cplusplus extern "C" { #endif @@ -161,17 +174,6 @@ /************************** Utility functions ************************/ /************************************************************************/ -/* XXX probably a better way to do this. can't use memset, though, - because of Unicode and char* support */ -Py_LOCAL_INLINE(void) -charset(CH_TYPE* dst, CH_TYPE ch, Py_ssize_t len) -{ - CH_TYPE* end = dst + len; - for(; dst < end; dst++) { - *dst = ch; - } -} - /************************************************************************/ /*********** Error handling and exception generation **************/ @@ -748,7 +750,8 @@ /* used to output simple strings, only supports a maximum width and total field alignment */ static int -output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len, const InternalFormatSpec *format) +output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len, + const InternalFormatSpec *format) { Py_ssize_t ok; Py_ssize_t width; /* total field width */ @@ -762,6 +765,10 @@ if (format->width >= 0) { width = format->width; + /* don't write out more than width characters */ + if (len > width) { + len = width; + } } else { /* not specified, use all of the chars and no more */ width = len; @@ -773,11 +780,13 @@ /* now write into that space */ - /* if right aligning, increment the destination allow space on the left */ - memcpy(dst + (format->align == '>' ? (width - len) : 0), src, len * sizeof(CH_TYPE)); + /* if right aligning, increment the destination allow space on the + left */ + memcpy(dst + (format->align == '>' ? (width - len) : 0), src, + len * sizeof(CH_TYPE)); /* do any padding */ - if (len != width) { + if (width > len) { CH_TYPE fill_char = format->fill_char; if (fill_char == '\0') { /* use the default, if not specified */ @@ -786,27 +795,19 @@ if (format->align == '>') { /* right align, pad on left */ - charset(dst, fill_char, width - len); + CH_TYPE_FILL(dst, fill_char, width - len); } else { /* left align, pad on right */ - charset(dst + len, fill_char, width - len); + CH_TYPE_FILL(dst + len, fill_char, width - len); } } return 1; } -/* - Our internal conversion functions have this signature. - - returns 0 on failure, else 1 -*/ -typedef int -(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format); - /* XXX delete this when all internal conversions are implemented */ static int -convert_DUMMY(PyObject *fieldobj, FmtState *fs) +format_DUMMY(PyObject *fieldobj, FmtState *fs) { PyObject *myobj; int ok; @@ -832,23 +833,70 @@ return 1; } -/* conversion functions */ +/************************************************************************/ +/************************* Builtin formatters *************************/ +/************************************************************************/ + +/* + return 0 on failure, else 1 +*/ +typedef int +(*FormatFunction)(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format); + static int -convert_binary(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_binary(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_char(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_char(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + int ok; + CH_TYPE buf; + + if (PyString_Check(fieldobj)) { + if (!PyArg_Parse(fieldobj, "c;%c requires int or char", &buf)) + return 0; + } + else { + if (!PyArg_Parse(fieldobj, "b;%c requires int or char", &buf)) + return -1; + } + + return output_data(fs, &buf, 1); } static int -convert_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_decimal(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); +#if 0 + if (PyLong_Check(fieldobj)) { + int ilen; + temp = _PyString_FormatLong(v, flags, + prec, c, &pbuf, &ilen); + len = ilen; + if (!temp) + goto error; + sign = 1; + } + else { + pbuf = formatbuf; + len = formatint(pbuf, + sizeof(formatbuf), + flags, prec, c, v); + if (len < 0) + goto error; + sign = 1; + } + if (flags & F_ZERO) + fill = '0'; +#endif + return format_DUMMY(fieldobj, fs); #if 0 PyObject *intobj; PyObject *strobj; @@ -884,55 +932,64 @@ } static int -convert_exponent(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_exponent(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_exponentUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_exponentUC(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_fixed(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_fixed(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_fixedUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_fixedUC(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_general(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_general(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_generalUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_generalUC(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_number(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_octal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_octal(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_repr(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_repr(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { int ok; PyObject *strobj; @@ -945,14 +1002,16 @@ strobj = STROBJ_STR(reprobj); Py_DECREF(reprobj); - ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format); + ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), + STROBJ_GET_SIZE(strobj), format); Py_DECREF(strobj); return ok; } static int -convert_string(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_string(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { PyObject *strobj; int ok; @@ -961,54 +1020,58 @@ if (strobj == NULL) return 0; - ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format); + ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), + STROBJ_GET_SIZE(strobj), format); Py_DECREF(strobj); return ok; } static int -convert_hex(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_hex(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_hexUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_hexUC(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } static int -convert_percentage(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) +format_percentage(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { - return convert_DUMMY(fieldobj, fs); + return format_DUMMY(fieldobj, fs); } /* returns a pointer to our conversion function, or NULL if invalid */ -Py_LOCAL_INLINE(ConversionFunction) -conversion_function(CH_TYPE c) +Py_LOCAL_INLINE(FormatFunction) +format_function(CH_TYPE c) { switch (c) { - case 'b': return convert_binary; /* base-2 */ - case 'c': return convert_char; /* as character */ - case 'd': return convert_decimal; /* decimal integer */ - case 'e': return convert_exponent; /* exponential notation */ - case 'E': return convert_exponentUC; /* exponential notation - with uppercase 'E' */ - case 'f': return convert_fixed; /* fixed-point */ - case 'F': return convert_fixedUC; /* fixed-point with uppercase */ - case 'g': return convert_general; /* general number notation */ - case 'G': return convert_generalUC; /* general number notation - with uppercase 'E' */ - case 'n': return convert_number; /* number in locale-specific - format */ - case 'o': return convert_octal; /* octal */ - case 'r': return convert_repr; /* in repr() format */ - case 's': return convert_string; /* convert using str() */ - case 'x': return convert_hex; /* base 16 */ - case 'X': return convert_hexUC; /* base 16 uppercase */ - case '%': return convert_percentage; /* as percentage */ + case 'b': return format_binary; /* base-2 */ + case 'c': return format_char; /* as character */ + case 'd': return format_decimal; /* decimal integer */ + case 'e': return format_exponent; /* exponential notation */ + case 'E': return format_exponentUC; /* exponential notation + with uppercase 'E' */ + case 'f': return format_fixed; /* fixed-point */ + case 'F': return format_fixedUC; /* fixed-point with uppercase */ + case 'g': return format_general; /* general number notation */ + case 'G': return format_generalUC; /* general number notation + with uppercase 'E' */ + case 'n': return format_number; /* number in locale-specific + format */ + case 'o': return format_octal; /* octal */ + case 'r': return format_repr; /* in repr() format */ + case 's': return format_string; /* convert using str() */ + case 'x': return format_hex; /* base 16 */ + case 'X': return format_hexUC; /* base 16 uppercase */ + case '%': return format_percentage; /* as percentage */ default: return NULL; } @@ -1020,7 +1083,7 @@ static int internal_render(FmtState *fs, PyObject *fieldobj) { InternalFormatSpec format; - ConversionFunction conversion; + FormatFunction formatter; if (!parse_internal_render_format_spec(fs, &format)) { return 0; @@ -1038,18 +1101,18 @@ } } - /* XXX handle conversion functions that logically map to - other conversion functions? percent is the only one, and I'm not wild + /* XXX handle conversion functions that logically map to other + conversion functions? percent is the only one, and I'm not wild about having percent at all*/ - conversion = conversion_function(format.type); - if (conversion == NULL) { + formatter = format_function(format.type); + if (formatter == NULL) { SetError(fs, "Invalid conversion character"); return 0; } - /* do the conversion, writing into the output string */ - return conversion(fieldobj, fs, &format); + /* do the formatting, writing into the output string */ + return formatter(fieldobj, fs, &format); #if 0 From python-checkins at python.org Sat Mar 3 01:42:19 2007 From: python-checkins at python.org (brett.cannon) Date: Sat, 3 Mar 2007 01:42:19 +0100 (CET) Subject: [Python-checkins] r54101 - peps/trunk/pep-0000.txt peps/trunk/pep-3113.txt Message-ID: <20070303004219.127141E4010@bag.python.org> Author: brett.cannon Date: Sat Mar 3 01:42:09 2007 New Revision: 54101 Added: peps/trunk/pep-3113.txt (contents, props changed) Modified: peps/trunk/pep-0000.txt Log: Add PEP 3113 (Removal of Tuple Parameter Unpacking). Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sat Mar 3 01:42:09 2007 @@ -114,6 +114,8 @@ S 3101 Advanced String Formatting Talin S 3104 Access to Names in Outer Scopes Yee I 3108 Standard Library Reorganization Cannon + S 3113 Removal of Tuple Parameter Unpacking Cannon + Finished PEPs (done, implemented in Subversion) @@ -457,6 +459,8 @@ SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge SA 3112 Bytes literals in Python 3000 Orendorff + S 3113 Removal of Tuple Parameter Unpacking Cannon + Key Added: peps/trunk/pep-3113.txt ============================================================================== --- (empty file) +++ peps/trunk/pep-3113.txt Sat Mar 3 01:42:09 2007 @@ -0,0 +1,266 @@ +PEP: 3113 +Title: Removal of Tuple Parameter Unpacking +Version: $Revision$ +Last-Modified: $Date$ +Author: Brett Cannon +Status: Draft +Type: Standards Track +Python-version: 3.0 +Content-Type: text/x-rst +Created: XX-XXX-XXXX + + +Abstract +======== + +Tuple parameter unpacking is the use of a tuple as a parameter in a +function signature so as to have a sequence argument automatically +unpacked. An example is:: + + def fxn(a, (b, c), d): + pass + +The use of ``(b, c)`` in the signature requires that the second +argument to the function be a sequence of length two (e.g., +``[42, -13]``). When such a sequence is passed it is unpacked and +has its values assigned to the parameters, just as if the statement +``b, c = [42, -13]`` had been executed in the parameter. + +Unfortunately this feature of Python's rich function signature +abilities, while handy in some situations, causes more issues than +they are worth. Thus this PEP proposes their removal from the +language in Python 3.0. + + +Why They Should Go +================== + +Introspection Issues +-------------------- + +Python has very powerful introspection capabilities. These extend to +function signatures. There are no hidden details as to what a +function's call signature is. In general it is fairly easy to figure +out various details about a function's signature by viewing the +function object and various attributes on it (including the function's +``func_code`` attribute). + +But there is great difficulty when it comes to tuple parameters. The +existence of a tuple parameter is denoted by it name being made of a +``.`` and a number in the ``co_varnames`` attribute of the function's +code object. This allows the tuple argument to be bound to a name +that only the bytecode is aware of and cannot be typed in Python +source. But this does not specify the format of the tuple: its +length, whether there are nested tuples, etc. + +In order to get all of the details about the tuple from the function +one must analyse the bytecode of the function. This is because the +first bytecode in the function literally translates into the tuple +argument being unpacked. Assuming the tuple parameter is +named ``.1`` and is expected to unpack to variables ``spam`` and +``monty`` (meaning it is the tuple ``(spam, monty)``), the first +bytecode in the function will be for the statement +``spam, monty = .1``. This means that to know all of the details of +the tuple parameter one must look at the initial bytecode of the +function to detect tuple unpacking for parameters formatted as +``\.\d+`` and deduce any and all information about the expected +argument. Bytecode analysis is how the ``inspect.getargspec`` +function is able to provide information on tuple parameters. This is +not easy to do and is burdensome on introspection tools as they must +know how Python bytecode works (an otherwise unneeded burden as all +other types of parameters do not require knowledge of Python +bytecode). + +The difficulty of analysing bytecode not withstanding, there is +another issue with the dependency on using Python bytecode. +IronPython [#ironpython]_ does not use Python's bytecode. Because it +is based on the .NET framework it instead stores MSIL [#MSIL]_ in +``func_code.co_code`` attribute of the function. This fact prevents +the ``inspect.getargspec`` function from working when run under +IronPython. It is unknown whether other Python implementations are +affected but is reasonable to assume if the implementation is not just +a re-implementation of the Python virtual machine. + + +No Loss of Abilities If Removed +------------------------------- + +As mentioned in `Introspection Issues`_, to handle tuple parameters +the function's bytecode starts with the bytecode required to unpack +the argument into the proper parameter names. This means that their +is no special support required to implement tuple parameters and thus +there is no loss of abilities if they were to be removed, only a +possible convenience (which is addressed in +`Why They Should (Supposedly) Stay`_). + +The example function at the beginning of this PEP could easily be +rewritten as:: + + def fxn(a, b_c, d): + b, c = b_c + pass + +and in no way lose functionality. + + +Exception To The Rule +--------------------- + +When looking at the various types of parameters that a Python function +can have, one will notice that tuple parameters tend to be an +exception rather than the rule. + +Consider PEP 3102 (keyword-only arguments) and PEP 3107 (function +annotations) [#pep-3102]_ [#pep-3107]_. Both PEPs have been accepted and +introduce new functionality within a function's signature. And yet +for both PEPs the new feature cannot be applied to tuple parameters. +This leads to tuple parameters not being on the same footing as +regular parameters that do not attempt to unpack their arguments. + +The existence of tuple parameters also places sequence objects +separately from mapping objects in a function signature. There is no +way to pass in a mapping object (e.g., a dict) as a parameter and have +it unpack in the same fashion as a sequence does into a tuple +parameter. + + +Uninformative Error Messages +---------------------------- + +Consider the following function:: + + def fxn((a, b), (c, d)): + pass + +If called as ``fxn(1, (2, 3))`` one is given the error message +``TypeError: unpack non-sequence``. This error message in no way +tells you which tuple was not unpacked properly. There is also no +indication that this was a result that occurred because of the +arguments. Other error messages regarding arguments to functions +explicitly state its relation to the signature: +``TypeError: fxn() takes exactly 2 arguments (0 given)``, etc. + + +Little Usage +------------ + +While an informal poll of the handful of Python programmers I know +personally and from the PyCon 2007 sprint indicates a huge majority of +people do not know of this feature and the rest just do not use it, +some hard numbers is needed to back up the claim that the feature is +not heavily used. + +Iterating over every line in Python's code repository in the ``Lib/`` +directory using the regular expression ``^\s*def\s*\w+\s*\(`` to +detect function and method definitions there were 22,252 matches in +the trunk. + +Tacking on ``.*,\s*\(`` to find ``def`` statements that contained a +tuple parameter, only 41 matches were found. This means that for +``def`` statements, only 0.18% of them seem to use a tuple parameter. + + +Why They Should (Supposedly) Stay +================================= + +Practical Use +------------- + +In certain instances tuple parameters can be useful. A common example +is code that expect a two-item tuple that reperesents a Cartesian +point. While true it is nice to be able to have the unpacking of the +x and y coordinates for you, the argument is that this small amount of +practical usefulness is heavily outweighed by other issues pertaining +to tuple parameters. And as shown in +`No Loss Of Abilities If Removed`_, their use is purely practical and +in no way provide a unique ability that cannot be handled in other +ways very easily. + + +Self-Documentation For Parameters +--------------------------------- + +It has been argued that tuple parameters provide a way of +self-documentation for parameters that are expected to be of a certain +sequence format. Using our Cartesian point example from +`Practical Use`_, seeing ``(x, y)`` as a parameter in a function makes +it obvious that a tuple of length two is expected as an argument for +that parameter. + +But Python provides several other ways to document what parameters are +for. Documentation strings are meant to provide enough information +needed to explain what arguments are expected. Tuple parameters might +tell you the expected length of a sequence argument, it does not tell +you what that data will be used for. One must also read the docstring +to know what other arguments are expected if not all parameters are +tuple parameters. + +Function annotations (which do not work with tuple parameters) can +also supply documentation. Because annotations can be of any form, +what was once a tuple parameter can be a single argument parameter +with an annotation of ``tuple``, ``tuple(2)``, ``Cartesian point``, +``(x, y)``, etc. Annotations provide great flexibility for +documenting what an argument is expected to be for a parameter, +including being a sequence of a certain length. + + +Transition Plan +=============== + +To transition Python 2.x code to 3.x where tuple parameters are +removed, two steps are suggested. First, the proper warning is to be +emitted when Python's compiler comes across a tuple parameter in +Python 2.6. This will be treated like any other syntactic change that +is to occur in Python 3.0 compared to Python 2.6. + +Second, the 2to3 refactoring tool [#2to3]_ will gain a rule for +translating tuple parameters to being a single parameter this is +unpacked as the first statement in the function. The name of the new +parameter will be a mangling of tuple parameter's names by joining +them with underscores. The new parameter will then be unpacked into +the names originally used in the tuple parameter. This means that the +following function:: + + def fxn((a, (b, c))): + pass + +will be translated into:: + + def fxn(a_b_c): + (a, (b, c)) = a_b_c + pass + + +References +========== + +.. [#2to3] 2to3 refactoring tool + (http://svn.python.org/view/sandbox/trunk/2to3/) + +.. [#ironpython] IronPython + (http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython) + +.. [#MSIL] Microsoft Intermediate Language + (http://msdn.microsoft.com/library/en-us/cpguide/html/cpconmicrosoftintermediatelanguagemsil.asp?frame=true) + +.. [#pep-3102] PEP 3102 (Keyword-Only Arguments) + (http://www.python.org/dev/peps/pep-3102/) + +.. [#pep-3107] PEP 3107 (Function Annotations) + (http://www.python.org/dev/peps/pep-3107/) + +Copyright +========= + +This document has been placed in the public domain. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: From python-checkins at python.org Sat Mar 3 01:46:17 2007 From: python-checkins at python.org (brett.cannon) Date: Sat, 3 Mar 2007 01:46:17 +0100 (CET) Subject: [Python-checkins] r54102 - peps/trunk/pep-3113.txt Message-ID: <20070303004617.42D221E4008@bag.python.org> Author: brett.cannon Date: Sat Mar 3 01:46:14 2007 New Revision: 54102 Modified: peps/trunk/pep-3113.txt Log: Fix the creation date. Modified: peps/trunk/pep-3113.txt ============================================================================== --- peps/trunk/pep-3113.txt (original) +++ peps/trunk/pep-3113.txt Sat Mar 3 01:46:14 2007 @@ -7,7 +7,7 @@ Type: Standards Track Python-version: 3.0 Content-Type: text/x-rst -Created: XX-XXX-XXXX +Created: 02-Mar-2007 Abstract From python-checkins at python.org Sat Mar 3 04:27:44 2007 From: python-checkins at python.org (collin.winter) Date: Sat, 3 Mar 2007 04:27:44 +0100 (CET) Subject: [Python-checkins] r54103 - sandbox/trunk/2to3/fixes/fix_except.py Message-ID: <20070303032744.093941E4008@bag.python.org> Author: collin.winter Date: Sat Mar 3 04:27:43 2007 New Revision: 54103 Modified: sandbox/trunk/2to3/fixes/fix_except.py Log: Style cleanup. Modified: sandbox/trunk/2to3/fixes/fix_except.py ============================================================================== --- sandbox/trunk/2to3/fixes/fix_except.py (original) +++ sandbox/trunk/2to3/fixes/fix_except.py Sat Mar 3 04:27:43 2007 @@ -30,8 +30,7 @@ from fixes.macros import Assign, Attr, Name def find_excepts(nodes): - for i in range(len(nodes)): - n = nodes[i] + for i, n in enumerate(nodes): if isinstance(n, pytree.Node): if n.children[0].value == 'except': yield (n, nodes[i+2]) From python-checkins at python.org Sat Mar 3 05:03:51 2007 From: python-checkins at python.org (collin.winter) Date: Sat, 3 Mar 2007 05:03:51 +0100 (CET) Subject: [Python-checkins] r54104 - in sandbox/trunk/2to3: fixes/fix_arg_tuples.py tests/test_fixers.py Message-ID: <20070303040351.1BB001E4008@bag.python.org> Author: collin.winter Date: Sat Mar 3 05:03:44 2007 New Revision: 54104 Added: sandbox/trunk/2to3/fixes/fix_arg_tuples.py (contents, props changed) Modified: sandbox/trunk/2to3/tests/test_fixers.py Log: Add a fixer for tuple parameters (per PEP 3113). Added: sandbox/trunk/2to3/fixes/fix_arg_tuples.py ============================================================================== --- (empty file) +++ sandbox/trunk/2to3/fixes/fix_arg_tuples.py Sat Mar 3 05:03:44 2007 @@ -0,0 +1,86 @@ +"""Fixer for function definitions with tuple parameters. + +def func(((a, b), c), d): + ... + + -> + +def func(x, d): + ((a, b), c) = x + ... +""" +# Author: Collin Winter + +# Local imports +import pytree +from pgen2 import token +from fixes import basefix +from fixes.macros import Assign, Name, Newline + +def is_docstring(stmt): + return isinstance(stmt, pytree.Node) and \ + stmt.children[0].type == token.STRING + +class FixArgTuples(basefix.BaseFix): + PATTERN = """funcdef< 'def' any parameters< '(' args=any ')' > + ['->' any] ':' suite=any+ >""" + + def transform(self, node): + syms = self.syms + results = self.match(node) + assert results + + new_lines = [] + suite = results["suite"] + args = results["args"] + # This crap is so "def foo(...): x = 5; y = 7" is handled correctly. + if suite[0].children[1].type == token.INDENT: + start = 2 + indent = suite[0].children[1].value + end = Newline() + else: + start = 0 + indent = "; " + end = pytree.Leaf(token.INDENT, "") + + # We need access to self for new_name(), and making this a method + # doesn't feel right. Closing over self and new_lines makes the + # code below cleaner. + def handle_tuple(tuple_arg, add_prefix=False): + n = Name(self.new_name()) + arg = tuple_arg.clone() + arg.set_prefix("") + stmt = Assign(arg, n.clone()) + if add_prefix: + n.set_prefix(" ") + tuple_arg.replace(n) + new_lines.append(pytree.Node(syms.simple_stmt, [stmt, end.clone()])) + + if args.type == syms.tfpdef: + handle_tuple(args) + elif args.type == syms.typedargslist: + for i, arg in enumerate(args.children): + if arg.type == syms.tfpdef: + # Without add_prefix, the emitted code is correct, + # just ugly. + handle_tuple(arg, add_prefix=(i > 0)) + + if not new_lines: + return node + + # This isn't strictly necessary, but it plays nicely with other fixers. + for line in new_lines: + line.parent = suite[0] + + after = start + if start == 0: + new_lines[0].set_prefix(" ") + elif is_docstring(suite[0].children[start]): + new_lines[0].set_prefix(indent) + after = start + 1 + + children = list(suite[0].children) + children[after:after] = new_lines + for i in range(after+1, after+len(new_lines)+1): + children[i].set_prefix(indent) + suite[0].children = tuple(children) Modified: sandbox/trunk/2to3/tests/test_fixers.py ============================================================================== --- sandbox/trunk/2to3/tests/test_fixers.py (original) +++ sandbox/trunk/2to3/tests/test_fixers.py Sat Mar 3 05:03:44 2007 @@ -496,8 +496,8 @@ def foo(): try: pass - except Exception as xxx_todo_changeme: - (f, e) = xxx_todo_changeme.message + except Exception as xxx_todo_changeme12: + (f, e) = xxx_todo_changeme12.message pass except ImportError as e: pass""" @@ -527,8 +527,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme1: - (a, b) = xxx_todo_changeme1.message + except Exception as xxx_todo_changeme13: + (a, b) = xxx_todo_changeme13.message pass""" self.check(b, a) @@ -542,8 +542,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme2: - d[5] = xxx_todo_changeme2 + except Exception as xxx_todo_changeme14: + d[5] = xxx_todo_changeme14 pass""" self.check(b, a) @@ -557,8 +557,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme3: - a.foo = xxx_todo_changeme3 + except Exception as xxx_todo_changeme15: + a.foo = xxx_todo_changeme15 pass""" self.check(b, a) @@ -572,8 +572,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme4: - a().foo = xxx_todo_changeme4 + except Exception as xxx_todo_changeme16: + a().foo = xxx_todo_changeme16 pass""" self.check(b, a) @@ -1111,6 +1111,123 @@ b = """x = input('prompt')""" a = """x = eval(input('prompt'))""" self.check(b, a) + + +class Test_arg_tuples(FixerTestCase): + fixer = "arg_tuples" + + def test_unchanged_1(self): + s = """def foo(): pass""" + self.check(s, s) + + def test_unchanged_2(self): + s = """def foo(a, b, c): pass""" + self.check(s, s) + + def test_unchanged_3(self): + s = """def foo(a=3, b=4, c=5): pass""" + self.check(s, s) + + def test_1(self): + b = """ + def foo(((a, b), c)): + x = 5""" + + a = """ + def foo(xxx_todo_changeme): + ((a, b), c) = xxx_todo_changeme + x = 5""" + self.check(b, a) + + def test_2(self): + b = """ + def foo(((a, b), c), d): + x = 5""" + + a = """ + def foo(xxx_todo_changeme1, d): + ((a, b), c) = xxx_todo_changeme1 + x = 5""" + self.check(b, a) + + def test_3(self): + b = """ + def foo(((a, b), c), d) -> e: + x = 5""" + + a = """ + def foo(xxx_todo_changeme2, d) -> e: + ((a, b), c) = xxx_todo_changeme2 + x = 5""" + self.check(b, a) + + def test_semicolon(self): + b = """ + def foo(((a, b), c)): x = 5; y = 7""" + + a = """ + def foo(xxx_todo_changeme10): ((a, b), c) = xxx_todo_changeme10; x = 5; y = 7""" + self.check(b, a) + + def test_keywords(self): + b = """ + def foo(((a, b), c), d, e=5) -> z: + x = 5""" + + a = """ + def foo(xxx_todo_changeme5, d, e=5) -> z: + ((a, b), c) = xxx_todo_changeme5 + x = 5""" + self.check(b, a) + + def test_varargs(self): + b = """ + def foo(((a, b), c), d, *vargs, **kwargs) -> z: + x = 5""" + + a = """ + def foo(xxx_todo_changeme11, d, *vargs, **kwargs) -> z: + ((a, b), c) = xxx_todo_changeme11 + x = 5""" + self.check(b, a) + + def test_multi_1(self): + b = """ + def foo(((a, b), c), (d, e, f)) -> z: + x = 5""" + + a = """ + def foo(xxx_todo_changeme6, xxx_todo_changeme7) -> z: + ((a, b), c) = xxx_todo_changeme6 + (d, e, f) = xxx_todo_changeme7 + x = 5""" + self.check(b, a) + + def test_multi_2(self): + b = """ + def foo(x, ((a, b), c), d, (e, f, g), y) -> z: + x = 5""" + + a = """ + def foo(x, xxx_todo_changeme8, d, xxx_todo_changeme9, y) -> z: + ((a, b), c) = xxx_todo_changeme8 + (e, f, g) = xxx_todo_changeme9 + x = 5""" + self.check(b, a) + + def test_docstring(self): + b = """ + def foo(((a, b), c), (d, e, f)) -> z: + "foo foo foo foo" + x = 5""" + + a = """ + def foo(xxx_todo_changeme3, xxx_todo_changeme4) -> z: + "foo foo foo foo" + ((a, b), c) = xxx_todo_changeme3 + (d, e, f) = xxx_todo_changeme4 + x = 5""" + self.check(b, a) if __name__ == "__main__": From python-checkins at python.org Sat Mar 3 05:19:14 2007 From: python-checkins at python.org (collin.winter) Date: Sat, 3 Mar 2007 05:19:14 +0100 (CET) Subject: [Python-checkins] r54105 - in sandbox/trunk/2to3: fixes/fix_arg_tuples.py fixes/fix_tuple_params.py tests/test_fixers.py Message-ID: <20070303041914.EA11D1E4008@bag.python.org> Author: collin.winter Date: Sat Mar 3 05:19:11 2007 New Revision: 54105 Added: sandbox/trunk/2to3/fixes/fix_tuple_params.py - copied, changed from r54104, sandbox/trunk/2to3/fixes/fix_arg_tuples.py Removed: sandbox/trunk/2to3/fixes/fix_arg_tuples.py Modified: sandbox/trunk/2to3/tests/test_fixers.py Log: Rename tests/fix_arg_tuples.py; they're called tuple parameters, not tuple arguments. Deleted: /sandbox/trunk/2to3/fixes/fix_arg_tuples.py ============================================================================== --- /sandbox/trunk/2to3/fixes/fix_arg_tuples.py Sat Mar 3 05:19:11 2007 +++ (empty file) @@ -1,86 +0,0 @@ -"""Fixer for function definitions with tuple parameters. - -def func(((a, b), c), d): - ... - - -> - -def func(x, d): - ((a, b), c) = x - ... -""" -# Author: Collin Winter - -# Local imports -import pytree -from pgen2 import token -from fixes import basefix -from fixes.macros import Assign, Name, Newline - -def is_docstring(stmt): - return isinstance(stmt, pytree.Node) and \ - stmt.children[0].type == token.STRING - -class FixArgTuples(basefix.BaseFix): - PATTERN = """funcdef< 'def' any parameters< '(' args=any ')' > - ['->' any] ':' suite=any+ >""" - - def transform(self, node): - syms = self.syms - results = self.match(node) - assert results - - new_lines = [] - suite = results["suite"] - args = results["args"] - # This crap is so "def foo(...): x = 5; y = 7" is handled correctly. - if suite[0].children[1].type == token.INDENT: - start = 2 - indent = suite[0].children[1].value - end = Newline() - else: - start = 0 - indent = "; " - end = pytree.Leaf(token.INDENT, "") - - # We need access to self for new_name(), and making this a method - # doesn't feel right. Closing over self and new_lines makes the - # code below cleaner. - def handle_tuple(tuple_arg, add_prefix=False): - n = Name(self.new_name()) - arg = tuple_arg.clone() - arg.set_prefix("") - stmt = Assign(arg, n.clone()) - if add_prefix: - n.set_prefix(" ") - tuple_arg.replace(n) - new_lines.append(pytree.Node(syms.simple_stmt, [stmt, end.clone()])) - - if args.type == syms.tfpdef: - handle_tuple(args) - elif args.type == syms.typedargslist: - for i, arg in enumerate(args.children): - if arg.type == syms.tfpdef: - # Without add_prefix, the emitted code is correct, - # just ugly. - handle_tuple(arg, add_prefix=(i > 0)) - - if not new_lines: - return node - - # This isn't strictly necessary, but it plays nicely with other fixers. - for line in new_lines: - line.parent = suite[0] - - after = start - if start == 0: - new_lines[0].set_prefix(" ") - elif is_docstring(suite[0].children[start]): - new_lines[0].set_prefix(indent) - after = start + 1 - - children = list(suite[0].children) - children[after:after] = new_lines - for i in range(after+1, after+len(new_lines)+1): - children[i].set_prefix(indent) - suite[0].children = tuple(children) Copied: sandbox/trunk/2to3/fixes/fix_tuple_params.py (from r54104, sandbox/trunk/2to3/fixes/fix_arg_tuples.py) ============================================================================== --- sandbox/trunk/2to3/fixes/fix_arg_tuples.py (original) +++ sandbox/trunk/2to3/fixes/fix_tuple_params.py Sat Mar 3 05:19:11 2007 @@ -21,7 +21,7 @@ return isinstance(stmt, pytree.Node) and \ stmt.children[0].type == token.STRING -class FixArgTuples(basefix.BaseFix): +class FixTupleParams(basefix.BaseFix): PATTERN = """funcdef< 'def' any parameters< '(' args=any ')' > ['->' any] ':' suite=any+ >""" Modified: sandbox/trunk/2to3/tests/test_fixers.py ============================================================================== --- sandbox/trunk/2to3/tests/test_fixers.py (original) +++ sandbox/trunk/2to3/tests/test_fixers.py Sat Mar 3 05:19:11 2007 @@ -496,8 +496,8 @@ def foo(): try: pass - except Exception as xxx_todo_changeme12: - (f, e) = xxx_todo_changeme12.message + except Exception as xxx_todo_changeme: + (f, e) = xxx_todo_changeme.message pass except ImportError as e: pass""" @@ -527,8 +527,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme13: - (a, b) = xxx_todo_changeme13.message + except Exception as xxx_todo_changeme1: + (a, b) = xxx_todo_changeme1.message pass""" self.check(b, a) @@ -542,8 +542,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme14: - d[5] = xxx_todo_changeme14 + except Exception as xxx_todo_changeme2: + d[5] = xxx_todo_changeme2 pass""" self.check(b, a) @@ -557,8 +557,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme15: - a.foo = xxx_todo_changeme15 + except Exception as xxx_todo_changeme3: + a.foo = xxx_todo_changeme3 pass""" self.check(b, a) @@ -572,8 +572,8 @@ a = """ try: pass - except Exception as xxx_todo_changeme16: - a().foo = xxx_todo_changeme16 + except Exception as xxx_todo_changeme4: + a().foo = xxx_todo_changeme4 pass""" self.check(b, a) @@ -1113,8 +1113,8 @@ self.check(b, a) -class Test_arg_tuples(FixerTestCase): - fixer = "arg_tuples" +class Test_tuple_params(FixerTestCase): + fixer = "tuple_params" def test_unchanged_1(self): s = """def foo(): pass""" @@ -1134,8 +1134,8 @@ x = 5""" a = """ - def foo(xxx_todo_changeme): - ((a, b), c) = xxx_todo_changeme + def foo(xxx_todo_changeme5): + ((a, b), c) = xxx_todo_changeme5 x = 5""" self.check(b, a) @@ -1145,8 +1145,8 @@ x = 5""" a = """ - def foo(xxx_todo_changeme1, d): - ((a, b), c) = xxx_todo_changeme1 + def foo(xxx_todo_changeme6, d): + ((a, b), c) = xxx_todo_changeme6 x = 5""" self.check(b, a) @@ -1156,8 +1156,8 @@ x = 5""" a = """ - def foo(xxx_todo_changeme2, d) -> e: - ((a, b), c) = xxx_todo_changeme2 + def foo(xxx_todo_changeme7, d) -> e: + ((a, b), c) = xxx_todo_changeme7 x = 5""" self.check(b, a) @@ -1166,7 +1166,7 @@ def foo(((a, b), c)): x = 5; y = 7""" a = """ - def foo(xxx_todo_changeme10): ((a, b), c) = xxx_todo_changeme10; x = 5; y = 7""" + def foo(xxx_todo_changeme15): ((a, b), c) = xxx_todo_changeme15; x = 5; y = 7""" self.check(b, a) def test_keywords(self): @@ -1175,8 +1175,8 @@ x = 5""" a = """ - def foo(xxx_todo_changeme5, d, e=5) -> z: - ((a, b), c) = xxx_todo_changeme5 + def foo(xxx_todo_changeme10, d, e=5) -> z: + ((a, b), c) = xxx_todo_changeme10 x = 5""" self.check(b, a) @@ -1186,8 +1186,8 @@ x = 5""" a = """ - def foo(xxx_todo_changeme11, d, *vargs, **kwargs) -> z: - ((a, b), c) = xxx_todo_changeme11 + def foo(xxx_todo_changeme16, d, *vargs, **kwargs) -> z: + ((a, b), c) = xxx_todo_changeme16 x = 5""" self.check(b, a) @@ -1197,9 +1197,9 @@ x = 5""" a = """ - def foo(xxx_todo_changeme6, xxx_todo_changeme7) -> z: - ((a, b), c) = xxx_todo_changeme6 - (d, e, f) = xxx_todo_changeme7 + def foo(xxx_todo_changeme11, xxx_todo_changeme12) -> z: + ((a, b), c) = xxx_todo_changeme11 + (d, e, f) = xxx_todo_changeme12 x = 5""" self.check(b, a) @@ -1209,9 +1209,9 @@ x = 5""" a = """ - def foo(x, xxx_todo_changeme8, d, xxx_todo_changeme9, y) -> z: - ((a, b), c) = xxx_todo_changeme8 - (e, f, g) = xxx_todo_changeme9 + def foo(x, xxx_todo_changeme13, d, xxx_todo_changeme14, y) -> z: + ((a, b), c) = xxx_todo_changeme13 + (e, f, g) = xxx_todo_changeme14 x = 5""" self.check(b, a) @@ -1222,10 +1222,10 @@ x = 5""" a = """ - def foo(xxx_todo_changeme3, xxx_todo_changeme4) -> z: + def foo(xxx_todo_changeme8, xxx_todo_changeme9) -> z: "foo foo foo foo" - ((a, b), c) = xxx_todo_changeme3 - (d, e, f) = xxx_todo_changeme4 + ((a, b), c) = xxx_todo_changeme8 + (d, e, f) = xxx_todo_changeme9 x = 5""" self.check(b, a) From python-checkins at python.org Sat Mar 3 19:14:04 2007 From: python-checkins at python.org (georg.brandl) Date: Sat, 3 Mar 2007 19:14:04 +0100 (CET) Subject: [Python-checkins] r54107 - sandbox/trunk/pep3101/test_simpleformat.py Message-ID: <20070303181404.107431E4009@bag.python.org> Author: georg.brandl Date: Sat Mar 3 19:14:00 2007 New Revision: 54107 Modified: sandbox/trunk/pep3101/test_simpleformat.py Log: Fix the test suite for simpleformat -- the wrong method was used, and per chance it succeeded. Also, use a specific exception type for assertRaises. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Sat Mar 3 19:14:00 2007 @@ -152,15 +152,17 @@ self.formatEquals("custom", "{0:{1}}", custom, "custom") def test_syntaxerror(self): - self.assertRaises(Exception, "}{", True) - self.assertRaises(Exception, "{0", True) - self.assertRaises(Exception, "{0.[]}", True) - self.assertRaises(Exception, "{0[0}", True) - self.assertRaises(Exception, "{0[0:foo}", True) - self.assertRaises(Exception, "{c]}", True) - self.assertRaises(Exception, "{{1}}", True, 0) - self.assertRaises(Exception, "{{ {{{0}}", True) - self.assertRaises(Exception, "{0}}", True) + self.formatRaises(ValueError, "}{", True) + self.formatRaises(ValueError, "{0", True) + self.formatRaises(ValueError, "{0.[]}", True) + # XXX: these raise the wrong exceptions + #self.formatRaises(ValueError, "{0[0}", True) + #self.formatRaises(ValueError, "{0[0:foo}", True) + self.formatRaises(ValueError, "{c]}", True) + # XXX: this should *not* raise + #self.formatRaises(ValueError, "{{1}}", True, 0) + self.formatRaises(ValueError, "{{ {{{0}}", True) + self.formatRaises(ValueError, "{0}}", True) def test_main(): From python-checkins at python.org Sat Mar 3 21:21:36 2007 From: python-checkins at python.org (patrick.maupin) Date: Sat, 3 Mar 2007 21:21:36 +0100 (CET) Subject: [Python-checkins] r54108 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/setup.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070303202136.1D8281E4008@bag.python.org> Author: patrick.maupin Date: Sat Mar 3 21:21:29 2007 New Revision: 54108 Modified: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/setup.py sandbox/trunk/pep3101/unicodeformat.c Log: Added pep_differences.txt to document initial implementation target. Updated README.txt to move info into pep_differences. Cleaned up escape-to-markup processing to fix bug and enable easy alternate syntax testing. Changed version number in setup.py to reflect the fact we're not at 1.0 yet. Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Sat Mar 3 21:21:29 2007 @@ -10,12 +10,10 @@ Eric V. Smith (eric at trueblade.com) Pete Shinner -The code is only half-baked at present -(development was started at PyCon 2007 and is in progress). - -Although the PEP3101 goal is a (unicode) string format method, since -this is a sandbox, we might do a few more ambitious things as well, -to see if people like them. +The code is only half-baked at present. Development was started at +PyCon 2007 and is steadily progressing. The feature set targeted +for the initial release is documented in pep_differences.txt in +this directory. The current plan of record is to make a pep3101 extension module. It will have at least the following features: @@ -25,53 +23,43 @@ - can be compiled against 2.4, 2.5, and Py3K - Works with the string object as well as unicode -The current code has a module which is progressing nicely, and some -unittests for the current version. - Files: - - unicodeformat.c is designed to be easily added to Python - as a method of the unicode object. - - stringformat.c is a wrapper around unicodeformat.c, which - "templatizes" the entire file to make it easy to add to Python - as a method of the string object. + - loadpep.py -- Attempts to add the appropriate build directory + to the Python path, then runs the tests. + - makefile -- At least one of the developers is a Luddite who + can barely make setup.py function. - pep3101.h contains definitions for the functions in stringformat and unicodeformat - pep3101.c contains a module implementation which can be linked with these method files for testing and/or use with earlier Python versions. + - pep_differences.txt documents differences between what is + built in this directory, and the original PEP + - README.txt -- this file. - setup.py -- Use "build" option to make the extension module - test_simpleformat.py -- initial unittests - StringFormat.py -- Talin's original implementation in Python. This is only for historical interest: it doesn't exactly match the PEP or C implementation. + - stringformat.c is a wrapper around unicodeformat.c, which + "templatizes" the entire file to make it easy to add to Python + as a method of the string object. + - unicodeformat.c is designed to be easily added to Python + as a method of the unicode object. Todo: - finish up format specifier handling - document differences between PEP and implementation + (in pep_differences.txt) - Add docstrings to module - - print string offset information on certain errors - - Add _flags options - - Play with possible implementations for formatting - strings against dictionaries as well as the format - (dangerous) + - Add keyword options and string metadata options + as described in pep_differences. - Play with possible implementations for exposing lowest level format specifier handler for use in compatible template systems. - - Play with possible options for specifying additional - escape syntaxes - Should we have stricter checking on format strings? For example type "s" doesn't allow a sign character. Should specifying one be an error? - Test suite needs to check for specific exceptions. - -_flags options to consider adding: - - - useall=1 means all arguments should be used - - allow_leading_under means leading underbars allowed - - syntax=0,1,2,3 -- different syntaxes - - hook=object -- callback hook as described in PEP - - informational mode to dump exceptions into string - (as described in pep) - - max_recursion=xxx (default 4) Modified: sandbox/trunk/pep3101/setup.py ============================================================================== --- sandbox/trunk/pep3101/setup.py (original) +++ sandbox/trunk/pep3101/setup.py Sat Mar 3 21:21:29 2007 @@ -6,6 +6,6 @@ ) setup (name = 'pep3101', - version = '1.0', + version = '0.01', description = 'Extension module to implement features of PEP 3101', ext_modules = [module1]) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Sat Mar 3 21:21:29 2007 @@ -161,6 +161,8 @@ /* For some interface functions, we could have a list or tuple of dictionaries to search, e.g. locals()/globals(). */ int keywords_is_tuple; + /* Support for different escape-to-markup syntaxes */ + int syntaxmode; } FmtState; /* Some forward declarations for recursion */ @@ -1220,6 +1222,25 @@ return result; } +/* + get_field_and_render calls get_field_and_spec to get + the field object and specification, then calls + render_field to output it. +*/ +static int +get_field_and_render(FmtState *fs) +{ + PyObject *myobj; + int ok; + + fs->fieldstart = fs->fmtstr.ptr; + myobj = get_field_and_spec(fs); + ok = (myobj != NULL) && render_field(fs, myobj); + Py_XDECREF(myobj); + Py_XDECREF(fs->fieldspec.obj); + return ok; +} + /************************************************************************/ /******* Output string allocation and escape-to-markup processing ******/ /************************************************************************/ @@ -1233,52 +1254,84 @@ static int do_markup(FmtState *fs) { - PyObject *myobj; - CH_TYPE c, *start; - Py_ssize_t count, total; SubString fmtstr; - int doubled, ok; - - fmtstr = fs->fmtstr; - ok = 1; - c = '\0'; /* Avoid compiler warning */ - while (fmtstr.ptr < fmtstr.end) { - start = fmtstr.ptr; - count = total = fmtstr.end - start; - while (count && ((c = *fmtstr.ptr) != '{') && (c != '}')) { - fmtstr.ptr++; - count--; - } - fs->fieldstart = fmtstr.ptr++; - count = total - count; - total -= count; - doubled = (total > 1) && (*fmtstr.ptr == c); - if (doubled) { - output_data(fs, start, count+1); - fmtstr.ptr++; - continue; - } else if (count) - output_data(fs, start, count); - fs->fmtstr.ptr = fmtstr.ptr; - if (c == '}') { - SetError(fs, "Single } encountered"); - ok = 0; + CH_TYPE c, *start, *ptr, *end; + Py_ssize_t count; + int syntaxmode, escape; + + end = fs->fmtstr.end; + syntaxmode = fs->syntaxmode; + + while (((start = ptr = fs->fmtstr.ptr) != NULL) && (ptr < end)) { + escape = 0; + while (ptr < end) { + switch (c = *ptr++) { + case '{': + if ((syntaxmode == 2) && + ((ptr == start) || (fmtstr.ptr[-2] != '$'))) + continue; + break; + case '}': + if (syntaxmode != 0) + continue; + break; + default: + continue; + } + escape = 1; break; } - if (total < 2) { - ok = !total || - (int)SetError(fs, "Single { encountered"); - break; + count = ptr - start; + if (ptr < end) { + switch (syntaxmode) { + case 0: + if ((c == '}') && (c != *ptr)) { + fs->fmtstr.ptr = ptr; + return (int)SetError(fs, "Single } encountered"); + } + case 1: + if (c == *ptr) { + ptr++; + escape = 0; + } + else + count--; + break; + case 2: + count -= 2; + escape = !count || (fmtstr.ptr[-3] != '$'); + if (!escape) + ptr--; + break; + case 3: + switch (*ptr) { + case ' ': + ptr++; + case '\n': case '\r': + escape = 0; + break; + default: + count--; + break; + } + break; + default: + fs->fmtstr.ptr = ptr; + return (int)SetError(fs, "Unsupported syntax mode"); + } + } + else if (escape) { + fs->fmtstr.ptr = ptr; + return (int)SetError(fs, "Unexpected escape to markup"); } - myobj = get_field_and_spec(fs); - ok = (myobj != NULL) && render_field(fs, myobj); - Py_XDECREF(fs->fieldspec.obj); - Py_XDECREF(myobj); - if (!ok) - break; - fmtstr.ptr = fs->fmtstr.ptr; + + fs->fmtstr.ptr = ptr; + if (count && !output_data(fs, start, count)) + return 0; + if (escape && !get_field_and_render(fs)) + return 0; } - return ok; + return 1; } /* @@ -1350,6 +1403,7 @@ fs->positional_arg_set = 0; fs->keyword_arg_set = NULL; fs->keywords_is_tuple = 0; + fs->syntaxmode = 0; fs->do_markup = do_markup; fs->keywords = keywords; From python-checkins at python.org Sat Mar 3 21:28:36 2007 From: python-checkins at python.org (patrick.maupin) Date: Sat, 3 Mar 2007 21:28:36 +0100 (CET) Subject: [Python-checkins] r54109 - sandbox/trunk/pep3101/pep_differences.txt Message-ID: <20070303202836.94DBD1E4008@bag.python.org> Author: patrick.maupin Date: Sat Mar 3 21:28:34 2007 New Revision: 54109 Added: sandbox/trunk/pep3101/pep_differences.txt Log: Added pep_differences.txt to document initial implementation target. Updated README.txt to move info into pep_differences. Cleaned up escape-to-markup processing to fix bug and enable easy alternate syntax testing. Changed version number in setup.py to reflect the fact we're not at 1.0 yet. Added: sandbox/trunk/pep3101/pep_differences.txt ============================================================================== --- (empty file) +++ sandbox/trunk/pep3101/pep_differences.txt Sat Mar 3 21:28:34 2007 @@ -0,0 +1,299 @@ + +This file describes differences between PEP 3101 and the C implementation +in this directory, and describes the reasoning behind the differences. + +PEP3101 is a well thought out, excellent starting point for advanced string +formatting, but as one might expect, there are a few gaps in it which were +not noticed until implementation, and there are almost certainly gaps in +the implementation which will not be noticed until the code is widely used. +Fortunately, the schedule for both Python 2.6 and Python 3.0 have enough +slack in them that if we work diligently, we can widely distribute a working +implementation, not just a theoretical document, well in advance of the code +freeze dates. This should allow for a robust discussion about the merits or +drawbacks of some of the fine points of the PEP and the implementation by +people who are actually **using** the code. + +This nice schedule has made at least one of the implementers bold enough +to consider the first cut of the implementation "experimental" in the sense +that, since there is time to correct any problems, the implementation can +diverge from the PEP (in documented ways!) both for perceived flaws in +the PEP, and also to add minor enhancements. The code is being structured +so that it should be easy to subsequently modify the operation to conform +to consensus opinion. + + +GOALS: + + Replace % + +The primary goal of the advanced string formatting is to replace the % +operator. Not in a coercive fashion. The goal is to be good enough +that nobody wants to use the % operator. + + + Modular design for subfunction reuse + +The PEP explicitly disclaims any attempt to replace string.Template, +concentrating exclusively on the % operator. While this narrow focus +is very useful in removing things like conditionals and looping from +the discussion about the PEP, it ignores the reality that it might +be useful to REUSE some of the C implementation code (particularly +the per-field formatting) in templating systems. So the design of +the implementation adds the goal of being able to expose some lower- +level functions. + + + Efficiency + +It is not claimed that the initial implementation is particularly +efficient, but it is desirable to tweak the specification in such +a fashion that an efficient implementation IS possible. Since the +goal is to replace the % operator, it is particularly important +that the formatting of small strings is not prohibitively expensive. + + + Security + +Security is a stated goal of the PEP, with an apparent goal of being +able to accept a string from J. Random User and format it without +potential adverse consequences. This may or may not be an achievable +goal; the PEP certainly has some features that should help with this +such as the restricted number of operators, and the implemetation has +some additional features, such as not allowing leading underscores +on attributes by default, but these may be attempts to solve an +intractable problem, similar to the original restricted Python +execution mode. + +In any case, security is a goal, and anything reasonable we can do to +support it should be done. Unreasonable things to support security +include things which would be very costly in terms of execution time, +and things which rely on the by now very much discredited "security +through obscurity" approach. + + + Older Python Versions + +Some of the implementers have very strong desires to use this formatting +on older Python versions, and Guido has mentioned that any 3.0 features +which do not break backward compatibility are potential candidates for +inclusion in 2.6. + + + No global state + +The PEP states "The string formatting system has two error handling modes, +which are controlled by the value of a class variable." As has been +discussed on the developer's list, this might be problematic, especially in +large systems where components are being aggregated from multiple sources. +One component might deliberately throw and catch exceptions in the string +processing, and disabling this on a global basis might cause this component +to stop working properly. If the ability to control this on a global +basis is desirable, it is easy enough to add in later, but if it is not +desirable, then deciding that after the fact and changing the code could +break code which has grown to rely on the feature. + + +FORMATTING METADATA + +The basic desired operation of the PEP is to be able to write: + + 'some format control string'.format(param1, param2, keyword1=whatever, ...) + +Unfortunately, there needs to be some mechanism to handle out of band +data for some formatting and error handling options. This could +be really costly, if multiple options are looked up in the **keywords +on every single call on even short strings, so some tweaks on the +initial implementation are designed to reduce the overhead of looking +up metadata. Two techniques are used: + + 1) Lazy evaluation where possible. For example, the code does not + need to look up error-handling options until an error occurs. + + 2) Metadata embedded in the string where appropriate. This + saves a dictionary lookup on every call. However this + is only appropriate when (a) the metadata arguably relates + to the actual control string and not the function where it + is being used; and (b) there are no security implications. + + +DIFFERENCES BETWEEN PEP AND INITIAL IMPLEMENTATION: + + Support for old Python versions + +The original PEP is Python 3000 only, which implies a lack of regular +string support (unicode only). To make the code compatible with 2.6, +it has been written to support regular strings as well, and to make +the code compatible with earlier versions, it has been written to be +usable as an extension module as well as/instead of as a string method: + + from pep3101 import format + format('control string', parameter1, ...) + + + format_item function + +A large portion of the code in the new advanced formatter is the code +which formats a single field according to the given format specifier. +(Thanks, Eric!) This code is useful on its own, especially for template +systems or other custom formatting solutions. The initial implementation +will have a format_item function which takes a format specifier and a +single object and returns a formatted result for that object and specifier. + + + comments + +The PEP does not have a mechanism for comments embedded in the format +strings. The usefulness of comments inside format strings may be +debatable, but the implementation is easy and easy to understand: + + {#This is a comment} + + + errors and exceptions + +The PEP defines a global flag for "strict" or "lenient" mode. The +implementation eschews the use of a global flag (see more information +in the goals section, above), and splits out the various error +features discussed by the PEP into different options. It also adds +an option. + +The first error option is controlled by the optional _leading_underscores +keyword argument. If this is present and evaluates non-zero, then leading +underscores are allowed on identifiers and attributes in the format string. +The implementation will lazily look for this argument the first time it +encounters a leading underscore. + +The next error option is controlled by metadata embedded in the string. +If "{!useall}" appears in the string, then a check is made that all +arguments are converted. The decision to embed this metadata in the +string can certainly be changed later; the reasons for doing it this +way in the initial implementation are as follows: + + 1) In the original % operator, the error reporting that an + extra argument is present is orthogonal to the error reporting + that not enough arguments are present. Both these errors are + easy to commit, because it is hard to count arguments and %s, + etc. In theory, the new string formatting should make it easier + to get the arguments right, because all arguments in the format + string are numbered or even named. + + 2) It is arguably not Pythonic to check that all arguments to + a function are actually used by the execution of the function, + and format() is, after all, just another function. So it seems + that the default should be to not check that all the arguments + are used. In fact, there are similar reasons for not using + all the arguments here as with any other function. For example, + for customization, the format method of a string might be called + with a superset of all the information which might be useful to + view. + + 3) Assuming that the normal case is to not check all arguments, + it is much cheaper (especially for small strings) to notice + the {! and process the metadata in the strings that want it + than it is to look for a keyword argument for every string. + +XXX -- need to add info on displaying exceptions in string vs. passing +them up for looked-up errors. Also adding or not of string position +information. + + + Getattr and getindex rely on underlying object exceptions + +For attribute and index lookup, the PEP specifies that digits will be +treated as numeric values, and non-digits should be valid Python +identifiers. The implementation does not rigorously enforce this, +instead deferring to the object's getattr or getindex to throw an +exception for an invalid lookup. The only time this is not true +is for leading underscores, which are disallowed by default. + + + User-defined Python format function + +The PEP specifies that an additional string method, cformat, can be +used to call the same formatting machinery, but with a "hook" function +that can intercept formatting on a per-field basis. + +The implementation does not have an additional cformat function/method. +Instead, user format hooks are accomplished as follows: + + 1) A format hook function, with call signature and semantics + as described in the PEP, may be passed to format() as the + keyword argument _hook. This argument will be lazily evaluated + the first time it is needed. + + 2) If "{!hook}" appears in the string, then the hook function + will be called on every single format field. + + 3) If the last character (the type specifier) in a format field + is "h" (for hook) then the hook function will be called for + that field, even if "{!hook}" has not been specified. + + + User-specified dictionary + +The call machinery to deal with keyword arguments is quite expensive, +especially for large numbers of arguments. For this reason, the +implementation supports the ability to pass in a dictionary as the +_dict argument. The _dict argument will be lazily retrieved the first +time the template requests a named parameter which was not passed +in as a keyword argument. + + + Name mapping + +To support the user-specified dictionary, a name mapper will first +look up names in the passed keywords arguments, then in the passed +_dict (if any). + + + Automatic locals/globals lookup + +This is likely to be a contentious feature, but it seems quite useful, +so in it goes for the initial implementation. For security reasons, +this happens only if format() is called with no parameters. Since +the whole purpose of format() is to apply parameters to a string, +a call to format() without any parameters would otherwise be a +silly thing to do. We can turn this degenerate case into something +useful by using the caller's locals and globals. An example from +Ian Bicking: + + assert x < 3, "x has the value of {x} (should be < 3)".format() + + + Syntax modes + +The PEP correctly notes that the mechanism used to delineate markup +vs. text is likely to be one of the most controversial features, +and gives reasons why the chosen mechanism is better than others. + +The chosen mechanism is quite readable and reasonable, but different +problem domains might have differing requirements. For example, +C code generated using the current mechanism could get quite ugly +with a large number of "{" and "}" characters. + +The initial implementation supports the notion of different syntax +modes. This is bad from the "more than one way to do it" perspective, +but is not quite so bad if the template itself has to indicate if it +is not using the default mechanism. To give reviewers an idea of +how this could work, the implementation supports 4 different modes: + + "{!syntax0}" -- the mode as described in the PEP + "{!syntax1}" -- same as mode 0, except close-braces + do not need to be doubled + "{!syntax2}" -- Uses "${" for escape to markup, "$${" for + literal "${" + "{!syntax3}" -- Like syntax0 "{" for escape to markup, + except literal "{" is denoted by "{ " + or "{\n" (where the space is removed but + the newline isn't). + + + Syntax for metadata in strings + +There have been several examples in this document of metadata +embedded inside strings, for "hook", "useall", and "syntax". + +The basic metadata syntax is "{!}", however to allow +more readable templates, in this case, if the "}" is immediately +followed by "\n" or "\r\n", this whitespace will not appear in +the formatted output. From python-checkins at python.org Sat Mar 3 23:09:15 2007 From: python-checkins at python.org (brett.cannon) Date: Sat, 3 Mar 2007 23:09:15 +0100 (CET) Subject: [Python-checkins] r54110 - peps/trunk/pep-3113.txt Message-ID: <20070303220915.0C1561E4008@bag.python.org> Author: brett.cannon Date: Sat Mar 3 23:09:12 2007 New Revision: 54110 Modified: peps/trunk/pep-3113.txt Log: Fix minor typo (thanks, Terry Reedy). Modified: peps/trunk/pep-3113.txt ============================================================================== --- peps/trunk/pep-3113.txt (original) +++ peps/trunk/pep-3113.txt Sat Mar 3 23:09:12 2007 @@ -214,7 +214,7 @@ is to occur in Python 3.0 compared to Python 2.6. Second, the 2to3 refactoring tool [#2to3]_ will gain a rule for -translating tuple parameters to being a single parameter this is +translating tuple parameters to being a single parameter that is unpacked as the first statement in the function. The name of the new parameter will be a mangling of tuple parameter's names by joining them with underscores. The new parameter will then be unpacked into From python-checkins at python.org Sun Mar 4 07:05:32 2007 From: python-checkins at python.org (david.goodger) Date: Sun, 4 Mar 2007 07:05:32 +0100 (CET) Subject: [Python-checkins] r54111 - peps/trunk/pep-3112.txt Message-ID: <20070304060532.0DB351E4015@bag.python.org> Author: david.goodger Date: Sun Mar 4 07:05:29 2007 New Revision: 54111 Modified: peps/trunk/pep-3112.txt Log: fixed markup; added PEP number to header Modified: peps/trunk/pep-3112.txt ============================================================================== --- peps/trunk/pep-3112.txt (original) +++ peps/trunk/pep-3112.txt Sun Mar 4 07:05:29 2007 @@ -1,4 +1,4 @@ -PEP: +PEP: 3112 Title: Bytes literals in Python 3000 Version: $Revision$ Last-Modified: $Date$ @@ -64,7 +64,7 @@ The proposed syntax is an extension of the existing string syntax. [#stringliterals]_ -The new syntax for strings, including the new bytes literal, is: +The new syntax for strings, including the new bytes literal, is:: stringliteral: [stringprefix] (shortstring | longstring) stringprefix: "b" | "r" | "br" | "B" | "R" | "BR" | "Br" | "bR" @@ -103,7 +103,7 @@ Each evaluation of a bytes literal produces a new ``bytes`` object. The bytes in the new object are the bytes represented by the -``shortstringitem``s or ``longstringitem``s in the literal, in the +``shortstringitem`` or ``longstringitem`` parts of the literal, in the same order. From python-checkins at python.org Sun Mar 4 07:05:43 2007 From: python-checkins at python.org (david.goodger) Date: Sun, 4 Mar 2007 07:05:43 +0100 (CET) Subject: [Python-checkins] r54112 - peps/trunk/pep-3100.txt Message-ID: <20070304060543.217831E4011@bag.python.org> Author: david.goodger Date: Sun Mar 4 07:05:40 2007 New Revision: 54112 Modified: peps/trunk/pep-3100.txt Log: fixed indentation errors Modified: peps/trunk/pep-3100.txt ============================================================================== --- peps/trunk/pep-3100.txt (original) +++ peps/trunk/pep-3100.txt Sun Mar 4 07:05:40 2007 @@ -143,19 +143,19 @@ literals with 'L' or 'l' suffix disappear [1]_ [done] * Make all strings be Unicode, and have a separate bytes() type [1]_ The new string type will be called 'str'. -* Return iterable views instead of lists where appropriate for atomic type methods - (e.g. ``dict.keys()``, ``dict.values()``, ``dict.items()``, etc.); iter* - methods will be removed. [done] +* Return iterable views instead of lists where appropriate for atomic + type methods (e.g. ``dict.keys()``, ``dict.values()``, + ``dict.items()``, etc.); iter* methods will be removed. [done] * Make ``string.join()`` stringify its arguments? [18]_ * Fix file() so it returns a ValueError if the mode is bad rather than IOError. (This probably affects lots of places, we should review the exceptions - and fix them if inappropriate.) + and fix them if inappropriate.) To be removed: * ``basestring.find()`` and ``basestring.rfind()``; use ``basestring.index()`` - or ``basestring.[r]partition()`` or - or ``basestring.rindex()`` in a try/except block??? [13]_ + or ``basestring.[r]partition()`` or + or ``basestring.rindex()`` in a try/except block??? [13]_ * ``file.xreadlines()`` method [#file-object]_ [done] * ``dict.setdefault()``? [15]_ * ``dict.has_key()`` method [done] From python-checkins at python.org Sun Mar 4 07:35:07 2007 From: python-checkins at python.org (david.goodger) Date: Sun, 4 Mar 2007 07:35:07 +0100 (CET) Subject: [Python-checkins] r54113 - peps/trunk/pep-3112.txt Message-ID: <20070304063507.295111E4006@bag.python.org> Author: david.goodger Date: Sun Mar 4 07:35:06 2007 New Revision: 54113 Modified: peps/trunk/pep-3112.txt Log: added Standards Track type Modified: peps/trunk/pep-3112.txt ============================================================================== --- peps/trunk/pep-3112.txt (original) +++ peps/trunk/pep-3112.txt Sun Mar 4 07:35:06 2007 @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Jason Orendorff Status: Accepted -Type: +Type: Standards Track Content-Type: text/x-rst Requires: 358 Created: 23-Feb-2007 From python-checkins at python.org Sun Mar 4 18:18:57 2007 From: python-checkins at python.org (georg.brandl) Date: Sun, 4 Mar 2007 18:18:57 +0100 (CET) Subject: [Python-checkins] r54114 - python/trunk/Lib/test/test_dict.py python/trunk/Lib/test/test_userdict.py Message-ID: <20070304171857.A76F51E4009@bag.python.org> Author: georg.brandl Date: Sun Mar 4 18:18:54 2007 New Revision: 54114 Modified: python/trunk/Lib/test/test_dict.py python/trunk/Lib/test/test_userdict.py Log: Fix a bug in test_dict and test_userdict, found at the PyPy sprint. Modified: python/trunk/Lib/test/test_dict.py ============================================================================== --- python/trunk/Lib/test/test_dict.py (original) +++ python/trunk/Lib/test/test_dict.py Sun Mar 4 18:18:54 2007 @@ -430,7 +430,7 @@ except RuntimeError, 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 @@ -441,7 +441,7 @@ except KeyError, 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() @@ -450,7 +450,7 @@ except KeyError, 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 Modified: python/trunk/Lib/test/test_userdict.py ============================================================================== --- python/trunk/Lib/test/test_userdict.py (original) +++ python/trunk/Lib/test/test_userdict.py Sun Mar 4 18:18:54 2007 @@ -174,7 +174,7 @@ except RuntimeError, 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 @@ -186,7 +186,7 @@ except KeyError, 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() @@ -195,7 +195,7 @@ except KeyError, 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 From python-checkins at python.org Sun Mar 4 18:19:04 2007 From: python-checkins at python.org (georg.brandl) Date: Sun, 4 Mar 2007 18:19:04 +0100 (CET) Subject: [Python-checkins] r54115 - python/branches/release25-maint/Lib/test/test_dict.py python/branches/release25-maint/Lib/test/test_userdict.py Message-ID: <20070304171904.56DC51E4009@bag.python.org> Author: georg.brandl Date: Sun Mar 4 18:19:02 2007 New Revision: 54115 Modified: python/branches/release25-maint/Lib/test/test_dict.py python/branches/release25-maint/Lib/test/test_userdict.py Log: Fix a bug in test_dict and test_userdict, found at the PyPy sprint. (backport from rev. 54114) Modified: python/branches/release25-maint/Lib/test/test_dict.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_dict.py (original) +++ python/branches/release25-maint/Lib/test/test_dict.py Sun Mar 4 18:19:02 2007 @@ -430,7 +430,7 @@ except RuntimeError, 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 @@ -441,7 +441,7 @@ except KeyError, 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() @@ -450,7 +450,7 @@ except KeyError, 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 Modified: python/branches/release25-maint/Lib/test/test_userdict.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_userdict.py (original) +++ python/branches/release25-maint/Lib/test/test_userdict.py Sun Mar 4 18:19:02 2007 @@ -174,7 +174,7 @@ except RuntimeError, 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 @@ -186,7 +186,7 @@ except KeyError, 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() @@ -195,7 +195,7 @@ except KeyError, 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 From python-checkins at python.org Sun Mar 4 18:48:00 2007 From: python-checkins at python.org (patrick.maupin) Date: Sun, 4 Mar 2007 18:48:00 +0100 (CET) Subject: [Python-checkins] r54116 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070304174800.7408B1E4009@bag.python.org> Author: patrick.maupin Date: Sun Mar 4 18:47:54 2007 New Revision: 54116 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Removed compile warnings and bugs Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Sun Mar 4 18:47:54 2007 @@ -36,7 +36,7 @@ #define CH_TYPE char #define CH_TYPE_ISDECIMAL(x) ((x >= '0') && (x <= '9')) #define CH_TYPE_TODECIMAL(x) (CH_TYPE_ISDECIMAL(x) ? (x - '0') : -1) -#define CH_TYPE_FILL Py_UNICODE_FILL +#define CH_TYPE_FILL memset #define STROBJ_AS_PTR PyString_AS_STRING #define STROBJ_GET_SIZE PyString_GET_SIZE #define STROBJ_NEW PyString_FromStringAndSize @@ -755,7 +755,6 @@ output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len, const InternalFormatSpec *format) { - Py_ssize_t ok; Py_ssize_t width; /* total field width */ CH_TYPE *dst; @@ -857,7 +856,6 @@ format_char(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - int ok; CH_TYPE buf; if (PyString_Check(fieldobj)) { @@ -1254,7 +1252,6 @@ static int do_markup(FmtState *fs) { - SubString fmtstr; CH_TYPE c, *start, *ptr, *end; Py_ssize_t count; int syntaxmode, escape; @@ -1268,7 +1265,7 @@ switch (c = *ptr++) { case '{': if ((syntaxmode == 2) && - ((ptr == start) || (fmtstr.ptr[-2] != '$'))) + ((ptr == start) || (ptr[-2] != '$'))) continue; break; case '}': @@ -1299,7 +1296,7 @@ break; case 2: count -= 2; - escape = !count || (fmtstr.ptr[-3] != '$'); + escape = !count || (ptr[-3] != '$'); if (!escape) ptr--; break; From python-checkins at python.org Sun Mar 4 20:26:52 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 20:26:52 +0100 (CET) Subject: [Python-checkins] r54117 - peps/trunk/pep-3113.txt Message-ID: <20070304192652.E6F6E1E4007@bag.python.org> Author: brett.cannon Date: Sun Mar 4 20:26:52 2007 New Revision: 54117 Modified: peps/trunk/pep-3113.txt Log: Clarify how tuple parameters do not work with PEP 3102 and 3107. Modified: peps/trunk/pep-3113.txt ============================================================================== --- peps/trunk/pep-3113.txt (original) +++ peps/trunk/pep-3113.txt Sun Mar 4 20:26:52 2007 @@ -113,9 +113,12 @@ Consider PEP 3102 (keyword-only arguments) and PEP 3107 (function annotations) [#pep-3102]_ [#pep-3107]_. Both PEPs have been accepted and introduce new functionality within a function's signature. And yet -for both PEPs the new feature cannot be applied to tuple parameters. -This leads to tuple parameters not being on the same footing as -regular parameters that do not attempt to unpack their arguments. +for both PEPs the new feature cannot be applied to tuple parameters as +a whole. PEP 3102 has no support for tuple parameters at all (which +makes sense as there is no way to reference a tuple parameter by +name). PEP 3107 allows annotations for each item within the tuple +(e.g., ``(x:int, y:int)``), but not the whole tuple (e.g., +``(x, y):int``). The existence of tuple parameters also places sequence objects separately from mapping objects in a function signature. There is no From python-checkins at python.org Sun Mar 4 20:56:17 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 20:56:17 +0100 (CET) Subject: [Python-checkins] r54118 - peps/trunk/pep-0000.txt peps/trunk/pep-3113.txt Message-ID: <20070304195617.CB3211E400F@bag.python.org> Author: brett.cannon Date: Sun Mar 4 20:56:13 2007 New Revision: 54118 Modified: peps/trunk/pep-0000.txt peps/trunk/pep-3113.txt Log: Mark PEP 3113 as accepted. Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 20:56:13 2007 @@ -84,6 +84,7 @@ SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge SA 3112 Bytes literals in Python 3000 Orendorff + SA 3113 Removal of Tuple Parameter Unpacking Cannon Open PEPs (under consideration) @@ -114,7 +115,6 @@ S 3101 Advanced String Formatting Talin S 3104 Access to Names in Outer Scopes Yee I 3108 Standard Library Reorganization Cannon - S 3113 Removal of Tuple Parameter Unpacking Cannon Finished PEPs (done, implemented in Subversion) @@ -459,7 +459,7 @@ SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge SA 3112 Bytes literals in Python 3000 Orendorff - S 3113 Removal of Tuple Parameter Unpacking Cannon + SA 3113 Removal of Tuple Parameter Unpacking Cannon Key Modified: peps/trunk/pep-3113.txt ============================================================================== --- peps/trunk/pep-3113.txt (original) +++ peps/trunk/pep-3113.txt Sun Mar 4 20:56:13 2007 @@ -3,7 +3,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Brett Cannon -Status: Draft +Status: Accepted Type: Standards Track Python-version: 3.0 Content-Type: text/x-rst From python-checkins at python.org Sun Mar 4 20:57:45 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 20:57:45 +0100 (CET) Subject: [Python-checkins] r54119 - peps/trunk/pep-0000.txt peps/trunk/pep-3108.txt Message-ID: <20070304195745.62E281E4009@bag.python.org> Author: brett.cannon Date: Sun Mar 4 20:57:43 2007 New Revision: 54119 Modified: peps/trunk/pep-0000.txt peps/trunk/pep-3108.txt Log: Change the classification of PEP 3108 from Information to Standards Track since it needs acceptance and is not just explaining something. Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 20:57:43 2007 @@ -114,7 +114,7 @@ S 754 IEEE 754 Floating Point Special Values Warnes S 3101 Advanced String Formatting Talin S 3104 Access to Names in Outer Scopes Yee - I 3108 Standard Library Reorganization Cannon + S 3108 Standard Library Reorganization Cannon Finished PEPs (done, implemented in Subversion) @@ -454,7 +454,7 @@ SF 3105 Make print a function Brandl S 3106 Revamping dict.keys(), .values() and .items() GvR SA 3107 Function Annotations Winter, Lownds - I 3108 Standard Library Reorganization Cannon + S 3108 Standard Library Reorganization Cannon SA 3109 Raising Exceptions in Python 3000 Winter SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge Modified: peps/trunk/pep-3108.txt ============================================================================== --- peps/trunk/pep-3108.txt (original) +++ peps/trunk/pep-3108.txt Sun Mar 4 20:57:43 2007 @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Brett Cannon Status: Draft -Type: Informational +Type: Standards Track Python-Version: 3.0 Content-Type: text/x-rst Created: 01-Jan-2007 From python-checkins at python.org Sun Mar 4 20:59:37 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 20:59:37 +0100 (CET) Subject: [Python-checkins] r54120 - peps/trunk/pep-0000.txt peps/trunk/pep-3106.txt Message-ID: <20070304195937.C35251E400D@bag.python.org> Author: brett.cannon Date: Sun Mar 4 20:59:36 2007 New Revision: 54120 Modified: peps/trunk/pep-0000.txt peps/trunk/pep-3106.txt Log: Fix up marking PEP 3106 as accepted (was already listed in the Accepted section of PEP 0). Not marking as Implemented as some Open Issues are still listed. Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 20:59:36 2007 @@ -78,7 +78,7 @@ Accepted PEPs (accepted; may not be implemented yet) SA 3102 Keyword-Only Arguments Talin - S 3106 Revamping dict.keys(), .values() and .items() GvR + SA 3106 Revamping dict.keys(), .values() and .items() GvR SA 3107 Function Annotations Winter, Lownds SA 3109 Raising Exceptions in Python 3000 Winter SA 3110 Catching Exceptions in Python 3000 Winter Modified: peps/trunk/pep-3106.txt ============================================================================== --- peps/trunk/pep-3106.txt (original) +++ peps/trunk/pep-3106.txt Sun Mar 4 20:59:36 2007 @@ -3,7 +3,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Guido van Rossum -Status: Draft +Status: Accepted Type: Standards Content-Type: text/x-rst Created: 19-Dec-2006 From python-checkins at python.org Sun Mar 4 21:02:58 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 21:02:58 +0100 (CET) Subject: [Python-checkins] r54121 - peps/trunk/pep-0000.txt peps/trunk/pep-3104.txt Message-ID: <20070304200258.456CF1E400A@bag.python.org> Author: brett.cannon Date: Sun Mar 4 21:02:51 2007 New Revision: 54121 Modified: peps/trunk/pep-0000.txt peps/trunk/pep-3104.txt Log: Mark PEP 3104 as finished (implemented at PyCon 2007 sprint by Jeremy Hylton). Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 21:02:51 2007 @@ -113,7 +113,6 @@ S 362 Function Signature Object Cannon, Seo S 754 IEEE 754 Floating Point Special Values Warnes S 3101 Advanced String Formatting Talin - S 3104 Access to Names in Outer Scopes Yee S 3108 Standard Library Reorganization Cannon @@ -176,6 +175,7 @@ SF 352 Required Superclass for Exceptions GvR, Cannon SF 353 Using ssize_t as the index type von Loewis SF 357 Allowing Any Object to be Used for Slicing Oliphant + SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl Empty PEPs (or containing only an abstract) @@ -450,9 +450,9 @@ S 3101 Advanced String Formatting Talin SA 3102 Keyword-Only Arguments Talin SR 3103 A Switch/Case Statement GvR - S 3104 Access to Names in Outer Scopes Yee + SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl - S 3106 Revamping dict.keys(), .values() and .items() GvR + SA 3106 Revamping dict.keys(), .values() and .items() GvR SA 3107 Function Annotations Winter, Lownds S 3108 Standard Library Reorganization Cannon SA 3109 Raising Exceptions in Python 3000 Winter Modified: peps/trunk/pep-3104.txt ============================================================================== --- peps/trunk/pep-3104.txt (original) +++ peps/trunk/pep-3104.txt Sun Mar 4 21:02:51 2007 @@ -3,7 +3,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Ka-Ping Yee -Status: Draft +Status: Accepted Type: Standards Track Python-Version: 3.0 Content-Type: text/x-rst From python-checkins at python.org Sun Mar 4 21:04:15 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 21:04:15 +0100 (CET) Subject: [Python-checkins] r54122 - peps/trunk/pep-0000.txt Message-ID: <20070304200415.3B4BB1E4009@bag.python.org> Author: brett.cannon Date: Sun Mar 4 21:04:12 2007 New Revision: 54122 Modified: peps/trunk/pep-0000.txt Log: Make PEP 3102 as finished. Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 21:04:12 2007 @@ -77,7 +77,6 @@ Accepted PEPs (accepted; may not be implemented yet) - SA 3102 Keyword-Only Arguments Talin SA 3106 Revamping dict.keys(), .values() and .items() GvR SA 3107 Function Annotations Winter, Lownds SA 3109 Raising Exceptions in Python 3000 Winter @@ -175,6 +174,7 @@ SF 352 Required Superclass for Exceptions GvR, Cannon SF 353 Using ssize_t as the index type von Loewis SF 357 Allowing Any Object to be Used for Slicing Oliphant + SF 3102 Keyword-Only Arguments Talin SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl @@ -448,7 +448,7 @@ I 3099 Things that will Not Change in Python 3000 Brandl I 3100 Python 3.0 Plans Kuchling, Cannon S 3101 Advanced String Formatting Talin - SA 3102 Keyword-Only Arguments Talin + SF 3102 Keyword-Only Arguments Talin SR 3103 A Switch/Case Statement GvR SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl From python-checkins at python.org Sun Mar 4 21:09:53 2007 From: python-checkins at python.org (brett.cannon) Date: Sun, 4 Mar 2007 21:09:53 +0100 (CET) Subject: [Python-checkins] r54123 - peps/trunk/pep-0000.txt Message-ID: <20070304200953.259851E4009@bag.python.org> Author: brett.cannon Date: Sun Mar 4 21:09:49 2007 New Revision: 54123 Modified: peps/trunk/pep-0000.txt Log: Mark PEP 3107 as finished. Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Sun Mar 4 21:09:49 2007 @@ -78,7 +78,6 @@ Accepted PEPs (accepted; may not be implemented yet) SA 3106 Revamping dict.keys(), .values() and .items() GvR - SA 3107 Function Annotations Winter, Lownds SA 3109 Raising Exceptions in Python 3000 Winter SA 3110 Catching Exceptions in Python 3000 Winter SA 3111 Simple input built-in in Python 3000 Roberge @@ -177,6 +176,7 @@ SF 3102 Keyword-Only Arguments Talin SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl + SF 3107 Function Annotations Winter, Lownds Empty PEPs (or containing only an abstract) @@ -453,7 +453,7 @@ SF 3104 Access to Names in Outer Scopes Yee SF 3105 Make print a function Brandl SA 3106 Revamping dict.keys(), .values() and .items() GvR - SA 3107 Function Annotations Winter, Lownds + SF 3107 Function Annotations Winter, Lownds S 3108 Standard Library Reorganization Cannon SA 3109 Raising Exceptions in Python 3000 Winter SA 3110 Catching Exceptions in Python 3000 Winter From python-checkins at python.org Sun Mar 4 21:52:31 2007 From: python-checkins at python.org (skip.montanaro) Date: Sun, 4 Mar 2007 21:52:31 +0100 (CET) Subject: [Python-checkins] r54124 - python/trunk/setup.py Message-ID: <20070304205231.4BD911E400A@bag.python.org> Author: skip.montanaro Date: Sun Mar 4 21:52:28 2007 New Revision: 54124 Modified: python/trunk/setup.py Log: Teach setup.py how to find Berkeley DB on Macs using MacPorts. Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Sun Mar 4 21:52:28 2007 @@ -616,10 +616,11 @@ '/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 @@ -630,6 +631,8 @@ 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) @@ -654,7 +657,7 @@ 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 = {} @@ -677,7 +680,7 @@ if ( (not db_ver_inc_map.has_key(db_ver)) 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 @@ -686,7 +689,8 @@ 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 = db_ver_inc_map.keys() db_found_vers.sort() @@ -697,10 +701,8 @@ # 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) From python-checkins at python.org Sun Mar 4 21:54:15 2007 From: python-checkins at python.org (skip.montanaro) Date: Sun, 4 Mar 2007 21:54:15 +0100 (CET) Subject: [Python-checkins] r54125 - python/trunk/Misc/NEWS Message-ID: <20070304205415.3C4DC1E400A@bag.python.org> Author: skip.montanaro Date: Sun Mar 4 21:54:12 2007 New Revision: 54125 Modified: python/trunk/Misc/NEWS Log: note MacPorts/BerkDB change in setup.py Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sun Mar 4 21:54:12 2007 @@ -136,6 +136,8 @@ Library ------- +- Taught setup.py how to locate Berkeley DB on Macs using MacPorts. + - Added heapq.merge() for merging sorted input streams. - Added collections.NamedTuple() for assigning field names to tuples. From buildbot at python.org Sun Mar 4 22:38:26 2007 From: buildbot at python.org (buildbot at python.org) Date: Sun, 04 Mar 2007 21:38:26 +0000 Subject: [Python-checkins] buildbot warnings in g4 osx.4 trunk Message-ID: <20070304213827.1A0721E400A@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%2520trunk/builds/1784 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sun Mar 4 22:53:32 2007 From: python-checkins at python.org (eric.smith) Date: Sun, 4 Mar 2007 22:53:32 +0100 (CET) Subject: [Python-checkins] r54126 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070304215332.4CAFA1E400A@bag.python.org> Author: eric.smith Date: Sun Mar 4 22:53:27 2007 New Revision: 54126 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Decimal ('d') formatting complete, including test cases, for ascii. Unicode for decimal does not work yet, but it's next on my list to fix. Hex and octal may also work, but since I haven't written test cases I'm still classifying them as not working. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Sun Mar 4 22:53:27 2007 @@ -44,6 +44,10 @@ "My name is {0} :-{{}}", "Fred") self.formatEquals("abc", "{0:}", "abc") # is this allowed? + def test_no_substitutions(self): + self.formatEquals("", "") + self.formatEquals("how now brown cow", "how now brown cow") + def test_missingargs(self): #self.formatRaises(None, "Doesn't use all {0} args", 42, 24) self.formatRaises(ValueError, "There is no {4} arg", 42, 24) @@ -85,13 +89,14 @@ # "The shiny red {0[-2]}", t) def test_formatlookup(self): - self.formatEquals("32_0>4d", "{0:{1}}", 32, "0>4d") - self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d") + pass +# self.formatEquals("32", "{0:{1}}", 32, "0>4d") +# self.formatEquals("32", "{0:{1}{2}4{3}}", 32, "*", ">", "d") def test_specifiers(self): self.formatEquals("a", "{0:c}", ord("a")) self.formatEquals("8_08b", "{0:08b}", 8) - self.formatEquals("8_ >3d", "{0: >3d}", 8) +# self.formatEquals("8", "{0: >3d}", 8) self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) def test_string_specifiers(self): @@ -116,11 +121,43 @@ self.formatEquals("'abcdefg", "{0:8r}", "abcdefg") def test_decimal_specifiers(self): - pass -# self.assertRaises(Exception, "{0:d}", "non-number") + self.assertRaises(TypeError, "{0:d}", "non-number") -# self.formatEquals("0", "{0:d}", 0) -# self.formatEquals("1" + "0" * 100, "{0:d}", 10**100) + self.formatEquals("0", "{0:d}", 0) + self.formatEquals("123", "{0:d}", 123) + self.formatEquals("-123", "{0:d}", -123) + self.formatEquals("+123", "{0:+d}", 123) + self.formatEquals("-123", "{0:+d}", -123) + self.formatEquals("123", "{0:-d}", 123) + self.formatEquals("-123", "{0:-d}", -123) + self.formatEquals("123", "{0:()d}", 123) + self.formatEquals("(123)", "{0:()d}", -123) + + # need a long padding to force a reallocation (and hopefully a + # memory move) in 'd' handling + self.formatEquals(" " * 997 + "100", "{0:1000d}", 100) + + # now test with the 3 kinds of padding + self.formatEquals("0 ", "{0:<10d}", 0) + self.formatEquals("123 ", "{0:<10d}", 123) + self.formatEquals("-123 ", "{0:<10d}", -123) + self.formatEquals(" 123", "{0:>10d}", 123) + self.formatEquals(" -123", "{0:>10d}", -123) + self.formatEquals(" 123", "{0:=10d}", 123) + self.formatEquals("+ 123", "{0:=+10d}", 123) + self.formatEquals("- 123", "{0:=10d}", -123) + self.formatEquals("- 123", "{0:=+10d}", -123) + self.formatEquals(" 123", "{0:=()10d}", 123) + + # XXX I'm not sure this is correct, maybe it should be " (123)" + self.formatEquals("( 123)", "{0:=()10d}", -123) + + self.formatEquals("1" + "0" * 100, "{0:d}", 10**100) + self.formatEquals("-1" + "0" * 100, "{0:d}", -10**100) + self.formatEquals("+1" + "0" * 100, "{0:+d}", 10**100) + self.formatEquals("(1" + "0" * 100 + ")", "{0:()d}", -10**100) + self.formatEquals("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) + self.formatEquals("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) def test_char_specifiers(self): self.formatEquals("A", "{0:c}", "A") Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Sun Mar 4 22:53:27 2007 @@ -63,6 +63,16 @@ #define PySet_GET_SIZE PyDict_Size #endif + +/* MAXLEN_INT_STRING is the maximum length of an integer represented + * as a string. The analysis in stringobject.c shows that 24 is the + * worst case. Allocate more, just in case. */ +/* fmt = '%#.' + `prec` + 'l' + `type` + worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine) + + 1 + 1 = 24 */ +#define MAXLEN_INT_STRING 64 + + /************************************************************************/ /*********** Global data structures and forward declarations *********/ /************************************************************************/ @@ -102,7 +112,7 @@ typedef struct { CH_TYPE *ptr; CH_TYPE *end; - PyObject * obj; + PyObject *obj; } SubStringObj; /* @@ -281,12 +291,32 @@ output_data(FmtState *fs, const CH_TYPE *s, Py_ssize_t count) { CH_TYPE *dst; - if (output_allocate(fs, count, &dst) == 0) - return 0; + + /* there is some duplicate code here with output_allocate, + which is here to avoid a function call if there's already + enough allocated space */ + Py_ssize_t room = fs->outstr.end - fs->outstr.ptr; + if (count > room) { + if (output_allocate(fs, count, &dst) == 0) + return 0; + } else { + dst = fs->outstr.ptr; + fs->outstr.ptr += count; + } memcpy(dst, s, count * sizeof(CH_TYPE)); return 1; } + +/* + shrink the allocated output size by count bytes +*/ +Py_LOCAL_INLINE(void) +output_shrink(FmtState* fs, Py_ssize_t count) +{ + fs->outstr.ptr -= count; +} + /************************************************************************/ /*********** Format string parsing -- integers and identifiers *********/ /************************************************************************/ @@ -870,65 +900,274 @@ return output_data(fs, &buf, 1); } + +/* code liberally borrowed from stringobject.c's formatint() */ +/* into the output buffer, put . the caller will + justify as needed */ +/* return the total number of bytes written, or -1 for error + sets pbuf to point to the output buffer */ +static Py_ssize_t +_format_int(PyObject* v, FmtState *fs, CH_TYPE type, CH_TYPE **pbuf) +{ + CH_TYPE *ptr; + Py_ssize_t buflen = MAXLEN_INT_STRING; + Py_ssize_t len; + long x; + char format[3]; /* a temporary buffer to use to build the format + string. */ + + x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, "int argument required, not %.200s", + v->ob_type->tp_name); + return -1; + } + + if (output_allocate(fs, MAXLEN_INT_STRING, pbuf) == 0) { + return -1; + } + + /* remember the start of the string */ + ptr = *pbuf; + + if (x < 0 && (type == 'x' || type == 'X' || type == 'o')) { + **pbuf = '-'; + *pbuf++; + buflen--; + x = -x; + } + + /* build up the format string */ + format[0] = '%'; + format[1] = type; + format[2] = '\0'; + + PyOS_snprintf(*pbuf, buflen, format, x); + + /* compute the length. I believe this is done because the return value from + snprintf above is unreliable */ + + len = strlen(ptr); + + /* shrink the buffer down to how many characters we actually + wrote. this is cheap, just pointer arithmetic */ + output_shrink(fs, MAXLEN_INT_STRING - len); + + return len; +} + static int format_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { -#if 0 + Py_ssize_t width; + CH_TYPE align = format->align; + CH_TYPE *p_buf; + CH_TYPE *p_digits; /* pointer to the digits we have */ + CH_TYPE n_digits; /* count of digits we have */ + CH_TYPE sign; + Py_ssize_t n_lpadding; + Py_ssize_t n_spadding; + Py_ssize_t n_rpadding; + CH_TYPE lsign; + Py_ssize_t n_lsign = 0; + CH_TYPE rsign; + Py_ssize_t n_rsign = 0; + Py_ssize_t n_total; /* the total length we're going to write */ + Py_ssize_t n_allocated; /* how much space we actually allocated + when we wrote the digits into the + output */ + Py_ssize_t ofs_buf; + Py_ssize_t ofs_digits; + CH_TYPE *tmp; + + /*************************************************************************/ + /* first, do everything as ascii *****************************************/ if (PyLong_Check(fieldobj)) { - int ilen; - temp = _PyString_FormatLong(v, flags, - prec, c, &pbuf, &ilen); - len = ilen; - if (!temp) - goto error; - sign = 1; - } - else { - pbuf = formatbuf; - len = formatint(pbuf, - sizeof(formatbuf), - flags, prec, c, v); - if (len < 0) - goto error; - sign = 1; + /* a long integer */ + + /* XXX this should probably be Py_ssize_t, but that's not how + the function is declared */ + int len; + int ok; + PyObject *strobj = _PyString_FormatLong(fieldobj, 0, + 0, format->type, &p_buf, &len); + + if (!strobj) + return 0; + + n_allocated = STROBJ_GET_SIZE(strobj); + p_buf = fs->outstr.ptr; + + /* allocate space in the output, and copy the data */ + ok = output_data(fs, STROBJ_AS_PTR(strobj), n_allocated); + + /* we're done with the string representation */ + Py_DECREF(strobj); + + if (ok == 0) + return 0; + } else { + /* a regular integer, we can be quicker in this case */ + + /* n_allocated includes the total number of characters + written, including the sign, if any */ + n_allocated = _format_int(fieldobj, fs, format->type, &p_buf); + if (n_allocated < 0) + return 0; } - if (flags & F_ZERO) - fill = '0'; -#endif - return format_DUMMY(fieldobj, fs); + + /* if needed, convert from asci to unicode */ +#if C_UNICODE + /* taken from unicodeobject.c's strtounicode() */ #if 0 - PyObject *intobj; - PyObject *strobj; - CH_TYPE* src; - Py_ssize_t len; - int negative = 0; - int ok; +strtounicode(Py_UNICODE *buffer, const char *charbuffer) +{ + register Py_ssize_t i; + Py_ssize_t len = strlen(charbuffer); + for (i = len - 1; i >= 0; i--) + buffer[i] = (Py_UNICODE) charbuffer[i]; + return len; +} +#endif +#endif + /* end ascii conversion **************************************************/ - intobj = PyIntObject(fieldobj); - if (intobj == NULL) - return 0; + /* determine if a sign was written, and how many digits we wrote */ + n_digits = n_allocated; + p_digits = p_buf; + + /* is a sign character present in the output? if so, remember it + and skip it */ + sign = p_buf[0]; + if (sign == '-') { + p_digits++; + n_digits--; + } + else + sign = '\0'; + + /* the output will look like: + | | + | | + | | + + lsign and rsign are computed from format->sign and the actual + sign of the number - strobj = STROBJ_STR(intobj); - Py_DECREF(intobj); + digits is already known - /* see if we're negative. we know src must point to at least one - character, so skip that check */ - src = STROBJ_AS_PTR(strobj); - len = STROBJ_GET_SIZE(strobj); - if (src[0] == '-') { - /* remember that we're negative, and skip the char */ - negative = 1; - src++; - len--; + the total width is either given, or computed from the + actual digits + + only one of lpadding, spadding, and rpadding can be non-zero, + and it's calculated from the width and other fields + */ + + /* compute the various parts we're going to write */ + if (format->sign == '+') { + /* always put a + or - */ + n_lsign = 1; + lsign = (sign == '-' ? '-' : '+'); + } else if (format->sign == '(') { + if (sign == '-') { + n_lsign = 1; + lsign = '('; + n_rsign = 1; + rsign = ')'; + } + } else if (format->sign == ' ') { + n_lsign = 1; + lsign = (sign == '-' ? '-' : ' '); + } else { + /* non specified, or the default (-) */ + if (sign == '-') { + n_lsign = 1; + lsign = '-'; + } } - ok = output_string_chars(fs, src, len, format); - Py_DECREF(strobj); + /* now the number of padding characters */ + n_lpadding = n_spadding = n_rpadding = 0; + if (format->width == -1) { + /* no padding at all, nothing to do */ + } else { + /* see if any padding is needed */ + if (n_lsign + n_digits + n_rsign >= format->width) { + /* no padding needed, we're already bigger than the + requested width */ + } else { + /* determine which of left, space, or right padding is + needed */ + Py_ssize_t padding = format->width - (n_lsign + n_digits + n_rsign); + if (format->align == '<') + n_rpadding = padding; + else if (format->align == '>') + n_lpadding = padding; + else + /* must be '=' */ + n_spadding = padding; + } + } - return ok; + /* set the total length of the string */ + n_total = n_lpadding + n_lsign + n_spadding + n_digits + n_rsign + n_rpadding; + assert(n_total >= n_allocated); + + /* because we're going to reallocate, our pointers might be + invalidated. remember the offsets, then re-create the pointers + after the allocation. */ + tmp = STROBJ_AS_PTR(fs->outstr.obj); + ofs_buf = p_buf - tmp; + ofs_digits = p_digits - tmp; + + output_allocate(fs, n_total - n_allocated, &tmp); + + tmp = STROBJ_AS_PTR(fs->outstr.obj); + p_buf = tmp + ofs_buf; + p_digits = tmp + ofs_digits; + +#if 0 + printf("p_buf %p\n", p_buf); + printf("p_digits %p\n", p_digits); + printf("n_digits: %d\n", n_digits); + printf("n_lpadding: %d\n", n_lpadding); + printf("n_lsign: %d\n", n_lsign); + printf("lsign: %d(%c)\n", lsign, lsign); + printf("n_rsign: %d\n", n_rsign); + printf("rsign: %d(%c)\n", rsign, rsign); + printf("n_spadding: %d\n", n_spadding); + printf("n_rpadding: %d\n", n_rpadding); #endif + + /* copy the characters into position first, since we're going to + overwrite some of that space */ + /* short circuit test, in case we don't have to move anything */ + if (p_buf + (n_lpadding + n_lsign + n_spadding) != p_digits) + memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits, n_digits * sizeof(CH_TYPE)); + + if (n_lpadding) { + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_lpadding); + p_buf += n_lpadding; + } + if (n_lsign == 1) { + *p_buf++ = lsign; + } + if (n_spadding) { + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_spadding); + p_buf += n_spadding; + } + p_buf += n_digits; + if (n_rsign == 1) { + *p_buf++ = rsign; + } + if (n_rpadding) { + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_rpadding); + p_buf += n_rpadding; + } + + return 1; } static int @@ -939,13 +1178,6 @@ } static int -format_exponentUC(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int format_fixed(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { @@ -953,13 +1185,6 @@ } static int -format_fixedUC(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int format_general(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { @@ -967,27 +1192,13 @@ } static int -format_generalUC(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int -format_number(PyObject *fieldobj, FmtState *fs, +format_locale_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { return format_DUMMY(fieldobj, fs); } static int -format_octal(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int format_repr(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { @@ -1028,20 +1239,6 @@ } static int -format_hex(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int -format_hexUC(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int format_percentage(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { @@ -1057,20 +1254,20 @@ case 'c': return format_char; /* as character */ case 'd': return format_decimal; /* decimal integer */ case 'e': return format_exponent; /* exponential notation */ - case 'E': return format_exponentUC; /* exponential notation + case 'E': return format_exponent; /* exponential notation with uppercase 'E' */ case 'f': return format_fixed; /* fixed-point */ - case 'F': return format_fixedUC; /* fixed-point with uppercase */ + case 'F': return format_fixed; /* fixed-point with uppercase */ case 'g': return format_general; /* general number notation */ - case 'G': return format_generalUC; /* general number notation + case 'G': return format_general; /* general number notation with uppercase 'E' */ - case 'n': return format_number; /* number in locale-specific + case 'n': return format_locale_number; /* number in locale-specific format */ - case 'o': return format_octal; /* octal */ + case 'o': return format_decimal; /* octal */ case 'r': return format_repr; /* in repr() format */ case 's': return format_string; /* convert using str() */ - case 'x': return format_hex; /* base 16 */ - case 'X': return format_hexUC; /* base 16 uppercase */ + case 'x': return format_decimal; /* base 16 */ + case 'X': return format_decimal; /* base 16 uppercase */ case '%': return format_percentage; /* as percentage */ default: return NULL; From buildbot at python.org Sun Mar 4 23:13:24 2007 From: buildbot at python.org (buildbot at python.org) Date: Sun, 04 Mar 2007 22:13:24 +0000 Subject: [Python-checkins] buildbot warnings in hppa Ubuntu dapper trunk Message-ID: <20070304221324.CE3F01E400A@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper trunk. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%2520trunk/builds/78 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Mon Mar 5 00:37:38 2007 From: python-checkins at python.org (eric.smith) Date: Mon, 5 Mar 2007 00:37:38 +0100 (CET) Subject: [Python-checkins] r54127 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070304233738.4BD2E1E400A@bag.python.org> Author: eric.smith Date: Mon Mar 5 00:37:37 2007 New Revision: 54127 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Decimal ('d') unicode formatting complete. Modified test suite to test for both Unicode and string versions, for some tests. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 00:37:37 2007 @@ -20,6 +20,16 @@ val = pep3101.format(text, *args, **kwargs) self.assertEquals(val, result) + def formatEqualsWithUnicode(self, result, text, *args, **kwargs): + text = str(text) + result = str(result) + val = pep3101.format(text, *args, **kwargs) + self.assertEquals(val, result) + + # a quick check for unicode version + val = pep3101.format(unicode(text), *args, **kwargs) + self.assertEquals(val, unicode(result)) + def formatRaises(self, exc, text, *args, **kwargs): exc = exc or Exception #StringFormat.FormatError text = str(text) @@ -100,64 +110,64 @@ self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) def test_string_specifiers(self): - self.formatEquals("abc", "{0:.3s}", "abc") + self.formatEqualsWithUnicode("abc", "{0:.3s}", "abc") - self.formatEquals("ab", "{0:.3s}", "ab") + self.formatEqualsWithUnicode("ab", "{0:.3s}", "ab") - self.formatEquals("abc", "{0:.3s}", "abcdef") - self.formatEquals("resultx", "{0:x<7s}", "result") - self.formatEquals("resultxx", "{0:x<8s}", "result") - self.formatEquals("result ", "{0: <7s}", "result") - self.formatEquals("result ", "{0:<7s}", "result") - self.formatEquals(" result", "{0:>7s}", "result") - self.formatEquals(" result", "{0:>8s}", "result") + self.formatEqualsWithUnicode("abc", "{0:.3s}", "abcdef") + self.formatEqualsWithUnicode("resultx", "{0:x<7s}", "result") + self.formatEqualsWithUnicode("resultxx", "{0:x<8s}", "result") + self.formatEqualsWithUnicode("result ", "{0: <7s}", "result") + self.formatEqualsWithUnicode("result ", "{0:<7s}", "result") + self.formatEqualsWithUnicode(" result", "{0:>7s}", "result") + self.formatEqualsWithUnicode(" result", "{0:>8s}", "result") def test_repr_specifiers(self): - self.formatEquals("3", "{0:r}", 3) - self.formatEquals("3.141", "{0:5r}", 3.141592654) + self.formatEqualsWithUnicode("3", "{0:r}", 3) + self.formatEqualsWithUnicode("3.141", "{0:5r}", 3.141592654) # I'm not sure this is a good test, since the quoting might change - self.formatEquals("'abcdefg'", "{0:r}", "abcdefg") - self.formatEquals("'abcdefg", "{0:8r}", "abcdefg") + self.formatEqualsWithUnicode("'abcdefg'", "{0:r}", "abcdefg") + self.formatEqualsWithUnicode("'abcdefg", "{0:8r}", "abcdefg") def test_decimal_specifiers(self): self.assertRaises(TypeError, "{0:d}", "non-number") - self.formatEquals("0", "{0:d}", 0) - self.formatEquals("123", "{0:d}", 123) - self.formatEquals("-123", "{0:d}", -123) - self.formatEquals("+123", "{0:+d}", 123) - self.formatEquals("-123", "{0:+d}", -123) - self.formatEquals("123", "{0:-d}", 123) - self.formatEquals("-123", "{0:-d}", -123) - self.formatEquals("123", "{0:()d}", 123) - self.formatEquals("(123)", "{0:()d}", -123) + self.formatEqualsWithUnicode("0", "{0:d}", 0) + self.formatEqualsWithUnicode("123", "{0:d}", 123) + self.formatEqualsWithUnicode("-123", "{0:d}", -123) + self.formatEqualsWithUnicode("+123", "{0:+d}", 123) + self.formatEqualsWithUnicode("-123", "{0:+d}", -123) + self.formatEqualsWithUnicode("123", "{0:-d}", 123) + self.formatEqualsWithUnicode("-123", "{0:-d}", -123) + self.formatEqualsWithUnicode("123", "{0:()d}", 123) + self.formatEqualsWithUnicode("(123)", "{0:()d}", -123) # need a long padding to force a reallocation (and hopefully a # memory move) in 'd' handling - self.formatEquals(" " * 997 + "100", "{0:1000d}", 100) + self.formatEqualsWithUnicode(" " * 997 + "100", "{0:1000d}", 100) # now test with the 3 kinds of padding - self.formatEquals("0 ", "{0:<10d}", 0) - self.formatEquals("123 ", "{0:<10d}", 123) - self.formatEquals("-123 ", "{0:<10d}", -123) - self.formatEquals(" 123", "{0:>10d}", 123) - self.formatEquals(" -123", "{0:>10d}", -123) - self.formatEquals(" 123", "{0:=10d}", 123) - self.formatEquals("+ 123", "{0:=+10d}", 123) - self.formatEquals("- 123", "{0:=10d}", -123) - self.formatEquals("- 123", "{0:=+10d}", -123) - self.formatEquals(" 123", "{0:=()10d}", 123) + self.formatEqualsWithUnicode("0 ", "{0:<10d}", 0) + self.formatEqualsWithUnicode("123 ", "{0:<10d}", 123) + self.formatEqualsWithUnicode("-123 ", "{0:<10d}", -123) + self.formatEqualsWithUnicode(" 123", "{0:>10d}", 123) + self.formatEqualsWithUnicode(" -123", "{0:>10d}", -123) + self.formatEqualsWithUnicode(" 123", "{0:=10d}", 123) + self.formatEqualsWithUnicode("+ 123", "{0:=+10d}", 123) + self.formatEqualsWithUnicode("- 123", "{0:=10d}", -123) + self.formatEqualsWithUnicode("- 123", "{0:=+10d}", -123) + self.formatEqualsWithUnicode(" 123", "{0:=()10d}", 123) # XXX I'm not sure this is correct, maybe it should be " (123)" - self.formatEquals("( 123)", "{0:=()10d}", -123) + self.formatEqualsWithUnicode("( 123)", "{0:=()10d}", -123) - self.formatEquals("1" + "0" * 100, "{0:d}", 10**100) - self.formatEquals("-1" + "0" * 100, "{0:d}", -10**100) - self.formatEquals("+1" + "0" * 100, "{0:+d}", 10**100) - self.formatEquals("(1" + "0" * 100 + ")", "{0:()d}", -10**100) - self.formatEquals("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) - self.formatEquals("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) + self.formatEqualsWithUnicode("1" + "0" * 100, "{0:d}", 10**100) + self.formatEqualsWithUnicode("-1" + "0" * 100, "{0:d}", -10**100) + self.formatEqualsWithUnicode("+1" + "0" * 100, "{0:+d}", 10**100) + self.formatEqualsWithUnicode("(1" + "0" * 100 + ")", "{0:()d}", -10**100) + self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) + self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) def test_char_specifiers(self): self.formatEquals("A", "{0:c}", "A") Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 00:37:37 2007 @@ -901,15 +901,40 @@ } +#if C_UNICODE +/* taken from unicodeobject.c */ +/* note that since we work backward, the ranges can overlap */ +static Py_ssize_t +strtounicode(Py_UNICODE *buffer, const char *charbuffer, Py_ssize_t len) +{ + register Py_ssize_t i; + + /* don't know the length, calculate it */ + if (len == -1) + len = strlen(charbuffer); + for (i = len - 1; i >= 0; i--) + buffer[i] = (Py_UNICODE) charbuffer[i]; + + return len; +} +#endif + /* code liberally borrowed from stringobject.c's formatint() */ /* into the output buffer, put . the caller will justify as needed */ -/* return the total number of bytes written, or -1 for error - sets pbuf to point to the output buffer */ +/* this code internally uses 8-bit chars, even when formatting + unicode. that's because we use PyOS_snprintf() from both 8-bit and + unicode. that means we need to cast the allocated pointer, which + is always in units of CH_TYPE */ +/* when this function returns, the result will be in char or unicode + (CH_TYPE), as needed */ +/* return the total number of characters written, or -1 for error sets + pbuf to point to the output buffer */ static Py_ssize_t -_format_int(PyObject* v, FmtState *fs, CH_TYPE type, CH_TYPE **pbuf) +_format_int(PyObject* v, FmtState *fs, char type, CH_TYPE **pbuf) { - CH_TYPE *ptr; + char *ptr; + char *start; Py_ssize_t buflen = MAXLEN_INT_STRING; Py_ssize_t len; long x; @@ -923,16 +948,17 @@ return -1; } - if (output_allocate(fs, MAXLEN_INT_STRING, pbuf) == 0) { + /* allocate as much space as we'll ever possibly need. note that + if we're doing unicode, we allocate as bytes, format as bytes, + but convert to unicode, all in the same buffer. */ + if (output_allocate(fs, MAXLEN_INT_STRING, pbuf) == 0) return -1; - } - /* remember the start of the string */ - ptr = *pbuf; + /* remember the start of the string, as 8-bit chars */ + start = ptr = (char*)*pbuf; if (x < 0 && (type == 'x' || type == 'X' || type == 'o')) { - **pbuf = '-'; - *pbuf++; + *ptr++ = '-'; buflen--; x = -x; } @@ -942,12 +968,17 @@ format[1] = type; format[2] = '\0'; - PyOS_snprintf(*pbuf, buflen, format, x); + PyOS_snprintf(ptr, buflen, format, x); + + /* convert from chars to unicode, if needed */ +#if C_UNICODE + len = strtounicode(*pbuf, start, -1); +#else /* compute the length. I believe this is done because the return value from snprintf above is unreliable */ - - len = strlen(ptr); + len = strlen(start); +#endif /* shrink the buffer down to how many characters we actually wrote. this is cheap, just pointer arithmetic */ @@ -956,11 +987,49 @@ return len; } +static Py_ssize_t +_format_long(PyObject* v, FmtState *fs, char type, CH_TYPE **pbuf) +{ + char* p_charbuf; + Py_ssize_t n_allocated; + CH_TYPE* ptr; + int ok; + /* XXX len should probably be Py_ssize_t, but that's not how the + function is declared in stringobject.c */ + int len; + PyObject *strobj; + + strobj = _PyString_FormatLong(v, 0, 0, type, &p_charbuf, &len); + if (!strobj) + return -1; + + n_allocated = len; + *pbuf = fs->outstr.ptr; + +#if C_UNICODE + /* allocate space in the output string, as CH_TYPE */ + ok = output_allocate(fs, n_allocated, &ptr); + if (ok != 0) { + strtounicode(ptr, p_charbuf, n_allocated); + } +#else + ok = output_data(fs, STROBJ_AS_PTR(strobj), n_allocated); +#endif + + /* we're done with the string representation */ + Py_DECREF(strobj); + + if (!ok) + return -1; + return n_allocated; +} + static int format_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { Py_ssize_t width; + char* p_abuf; CH_TYPE align = format->align; CH_TYPE *p_buf; CH_TYPE *p_digits; /* pointer to the digits we have */ @@ -981,58 +1050,20 @@ Py_ssize_t ofs_digits; CH_TYPE *tmp; - /*************************************************************************/ - /* first, do everything as ascii *****************************************/ - if (PyLong_Check(fieldobj)) { - /* a long integer */ - - /* XXX this should probably be Py_ssize_t, but that's not how - the function is declared */ - int len; - int ok; - PyObject *strobj = _PyString_FormatLong(fieldobj, 0, - 0, format->type, &p_buf, &len); + /* n_allocated includes the total number of characters written, + including the sign, if any */ + /* note that we're potentially converting format->type from + Unicode to char, but that's okay because we know what the valid + values can be */ - if (!strobj) - return 0; - - n_allocated = STROBJ_GET_SIZE(strobj); - p_buf = fs->outstr.ptr; - - /* allocate space in the output, and copy the data */ - ok = output_data(fs, STROBJ_AS_PTR(strobj), n_allocated); - - /* we're done with the string representation */ - Py_DECREF(strobj); - - if (ok == 0) - return 0; + if (PyLong_Check(fieldobj)) { + n_allocated = _format_long(fieldobj, fs, (char)format->type, &p_buf); } else { /* a regular integer, we can be quicker in this case */ - - /* n_allocated includes the total number of characters - written, including the sign, if any */ - n_allocated = _format_int(fieldobj, fs, format->type, &p_buf); - if (n_allocated < 0) - return 0; + n_allocated = _format_int(fieldobj, fs, (char)format->type, &p_buf); } - - /* if needed, convert from asci to unicode */ -#if C_UNICODE - /* taken from unicodeobject.c's strtounicode() */ -#if 0 -strtounicode(Py_UNICODE *buffer, const char *charbuffer) -{ - register Py_ssize_t i; - Py_ssize_t len = strlen(charbuffer); - for (i = len - 1; i >= 0; i--) - buffer[i] = (Py_UNICODE) charbuffer[i]; - - return len; -} -#endif -#endif - /* end ascii conversion **************************************************/ + if (n_allocated < 0) + return 0; /* determine if a sign was written, and how many digits we wrote */ n_digits = n_allocated; From python-checkins at python.org Mon Mar 5 01:22:43 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 01:22:43 +0100 (CET) Subject: [Python-checkins] r54128 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305002243.9F5EB1E400A@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 01:22:42 2007 New Revision: 54128 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Removed compiler warnings Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 01:22:42 2007 @@ -992,7 +992,9 @@ { char* p_charbuf; Py_ssize_t n_allocated; +#if C_UNICODE CH_TYPE* ptr; +#endif int ok; /* XXX len should probably be Py_ssize_t, but that's not how the function is declared in stringobject.c */ @@ -1028,9 +1030,6 @@ format_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - Py_ssize_t width; - char* p_abuf; - CH_TYPE align = format->align; CH_TYPE *p_buf; CH_TYPE *p_digits; /* pointer to the digits we have */ CH_TYPE n_digits; /* count of digits we have */ @@ -1038,9 +1037,9 @@ Py_ssize_t n_lpadding; Py_ssize_t n_spadding; Py_ssize_t n_rpadding; - CH_TYPE lsign; + CH_TYPE lsign = 0; Py_ssize_t n_lsign = 0; - CH_TYPE rsign; + CH_TYPE rsign = 0; Py_ssize_t n_rsign = 0; Py_ssize_t n_total; /* the total length we're going to write */ Py_ssize_t n_allocated; /* how much space we actually allocated From python-checkins at python.org Mon Mar 5 01:45:33 2007 From: python-checkins at python.org (eric.smith) Date: Mon, 5 Mar 2007 01:45:33 +0100 (CET) Subject: [Python-checkins] r54129 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305004533.11CA81E401E@bag.python.org> Author: eric.smith Date: Mon Mar 5 01:45:26 2007 New Revision: 54129 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Fix hex and octal formatting. Added testcases. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 01:45:26 2007 @@ -30,6 +30,27 @@ val = pep3101.format(unicode(text), *args, **kwargs) self.assertEquals(val, unicode(result)) + def formatEqualsWithUnicodeUC(self, result, text, *args, **kwargs): + # test both the upper and lowercase versions. assume the + # result and text come in as lowercase + + text = str(text) + result = str(result) + val = pep3101.format(text, *args, **kwargs) + self.assertEquals(val, result) + + # a quick check for unicode version + val = pep3101.format(unicode(text), *args, **kwargs) + self.assertEquals(val, unicode(result)) + + # test the uppercase text version + val = pep3101.format(text.upper(), *args, **kwargs) + self.assertEquals(val, result.upper()) + + # test the uppercase unicode version + val = pep3101.format(unicode(text.upper()), *args, **kwargs) + self.assertEquals(val, unicode(result.upper())) + def formatRaises(self, exc, text, *args, **kwargs): exc = exc or Exception #StringFormat.FormatError text = str(text) @@ -169,6 +190,33 @@ self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) + def test_octal_specifiers(self): + n = int("31415", 8) + + self.assertRaises(TypeError, "{0:o", "non-number") + + self.formatEqualsWithUnicode("0", "{0:o}", 0) + self.formatEqualsWithUnicode("31415", "{0:o}", n) + self.formatEqualsWithUnicode("-31415", "{0:o}", -n) + self.formatEqualsWithUnicode(" " * 995 + "31415", "{0:1000o}", n) + + n = int("314153141531415", 8) + self.formatEqualsWithUnicode("314153141531415", "{0:o}", n) + self.formatEqualsWithUnicode("-314153141531415", "{0:o}", -n) + + def test_hex_specifiers(self): + n = int("beef", 16) + + self.assertRaises(TypeError, "{0:x", "non-number") + + self.formatEqualsWithUnicodeUC("0", "{0:x}", 0) + self.formatEqualsWithUnicodeUC("beef", "{0:x}", n) + self.formatEqualsWithUnicodeUC("-beef", "{0:x}", -n) + + n = int("deadbeef", 16) + self.formatEqualsWithUnicodeUC("deadbeef", "{0:x}", n) + self.formatEqualsWithUnicodeUC("-deadbeef", "{0:x}", -n) + def test_char_specifiers(self): self.formatEquals("A", "{0:c}", "A") self.formatEquals("8", "{0:c}", "8") Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 01:45:26 2007 @@ -1008,6 +1008,19 @@ n_allocated = len; *pbuf = fs->outstr.ptr; + /* if we're hex or octal, check to see if 0 or 0x or 0X was at the + front of the string. if so, skip it. */ + if (type == 'o' && n_allocated >= 1 && *pbuf[0] == '0') { + p_charbuf++; + n_allocated -= 1; + } else if (type == 'x' && n_allocated >= 2 && *pbuf[1] == 'x') { + p_charbuf += 2; + n_allocated -= 1; + } else if (type == 'X' && n_allocated >= 2 && *pbuf[1] == 'X') { + p_charbuf += 2; + n_allocated -= 1; + } + #if C_UNICODE /* allocate space in the output string, as CH_TYPE */ ok = output_allocate(fs, n_allocated, &ptr); @@ -1015,7 +1028,7 @@ strtounicode(ptr, p_charbuf, n_allocated); } #else - ok = output_data(fs, STROBJ_AS_PTR(strobj), n_allocated); + ok = output_data(fs, p_charbuf, n_allocated); #endif /* we're done with the string representation */ From python-checkins at python.org Mon Mar 5 02:31:19 2007 From: python-checkins at python.org (eric.smith) Date: Mon, 5 Mar 2007 02:31:19 +0100 (CET) Subject: [Python-checkins] r54130 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305013119.B0DBD1E400A@bag.python.org> Author: eric.smith Date: Mon Mar 5 02:31:11 2007 New Revision: 54130 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Removed dead code. Fixed unicode 'c' formatter and added test cases for it. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 02:31:11 2007 @@ -15,14 +15,10 @@ # overridden to change the class of string being tested # and the function being used. def formatEquals(self, result, text, *args, **kwargs): - text = str(text) - result = str(result) val = pep3101.format(text, *args, **kwargs) self.assertEquals(val, result) def formatEqualsWithUnicode(self, result, text, *args, **kwargs): - text = str(text) - result = str(result) val = pep3101.format(text, *args, **kwargs) self.assertEquals(val, result) @@ -34,8 +30,6 @@ # test both the upper and lowercase versions. assume the # result and text come in as lowercase - text = str(text) - result = str(result) val = pep3101.format(text, *args, **kwargs) self.assertEquals(val, result) @@ -120,14 +114,13 @@ # "The shiny red {0[-2]}", t) def test_formatlookup(self): - pass -# self.formatEquals("32", "{0:{1}}", 32, "0>4d") -# self.formatEquals("32", "{0:{1}{2}4{3}}", 32, "*", ">", "d") + self.formatEquals("0032", "{0:{1}}", 32, "0>4d") + self.formatEquals("**32", "{0:{1}{2}4{3}}", 32, "*", ">", "d") def test_specifiers(self): self.formatEquals("a", "{0:c}", ord("a")) self.formatEquals("8_08b", "{0:08b}", 8) -# self.formatEquals("8", "{0: >3d}", 8) + self.formatEquals(" 8", "{0: >3d}", 8) self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) def test_string_specifiers(self): @@ -218,10 +211,11 @@ self.formatEqualsWithUnicodeUC("-deadbeef", "{0:x}", -n) def test_char_specifiers(self): - self.formatEquals("A", "{0:c}", "A") - self.formatEquals("8", "{0:c}", "8") - self.formatEquals(";", "{0:c}", ";") - self.formatEquals(";", "{0:c}", long(ord(";"))) + self.formatEqualsWithUnicode("A", "{0:c}", "A") + self.formatEqualsWithUnicode("8", "{0:c}", "8") + self.formatEqualsWithUnicode(";", "{0:c}", ";") + self.formatEqualsWithUnicode(";", "{0:c}", long(ord(";"))) + self.formatEquals(u"f", u"{0:c}", u"f") self.formatRaises(TypeError, "{0:c}", "abcd") Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 02:31:11 2007 @@ -886,18 +886,36 @@ format_char(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - CH_TYPE buf; + char ch; + CH_TYPE uch; if (PyString_Check(fieldobj)) { - if (!PyArg_Parse(fieldobj, "c;%c requires int or char", &buf)) + if (!PyArg_Parse(fieldobj, "c;\"c\" format requires int or char", &ch)) return 0; + /* convert to unicode, if needed */ + uch = (CH_TYPE)ch; +#if C_UNICODE + } else if (PyUnicode_Check(fieldobj)) { + CH_TYPE *ubuf; + int len; + if (!PyArg_Parse(fieldobj, "u#;\"c\" format requires int or char", &ubuf, &len)) + return 0; + + if (len != 1) { + PyErr_Format(PyExc_TypeError, "\"c\" format requires int or char"); + return 0; + } + uch = ubuf[0]; +#endif } else { - if (!PyArg_Parse(fieldobj, "b;%c requires int or char", &buf)) + if (!PyArg_Parse(fieldobj, "b;\"c\" format requires int or char", &ch)) return -1; + /* convert to unicode, if needed */ + uch = (CH_TYPE)ch; } - return output_data(fs, &buf, 1); + return output_data(fs, &uch, 1); } @@ -1353,46 +1371,6 @@ /* do the formatting, writing into the output string */ return formatter(fieldobj, fs, &format); - -#if 0 - - /* Handle the sign logic */ - prefix = '\0'; - suffix = '\0'; - if (sign == '-') { - if (format.sign == '(') { - prefix = '('; - suffix = ')'; - } else { - prefix = '-'; - } - } else if (sign == '+') { - if (format.sign == '+') { - prefix = '+'; - } else if (format.sign == ' ') { - prefix = ' '; - } - } - - /* Handle the padding logic */ - if (format.width != -1) { - padding = format.width - len - (prefix == '\0' ? 0 : 1) - - (suffix == '\0' ? 0 : 1); - if (padding > 0) { -#if 0 - if align == '>': - return fill_char * padding + prefix + result + suffix - elif align == '=' - return prefix + fill_char * padding + result + suffix - else: - return prefix + result + suffix + fill_char * padding - -#endif - } - } - - return 1; -#endif } /* From python-checkins at python.org Mon Mar 5 03:38:29 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 03:38:29 +0100 (CET) Subject: [Python-checkins] r54131 - sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/pep3101.h sandbox/trunk/pep3101/pep_differences.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305023829.AD13E1E400A@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 03:38:25 2007 New Revision: 54131 Modified: sandbox/trunk/pep3101/pep3101.c sandbox/trunk/pep3101/pep3101.h sandbox/trunk/pep3101/pep_differences.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Reworked overall structure to support feature set as described in pep_differences.txt. All the old regression tests still pass; but I expect some of the new features enabled by the reworked structure will not work, so it's off to write lots of new regression tests. Modified: sandbox/trunk/pep3101/pep3101.c ============================================================================== --- sandbox/trunk/pep3101/pep3101.c (original) +++ sandbox/trunk/pep3101/pep3101.c Mon Mar 5 03:38:25 2007 @@ -10,6 +10,10 @@ "" ; +static char pep3101_formatitem__doc__[] = +"" +; + static PyObject * StringDispatch(ternaryfunc *table, PyObject *self, PyObject *args, PyObject *keywords) { @@ -42,11 +46,22 @@ return StringDispatch(table, self, args, keywords); } +static PyObject * +pep3101_format_item(PyObject *self, PyObject *args, PyObject *keywords) +{ + static ternaryfunc table[2] = + {PyUnicode_FormatItemMethod, PyString_FormatItemMethod}; + return StringDispatch(table, self, args, keywords); +} + /* List of methods defined in the module */ static struct PyMethodDef pep3101_methods[] = { - {"format", (PyCFunction)pep3101_format, METH_VARARGS | METH_KEYWORDS, pep3101_format__doc__}, - + {"format", (PyCFunction)pep3101_format, + METH_VARARGS | METH_KEYWORDS, pep3101_format__doc__}, + {"format_item", (PyCFunction)pep3101_format_item, + METH_VARARGS | METH_KEYWORDS, pep3101_formatitem__doc__}, + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ }; Modified: sandbox/trunk/pep3101/pep3101.h ============================================================================== --- sandbox/trunk/pep3101/pep3101.h (original) +++ sandbox/trunk/pep3101/pep3101.h Mon Mar 5 03:38:25 2007 @@ -9,4 +9,10 @@ PyObject * PyUnicode_FormatMethod(PyObject *self, PyObject *args, PyObject *keywords); +PyObject * +PyString_FormatItemMethod(PyObject *self, PyObject *args, PyObject *keywords); + +PyObject * +PyUnicode_FormatItemMethod(PyObject *self, PyObject *args, PyObject *keywords); + #endif Modified: sandbox/trunk/pep3101/pep_differences.txt ============================================================================== --- sandbox/trunk/pep3101/pep_differences.txt (original) +++ sandbox/trunk/pep3101/pep_differences.txt Mon Mar 5 03:38:25 2007 @@ -157,7 +157,7 @@ features discussed by the PEP into different options. It also adds an option. -The first error option is controlled by the optional _leading_underscores +The first error option is controlled by the optional _allow_leading_underscores keyword argument. If this is present and evaluates non-zero, then leading underscores are allowed on identifiers and attributes in the format string. The implementation will lazily look for this argument the first time it @@ -246,6 +246,12 @@ _dict (if any). + User specified tuple of dictionaries + +Since we need a name mapper to look up items in the keywords dictionary, +then in the passed-in dictionary, it is only a small feature creep to +allow _dict itself to be a tuple of dictionaries. + Automatic locals/globals lookup This is likely to be a contentious feature, but it seems quite useful, Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 03:38:25 2007 @@ -94,7 +94,7 @@ self.formatEquals( "Count with me; 1 2 4", "Count with me; {0.one} {item._two} {1.four4}", - Container, Container, item=Container, _flags=dict(allow_leading_under=1)) + Container, Container, item=Container, _allow_leading_underscores=1) self.formatEquals( "Five is 5", "Five is {c.five}", c=Container()) self.formatRaises(AttributeError, Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 03:38:25 2007 @@ -31,6 +31,7 @@ #define STROBJ_RESIZE PyUnicode_Resize #define STROBJ_CHECK PyUnicode_Check #define STROBJ_FORMAT PyUnicode_FormatMethod +#define STROBJ_FORMAT_ITEM PyUnicode_FormatItemMethod #define STROBJ_STR PyObject_Unicode #else #define CH_TYPE char @@ -43,6 +44,7 @@ #define STROBJ_RESIZE _PyString_Resize #define STROBJ_CHECK PyString_Check #define STROBJ_FORMAT PyString_FormatMethod +#define STROBJ_FORMAT_ITEM PyString_FormatItemMethod #define STROBJ_STR PyObject_Str #endif @@ -61,6 +63,7 @@ #define PySet_Discard PyDict_DelItem #define PySet_New PyDict_Copy #define PySet_GET_SIZE PyDict_Size +#define PySet_Contains PyDict_Contains #endif @@ -168,11 +171,14 @@ int positional_arg_set; /* Keyword arguments can be checked as well */ PyObject *keyword_arg_set; - /* For some interface functions, we could have a list or tuple of + /* For some interface functions, we could have a tuple of dictionaries to search, e.g. locals()/globals(). */ - int keywords_is_tuple; + PyObject *keywords_tuple; /* Support for different escape-to-markup syntaxes */ int syntaxmode; + /* Support for hooking render calls */ + PyObject *hookfunc; + int hookalways; } FmtState; /* Some forward declarations for recursion */ @@ -230,6 +236,176 @@ } /************************************************************************/ +/************** Keyword parameter retrieval functions *****************/ +/************************************************************************/ + +/* + Keyword parameters can come from several sources. These functions + find them. +*/ + +/* + get_keyword_tuple is called when we need to build a tuple of keyword + dictionaries. It returns 1 on success and 0 on failure. +*/ +static int +get_keyword_tuple(FmtState *fs) +{ + PyObject *dict, *temp, *keywords, *result; + int use_original, dict_is_tuple, dict_count, i; + + keywords = fs->keywords; + temp = PyString_FromString("_dict"); + if (temp == NULL) + return 0; + dict = PyDict_GetItem(keywords, temp); + Py_DECREF(temp); + if (dict == NULL) + return (int)SetError(fs, "_dict not found"); + use_original = PyDict_Size(keywords) > 1; + dict_is_tuple = PyTuple_Check(dict); + dict_count = dict_is_tuple ? PyTuple_GET_SIZE(dict) : 1; + for (i = 0; i < dict_count; i++) + if (!PyDict_Check(dict_is_tuple ? PyTuple_GET_ITEM(dict, i) : dict)) + return (int)SetError(fs, "Invalid _dict parameter"); + if (dict_is_tuple && !use_original) { + Py_INCREF(dict); + fs->keywords_tuple = dict; + return 1; + } + result = PyTuple_New(dict_count + use_original); + if (result == NULL) + return 0; + if (use_original) { + Py_INCREF(keywords); + PyTuple_SET_ITEM(result, 0, keywords); + } + for (i = 0; i < dict_count; i++) { + temp = dict_is_tuple ? PyTuple_GET_ITEM(dict, i) : dict; + Py_INCREF(temp); + PyTuple_SET_ITEM(result, i + use_original, temp); + } + fs->keywords_tuple = result; + return 1; +} + +static int +get_locals_globals(FmtState *fs) +{ + PyObject *locals, *globals, *result; + int gotglobals, gotlocals; + if ((fs->args != NULL)) + return (int)SetError(fs, "no keywords parameters available"); + locals = PyEval_GetLocals(); + gotlocals = locals != NULL; + globals = PyEval_GetGlobals(); + gotglobals = globals != NULL; + result = PyTuple_New(gotlocals + gotglobals); + if (result == NULL) + return 0; + if (gotlocals) { + Py_INCREF(locals); + PyTuple_SET_ITEM(result, 0, locals); + } + if (gotglobals) { + Py_INCREF(globals); + PyTuple_SET_ITEM(result, 0, globals); + } + fs->keywords_tuple = result; + return 1; +} + +/* + name_mapper searches the "namespace" for the given key and returns + the first object found bound to that name. + + The search of the namespace logically proceeds as follows: + + 1) The keywords argument dictionary passed into the main function + (if any) is searched. + 2) If a keyword argument _dict was given: + a) If it was a sequence, the dictionaries in the sequence + are searched in order; or + b) If it was a dictionary, it is searched. + 3) If no keywords arguments were passed into the main function, + and no positional arguments were passed in either, a search + of the caller's locals() and globals() is performed. + + For efficiency, the actual search does not proceed exactly like + this. +*/ +static PyObject * +name_mapper(FmtState *fs, PyObject *key) +{ + PyObject *result; + int index, lastindex; + + if (fs->keywords_tuple == NULL) { + if (fs->keywords != NULL) { + if ((result = PyDict_GetItem(fs->keywords, key)) != NULL) { + Py_INCREF(result); + return result; + } + if (!get_keyword_tuple(fs)) + return NULL; + } + else if (!get_locals_globals(fs)) + return NULL; + } + lastindex = PyTuple_GET_SIZE(fs->keywords_tuple)-1; + for (index=0; index <= lastindex; index++) { + result = PyDict_GetItem(PyTuple_GET_ITEM(fs->keywords_tuple, index), key); + if (result != NULL) { + Py_INCREF(result); + return result; + } + } + return SetError(fs, "name lookup failed"); +} + +static PyObject * +read_parameter(FmtState *fs, const char *keyword) +{ + PyObject *result; + PyObject *k = PyString_FromString(keyword); + if (k == NULL) + return NULL; + result = name_mapper(fs, k); + Py_DECREF(k); + return result; +} + +static int +read_bool_parameter(FmtState *fs, const char *keyword) +{ + PyObject *obj; + int result; + obj = read_parameter(fs, keyword); + if (obj == NULL) + return -1; + result = PyObject_IsTrue(obj); + Py_DECREF(obj); + return result; +} + +static int +read_allow_under_parameter(FmtState *fs) +{ + int result = read_bool_parameter(fs, "_allow_leading_underscores"); + result = fs->allow_leading_under = (result > 0); + return result; +} + +static int +read_hook_parameter(FmtState *fs) +{ + if (fs->hookfunc != NULL) + fs->hookfunc = read_parameter(fs, "_hook"); + return (fs->hookfunc != NULL) || + SetError(fs, "No _hook function supplied"); +} + +/************************************************************************/ /*********** Output string management functions ****************/ /************************************************************************/ @@ -392,7 +568,7 @@ /* This little bit of mutual recursion allows nested dictionary lookups and computed attribute names */ - if (--fs->max_recursion < 0) + if (--(fs->max_recursion) < 0) return SetError(fs, "Maximum string recursion limit exceeded"); result = get_field_object(fs); fs->max_recursion++; @@ -402,7 +578,9 @@ } if (end_identifier(*fs->fmtstr.ptr)) return SetError(fs, "Expected attribute or index"); - if ((*fs->fmtstr.ptr == '_') && !fs->allow_leading_under) + if ((*fs->fmtstr.ptr == '_') && + !fs->allow_leading_under && + !read_allow_under_parameter(fs)) return SetError(fs, "Leading underscores not allowed in attribute/index strings"); @@ -446,37 +624,14 @@ */ /* - If keywords are supplied as a sequence of dictionaries - (e.g. locals/globals) then name_mapper will do multiple - lookups until it finds the right information. This - should not be called (keywords_is_tuple should not be - set) unless fs->keywords is a tuple. -*/ -static PyObject * -name_mapper(PyObject *keywords, PyObject *key) -{ - PyObject *result; - int index; - int lastindex = PyTuple_GET_SIZE(keywords)-1; - - for (index=0;; index++) { - result = PyObject_GetItem(PyTuple_GET_ITEM(keywords, index), key); - if (result != NULL) { - Py_INCREF(result); - return result; - } - if (index >= lastindex) - return NULL; - PyErr_Clear(); - } -} - -/* get_specifier retrieves the part of the format string between the colon and trailing }. + + It is also used to parse past comments and format + metadata. */ static int -get_specifier(FmtState *fs) +get_specifier(FmtState *fs, int allow_recursion) { CH_TYPE c; @@ -501,7 +656,7 @@ } } fs->fieldspec.end = fs->fmtstr.ptr - 1; - if (gotcurly) { + if (gotcurly && allow_recursion) { PyObject *myobject; SubString savefmt = fs->fmtstr; fs->fmtstr.ptr = fs->fieldspec.ptr; @@ -527,27 +682,20 @@ PyObject *myobj, *subobj, *newobj; CH_TYPE c; Py_ssize_t index; - int isindex, expectclose, isnumeric, isargument; + int isindex, isnumeric, isargument; - index = 0; /* Just to shut up the compiler warning */ - if (!check_fmtstr(fs)) - return NULL; - isargument=1; - isnumeric = (CH_TYPE_ISDECIMAL(*fs->fmtstr.ptr)); - myobj = isnumeric ? fs->args : fs->keywords; - if (myobj == NULL) - goto ERROR; + index = isnumeric = 0; /* Just to shut up the compiler warnings */ + ; + myobj = fs->args; Py_INCREF(myobj); - for (isindex=1, expectclose=0;;) { + for (isindex=1, isargument=1;;) { if (!check_fmtstr(fs)) break; if (!isindex) { if ((subobj = get_python_identifier(fs, isargument)) == NULL) break; - newobj = (isargument && fs->keywords_is_tuple) - ? name_mapper(myobj, subobj) - : PyObject_GetAttr(myobj, subobj); + newobj = PyObject_GetAttr(myobj, subobj); Py_DECREF(subobj); } else { @@ -570,7 +718,9 @@ get_python_identifier(fs, isargument); if (subobj == NULL) break; - newobj = PyObject_GetItem(myobj, subobj); + newobj = isargument + ? name_mapper(fs, subobj) + : PyObject_GetItem(myobj, subobj); Py_DECREF(subobj); } } @@ -578,7 +728,7 @@ myobj = newobj; if (myobj == NULL) break; - if (expectclose) + if (!isargument && isindex) if ((!check_fmtstr(fs)) || (*fs->fmtstr.ptr++ != ']')) { SetError(fs, "Expected ]"); break; @@ -590,13 +740,12 @@ return myobj; fs->fmtstr.ptr++; isargument = 0; - isindex = expectclose = (c == '['); + isindex = (c == '['); if (!isindex && (c != '.')) { SetError(fs, "Expected ., [, :, or }"); break; } } -ERROR: if ((myobj == NULL) && isargument) { PyErr_Clear(); @@ -626,7 +775,7 @@ if (myobj != NULL) { if (check_fmtstr(fs)) { c = *fs->fmtstr.ptr++; - if ((c == '}') || ((c == ':') && (get_specifier(fs)))) + if ((c == '}') || ((c == ':') && (get_specifier(fs, 1)))) return myobj; } Py_DECREF(myobj); @@ -643,7 +792,7 @@ object and field specification string generated by get_field_and_spec, and renders the field into the output string. - The two main subfunctions of render_field are caller_render (which + The two main subfunctions of render_field are object_self_render (which calls the object-supplied __format__ hook), and internal_render, which renders objects which don't have format hooks. */ @@ -1373,24 +1522,28 @@ return formatter(fieldobj, fs, &format); } +static PyObject * +get_field_spec_obj(FmtState *fs) +{ + PyObject *result = fs->fieldspec.obj; + if (result == NULL) { + result = STROBJ_NEW(fs->fieldspec.ptr, + fs->fieldspec.end - fs->fieldspec.ptr); + fs->fieldspec.obj = result; /* Owned by someone else now... */ + } + return result; +} + /* - caller_render is invoked to format an object with a defined __format__ + object_self_render is invoked to format an object with a defined __format__ attribute. */ static int -caller_render(FmtState *fs, PyObject *__format__) +object_self_render(FmtState *fs, PyObject *__format__) { - PyObject *myobj; int ok; + PyObject *myobj = get_field_spec_obj(fs); - myobj = fs->fieldspec.obj; - if (myobj == NULL) { - myobj = STROBJ_NEW(fs->fieldspec.ptr, - fs->fieldspec.end - fs->fieldspec.ptr); - if (myobj == NULL) - return 0; - fs->fieldspec.obj = myobj; /* Owned by our caller now */ - } /* XXX -- possible optimization to CallFunctionWithArgs */ myobj = PyObject_CallFunction(__format__, "(O)", myobj); if (myobj == NULL) @@ -1406,6 +1559,41 @@ } /* + hook_render is invoked to format an object with the hook callback. + Returns -1 for err, 1 if did it, 0 if didn't +*/ +static int +hook_render(FmtState *fs, PyObject *fieldobj) +{ + int result; + PyObject *strobj; + PyObject *specobj = get_field_spec_obj(fs); + + if ((specobj == NULL) || + ((fs->hookfunc == NULL) && + !read_hook_parameter(fs))) + return -1; + + /* XXX -- possible optimization to CallFunctionWithArgs */ + strobj = PyObject_CallFunction(fs->hookfunc, "(OO)", fieldobj, specobj); + if (strobj == NULL) + return -1; + result = (strobj != Py_None); + if (result) { + if (!STROBJ_CHECK(strobj)) { + result = -1; + SetError(fs, + "hook or __format__ method did not return correct string type"); + } + else + result = output_data(fs, STROBJ_AS_PTR(strobj), + STROBJ_GET_SIZE(strobj)) ? 1 : -1; + } + Py_DECREF(strobj); + return result; +} + +/* render_field determines if the field object has a defined __format__ method, and dispatches to the appropriate subfunction. */ @@ -1414,10 +1602,19 @@ { int result; SubString savefmt; + PyObject *__format__; - PyObject *__format__ = PyObject_GetAttrString(fieldobj, "__format__"); + if (fs->hookalways || + ((fs->fieldspec.ptr < fs->fieldspec.end) && + fs->fieldspec.end[-1] == 'p')) { + result = hook_render(fs, fieldobj); + if (result != 0) + return result == 1; + } + + __format__ = PyObject_GetAttrString(fieldobj, "__format__"); if (__format__ != NULL) { - result = caller_render(fs, __format__); + result = object_self_render(fs, __format__); Py_DECREF(__format__); } else { @@ -1438,6 +1635,60 @@ return result; } +static int +check_keyword(const CH_TYPE *ptr, const char *expected, int count) +{ + while (count--) + if (*ptr++ != *expected++) + return 0; + return 1; +} + +static int +process_metadata(FmtState *fs) +{ + CH_TYPE *ptr; + int count; + + if (!get_specifier(fs, 0)) + return 0; + if ((fs->fmtstr.ptr < fs->fmtstr.end) && (*fs->fmtstr.ptr == '\r')) + fs->fmtstr.ptr++; + if ((fs->fmtstr.ptr < fs->fmtstr.end) && (*fs->fmtstr.ptr == '\n')) + fs->fmtstr.ptr++; + ptr = fs->fieldspec.ptr; + count = fs->fieldspec.end - ptr; + switch (*ptr) { + case 'u': + if ((count == 6) && check_keyword(ptr, "useall", count)) { + fs->positional_arg_set = (1 << fs->num_args) - 1; + if (fs->keywords) { + Py_XDECREF(fs->keyword_arg_set); + fs->keyword_arg_set = PySet_New(fs->keywords); + if (fs->keyword_arg_set == NULL) + return 0; + } + return 1; + } + break; + case 'h': + if ((count == 4) && check_keyword(ptr, "hook", count)) { + fs->hookalways = 1; + return read_hook_parameter(fs); + } + break; + case 's': + if ((count == 7) && check_keyword(ptr, "syntax", 6)) { + int mode = CH_TYPE_TODECIMAL(ptr[6]); + if ((mode < 0) || (mode > 3)) + return (int)SetError(fs, "Invalid syntax mode"); + fs->syntaxmode = mode; + return 1; + } + } + return (int)SetError(fs, "Invalid string metadata keyword"); +} + /* get_field_and_render calls get_field_and_spec to get the field object and specification, then calls @@ -1446,15 +1697,20 @@ static int get_field_and_render(FmtState *fs) { - PyObject *myobj; - int ok; - fs->fieldstart = fs->fmtstr.ptr; - myobj = get_field_and_spec(fs); - ok = (myobj != NULL) && render_field(fs, myobj); - Py_XDECREF(myobj); - Py_XDECREF(fs->fieldspec.obj); - return ok; + switch (*fs->fmtstr.ptr) { + case '#': + return get_specifier(fs, 0); + case '!': + return process_metadata(fs); + default: { + PyObject *myobj = get_field_and_spec(fs); + int ok = (myobj != NULL) && render_field(fs, myobj); + Py_XDECREF(myobj); + Py_XDECREF(fs->fieldspec.obj); + return ok; + } + } } /************************************************************************/ @@ -1550,11 +1806,11 @@ } /* - do_format allocates the output string and then + build_string allocates the output string and then calls a markup handler to do the dirty work. */ static PyObject * -do_format(FmtState *fs) +build_string(FmtState *fs) { PyObject *myobj; int ok; @@ -1581,7 +1837,7 @@ /* recurse_format is called for nested format specifiers, e.g. {1:{2}}. It saves off the current information, - and recursively calls do_format. + and recursively calls build_string. */ static PyObject * recurse_format(FmtState *fs) @@ -1591,7 +1847,7 @@ int saveincrement = fs->size_increment; if (--(fs->max_recursion) < 0) return SetError(fs, "Max string recursion exceeded"); - result = do_format(fs); + result = build_string(fs); fs->max_recursion++; fs->outstr = saveoutstr; fs->size_increment = saveincrement; @@ -1603,58 +1859,11 @@ /************************************************************************/ static int -get_options(PyObject *keywords, FmtState *fs) -{ - static char* keys[3] = { - "useall", "allow_leading_under", NULL - }; - int values[2]; - int index; - - PyObject *flags, *myobj; - - fs->max_recursion = 4; - fs->allow_leading_under = 0; - fs->positional_arg_set = 0; - fs->keyword_arg_set = NULL; - fs->keywords_is_tuple = 0; - fs->syntaxmode = 0; - fs->do_markup = do_markup; - fs->keywords = keywords; - - if (keywords == NULL) - return 1; - - flags = PyDict_GetItemString(keywords, "_flags"); - if (flags == NULL) - return 1; - - for (index=0; keys[index] != NULL; index++) { - myobj = PyDict_GetItemString(flags, keys[index]); - if (myobj == NULL) - values[index] = 0; - else if (PyInt_Check(myobj)) - values[index] = PyInt_AS_LONG(myobj); - else { - PyErr_SetString(PyExc_TypeError, - "All flags values must be integers"); - return 0; - } - } - if (values[0]) { - fs->positional_arg_set = (1 << fs->num_args) - 1; - fs->keyword_arg_set = PySet_New(keywords); - if (!fs->keyword_arg_set) - return 0; - } - fs->allow_leading_under = values[1]; - return 1; -} - -static int get_self_args(PyObject *self, PyObject *args, FmtState *fs) { + memset(fs, 0, sizeof(*fs)); fs->num_args = PyTuple_GET_SIZE(args); + fs->args = args; if (self == NULL) { if (!fs->num_args) { PyErr_SetString(PyExc_TypeError, @@ -1670,49 +1879,110 @@ fs->arg_param_offset = 1; fs->num_args -= 1; } - else - fs->arg_param_offset = 0; - fs->args = args; fs->fmtstr.ptr = fs->fmtstart = STROBJ_AS_PTR(self); fs->fmtstr.end = fs->fmtstr.ptr + STROBJ_GET_SIZE(self); return 1; } static PyObject * -fs_cleanup(PyObject *result, FmtState *fs) +check_args_consumed(FmtState *fs, PyObject *result) { - PyObject *used; - int ok = result != NULL; - used = fs->keyword_arg_set; - if (ok && used) { - ok = (PySet_GET_SIZE(used) <= 1) && !fs->positional_arg_set; - if (!ok) { + static const char* ignore[] = { + "_dict", + "_allow_leading_underscores", + "_hook", + NULL}; + + int num_unused, i, contains; + PyObject *used = fs->keyword_arg_set; + if (result != NULL) { + num_unused = fs->positional_arg_set; + if (!num_unused) { + num_unused = PySet_GET_SIZE(used); + i = 0; + while (num_unused && (ignore[i] != NULL)) { + PyObject *temp= PyString_FromString(ignore[i++]); + if ((temp==NULL) || + ((contains = PySet_Contains(used, temp)) < 0)) { + Py_DECREF(used); + Py_XDECREF(temp); + return NULL; + } + num_unused -= contains; + } + } + if (num_unused) { Py_DECREF(result); result = SetError(fs, "Not all arguments consumed"); } } - Py_XDECREF(used); + Py_DECREF(used); return result; } /* STROBJ_FORMAT (actually PyUnicode_FormatMethod or PyString_FormatMethod) - is the public interface to the module. - - XXX -- do we need to check input types here, or are we guaranteed - they are right???? + is the main public interface to the module. */ PyObject * STROBJ_FORMAT(PyObject *self, PyObject *args, PyObject *keywords) { FmtState fs; + PyObject *result; + + if (!get_self_args(self, args, &fs)) + return NULL; + + fs.max_recursion = 4; + fs.do_markup = do_markup; + fs.keywords = keywords; + + result = build_string(&fs); + Py_XDECREF(fs.keywords_tuple); + Py_XDECREF(fs.hookfunc); + return (fs.keyword_arg_set != NULL) + ? check_args_consumed(&fs, result) + : result; +} + +/* + single_field_markup is the markup processor for FormatItem +*/ +static int +single_field_markup(FmtState *fs) +{ + return render_field(fs, PyTuple_GET_ITEM(fs->args, fs->arg_param_offset)); +} + +/* + STROBJ_FORMAT_ITEM (actually PyUnicode_FormatItemMethod or + PyString_FormatItemMethod) is another interface to this + module, to allow external code to access our internal + single-field formatter. +*/ +PyObject * +STROBJ_FORMAT_ITEM(PyObject *self, PyObject *args, PyObject *keywords) +{ + FmtState fs; + PyObject *result; - /* This function can be called as a python function or as a method */ - if (!get_self_args(self, args, &fs) || - !get_options(keywords, &fs)) + if (!get_self_args(self, args, &fs)) return NULL; - return fs_cleanup(do_format(&fs), &fs); + if (fs.num_args != 1) { + PyErr_SetString(PyExc_TypeError, + "Function self plus one positional argument"); + return 0; + } + fs.do_markup = single_field_markup; + fs.keywords = keywords; + fs.fieldspec.ptr = fs.fmtstr.ptr; + fs.fieldspec.end = fs.fmtstr.end; + + result = build_string(&fs); + Py_XDECREF(fs.keywords_tuple); + Py_XDECREF(fs.hookfunc); + return result; } #ifdef __cplusplus From python-checkins at python.org Mon Mar 5 04:23:05 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 04:23:05 +0100 (CET) Subject: [Python-checkins] r54132 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305032305.3D3D11E400A@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 04:23:01 2007 New Revision: 54132 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added tests for name_mapper and made them pass Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 04:23:01 2007 @@ -3,6 +3,10 @@ import pep3101 +# Variables used for testing name mapper +global1 = 10 +global2 = 20 +global3 = 30 # The test implementation does not allow an argument # index or keyword name to be used more than once. The @@ -253,6 +257,21 @@ self.formatRaises(ValueError, "{{ {{{0}}", True) self.formatRaises(ValueError, "{0}}", True) + def test_name_mapper(self): + mydict = dict(foo=1, bar=2) + dict2 = mydict, dict(foobar=3) + foo = 27 + global3 = 50 + self.formatRaises(ValueError, "{foo}") + self.formatRaises(ValueError, "{foo} {foobar}", _dict=mydict) + self.formatEquals("1", "{foo}", _dict=mydict) + self.formatEquals("1 2", "{foo} {bar}", _dict=mydict) + self.formatRaises(ValueError, "{foo} {bar} {global3}", _dict=mydict) + self.formatEquals("1 2 3", "{foo} {bar} {foobar}", _dict=dict2) + self.formatEquals("27 2", "{foo} {bar}", _dict=mydict, foo=foo) + self.assertEquals( + pep3101.format("{foo} {global3} {global1}"), + "27 50 10") def test_main(): test_support.run_unittest(FormatTest) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 04:23:01 2007 @@ -294,7 +294,8 @@ { PyObject *locals, *globals, *result; int gotglobals, gotlocals; - if ((fs->args != NULL)) + + if (!PyTuple_GET_SIZE(fs->args)) return (int)SetError(fs, "no keywords parameters available"); locals = PyEval_GetLocals(); gotlocals = locals != NULL; @@ -309,7 +310,7 @@ } if (gotglobals) { Py_INCREF(globals); - PyTuple_SET_ITEM(result, 0, globals); + PyTuple_SET_ITEM(result, gotlocals, globals); } fs->keywords_tuple = result; return 1; @@ -1971,7 +1972,7 @@ if (fs.num_args != 1) { PyErr_SetString(PyExc_TypeError, - "Function self plus one positional argument"); + "Function expects self plus one positional argument"); return 0; } fs.do_markup = single_field_markup; From python-checkins at python.org Mon Mar 5 05:02:13 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 05:02:13 +0100 (CET) Subject: [Python-checkins] r54133 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305040213.DB1961E400A@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 05:02:08 2007 New Revision: 54133 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added tests for {!useall} and made them pass. Also, removed call that was failing for Eric under Python 2.3. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 05:02:08 2007 @@ -273,6 +273,23 @@ pep3101.format("{foo} {global3} {global1}"), "27 50 10") + def test_check_unused(self): + mydict = dict(foo=1, foo2=1) + bar = 3 + bar2 = 4 + s = '{0} {1} {foo} {bar}' + s2 = '{!useall}' + s + self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals('a b 1 3', s2, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals('a b 1 3', s, 'a', 'b', 3, bar=bar, _dict=mydict) + self.formatRaises(ValueError, s2, 'a', 'b', 3, bar=bar, _dict=mydict) + self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, _dict=mydict, sam=27) + self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, _dict=mydict, sam=27) + self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1) + self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1) + self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1, sam=27) + self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, foo=1, sam=27) + def test_main(): test_support.run_unittest(FormatTest) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 05:02:08 2007 @@ -63,7 +63,6 @@ #define PySet_Discard PyDict_DelItem #define PySet_New PyDict_Copy #define PySet_GET_SIZE PyDict_Size -#define PySet_Contains PyDict_Contains #endif @@ -1703,6 +1702,7 @@ case '#': return get_specifier(fs, 0); case '!': + fs->fmtstr.ptr++; return process_metadata(fs); default: { PyObject *myobj = get_field_and_spec(fs); @@ -1894,7 +1894,7 @@ "_hook", NULL}; - int num_unused, i, contains; + int i, num_unused; PyObject *used = fs->keyword_arg_set; if (result != NULL) { num_unused = fs->positional_arg_set; @@ -1903,13 +1903,13 @@ i = 0; while (num_unused && (ignore[i] != NULL)) { PyObject *temp= PyString_FromString(ignore[i++]); - if ((temp==NULL) || - ((contains = PySet_Contains(used, temp)) < 0)) { + if (temp==NULL) { Py_DECREF(used); - Py_XDECREF(temp); return NULL; } - num_unused -= contains; + PySet_Discard(used, temp); + Py_DECREF(temp); + num_unused = PySet_GET_SIZE(used); } } if (num_unused) { From python-checkins at python.org Mon Mar 5 06:53:30 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 06:53:30 +0100 (CET) Subject: [Python-checkins] r54134 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305055330.1D52B1E400B@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 06:53:26 2007 New Revision: 54134 Modified: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: More tests; fixed bugs in hook function and alternate syntax 2 Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Mon Mar 5 06:53:26 2007 @@ -54,12 +54,11 @@ - document differences between PEP and implementation (in pep_differences.txt) - Add docstrings to module - - Add keyword options and string metadata options - as described in pep_differences. - - Play with possible implementations for exposing - lowest level format specifier handler for use in - compatible template systems. - Should we have stricter checking on format strings? For example type "s" doesn't allow a sign character. Should specifying one be an error? - Test suite needs to check for specific exceptions. + - Add capability to control exception handling to pep_differences + and to code: 1) ability to dump exceptions into string, 2) + ability to re-do lower exceptions or "pile-on" + Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 06:53:26 2007 @@ -277,19 +277,53 @@ mydict = dict(foo=1, foo2=1) bar = 3 bar2 = 4 - s = '{0} {1} {foo} {bar}' + result = ' a b 1 3' + s = ' {0} {1} {foo} {bar}' s2 = '{!useall}' + s - self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, _dict=mydict) - self.formatEquals('a b 1 3', s2, 'a', 'b', bar=bar, _dict=mydict) - self.formatEquals('a b 1 3', s, 'a', 'b', 3, bar=bar, _dict=mydict) + s3 = '{!useall}\r\n' + s + s4 = '{!useall}\n' + s + s5 = '{!useall}\r\n' + s + self.formatEquals(result, s, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals(result, s2, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals(result, s3, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals(result, s4, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals(result, s5, 'a', 'b', bar=bar, _dict=mydict) + self.formatEquals(result, s, 'a', 'b', 3, bar=bar, _dict=mydict) self.formatRaises(ValueError, s2, 'a', 'b', 3, bar=bar, _dict=mydict) - self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, _dict=mydict, sam=27) + self.formatEquals(result, s, 'a', 'b', bar=bar, _dict=mydict, sam=27) self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, _dict=mydict, sam=27) - self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1) - self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1) - self.formatEquals('a b 1 3', s, 'a', 'b', bar=bar, foo=1, sam=27) + self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1) + self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1) + self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1, sam=27) self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, foo=1, sam=27) + def test_comments(self): + self.formatEquals('Hi there','''{#Comment}Hi{# + This is a comment}{#Another} th{#comment}ere{#}''') + + def test_format_hook(self): + def hookfunc(obj, spec): + if obj > 100: + return None + return '_%s_' % (obj, spec)[obj==3] + self.formatEquals('_a_ _4_ 123', + '{!hook}{0:a} {1:b}{2:>10d}', + 3, 4, 123, _hook=hookfunc) + self.formatEquals('_ap_ _4_ 123', + '{0:ap} {1:bp}{2:>10d}', + 3, 4, 123, _hook=hookfunc) + self.formatRaises(ValueError, '{0:ap}') + self.formatEquals('_ap_', '{0:ap}', 3, _hook=hookfunc) + self.formatRaises(ValueError, '{0:ap}', 123, _hook=hookfunc) + + def test_alt_syntax(self): + self.formatEquals('{}1', '{!syntax1}{{}{0}', 1) + self.formatEquals('{ ${ 1 $1 $${0}', + '{!syntax2}{ $${ ${0} $$${0} $$$${0}', + 1) + self.formatEquals('1 {0} {\n0}', + '{!syntax3}{0} { 0} {\n0}', 1) + def test_main(): test_support.run_unittest(FormatTest) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 06:53:26 2007 @@ -399,7 +399,7 @@ static int read_hook_parameter(FmtState *fs) { - if (fs->hookfunc != NULL) + if (fs->hookfunc == NULL) fs->hookfunc = read_parameter(fs, "_hook"); return (fs->hookfunc != NULL) || SetError(fs, "No _hook function supplied"); @@ -1740,7 +1740,7 @@ switch (c = *ptr++) { case '{': if ((syntaxmode == 2) && - ((ptr == start) || (ptr[-2] != '$'))) + ((ptr == start+1) || (ptr[-2] != '$'))) continue; break; case '}': @@ -1769,12 +1769,19 @@ else count--; break; - case 2: - count -= 2; - escape = !count || (ptr[-3] != '$'); + case 2: { + CH_TYPE *first_dollar = ptr - 2; + int num_dollars; + while ((first_dollar > start) && + (first_dollar[-1] == '$')) + first_dollar--; + num_dollars = ptr - 1 - first_dollar; + count = first_dollar - start + (num_dollars / 2); + escape = num_dollars & 1; if (!escape) ptr--; break; + } case 3: switch (*ptr) { case ' ': @@ -1800,8 +1807,12 @@ fs->fmtstr.ptr = ptr; if (count && !output_data(fs, start, count)) return 0; - if (escape && !get_field_and_render(fs)) - return 0; + if (escape) { + if (!get_field_and_render(fs)) + return 0; + else + syntaxmode = fs->syntaxmode; + } } return 1; } From python-checkins at python.org Mon Mar 5 07:34:26 2007 From: python-checkins at python.org (patrick.maupin) Date: Mon, 5 Mar 2007 07:34:26 +0100 (CET) Subject: [Python-checkins] r54135 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305063426.5F3351E400B@bag.python.org> Author: patrick.maupin Date: Mon Mar 5 07:34:22 2007 New Revision: 54135 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: More alt-syntax checks and a fix for markup escape at string end on syntax 2 Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 07:34:22 2007 @@ -318,11 +318,22 @@ def test_alt_syntax(self): self.formatEquals('{}1', '{!syntax1}{{}{0}', 1) - self.formatEquals('{ ${ 1 $1 $${0}', - '{!syntax2}{ $${ ${0} $$${0} $$$${0}', + self.formatEquals('{ ${ 1 $1 $${0} ${', + '{!syntax2}{ $${ ${0} $$${0} $$$${0} $${', 1) self.formatEquals('1 {0} {\n0}', '{!syntax3}{0} { 0} {\n0}', 1) + self.formatRaises(ValueError, '}') + self.formatRaises(ValueError, '{') + self.formatEquals('}', '{!syntax1}}') + self.formatRaises(ValueError, '{!syntax1}{') + self.formatEquals('}', '{!syntax2}}') + self.formatEquals('{', '{!syntax2}{') + self.formatRaises(ValueError, '{!syntax1}${') + self.formatEquals('${', '{!syntax2}$${') + self.formatEquals('}', '{!syntax3}}') + self.formatRaises(ValueError, '{!syntax3}{') + self.formatEquals('{', '{!syntax3}{ ') def test_main(): test_support.run_unittest(FormatTest) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 07:34:22 2007 @@ -1729,7 +1729,7 @@ { CH_TYPE c, *start, *ptr, *end; Py_ssize_t count; - int syntaxmode, escape; + int syntaxmode, escape, at_end; end = fs->fmtstr.end; syntaxmode = fs->syntaxmode; @@ -1753,61 +1753,61 @@ escape = 1; break; } + fs->fmtstr.ptr = ptr; count = ptr - start; - if (ptr < end) { - switch (syntaxmode) { - case 0: - if ((c == '}') && (c != *ptr)) { - fs->fmtstr.ptr = ptr; - return (int)SetError(fs, "Single } encountered"); - } - case 1: - if (c == *ptr) { - ptr++; - escape = 0; - } - else - count--; - break; - case 2: { - CH_TYPE *first_dollar = ptr - 2; - int num_dollars; - while ((first_dollar > start) && - (first_dollar[-1] == '$')) - first_dollar--; - num_dollars = ptr - 1 - first_dollar; - count = first_dollar - start + (num_dollars / 2); - escape = num_dollars & 1; - if (!escape) - ptr--; + at_end = ptr >= end; + switch (syntaxmode) { + case 0: + if ((c == '}') && (!at_end) && (c != *ptr)) + return (int)SetError(fs, "Single } encountered"); + case 1: + if (at_end) break; + if (c == *ptr) { + fs->fmtstr.ptr++; + escape = 0; } - case 3: - switch (*ptr) { - case ' ': - ptr++; - case '\n': case '\r': - escape = 0; - break; - default: - count--; - break; - } + else + count--; + break; + case 2: { + CH_TYPE *first_dollar = ptr - 2; + int num_dollars; + if (!escape) break; - default: - fs->fmtstr.ptr = ptr; - return (int)SetError(fs, "Unsupported syntax mode"); + while ((first_dollar > start) && + (first_dollar[-1] == '$')) + first_dollar--; + num_dollars = ptr - 1 - first_dollar; + count = first_dollar - start + (num_dollars / 2); + escape = num_dollars & 1; + if (!escape) + fs->fmtstr.ptr--; + break; } - } - else if (escape) { - fs->fmtstr.ptr = ptr; - return (int)SetError(fs, "Unexpected escape to markup"); + case 3: + if (at_end) + break; + switch (*ptr) { + case ' ': + fs->fmtstr.ptr++; + case '\n': case '\r': + escape = 0; + break; + default: + count--; + break; + } + break; + default: + return (int)SetError(fs, "Unsupported syntax mode"); } - fs->fmtstr.ptr = ptr; if (count && !output_data(fs, start, count)) return 0; if (escape) { + if (at_end) + return (int)SetError(fs, "Unexpected escape to markup"); if (!get_field_and_render(fs)) return 0; else From python-checkins at python.org Mon Mar 5 08:52:03 2007 From: python-checkins at python.org (neal.norwitz) Date: Mon, 5 Mar 2007 08:52:03 +0100 (CET) Subject: [Python-checkins] r54136 - python/trunk/Misc/developers.txt Message-ID: <20070305075203.4E8511E400B@bag.python.org> Author: neal.norwitz Date: Mon Mar 5 08:52:01 2007 New Revision: 54136 Modified: python/trunk/Misc/developers.txt Log: Added Pete for 3101 too Modified: python/trunk/Misc/developers.txt ============================================================================== --- python/trunk/Misc/developers.txt (original) +++ python/trunk/Misc/developers.txt Mon Mar 5 08:52:01 2007 @@ -17,6 +17,9 @@ Permissions History ------------------- +- 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. From python-checkins at python.org Mon Mar 5 13:05:30 2007 From: python-checkins at python.org (eric.smith) Date: Mon, 5 Mar 2007 13:05:30 +0100 (CET) Subject: [Python-checkins] r54137 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070305120530.941B91E4015@bag.python.org> Author: eric.smith Date: Mon Mar 5 13:05:29 2007 New Revision: 54137 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Completed all floating point formats, with tests. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Mon Mar 5 13:05:29 2007 @@ -125,7 +125,7 @@ self.formatEquals("a", "{0:c}", ord("a")) self.formatEquals("8_08b", "{0:08b}", 8) self.formatEquals(" 8", "{0: >3d}", 8) - self.formatEquals("0.1515_.0%", "{0:.0%}", .1515) + self.formatEquals("15%", "{0:.0%}", .1515) def test_string_specifiers(self): self.formatEqualsWithUnicode("abc", "{0:.3s}", "abc") @@ -232,6 +232,22 @@ # XXX this should raise, but instead gives a DeprecationWarning #self.formatRaises(TypeError, "{0:c}", 3.14) + def test_exponent_specifiers(self): + self.formatEqualsWithUnicodeUC("3.141500e+00", "{0:e}", 3.1415) + self.formatEqualsWithUnicodeUC("3.1415000000e+00", "{0:.10e}", 3.1415) + + def test_fixed_specifiers(self): + self.formatEqualsWithUnicode("3.141500", "{0:f}", 3.1415) + self.formatEqualsWithUnicode("3.1415000000", "{0:.10f}", 3.1415) + self.formatEqualsWithUnicode("3.1415e+200", "{0:f}", 3.1415e200) + self.formatEqualsWithUnicode("3.1415e+200", "{0:F}", 3.1415e200) + + def test_general_specifiers(self): + self.formatEqualsWithUnicodeUC("3.1415", "{0:g}", 3.1415) + self.formatEqualsWithUnicodeUC("3.1415", "{0:.10g}", 3.1415) + self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200) + self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200) + def test_missing_type_specifier(self): # make sure floats use 'g', ints and longs 'd', and everything else 's' pass Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Mon Mar 5 13:05:29 2007 @@ -60,9 +60,10 @@ #define MAX_SIZE_INCREMENT 3200 #if PYTHON_API_VERSION < 1013 -#define PySet_Discard PyDict_DelItem -#define PySet_New PyDict_Copy -#define PySet_GET_SIZE PyDict_Size +#define PySet_Discard PyDict_DelItem +#define PySet_New PyDict_Copy +#define PySet_GET_SIZE PyDict_Size +#define PyOS_ascii_formatd PyOS_snprintf #endif @@ -74,7 +75,6 @@ + 1 + 1 = 24 */ #define MAXLEN_INT_STRING 64 - /************************************************************************/ /*********** Global data structures and forward declarations *********/ /************************************************************************/ @@ -1142,8 +1142,8 @@ #if C_UNICODE len = strtounicode(*pbuf, start, -1); #else - /* compute the length. I believe this is done because the return value from - snprintf above is unreliable */ + /* compute the length. I believe this is done because the return + value from snprintf above is unreliable */ len = strlen(start); #endif @@ -1207,7 +1207,7 @@ } static int -format_decimal(PyObject *fieldobj, FmtState *fs, +format_integer(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { CH_TYPE *p_buf; @@ -1322,7 +1322,8 @@ } /* set the total length of the string */ - n_total = n_lpadding + n_lsign + n_spadding + n_digits + n_rsign + n_rpadding; + n_total = n_lpadding + n_lsign + n_spadding + n_digits + + n_rsign + n_rpadding; assert(n_total >= n_allocated); /* because we're going to reallocate, our pointers might be @@ -1355,17 +1356,20 @@ overwrite some of that space */ /* short circuit test, in case we don't have to move anything */ if (p_buf + (n_lpadding + n_lsign + n_spadding) != p_digits) - memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits, n_digits * sizeof(CH_TYPE)); + memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits, + n_digits * sizeof(CH_TYPE)); if (n_lpadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_lpadding); + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, + n_lpadding); p_buf += n_lpadding; } if (n_lsign == 1) { *p_buf++ = lsign; } if (n_spadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_spadding); + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, + n_spadding); p_buf += n_spadding; } p_buf += n_digits; @@ -1373,7 +1377,8 @@ *p_buf++ = rsign; } if (n_rpadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_rpadding); + CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, + n_rpadding); p_buf += n_rpadding; } @@ -1381,24 +1386,87 @@ } static int -format_exponent(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - -static int -format_fixed(PyObject *fieldobj, FmtState *fs, +format_float(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return format_DUMMY(fieldobj, fs); -} + /* first, do the conversion as 8-bit chars, using the platform's + snprintf. then, if needed, convert to unicode. */ -static int -format_general(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); + /* fmt = '%.' + `prec` + `type` + '%%' + worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ + char fmt[20]; + + double x; + CH_TYPE type = format->type; + Py_ssize_t precision = format->precision; + CH_TYPE *buf; + int buflen; + int len; + char* trailing = ""; + + /* 'F' is the same as 'f', per the PEP */ + if (type == 'F') + type = 'f'; + + x = PyFloat_AsDouble(fieldobj); + if (x == -1.0 && PyErr_Occurred()) + return 0; + + if (type == '%') { + type = 'f'; + x *= 100; + trailing = "%%"; + } + + if (precision < 0) + precision = 6; + if (type == 'f' && (fabs(x) / 1e25) >= 1e25) + type = 'g'; + + /* cast "type", because if we're in unicode we need to pass a + 8-bit char. this is safe, because we've restricted what "type" + can be */ + PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing); + + /* this is taken from unicodeobject.c, except we don't force a + limit here, we dynamically allocate instead */ + /* Worst case length calc to ensure no buffer overrun: + + 'g' formats: + fmt = %#.g + buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp + for any double rep.) + len = 1 + prec + 1 + 2 + 5 = 9 + prec + + 'f' formats: + buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50) + len = 1 + 50 + 1 + prec = 52 + prec + + If prec=0 the effective precision is 1 (the leading digit is + always given), therefore increase the length by one. + + */ + /* so, allocate the precision plus 54 chars (we add one additional + for the trailing percent). do this allocation as the native + type, because we're going to convert to unicode anyway */ + buflen = 54 + precision; + if (output_allocate(fs, buflen, &buf) == 0) + return 0; + PyOS_ascii_formatd((char *)buf, buflen, fmt, x); + +#if C_UNICODE + len = strtounicode(buf, (char*)buf, -1); +#else + /* compute the length. I believe this is done because the return + value from snprintf above is unreliable */ + len = strlen(buf); +#endif + + /* shrink the buffer down to how many characters we actually + wrote. this is cheap, just pointer arithmetic */ + output_shrink(fs, buflen - len); + + return 1; } static int @@ -1448,13 +1516,6 @@ return ok; } -static int -format_percentage(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) -{ - return format_DUMMY(fieldobj, fs); -} - /* returns a pointer to our conversion function, or NULL if invalid */ Py_LOCAL_INLINE(FormatFunction) format_function(CH_TYPE c) @@ -1462,23 +1523,23 @@ switch (c) { case 'b': return format_binary; /* base-2 */ case 'c': return format_char; /* as character */ - case 'd': return format_decimal; /* decimal integer */ - case 'e': return format_exponent; /* exponential notation */ - case 'E': return format_exponent; /* exponential notation - with uppercase 'E' */ - case 'f': return format_fixed; /* fixed-point */ - case 'F': return format_fixed; /* fixed-point with uppercase */ - case 'g': return format_general; /* general number notation */ - case 'G': return format_general; /* general number notation - with uppercase 'E' */ case 'n': return format_locale_number; /* number in locale-specific format */ - case 'o': return format_decimal; /* octal */ + case 'd': /* decimal integer */ + case 'o': /* octal */ + case 'x': /* base 16 */ + case 'X': return format_integer; /* base 16 uppercase */ case 'r': return format_repr; /* in repr() format */ case 's': return format_string; /* convert using str() */ - case 'x': return format_decimal; /* base 16 */ - case 'X': return format_decimal; /* base 16 uppercase */ - case '%': return format_percentage; /* as percentage */ + case 'e': /* exponential notation */ + case 'E': /* exponential notation + with uppercase 'E' */ + case 'f': /* fixed-point */ + case 'F': /* fixed-point with uppercase */ + case 'g': /* general number notation */ + case 'G': /* general number notation + with uppercase 'E' */ + case '%': return format_float; /* as percentage */ default: return NULL; } @@ -1727,7 +1788,7 @@ static int do_markup(FmtState *fs) { - CH_TYPE c, *start, *ptr, *end; + CH_TYPE c = 0, *start, *ptr, *end; Py_ssize_t count; int syntaxmode, escape, at_end; From python-checkins at python.org Mon Mar 5 17:31:55 2007 From: python-checkins at python.org (facundo.batista) Date: Mon, 5 Mar 2007 17:31:55 +0100 (CET) Subject: [Python-checkins] r54138 - python/trunk/Lib/urllib2.py Message-ID: <20070305163155.02BB31E400B@bag.python.org> Author: facundo.batista Date: Mon Mar 5 17:31:54 2007 New Revision: 54138 Modified: python/trunk/Lib/urllib2.py Log: Minor corrections to docs, and an explanation comentary Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Mon Mar 5 17:31:54 2007 @@ -14,36 +14,36 @@ 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 -- +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 @@ -334,7 +334,8 @@ 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) From python-checkins at python.org Mon Mar 5 23:28:10 2007 From: python-checkins at python.org (georg.brandl) Date: Mon, 5 Mar 2007 23:28:10 +0100 (CET) Subject: [Python-checkins] r54139 - in python/trunk: Lib/test/test_descr.py Misc/NEWS Python/ceval.c Message-ID: <20070305222810.37D261E400C@bag.python.org> Author: georg.brandl Date: Mon Mar 5 23:28:08 2007 New Revision: 54139 Modified: python/trunk/Lib/test/test_descr.py python/trunk/Misc/NEWS python/trunk/Python/ceval.c Log: Patch #1674228: when assigning a slice (old-style), check for the sq_ass_slice instead of the sq_slice slot. Modified: python/trunk/Lib/test/test_descr.py ============================================================================== --- python/trunk/Lib/test/test_descr.py (original) +++ python/trunk/Lib/test/test_descr.py Mon Mar 5 23:28:08 2007 @@ -4206,6 +4206,19 @@ 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() @@ -4301,6 +4314,7 @@ test_init() methodwrapper() notimplemented() + test_assign_slice() from test import test_descr run_doctest(test_descr, verbosity=True) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Mar 5 23:28:08 2007 @@ -12,6 +12,9 @@ Core and builtins ----------------- +- Patch #1674228: when assigning a slice (old-style), check for the + sq_ass_slice instead of the sq_slice slot. + - When printing an unraisable error, don't print exceptions. before the name. This duplicates the behavior whening normally printing exceptions. Modified: python/trunk/Python/ceval.c ============================================================================== --- python/trunk/Python/ceval.c (original) +++ python/trunk/Python/ceval.c Mon Mar 5 23:28:08 2007 @@ -3927,7 +3927,7 @@ 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; From python-checkins at python.org Mon Mar 5 23:28:14 2007 From: python-checkins at python.org (georg.brandl) Date: Mon, 5 Mar 2007 23:28:14 +0100 (CET) Subject: [Python-checkins] r54140 - in python/branches/release25-maint: Lib/test/test_descr.py Misc/NEWS Python/ceval.c Message-ID: <20070305222814.B3EDD1E400C@bag.python.org> Author: georg.brandl Date: Mon Mar 5 23:28:13 2007 New Revision: 54140 Modified: python/branches/release25-maint/Lib/test/test_descr.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Python/ceval.c Log: Patch #1674228: when assigning a slice (old-style), check for the sq_ass_slice instead of the sq_slice slot. (backport from rev. 54139) Modified: python/branches/release25-maint/Lib/test/test_descr.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_descr.py (original) +++ python/branches/release25-maint/Lib/test/test_descr.py Mon Mar 5 23:28:13 2007 @@ -4143,6 +4143,19 @@ 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() @@ -4239,6 +4252,7 @@ test_init() methodwrapper() notimplemented() + test_assign_slice() if verbose: print "All OK" Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Mon Mar 5 23:28:13 2007 @@ -12,6 +12,9 @@ Core and builtins ----------------- +- Patch #1674228: when assigning a slice (old-style), check for the + sq_ass_slice instead of the sq_slice slot. + - Bug #1669182: prevent crash when trying to print an unraisable error from a string exception. Modified: python/branches/release25-maint/Python/ceval.c ============================================================================== --- python/branches/release25-maint/Python/ceval.c (original) +++ python/branches/release25-maint/Python/ceval.c Mon Mar 5 23:28:13 2007 @@ -3926,7 +3926,7 @@ 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; From nnorwitz at gmail.com Mon Mar 5 23:33:18 2007 From: nnorwitz at gmail.com (Neal Norwitz) Date: Mon, 5 Mar 2007 17:33:18 -0500 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20070305223318.GA2434@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_bigaddrspace test_bigmem test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_collections test_colorsys test_commands test_compare test_compile test_compiler test_complex test_complex_args test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_doctest test_doctest2 test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functools test_future test_gc test_gdbm test_generators test_genericpath test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_modulefinder test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_normalization test_ntpath test_old_mailbox test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [7321 refs] [7321 refs] [7321 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [7696 refs] [7696 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test test_socket_ssl crashed -- : [Errno socket error] (110, 'Connection timed out') test_socketserver test_softspace test_sort test_sqlite test_startfile test_startfile skipped -- cannot import name startfile test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structmembers test_structseq test_subprocess [7316 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7317 refs] [8865 refs] [7532 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] . [7316 refs] [7316 refs] this bit of output is from a test of stdout in a different process ... [7316 refs] [7316 refs] [7532 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [7316 refs] [7316 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [7323 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_uuid WARNING: uuid.getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._ifconfig_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._unixdll_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_wsgiref test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipfile64 test_zipfile64 skipped -- test requires loads of disk-space bytes and a long time to run test_zipimport test_zlib 299 tests OK. 1 test failed: test_socket_ssl 21 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_pep277 test_plistlib test_scriptpackages test_startfile test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound test_zipfile64 1 skip unexpected on linux2: test_ioctl [463190 refs] From buildbot at python.org Mon Mar 5 23:57:48 2007 From: buildbot at python.org (buildbot at python.org) Date: Mon, 05 Mar 2007 22:57:48 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20070305225749.1ED401E4016@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1965 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 864, in endheaders self._send_output() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 736, in _send_output self.send(msg) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 695, in send self.connect() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 1152, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 00:15:07 2007 From: buildbot at python.org (buildbot at python.org) Date: Mon, 05 Mar 2007 23:15:07 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable trunk Message-ID: <20070305231508.1B2FA1E4016@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/116 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_bsddb3 make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 00:22:21 2007 From: buildbot at python.org (buildbot at python.org) Date: Mon, 05 Mar 2007 23:22:21 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo 2.5 Message-ID: <20070305232221.A80491E400C@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%25202.5/builds/253 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 695, in send self.connect() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 1130, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 01:10:41 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 00:10:41 +0000 Subject: [Python-checkins] buildbot warnings in ia64 Ubuntu trunk trunk Message-ID: <20070306001041.6DFD71E400C@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%2520Ubuntu%2520trunk%2520trunk/builds/425 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/httplib.py", line 864, in endheaders self._send_output() File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/httplib.py", line 736, in _send_output self.send(msg) File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/httplib.py", line 1153, in connect ssl = socket.ssl(sock, self.key_file, self.cert_file) File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/socket.py", line 74, in ssl return _realssl(sock, keyfile, certfile) IOError: [Errno socket error] (8, 'EOF occurred in violation of protocol') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 02:47:57 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 01:47:57 +0000 Subject: [Python-checkins] buildbot warnings in ia64 Ubuntu trunk 2.5 Message-ID: <20070306014757.660C11E400C@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%2520Ubuntu%2520trunk%25202.5/builds/204 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/httplib.py", line 1131, in connect ssl = socket.ssl(sock, self.key_file, self.cert_file) File "/home/pybot/buildarea/2.5.klose-debian-ia64/build/Lib/socket.py", line 74, in ssl return _realssl(sock, keyfile, certfile) IOError: [Errno socket error] (8, 'EOF occurred in violation of protocol') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 02:50:46 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 01:50:46 +0000 Subject: [Python-checkins] buildbot warnings in MIPS Debian trunk Message-ID: <20070306015046.7F53D1E400C@bag.python.org> The Buildbot has detected a new failure of MIPS Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/MIPS%2520Debian%2520trunk/builds/652 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/httplib.py", line 864, in endheaders self._send_output() File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/httplib.py", line 736, in _send_output self.send(msg) File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/httplib.py", line 1153, in connect ssl = socket.ssl(sock, self.key_file, self.cert_file) File "/home/pybot/buildarea/trunk.klose-debian-mips/build/Lib/socket.py", line 74, in ssl return _realssl(sock, keyfile, certfile) IOError: [Errno socket error] (8, 'EOF occurred in violation of protocol') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 03:36:51 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 03:36:51 +0100 (CET) Subject: [Python-checkins] r54141 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306023651.6275A1E400D@bag.python.org> Author: eric.smith Date: Tue Mar 6 03:36:49 2007 New Revision: 54141 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added binary formatting. Factored out _calc_integer_widths() and _fill_integer(), so they can be shared by format_integer() and format_binary(). Added test cases for binary. This code accesses PyLongObject's ob_digit[] directly, that might not be the best way to go about binary formatting. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Tue Mar 6 03:36:49 2007 @@ -123,7 +123,7 @@ def test_specifiers(self): self.formatEquals("a", "{0:c}", ord("a")) - self.formatEquals("8_08b", "{0:08b}", 8) + self.formatEquals("00001000", "{0:08b}", 8) self.formatEquals(" 8", "{0: >3d}", 8) self.formatEquals("15%", "{0:.0%}", .1515) @@ -152,6 +152,7 @@ self.assertRaises(TypeError, "{0:d}", "non-number") self.formatEqualsWithUnicode("0", "{0:d}", 0) + self.formatEqualsWithUnicode("0", "{0:d}", long(0)) self.formatEqualsWithUnicode("123", "{0:d}", 123) self.formatEqualsWithUnicode("-123", "{0:d}", -123) self.formatEqualsWithUnicode("+123", "{0:+d}", 123) @@ -204,7 +205,7 @@ def test_hex_specifiers(self): n = int("beef", 16) - self.assertRaises(TypeError, "{0:x", "non-number") + self.formatRaises(TypeError, "{0:x}", "non-number") self.formatEqualsWithUnicodeUC("0", "{0:x}", 0) self.formatEqualsWithUnicodeUC("beef", "{0:x}", n) @@ -248,6 +249,31 @@ self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200) self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200) + def test_percent_specifiers(self): + self.formatEqualsWithUnicode("314.15%", "{0:.2%}", 3.1415) + self.formatEqualsWithUnicode("3.14e+202%", "{0:.3%}", 3.1415e200) + + def test_binary_specifiers(self): + self.formatRaises(TypeError, "{0:b}", "string") + + self.formatEqualsWithUnicode("0", "{0:b}", 0) + self.formatEqualsWithUnicode("0", "{0:b}", long(0)) + self.formatEqualsWithUnicode("1", "{0:b}", 1) + self.formatEqualsWithUnicode("1", "{0:b}", long(1)) + self.formatEqualsWithUnicode("-1", "{0:b}", -1) + self.formatEqualsWithUnicode("-1", "{0:b}", long(-1)) + self.formatEqualsWithUnicode("0 ", "{0:<10b}", 0) + self.formatEqualsWithUnicode(" 0", "{0:>10b}", 0) + self.formatEqualsWithUnicode("1001 ", "{0:<10b}", 9) + self.formatEqualsWithUnicode(" 1001", "{0:>10b}", 9) + self.formatEqualsWithUnicode("1" + "0" * 100, "{0:b}", 2**100) + self.formatEqualsWithUnicode("-1" + "0" * 100, "{0:b}", -2**100) + self.formatEqualsWithUnicode("1" + "0" * 98 + "11", "{0:b}", 2**100 + 3) + self.formatEqualsWithUnicode("1" * 100, "{0:b}", 2**100 - 1) + self.formatEqualsWithUnicode("-" + "1" * 100, "{0:b}", -(2**100 - 1)) + self.formatEqualsWithUnicode("(" + "1" * 100 + ")", "{0:()b}", -(2**100 - 1)) + self.formatEqualsWithUnicode("(" + " " * 98 + "1" * 100 + ")", "{0:=()200b}", -(2**100 - 1)) + def test_missing_type_specifier(self): # make sure floats use 'g', ints and longs 'd', and everything else 's' pass Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 03:36:49 2007 @@ -20,6 +20,9 @@ #define C_UNICODE 1 #endif +/* we need access to a PyLongObject's internals */ +#include "longintrepr.h" + #if C_UNICODE #define CH_TYPE Py_UNICODE #define CH_TYPE_ISDECIMAL Py_UNICODE_ISDECIMAL @@ -91,6 +94,7 @@ */ #define FORMATBUFLEN (size_t)120 +#define ABS(x) ((x) < 0 ? -(x) : (x)) #ifdef __cplusplus extern "C" { @@ -806,6 +810,20 @@ CH_TYPE type; } InternalFormatSpec; +/* describes the layout for an integer, see the comment in + _calc_integer_widths() for details */ +typedef struct { + Py_ssize_t n_lpadding; + Py_ssize_t n_spadding; + Py_ssize_t n_rpadding; + char lsign; + Py_ssize_t n_lsign; + char rsign; + Py_ssize_t n_rsign; + Py_ssize_t n_total; /* just a convenience, it's derivable from the + other fields */ +} IntegerFieldWidths; + /* returns true if this character is a specifier alignment token */ Py_LOCAL_INLINE(int) alignment_token(CH_TYPE c) @@ -1024,11 +1042,233 @@ (*FormatFunction)(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format); +static void +_calc_integer_widths(IntegerFieldWidths *r, CH_TYPE sign, Py_ssize_t n_digits, + const InternalFormatSpec *format) +{ + r->n_lpadding = 0; + r->n_spadding = 0; + r->n_rpadding = 0; + r->lsign = '\0'; + r->n_lsign = 0; + r->rsign = '\0'; + r->n_rsign = 0; + + /* the output will look like: + | | + | | + | | + + lsign and rsign are computed from format->sign and the actual + sign of the number + + digits is already known + + the total width is either given, or computed from the + actual digits + + only one of lpadding, spadding, and rpadding can be non-zero, + and it's calculated from the width and other fields + */ + + /* compute the various parts we're going to write */ + if (format->sign == '+') { + /* always put a + or - */ + r->n_lsign = 1; + r->lsign = (sign == '-' ? '-' : '+'); + } else if (format->sign == '(') { + if (sign == '-') { + r->n_lsign = 1; + r->lsign = '('; + r->n_rsign = 1; + r->rsign = ')'; + } + } else if (format->sign == ' ') { + r->n_lsign = 1; + r->lsign = (sign == '-' ? '-' : ' '); + } else { + /* non specified, or the default (-) */ + if (sign == '-') { + r->n_lsign = 1; + r->lsign = '-'; + } + } + + /* now the number of padding characters */ + if (format->width == -1) { + /* no padding at all, nothing to do */ + } else { + /* see if any padding is needed */ + if (r->n_lsign + n_digits + r->n_rsign >= format->width) { + /* no padding needed, we're already bigger than the + requested width */ + } else { + /* determine which of left, space, or right padding is + needed */ + Py_ssize_t padding = format->width - (r->n_lsign + n_digits + r->n_rsign); + if (format->align == '<') + r->n_rpadding = padding; + else if (format->align == '>') + r->n_lpadding = padding; + else + /* must be '=' */ + r->n_spadding = padding; + } + } + r->n_total = r->n_lpadding + r->n_lsign + r->n_spadding + + n_digits + r->n_rsign + r->n_rpadding; +} + +/* fill in the non-digit parts of an integer's string representation, + as determined in _calc_integer_widths(). returns the pointer to + where the digits go. */ +static CH_TYPE* +_fill_integer(CH_TYPE *p_buf, const IntegerFieldWidths *spec, + Py_ssize_t n_digits, CH_TYPE fill_char) +{ + CH_TYPE* p_digits; + + if (spec->n_lpadding) { + CH_TYPE_FILL(p_buf, fill_char, spec->n_lpadding); + p_buf += spec->n_lpadding; + } + if (spec->n_lsign == 1) { + *p_buf++ = spec->lsign; + } + if (spec->n_spadding) { + CH_TYPE_FILL(p_buf, fill_char, spec->n_spadding); + p_buf += spec->n_spadding; + } + p_digits = p_buf; + p_buf += n_digits; + if (spec->n_rsign == 1) { + *p_buf++ = spec->rsign; + } + if (spec->n_rpadding) { + CH_TYPE_FILL(p_buf, fill_char, spec->n_rpadding); + p_buf += spec->n_rpadding; + } + return p_digits; +} + +static int +_format_long_binary(PyObject *v, FmtState *fs, const InternalFormatSpec *format) +{ + /* we know that v is a PyLongObject */ + PyLongObject* l = (PyLongObject*)v; + + IntegerFieldWidths spec; + CH_TYPE *pbuf; + CH_TYPE *start; + char sign = _PyLong_Sign(v) >= 0 ? '\0' : '-'; + Py_ssize_t n_digits = _PyLong_NumBits(v); + Py_ssize_t i; + + /* special case for zero */ + if (l->ob_size == 0) + n_digits = 1; + + _calc_integer_widths(&spec, sign, n_digits, format); + + /* allocate space */ + if (output_allocate(fs, spec.n_total, &pbuf) == 0) + return 0; + + /* fill in the non-digit parts, and return a pointer where the digits go */ + start = _fill_integer(pbuf, &spec, n_digits, + format->fill_char == '\0' ? ' ' : format->fill_char); + + /* degenerate case for zero. handle it and get out */ + if (l->ob_size == 0) { + *pbuf = '0'; + return 1; + } + + /* finally, fill in the digits, starting at the right and working left */ + pbuf = start + n_digits - 1; + + for (i = 0; i < ABS(l->ob_size); i++) { + Py_ssize_t j; + digit d = l->ob_digit[i]; + for (j = 0; j < SHIFT; j++, d >>= 1) { + if (d & 1) + *pbuf = '1'; + else + *pbuf = '0'; + + /* see if we're done mid-digit */ + pbuf--; + if (pbuf < start) + goto DONE; + } + } + +DONE: + return 1; +} + +static int +_format_int_binary(PyObject *v, FmtState *fs, const InternalFormatSpec *format) +{ + /* see http://graphics.stanford.edu/~seander/bithacks.html for + various bit related hacks used here */ + + long x; + char sign = '\0'; + unsigned n_digits; + long tmp; + IntegerFieldWidths spec; + CH_TYPE *pbuf; + + x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, "int argument required, not %.200s", + v->ob_type->tp_name); + return 0; + } + + if (x < 0) { + sign = '-'; + x *= -1; + } + + /* number of binary digits is one more than lg(x). this also works for 0 */ + n_digits = 1; + tmp = x; + while (tmp >>= 1) + n_digits++; + + _calc_integer_widths(&spec, sign, n_digits, format); + + /* allocate space */ + if (output_allocate(fs, spec.n_total, &pbuf) == 0) + return 0; + + /* fill in the non-digit parts, and return a pointer where the digits go */ + pbuf = _fill_integer(pbuf, &spec, n_digits, + format->fill_char == '\0' ? ' ' : format->fill_char); + + /* finally, fill in the digits, starting at the right and working left */ + /* note that if x == 0, n_digits will be 1 and this loop will still work */ + pbuf += n_digits-1; + for (; n_digits; pbuf--, n_digits--, x >>= 1) { + if (x & 1) + *pbuf = '1'; + else + *pbuf = '0'; + } + + return 1; +} + static int format_binary(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return format_DUMMY(fieldobj, fs); + if (PyLong_Check(fieldobj)) + return _format_long_binary(fieldobj, fs, format); + else + return _format_int_binary(fieldobj, fs, format); } static int @@ -1137,7 +1377,6 @@ PyOS_snprintf(ptr, buflen, format, x); - /* convert from chars to unicode, if needed */ #if C_UNICODE len = strtounicode(*pbuf, start, -1); @@ -1177,13 +1416,13 @@ /* if we're hex or octal, check to see if 0 or 0x or 0X was at the front of the string. if so, skip it. */ - if (type == 'o' && n_allocated >= 1 && *pbuf[0] == '0') { + if (type == 'o' && n_allocated >= 1 && (*pbuf)[0] == '0') { p_charbuf++; n_allocated -= 1; - } else if (type == 'x' && n_allocated >= 2 && *pbuf[1] == 'x') { + } else if (type == 'x' && n_allocated >= 2 && (*pbuf)[1] == 'x') { p_charbuf += 2; n_allocated -= 1; - } else if (type == 'X' && n_allocated >= 2 && *pbuf[1] == 'X') { + } else if (type == 'X' && n_allocated >= 2 && (*pbuf)[1] == 'X') { p_charbuf += 2; n_allocated -= 1; } @@ -1214,14 +1453,7 @@ CH_TYPE *p_digits; /* pointer to the digits we have */ CH_TYPE n_digits; /* count of digits we have */ CH_TYPE sign; - Py_ssize_t n_lpadding; - Py_ssize_t n_spadding; - Py_ssize_t n_rpadding; - CH_TYPE lsign = 0; - Py_ssize_t n_lsign = 0; - CH_TYPE rsign = 0; - Py_ssize_t n_rsign = 0; - Py_ssize_t n_total; /* the total length we're going to write */ + IntegerFieldWidths spec; Py_ssize_t n_allocated; /* how much space we actually allocated when we wrote the digits into the output */ @@ -1258,73 +1490,7 @@ else sign = '\0'; - /* the output will look like: - | | - | | - | | - - lsign and rsign are computed from format->sign and the actual - sign of the number - - digits is already known - - the total width is either given, or computed from the - actual digits - - only one of lpadding, spadding, and rpadding can be non-zero, - and it's calculated from the width and other fields - */ - - /* compute the various parts we're going to write */ - if (format->sign == '+') { - /* always put a + or - */ - n_lsign = 1; - lsign = (sign == '-' ? '-' : '+'); - } else if (format->sign == '(') { - if (sign == '-') { - n_lsign = 1; - lsign = '('; - n_rsign = 1; - rsign = ')'; - } - } else if (format->sign == ' ') { - n_lsign = 1; - lsign = (sign == '-' ? '-' : ' '); - } else { - /* non specified, or the default (-) */ - if (sign == '-') { - n_lsign = 1; - lsign = '-'; - } - } - - /* now the number of padding characters */ - n_lpadding = n_spadding = n_rpadding = 0; - if (format->width == -1) { - /* no padding at all, nothing to do */ - } else { - /* see if any padding is needed */ - if (n_lsign + n_digits + n_rsign >= format->width) { - /* no padding needed, we're already bigger than the - requested width */ - } else { - /* determine which of left, space, or right padding is - needed */ - Py_ssize_t padding = format->width - (n_lsign + n_digits + n_rsign); - if (format->align == '<') - n_rpadding = padding; - else if (format->align == '>') - n_lpadding = padding; - else - /* must be '=' */ - n_spadding = padding; - } - } - - /* set the total length of the string */ - n_total = n_lpadding + n_lsign + n_spadding + n_digits - + n_rsign + n_rpadding; - assert(n_total >= n_allocated); + _calc_integer_widths(&spec, sign, n_digits, format); /* because we're going to reallocate, our pointers might be invalidated. remember the offsets, then re-create the pointers @@ -1333,7 +1499,7 @@ ofs_buf = p_buf - tmp; ofs_digits = p_digits - tmp; - output_allocate(fs, n_total - n_allocated, &tmp); + output_allocate(fs, spec.n_total - n_allocated, &tmp); tmp = STROBJ_AS_PTR(fs->outstr.obj); p_buf = tmp + ofs_buf; @@ -1342,46 +1508,27 @@ #if 0 printf("p_buf %p\n", p_buf); printf("p_digits %p\n", p_digits); + printf("digits '%.*s'\n", n_digits, p_digits); printf("n_digits: %d\n", n_digits); - printf("n_lpadding: %d\n", n_lpadding); - printf("n_lsign: %d\n", n_lsign); - printf("lsign: %d(%c)\n", lsign, lsign); - printf("n_rsign: %d\n", n_rsign); - printf("rsign: %d(%c)\n", rsign, rsign); - printf("n_spadding: %d\n", n_spadding); - printf("n_rpadding: %d\n", n_rpadding); + printf("n_lpadding: %d\n", spec.n_lpadding); + printf("n_lsign: %d\n", spec.n_lsign); + printf("lsign: %d(%c)\n", spec.lsign, spec.lsign); + printf("n_rsign: %d\n", spec.n_rsign); + printf("rsign: %d(%c)\n", spec.rsign, spec.rsign); + printf("n_spadding: %d\n", spec.n_spadding); + printf("n_rpadding: %d\n", spec.n_rpadding); #endif /* copy the characters into position first, since we're going to overwrite some of that space */ /* short circuit test, in case we don't have to move anything */ - if (p_buf + (n_lpadding + n_lsign + n_spadding) != p_digits) - memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits, - n_digits * sizeof(CH_TYPE)); - - if (n_lpadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, - n_lpadding); - p_buf += n_lpadding; - } - if (n_lsign == 1) { - *p_buf++ = lsign; - } - if (n_spadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, - n_spadding); - p_buf += n_spadding; - } - p_buf += n_digits; - if (n_rsign == 1) { - *p_buf++ = rsign; - } - if (n_rpadding) { - CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, - n_rpadding); - p_buf += n_rpadding; - } - + if (p_buf + (spec.n_lpadding + spec.n_lsign + spec.n_spadding) != p_digits) + memmove(p_buf + (spec.n_lpadding + spec.n_lsign + spec.n_spadding), + p_digits, n_digits * sizeof(CH_TYPE)); + + /* now fill in the non-digit parts */ + _fill_integer(p_buf, &spec, n_digits, + format->fill_char == '\0' ? ' ' : format->fill_char); return 1; } @@ -1569,10 +1716,7 @@ } } - /* XXX handle conversion functions that logically map to other - conversion functions? percent is the only one, and I'm not wild - about having percent at all*/ - + /* find the formatter function */ formatter = format_function(format.type); if (formatter == NULL) { SetError(fs, "Invalid conversion character"); From buildbot at python.org Tue Mar 6 03:38:19 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 02:38:19 +0000 Subject: [Python-checkins] buildbot warnings in hppa Ubuntu dapper 2.5 Message-ID: <20070306023819.2BD171E400D@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%25202.5/builds/38 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/httplib.py", line 1131, in connect ssl = socket.ssl(sock, self.key_file, self.cert_file) File "/home/pybot/buildarea/2.5.klose-ubuntu-hppa/build/Lib/socket.py", line 74, in ssl return _realssl(sock, keyfile, certfile) IOError: [Errno socket error] (8, 'EOF occurred in violation of protocol') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 03:43:11 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 03:43:11 +0100 (CET) Subject: [Python-checkins] r54142 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306024311.6FF1F1E4018@bag.python.org> Author: eric.smith Date: Tue Mar 6 03:43:07 2007 New Revision: 54142 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Fixed potentially unsafe pointer arithmetic. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 03:43:07 2007 @@ -1197,9 +1197,9 @@ *pbuf = '0'; /* see if we're done mid-digit */ - pbuf--; - if (pbuf < start) + if (pbuf == start) goto DONE; + pbuf--; } } From python-checkins at python.org Tue Mar 6 04:02:42 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 04:02:42 +0100 (CET) Subject: [Python-checkins] r54143 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306030242.740561E400C@bag.python.org> Author: eric.smith Date: Tue Mar 6 04:02:40 2007 New Revision: 54143 Modified: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added 'n' formatting, as I understand it. Added test cases, but they could use some work, such as testing different locales. Added notes to README.txt about these issues. Removed dummy formatter. Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Tue Mar 6 04:02:40 2007 @@ -61,4 +61,8 @@ - Add capability to control exception handling to pep_differences and to code: 1) ability to dump exceptions into string, 2) ability to re-do lower exceptions or "pile-on" + - Refactor format_locale_number() and format_float() to avoid + massive code duplication. + - Test suite needs to test different locales for 'n' formatting. + Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Tue Mar 6 04:02:40 2007 @@ -1,6 +1,8 @@ import unittest from test import test_support +import locale + import pep3101 # Variables used for testing name mapper @@ -274,6 +276,14 @@ self.formatEqualsWithUnicode("(" + "1" * 100 + ")", "{0:()b}", -(2**100 - 1)) self.formatEqualsWithUnicode("(" + " " * 98 + "1" * 100 + ")", "{0:=()200b}", -(2**100 - 1)) + def test_number_specifier(self): + def test(value): + self.formatEqualsWithUnicode(locale.format("%f", value), "{0:n}", value) + + test(0) + test(1) + test(10**100) + def test_missing_type_specifier(self): # make sure floats use 'g', ints and longs 'd', and everything else 's' pass Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 04:02:40 2007 @@ -1003,34 +1003,6 @@ } -/* XXX delete this when all internal conversions are implemented */ -static int -format_DUMMY(PyObject *fieldobj, FmtState *fs) -{ - PyObject *myobj; - int ok; - - /* Test implementation, only at top level */ - CH_TYPE under = '_'; - - myobj = STROBJ_STR(fieldobj); - if (myobj == NULL) - return 0; - ok = output_data(fs, STROBJ_AS_PTR(myobj), - STROBJ_GET_SIZE(myobj)); - Py_DECREF(myobj); - if (!ok) - return 0; - if (fs->fieldspec.ptr != fs->fieldspec.end) { - if (!output_data(fs, &under, 1)) - return 0; - if (!output_data(fs, fs->fieldspec.ptr, - fs->fieldspec.end - fs->fieldspec.ptr)) - return 0; - } - return 1; -} - /************************************************************************/ /************************* Builtin formatters *************************/ /************************************************************************/ @@ -1616,11 +1588,85 @@ return 1; } +/* this code is really the same as format_exponent, except it calls + PyOS_snprintf() instead of PyOS_ascii_formatd(). it needs to be + refactored to avoid all of the duplicate code. */ static int format_locale_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - return format_DUMMY(fieldobj, fs); + /* first, do the conversion as 8-bit chars, using the platform's + snprintf. then, if needed, convert to unicode. */ + + /* fmt = '%.' + `prec` + `type` + '%%' + worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ + char fmt[20]; + + double x; + CH_TYPE type = 'f'; + Py_ssize_t precision = format->precision; + CH_TYPE *buf; + int buflen; + int len; + char* trailing = ""; + + /* 'F' is the same as 'f', per the PEP */ + if (type == 'F') + type = 'f'; + + x = PyFloat_AsDouble(fieldobj); + if (x == -1.0 && PyErr_Occurred()) + return 0; + + if (precision < 0) + precision = 6; + if (type == 'f' && (fabs(x) / 1e25) >= 1e25) + type = 'g'; + + /* cast "type", because if we're in unicode we need to pass a + 8-bit char. this is safe, because we've restricted what "type" + can be */ + PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing); + + /* this is taken from unicodeobject.c, except we don't force a + limit here, we dynamically allocate instead */ + /* Worst case length calc to ensure no buffer overrun: + + 'g' formats: + fmt = %#.g + buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp + for any double rep.) + len = 1 + prec + 1 + 2 + 5 = 9 + prec + + 'f' formats: + buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50) + len = 1 + 50 + 1 + prec = 52 + prec + + If prec=0 the effective precision is 1 (the leading digit is + always given), therefore increase the length by one. + + */ + /* so, allocate the precision plus 54 chars (we add one additional + for the trailing percent). do this allocation as the native + type, because we're going to convert to unicode anyway */ + buflen = 54 + precision; + if (output_allocate(fs, buflen, &buf) == 0) + return 0; + PyOS_snprintf((char *)buf, buflen, fmt, x); + +#if C_UNICODE + len = strtounicode(buf, (char*)buf, -1); +#else + /* compute the length. I believe this is done because the return + value from snprintf above is unreliable */ + len = strlen(buf); +#endif + + /* shrink the buffer down to how many characters we actually + wrote. this is cheap, just pointer arithmetic */ + output_shrink(fs, buflen - len); + + return 1; } static int From python-checkins at python.org Tue Mar 6 04:27:49 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 04:27:49 +0100 (CET) Subject: [Python-checkins] r54144 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306032749.B37811E400C@bag.python.org> Author: eric.smith Date: Tue Mar 6 04:27:46 2007 New Revision: 54144 Modified: sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/unicodeformat.c Log: Refactored so that format_locale_number() and format_float() share the same code, except for the call in the middle that does the actual formatting. Modified: sandbox/trunk/pep3101/README.txt ============================================================================== --- sandbox/trunk/pep3101/README.txt (original) +++ sandbox/trunk/pep3101/README.txt Tue Mar 6 04:27:46 2007 @@ -61,8 +61,6 @@ - Add capability to control exception handling to pep_differences and to code: 1) ability to dump exceptions into string, 2) ability to re-do lower exceptions or "pile-on" - - Refactor format_locale_number() and format_float() to avoid - massive code duplication. - Test suite needs to test different locales for 'n' formatting. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 04:27:46 2007 @@ -1504,48 +1504,55 @@ return 1; } +/* state that needs to be passed between _format_float_phase1() and + _format_float_phase2() */ +typedef struct { + /* fmt = '%.' + `prec` + `type` + '%%' + worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ + char fmt[20]; + CH_TYPE *buf; + int buflen; + double x; +} FloatState; + +/* use type instead of format->type, so that it can be overridden by + format_locale_number() */ static int -format_float(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) +_format_float_phase1(FloatState *state, CH_TYPE type, PyObject *fieldobj, + FmtState *fs, const InternalFormatSpec *format) { /* first, do the conversion as 8-bit chars, using the platform's snprintf. then, if needed, convert to unicode. */ - /* fmt = '%.' + `prec` + `type` + '%%' - worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ - char fmt[20]; - - double x; - CH_TYPE type = format->type; Py_ssize_t precision = format->precision; - CH_TYPE *buf; - int buflen; - int len; char* trailing = ""; /* 'F' is the same as 'f', per the PEP */ if (type == 'F') type = 'f'; - x = PyFloat_AsDouble(fieldobj); - if (x == -1.0 && PyErr_Occurred()) + state->x = PyFloat_AsDouble(fieldobj); + if (state->x == -1.0 && PyErr_Occurred()) { + printf("not a float\n"); return 0; + } if (type == '%') { type = 'f'; - x *= 100; + state->x *= 100; trailing = "%%"; } if (precision < 0) precision = 6; - if (type == 'f' && (fabs(x) / 1e25) >= 1e25) + if (type == 'f' && (fabs(state->x) / 1e25) >= 1e25) type = 'g'; /* cast "type", because if we're in unicode we need to pass a 8-bit char. this is safe, because we've restricted what "type" can be */ - PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing); + PyOS_snprintf(state->fmt, sizeof(state->fmt), "%%.%d%c%s", precision, + (char)type, trailing); /* this is taken from unicodeobject.c, except we don't force a limit here, we dynamically allocate instead */ @@ -1568,108 +1575,60 @@ /* so, allocate the precision plus 54 chars (we add one additional for the trailing percent). do this allocation as the native type, because we're going to convert to unicode anyway */ - buflen = 54 + precision; - if (output_allocate(fs, buflen, &buf) == 0) + state->buflen = 54 + precision; + if (output_allocate(fs, state->buflen, &state->buf) == 0) return 0; - PyOS_ascii_formatd((char *)buf, buflen, fmt, x); - -#if C_UNICODE - len = strtounicode(buf, (char*)buf, -1); -#else - /* compute the length. I believe this is done because the return - value from snprintf above is unreliable */ - len = strlen(buf); -#endif - - /* shrink the buffer down to how many characters we actually - wrote. this is cheap, just pointer arithmetic */ - output_shrink(fs, buflen - len); return 1; } -/* this code is really the same as format_exponent, except it calls - PyOS_snprintf() instead of PyOS_ascii_formatd(). it needs to be - refactored to avoid all of the duplicate code. */ + static int -format_locale_number(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) +_format_float_phase2(FloatState *state, FmtState *fs) { - /* first, do the conversion as 8-bit chars, using the platform's - snprintf. then, if needed, convert to unicode. */ - - /* fmt = '%.' + `prec` + `type` + '%%' - worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ - char fmt[20]; - - double x; - CH_TYPE type = 'f'; - Py_ssize_t precision = format->precision; - CH_TYPE *buf; - int buflen; int len; - char* trailing = ""; - - /* 'F' is the same as 'f', per the PEP */ - if (type == 'F') - type = 'f'; - - x = PyFloat_AsDouble(fieldobj); - if (x == -1.0 && PyErr_Occurred()) - return 0; - - if (precision < 0) - precision = 6; - if (type == 'f' && (fabs(x) / 1e25) >= 1e25) - type = 'g'; - - /* cast "type", because if we're in unicode we need to pass a - 8-bit char. this is safe, because we've restricted what "type" - can be */ - PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing); - - /* this is taken from unicodeobject.c, except we don't force a - limit here, we dynamically allocate instead */ - /* Worst case length calc to ensure no buffer overrun: - - 'g' formats: - fmt = %#.g - buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp - for any double rep.) - len = 1 + prec + 1 + 2 + 5 = 9 + prec - - 'f' formats: - buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50) - len = 1 + 50 + 1 + prec = 52 + prec - - If prec=0 the effective precision is 1 (the leading digit is - always given), therefore increase the length by one. - - */ - /* so, allocate the precision plus 54 chars (we add one additional - for the trailing percent). do this allocation as the native - type, because we're going to convert to unicode anyway */ - buflen = 54 + precision; - if (output_allocate(fs, buflen, &buf) == 0) - return 0; - PyOS_snprintf((char *)buf, buflen, fmt, x); #if C_UNICODE - len = strtounicode(buf, (char*)buf, -1); + len = strtounicode(state->buf, (char*)state->buf, -1); #else /* compute the length. I believe this is done because the return value from snprintf above is unreliable */ - len = strlen(buf); + len = strlen(state->buf); #endif /* shrink the buffer down to how many characters we actually wrote. this is cheap, just pointer arithmetic */ - output_shrink(fs, buflen - len); + output_shrink(fs, state->buflen - len); return 1; } static int +format_float(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) +{ + FloatState state; + if (_format_float_phase1(&state, format->type, fieldobj, fs, format) == 0) + return 0; + PyOS_ascii_formatd((char *)state.buf, state.buflen, state.fmt, state.x); + return _format_float_phase2(&state, fs); +} + +/* this code is really the same as format_exponent, except it calls + PyOS_snprintf() instead of PyOS_ascii_formatd(). */ +static int +format_locale_number(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) +{ + FloatState state; + if (_format_float_phase1(&state, 'f', fieldobj, fs, format) == 0) { + return 0; + } + PyOS_snprintf((char *)state.buf, state.buflen, state.fmt, state.x); + return _format_float_phase2(&state, fs); +} + +static int format_repr(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { From python-checkins at python.org Tue Mar 6 04:58:52 2007 From: python-checkins at python.org (patrick.maupin) Date: Tue, 6 Mar 2007 04:58:52 +0100 (CET) Subject: [Python-checkins] r54145 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306035852.682831E400C@bag.python.org> Author: patrick.maupin Date: Tue Mar 6 04:58:48 2007 New Revision: 54145 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Added some tests, squashed a recursion bug and fixed an error message Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Tue Mar 6 04:58:48 2007 @@ -300,12 +300,9 @@ self.formatRaises(ValueError, "}{", True) self.formatRaises(ValueError, "{0", True) self.formatRaises(ValueError, "{0.[]}", True) - # XXX: these raise the wrong exceptions - #self.formatRaises(ValueError, "{0[0}", True) - #self.formatRaises(ValueError, "{0[0:foo}", True) + self.formatRaises(ValueError, "{0[0}", [1]) + self.formatRaises(ValueError, "{0[0:foo}", [1]) self.formatRaises(ValueError, "{c]}", True) - # XXX: this should *not* raise - #self.formatRaises(ValueError, "{{1}}", True, 0) self.formatRaises(ValueError, "{{ {{{0}}", True) self.formatRaises(ValueError, "{0}}", True) @@ -387,6 +384,15 @@ self.formatRaises(ValueError, '{!syntax3}{') self.formatEquals('{', '{!syntax3}{ ') + def test_computed_lookups(self): + class Custom(object): + fred = 'Hi there' + self.formatEquals('e', '{0[{1}]}', 'abcdefg', 4) + self.formatEquals('e', '{foo[{bar}]}', foo='abcdefg', bar=4) + self.formatEquals(Custom.fred, '{0.{1}}', Custom, 'fred') + self.formatEquals(Custom.fred, '{x.{y}}', x=Custom, y='fred') + self.formatEquals('t', '{x.{y}[{0}]}', 3, x=Custom, y='fred') + def test_main(): test_support.run_unittest(FormatTest) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 04:58:48 2007 @@ -572,6 +572,7 @@ /* This little bit of mutual recursion allows nested dictionary lookups and computed attribute names */ + fs->fmtstr.ptr++; if (--(fs->max_recursion) < 0) return SetError(fs, "Maximum string recursion limit exceeded"); result = get_field_object(fs); @@ -1968,7 +1969,7 @@ at_end = ptr >= end; switch (syntaxmode) { case 0: - if ((c == '}') && (!at_end) && (c != *ptr)) + if ((c == '}') && (at_end || (c != *ptr))) return (int)SetError(fs, "Single } encountered"); case 1: if (at_end) From python-checkins at python.org Tue Mar 6 05:04:01 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 05:04:01 +0100 (CET) Subject: [Python-checkins] r54146 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306040401.7F7471E400C@bag.python.org> Author: eric.smith Date: Tue Mar 6 05:03:49 2007 New Revision: 54146 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Refactored float formatting, again. This time it's simpler by using a callback, instead of the two-phase version I used last time. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 05:03:49 2007 @@ -66,7 +66,6 @@ #define PySet_Discard PyDict_DelItem #define PySet_New PyDict_Copy #define PySet_GET_SIZE PyDict_Size -#define PyOS_ascii_formatd PyOS_snprintf #endif @@ -1505,54 +1504,65 @@ return 1; } -/* state that needs to be passed between _format_float_phase1() and - _format_float_phase2() */ -typedef struct { +/* the callback function to call to do the actual float formatting. + it matches the definition of PyOS_ascii_formatd */ +typedef char* +(*DoubleSnprintfFunction)(char *buffer, size_t buf_len, + const char *format, double d); + +/* just a wrapper to make PyOS_snprintf look like DoubleSnprintfFunction */ +static char* +snprintf_double(char *buffer, size_t buf_len, const char *format, double d) +{ + PyOS_snprintf(buffer, buf_len, format, d); + return NULL; +} + +/* use type instead of format->type, so that it can be overridden by + format_locale_number() */ +static int +_format_float(CH_TYPE type, PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format, + DoubleSnprintfFunction snprintf) +{ /* fmt = '%.' + `prec` + `type` + '%%' worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/ char fmt[20]; CH_TYPE *buf; int buflen; double x; -} FloatState; + int len; + Py_ssize_t precision = format->precision; + char* trailing = ""; -/* use type instead of format->type, so that it can be overridden by - format_locale_number() */ -static int -_format_float_phase1(FloatState *state, CH_TYPE type, PyObject *fieldobj, - FmtState *fs, const InternalFormatSpec *format) -{ /* first, do the conversion as 8-bit chars, using the platform's snprintf. then, if needed, convert to unicode. */ - Py_ssize_t precision = format->precision; - char* trailing = ""; - /* 'F' is the same as 'f', per the PEP */ if (type == 'F') type = 'f'; - state->x = PyFloat_AsDouble(fieldobj); - if (state->x == -1.0 && PyErr_Occurred()) { + x = PyFloat_AsDouble(fieldobj); + if (x == -1.0 && PyErr_Occurred()) { printf("not a float\n"); return 0; } if (type == '%') { type = 'f'; - state->x *= 100; + x *= 100; trailing = "%%"; } if (precision < 0) precision = 6; - if (type == 'f' && (fabs(state->x) / 1e25) >= 1e25) + if (type == 'f' && (fabs(x) / 1e25) >= 1e25) type = 'g'; /* cast "type", because if we're in unicode we need to pass a 8-bit char. this is safe, because we've restricted what "type" can be */ - PyOS_snprintf(state->fmt, sizeof(state->fmt), "%%.%d%c%s", precision, + PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing); /* this is taken from unicodeobject.c, except we don't force a @@ -1576,30 +1586,24 @@ /* so, allocate the precision plus 54 chars (we add one additional for the trailing percent). do this allocation as the native type, because we're going to convert to unicode anyway */ - state->buflen = 54 + precision; - if (output_allocate(fs, state->buflen, &state->buf) == 0) + buflen = 54 + precision; + if (output_allocate(fs, buflen, &buf) == 0) return 0; - return 1; -} - - -static int -_format_float_phase2(FloatState *state, FmtState *fs) -{ - int len; + /* call the passed in function to do the actual formatting */ + snprintf((char*)buf, buflen, fmt, x); #if C_UNICODE - len = strtounicode(state->buf, (char*)state->buf, -1); + len = strtounicode(buf, (char*)buf, -1); #else /* compute the length. I believe this is done because the return value from snprintf above is unreliable */ - len = strlen(state->buf); + len = strlen(buf); #endif /* shrink the buffer down to how many characters we actually wrote. this is cheap, just pointer arithmetic */ - output_shrink(fs, state->buflen - len); + output_shrink(fs, buflen - len); return 1; } @@ -1608,11 +1612,13 @@ format_float(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - FloatState state; - if (_format_float_phase1(&state, format->type, fieldobj, fs, format) == 0) - return 0; - PyOS_ascii_formatd((char *)state.buf, state.buflen, state.fmt, state.x); - return _format_float_phase2(&state, fs); + return _format_float(format->type, fieldobj, fs, format, +#if PYTHON_API_VERSION < 1013 + snprintf_double +#else + PyOS_ascii_formatd +#endif + ); } /* this code is really the same as format_exponent, except it calls @@ -1621,12 +1627,7 @@ format_locale_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format) { - FloatState state; - if (_format_float_phase1(&state, 'f', fieldobj, fs, format) == 0) { - return 0; - } - PyOS_snprintf((char *)state.buf, state.buflen, state.fmt, state.x); - return _format_float_phase2(&state, fs); + return _format_float('f', fieldobj, fs, format, snprintf_double); } static int From python-checkins at python.org Tue Mar 6 06:04:39 2007 From: python-checkins at python.org (patrick.maupin) Date: Tue, 6 Mar 2007 06:04:39 +0100 (CET) Subject: [Python-checkins] r54147 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306050439.CB6AF1E400C@bag.python.org> Author: patrick.maupin Date: Tue Mar 6 06:04:37 2007 New Revision: 54147 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Broke output_allocate into two functions Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 06:04:37 2007 @@ -424,6 +424,30 @@ } /* + output_extend reallocates the output string buffer. + It returns a status: 0 for a failed reallocation, + 1 for success. +*/ + +static int +output_extend(FmtState *fs, Py_ssize_t count) +{ + CH_TYPE *startptr; + Py_ssize_t curlen, maxlen; + startptr = STROBJ_AS_PTR(fs->outstr.obj); + curlen = fs->outstr.ptr - startptr; + maxlen = curlen + count + fs->size_increment; + if (STROBJ_RESIZE(&fs->outstr.obj, maxlen) < 0) + return 0; + startptr = STROBJ_AS_PTR(fs->outstr.obj); + fs->outstr.ptr = startptr + curlen; + fs->outstr.end = startptr + maxlen; + if (fs->size_increment < MAX_SIZE_INCREMENT) + fs->size_increment *= SIZE_MULTIPLIER; + return 1; +} + +/* output_allocate reserves space in our output string buffer In some cases, it has to reallocate the string. @@ -437,21 +461,9 @@ static int output_allocate(FmtState *fs, Py_ssize_t count, CH_TYPE **ptr) { - Py_ssize_t room = fs->outstr.end - fs->outstr.ptr; - if (count > room) { - CH_TYPE *startptr; - Py_ssize_t curlen, maxlen; - startptr = STROBJ_AS_PTR(fs->outstr.obj); - curlen = fs->outstr.ptr - startptr; - maxlen = curlen + count + fs->size_increment; - if (STROBJ_RESIZE(&fs->outstr.obj, maxlen) < 0) - return 0; - startptr = STROBJ_AS_PTR(fs->outstr.obj); - fs->outstr.ptr = startptr + curlen; - fs->outstr.end = startptr + maxlen; - if (fs->size_increment < MAX_SIZE_INCREMENT) - fs->size_increment *= SIZE_MULTIPLIER; - } + if ((count > fs->outstr.end - fs->outstr.ptr) && + !output_extend(fs, count)) + return 0; *ptr = fs->outstr.ptr; fs->outstr.ptr += count; return 1; @@ -469,20 +481,11 @@ static int output_data(FmtState *fs, const CH_TYPE *s, Py_ssize_t count) { - CH_TYPE *dst; - - /* there is some duplicate code here with output_allocate, - which is here to avoid a function call if there's already - enough allocated space */ - Py_ssize_t room = fs->outstr.end - fs->outstr.ptr; - if (count > room) { - if (output_allocate(fs, count, &dst) == 0) - return 0; - } else { - dst = fs->outstr.ptr; - fs->outstr.ptr += count; - } - memcpy(dst, s, count * sizeof(CH_TYPE)); + if ((count > fs->outstr.end - fs->outstr.ptr) && + !output_extend(fs, count)) + return 0; + memcpy(fs->outstr.ptr, s, count * sizeof(CH_TYPE)); + fs->outstr.ptr += count; return 1; } From buildbot at python.org Tue Mar 6 06:11:47 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 05:11:47 +0000 Subject: [Python-checkins] buildbot warnings in MIPS Debian 2.5 Message-ID: <20070306051147.909351E400C@bag.python.org> The Buildbot has detected a new failure of MIPS Debian 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/MIPS%2520Debian%25202.5/builds/148 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/httplib.py", line 1131, in connect ssl = socket.ssl(sock, self.key_file, self.cert_file) File "/home/pybot/buildarea/2.5.klose-debian-mips/build/Lib/socket.py", line 74, in ssl return _realssl(sock, keyfile, certfile) IOError: [Errno socket error] (8, 'EOF occurred in violation of protocol') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 10:30:32 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 10:30:32 +0100 (CET) Subject: [Python-checkins] r54148 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306093032.4E7111E400C@bag.python.org> Author: eric.smith Date: Tue Mar 6 10:30:21 2007 New Revision: 54148 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Removed unused FORMATBUFLEN. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 10:30:21 2007 @@ -77,23 +77,12 @@ + 1 + 1 = 24 */ #define MAXLEN_INT_STRING 64 +#define ABS(x) ((x) < 0 ? -(x) : (x)) + /************************************************************************/ /*********** Global data structures and forward declarations *********/ /************************************************************************/ -/* FORMATBUFLEN is taken from stringobject.c, it should probably be - factored out */ -/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) - - FORMATBUFLEN is the length of the buffer in which the floats, ints, & - chars are formatted. XXX This is a magic number. Each formatting - routine does bounds checking to ensure no overflow, but a better - solution may be to malloc a buffer of appropriate size for each - format. For now, the current solution is sufficient. -*/ -#define FORMATBUFLEN (size_t)120 - -#define ABS(x) ((x) < 0 ? -(x) : (x)) #ifdef __cplusplus extern "C" { From python-checkins at python.org Tue Mar 6 10:33:05 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 10:33:05 +0100 (CET) Subject: [Python-checkins] r54149 - python/trunk/Doc/api/newtypes.tex Message-ID: <20070306093305.D294C1E400C@bag.python.org> Author: georg.brandl Date: Tue Mar 6 10:33:01 2007 New Revision: 54149 Modified: python/trunk/Doc/api/newtypes.tex Log: Nit: a struct field is set to GenericAlloc, not GenericAlloc(). Modified: python/trunk/Doc/api/newtypes.tex ============================================================================== --- python/trunk/Doc/api/newtypes.tex (original) +++ python/trunk/Doc/api/newtypes.tex Tue Mar 6 10:33:01 2007 @@ -1316,7 +1316,7 @@ 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} From python-checkins at python.org Tue Mar 6 11:02:53 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 11:02:53 +0100 (CET) Subject: [Python-checkins] r54150 - in python/trunk: Doc/ext/newtypes.tex Doc/ext/shoddy.c Misc/NEWS Message-ID: <20070306100253.057651E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 11:02:47 2007 New Revision: 54150 Added: python/trunk/Doc/ext/shoddy.c Modified: python/trunk/Doc/ext/newtypes.tex python/trunk/Misc/NEWS Log: Patch #1671450: add a section about subclassing builtin types to the "extending and embedding" tutorial. Modified: python/trunk/Doc/ext/newtypes.tex ============================================================================== --- python/trunk/Doc/ext/newtypes.tex (original) +++ python/trunk/Doc/ext/newtypes.tex Tue Mar 6 11:02:47 2007 @@ -489,7 +489,6 @@ 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 @@ 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}} Added: python/trunk/Doc/ext/shoddy.c ============================================================================== --- (empty file) +++ python/trunk/Doc/ext/shoddy.c Tue Mar 6 11:02:47 2007 @@ -0,0 +1,91 @@ +#include + +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); +} Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 11:02:47 2007 @@ -502,6 +502,9 @@ Documentation ------------- +- Patch #1671450: add a section about subclassing builtin types to the + "extending and embedding" tutorial. + - Bug #1629125: fix wrong data type (int -> Py_ssize_t) in PyDict_Next docs. From python-checkins at python.org Tue Mar 6 11:03:03 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 11:03:03 +0100 (CET) Subject: [Python-checkins] r54151 - in python/branches/release25-maint: Doc/ext/newtypes.tex Doc/ext/shoddy.c Misc/NEWS Message-ID: <20070306100303.16B9A1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 11:02:59 2007 New Revision: 54151 Added: python/branches/release25-maint/Doc/ext/shoddy.c Modified: python/branches/release25-maint/Doc/ext/newtypes.tex python/branches/release25-maint/Misc/NEWS Log: Patch #1671450: add a section about subclassing builtin types to the "extending and embedding" tutorial. (backport from rev. 54150) Modified: python/branches/release25-maint/Doc/ext/newtypes.tex ============================================================================== --- python/branches/release25-maint/Doc/ext/newtypes.tex (original) +++ python/branches/release25-maint/Doc/ext/newtypes.tex Tue Mar 6 11:02:59 2007 @@ -489,7 +489,6 @@ 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 @@ 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}} Added: python/branches/release25-maint/Doc/ext/shoddy.c ============================================================================== --- (empty file) +++ python/branches/release25-maint/Doc/ext/shoddy.c Tue Mar 6 11:02:59 2007 @@ -0,0 +1,91 @@ +#include + +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); +} Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 11:02:59 2007 @@ -457,6 +457,9 @@ Documentation ------------- +- Patch #1671450: add a section about subclassing builtin types to the + "extending and embedding" tutorial. + - Bug #1629125: fix wrong data type (int -> Py_ssize_t) in PyDict_Next docs. From nnorwitz at gmail.com Tue Mar 6 11:33:55 2007 From: nnorwitz at gmail.com (Neal Norwitz) Date: Tue, 6 Mar 2007 05:33:55 -0500 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20070306103355.GA19215@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_atexit test_audioop test_augassign test_base64 test_bastion test_bigaddrspace test_bigmem test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_collections test_colorsys test_commands test_compare test_compile test_compiler test_complex test_complex_args test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_doctest test_doctest2 test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functools test_future test_gc test_gdbm test_generators test_genericpath test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_modulefinder test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_normalization test_ntpath test_old_mailbox test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [7321 refs] [7321 refs] [7321 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [7696 refs] [7696 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test test_socket_ssl crashed -- : [Errno socket error] (110, 'Connection timed out') test_socketserver test_softspace test_sort test_sqlite test_startfile test_startfile skipped -- cannot import name startfile test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structmembers test_structseq test_subprocess [7316 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7317 refs] [8865 refs] [7532 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] . [7316 refs] [7316 refs] this bit of output is from a test of stdout in a different process ... [7316 refs] [7316 refs] [7532 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [7316 refs] [7316 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [7320 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_unittest test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_uuid WARNING: uuid.getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._ifconfig_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._unixdll_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_wsgiref test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipfile64 test_zipfile64 skipped -- test requires loads of disk-space bytes and a long time to run test_zipimport test_zlib 299 tests OK. 1 test failed: test_socket_ssl 21 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_pep277 test_plistlib test_scriptpackages test_startfile test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound test_zipfile64 1 skip unexpected on linux2: test_ioctl warning: DBTxn aborted in destructor. No prior commit() or abort(). [463197 refs] From python-checkins at python.org Tue Mar 6 11:41:26 2007 From: python-checkins at python.org (martin.v.loewis) Date: Tue, 6 Mar 2007 11:41:26 +0100 (CET) Subject: [Python-checkins] r54152 - in python/trunk: Doc/lib/libzipfile.tex Lib/test/test_zipfile.py Lib/zipfile.py Misc/NEWS Message-ID: <20070306104126.1E3B61E400E@bag.python.org> Author: martin.v.loewis Date: Tue Mar 6 11:41:24 2007 New Revision: 54152 Modified: python/trunk/Doc/lib/libzipfile.tex python/trunk/Lib/test/test_zipfile.py python/trunk/Lib/zipfile.py python/trunk/Misc/NEWS Log: Patch #1121142: Implement ZipFile.open. Modified: python/trunk/Doc/lib/libzipfile.tex ============================================================================== --- python/trunk/Doc/lib/libzipfile.tex (original) +++ python/trunk/Doc/lib/libzipfile.tex Tue Mar 6 11:41:24 2007 @@ -141,6 +141,32 @@ 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} +\end{methoddesc} + \begin{methoddesc}{printdir}{} Print a table of contents for the archive to \code{sys.stdout}. \end{methoddesc} Modified: python/trunk/Lib/test/test_zipfile.py ============================================================================== --- python/trunk/Lib/test/test_zipfile.py (original) +++ python/trunk/Lib/test/test_zipfile.py Tue Mar 6 11:41:24 2007 @@ -4,26 +4,29 @@ 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 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 +34,9 @@ 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 +91,144 @@ # 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 testRandomOpenStored(self): + for f in (TESTFN2, TemporaryFile(), StringIO()): + self.zipRandomOpenTest(f, zipfile.ZIP_STORED) + + def zipReadlineTest(self, f, compression): + self.makeTestArchive(f, compression) - def testStored(self): + # 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.zipTest(f, zipfile.ZIP_STORED) + 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 +238,6 @@ self.assertEqual(zipfp.namelist(), ["absolute"]) zipfp.close() - def tearDown(self): os.remove(TESTFN) os.remove(TESTFN2) @@ -123,7 +250,7 @@ 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 @@ -344,6 +471,26 @@ except zipfile.BadZipfile: os.unlink(TESTFN) + 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) + os.unlink(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) + os.unlink(TESTFN) + self.assert_(chk is True) + def testNonExistentFileRaisesIOError(self): # make sure we don't raise an AttributeError when a partially-constructed # ZipFile instance is finalized; this tests for regression on SF tracker @@ -371,7 +518,6 @@ # and report that the first file in the archive was corrupt. self.assertRaises(RuntimeError, zipf.testzip) - class DecryptionTests(unittest.TestCase): # This test checks that ZIP decryption works. Since the library does not # support encryption at the moment, we use a pre-generated encrypted @@ -411,9 +557,255 @@ 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('= 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. @@ -534,73 +728,75 @@ 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 - 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 + + # build and return a ZipExtFile + if zd is None: + zef = ZipExtFile(zef_file, zinfo) + else: + 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.""" Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 11:41:24 2007 @@ -139,6 +139,8 @@ Library ------- +- Patch #1121142: Implement ZipFile.open. + - Taught setup.py how to locate Berkeley DB on Macs using MacPorts. - Added heapq.merge() for merging sorted input streams. From python-checkins at python.org Tue Mar 6 12:23:23 2007 From: python-checkins at python.org (eric.smith) Date: Tue, 6 Mar 2007 12:23:23 +0100 (CET) Subject: [Python-checkins] r54153 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070306112323.7CCBB1E400D@bag.python.org> Author: eric.smith Date: Tue Mar 6 12:23:22 2007 New Revision: 54153 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: At Patrick Maupin's suggestion, improved the logic for looking up default internal formatter if not type specifier is present. Added test cases for same. Removed bogus printf left over from debugging. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Tue Mar 6 12:23:22 2007 @@ -52,18 +52,7 @@ self.assertEquals(val, unicode(result.upper())) def formatRaises(self, exc, text, *args, **kwargs): - exc = exc or Exception #StringFormat.FormatError - text = str(text) - #prevState = StringFormat.strict_format_errors - #StringFormat.strict_format_errors = True - try: - self.assertRaises(exc, - lambda: pep3101.format( - text, *args, **kwargs)) - finally: - pass - #StringFormat.strict_format_errors = prevState - + self.assertRaises(exc, pep3101.format, text, *args, **kwargs) def test_basic(self): # directly from the pep intro @@ -286,7 +275,17 @@ def test_missing_type_specifier(self): # make sure floats use 'g', ints and longs 'd', and everything else 's' - pass + + self.formatEqualsWithUnicode("3.5", "{0}", 3.5) + self.formatEqualsWithUnicode("5e+99", "{0}", 0.5e100) + self.formatEqualsWithUnicode("5e-101", "{0}", 0.5e-100) + self.formatEqualsWithUnicode("0", "{0}", 0) + self.formatEqualsWithUnicode("1", "{0}", 1) + self.formatEqualsWithUnicode("-1", "{0}", -1) + self.formatEqualsWithUnicode("(1)", "{0:()}", -1) + self.formatEqualsWithUnicode("1" + "0" * 100, "{0}", 10**100) + self.formatEqualsWithUnicode("hello, world", "{0}", "hello, world") + self.formatEqualsWithUnicode(" hello, world", "{0:>20}", "hello, world") def test_custom_format(self): class Custom(object): @@ -306,6 +305,12 @@ self.formatRaises(ValueError, "{{ {{{0}}", True) self.formatRaises(ValueError, "{0}}", True) + def test_invalid_type_specifier(self): + self.formatRaises(ValueError, "{0::}", 0) + self.formatRaises(ValueError, "{0:&}", 0) + self.formatRaises(ValueError, "{0:k}", 0) + self.formatRaises(ValueError, "{0:4<10.20K}", 0) + def test_name_mapper(self): mydict = dict(foo=1, bar=2) dict2 = mydict, dict(foobar=3) Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Tue Mar 6 12:23:22 2007 @@ -1511,7 +1511,7 @@ } /* use type instead of format->type, so that it can be overridden by - format_locale_number() */ + format_number() */ static int _format_float(CH_TYPE type, PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format, @@ -1535,10 +1535,8 @@ type = 'f'; x = PyFloat_AsDouble(fieldobj); - if (x == -1.0 && PyErr_Occurred()) { - printf("not a float\n"); + if (x == -1.0 && PyErr_Occurred()) return 0; - } if (type == '%') { type = 'f'; @@ -1616,8 +1614,8 @@ /* this code is really the same as format_exponent, except it calls PyOS_snprintf() instead of PyOS_ascii_formatd(). */ static int -format_locale_number(PyObject *fieldobj, FmtState *fs, - const InternalFormatSpec *format) +format_number(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) { return _format_float('f', fieldobj, fs, format, snprintf_double); } @@ -1662,32 +1660,52 @@ return ok; } -/* returns a pointer to our conversion function, or NULL if invalid */ +static int +format_bad_type_specifier(PyObject *fieldobj, FmtState *fs, + const InternalFormatSpec *format) +{ + SetError(fs, "Invalid conversion character"); + return 0; +} + +/* returns a pointer to our conversion function. also sets + format->type based on the type of fieldobj, if format->type was not + specified. if format->type is unknown, returns a pointer to the + psueod-conversion function format_bad_type_specifier() */ Py_LOCAL_INLINE(FormatFunction) -format_function(CH_TYPE c) +format_function(PyObject *fieldobj, InternalFormatSpec *format) { - switch (c) { - case 'b': return format_binary; /* base-2 */ - case 'c': return format_char; /* as character */ - case 'n': return format_locale_number; /* number in locale-specific + while (1) + switch (format->type) { + case 'b': return format_binary; /* base-2 */ + case 'c': return format_char; /* as character */ + case 'n': return format_number; /* number in locale-specific format */ - case 'd': /* decimal integer */ - case 'o': /* octal */ - case 'x': /* base 16 */ - case 'X': return format_integer; /* base 16 uppercase */ - case 'r': return format_repr; /* in repr() format */ - case 's': return format_string; /* convert using str() */ - case 'e': /* exponential notation */ - case 'E': /* exponential notation + case 'd': /* decimal integer */ + case 'o': /* octal */ + case 'x': /* base 16 */ + case 'X': return format_integer; /* base 16 uppercase */ + case 'r': return format_repr; /* in repr() format */ + case 's': return format_string; /* convert using str() */ + case 'e': /* exponential notation */ + case 'E': /* exponential notation with uppercase 'E' */ - case 'f': /* fixed-point */ - case 'F': /* fixed-point with uppercase */ - case 'g': /* general number notation */ - case 'G': /* general number notation + case 'f': /* fixed-point */ + case 'F': /* fixed-point with uppercase */ + case 'g': /* general number notation */ + case 'G': /* general number notation with uppercase 'E' */ - case '%': return format_float; /* as percentage */ - default: - return NULL; + case '%': return format_float; /* as percentage */ + case '\0': + /* if no type character was specified, look up the default + type character, based on the type of our object, then + loop around and execute the switch statement again */ + format->type = (PyInt_Check(fieldobj) || + PyLong_Check(fieldobj)) ? 'd' + : (PyFloat_Check(fieldobj)) ?'g' : 's'; + continue; + default: + return format_bad_type_specifier; } } @@ -1697,33 +1715,15 @@ static int internal_render(FmtState *fs, PyObject *fieldobj) { InternalFormatSpec format; - FormatFunction formatter; if (!parse_internal_render_format_spec(fs, &format)) { return 0; } - /* if no type character was specified, look up the default - type character, based on the type of our object */ - if (format.type == '\0') { - if (PyInt_Check(fieldobj) || PyLong_Check(fieldobj)) { - format.type = 'd'; - } else if (PyFloat_Check(fieldobj)) { - format.type = 'g'; - } else { - format.type = 's'; - } - } - - /* find the formatter function */ - formatter = format_function(format.type); - if (formatter == NULL) { - SetError(fs, "Invalid conversion character"); - return 0; - } - - /* do the formatting, writing into the output string */ - return formatter(fieldobj, fs, &format); + /* do the formatting, writing into the output string. if there's + * an error in format.type, this still works by calling + * format_bad_type_specifier() to set an error */ + return format_function(fieldobj, &format)(fieldobj, fs, &format); } static PyObject * From python-checkins at python.org Tue Mar 6 12:51:17 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 12:51:17 +0100 (CET) Subject: [Python-checkins] r54154 - python/trunk/Lib/test/test_slice.py Message-ID: <20070306115117.ECFC81E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 12:51:14 2007 New Revision: 54154 Modified: python/trunk/Lib/test/test_slice.py Log: A test case for the fix in #1674228. Modified: python/trunk/Lib/test/test_slice.py ============================================================================== --- python/trunk/Lib/test/test_slice.py (original) +++ python/trunk/Lib/test/test_slice.py Tue Mar 6 12:51:14 2007 @@ -92,6 +92,17 @@ self.assertRaises(OverflowError, slice(None).indices, 1L<<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_main(): test_support.run_unittest(SliceTest) From python-checkins at python.org Tue Mar 6 12:51:29 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 12:51:29 +0100 (CET) Subject: [Python-checkins] r54155 - python/branches/release25-maint/Lib/test/test_slice.py Message-ID: <20070306115129.814E41E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 12:51:27 2007 New Revision: 54155 Modified: python/branches/release25-maint/Lib/test/test_slice.py Log: A test case for the fix in #1674228. (backport from rev. 54154) Modified: python/branches/release25-maint/Lib/test/test_slice.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_slice.py (original) +++ python/branches/release25-maint/Lib/test/test_slice.py Tue Mar 6 12:51:27 2007 @@ -92,6 +92,17 @@ self.assertRaises(OverflowError, slice(None).indices, 1L<<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_main(): test_support.run_unittest(SliceTest) From python-checkins at python.org Tue Mar 6 12:52:26 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 12:52:26 +0100 (CET) Subject: [Python-checkins] r54156 - python/trunk/Lib/idlelib/MultiCall.py Message-ID: <20070306115226.6C09E1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 12:52:24 2007 New Revision: 54156 Modified: python/trunk/Lib/idlelib/MultiCall.py Log: Patch #1672481: fix bug in idlelib.MultiCall. Modified: python/trunk/Lib/idlelib/MultiCall.py ============================================================================== --- python/trunk/Lib/idlelib/MultiCall.py (original) +++ python/trunk/Lib/idlelib/MultiCall.py Tue Mar 6 12:52:24 2007 @@ -349,6 +349,8 @@ 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) From python-checkins at python.org Tue Mar 6 12:52:34 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 12:52:34 +0100 (CET) Subject: [Python-checkins] r54157 - python/branches/release25-maint/Lib/idlelib/MultiCall.py Message-ID: <20070306115234.31F541E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 12:52:33 2007 New Revision: 54157 Modified: python/branches/release25-maint/Lib/idlelib/MultiCall.py Log: Patch #1672481: fix bug in idlelib.MultiCall. (backport from rev. 54156) Modified: python/branches/release25-maint/Lib/idlelib/MultiCall.py ============================================================================== --- python/branches/release25-maint/Lib/idlelib/MultiCall.py (original) +++ python/branches/release25-maint/Lib/idlelib/MultiCall.py Tue Mar 6 12:52:33 2007 @@ -349,6 +349,8 @@ 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) From python-checkins at python.org Tue Mar 6 13:16:54 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 13:16:54 +0100 (CET) Subject: [Python-checkins] r54158 - in python/branches/release25-maint: Misc/NEWS Python/pythonrun.c Message-ID: <20070306121654.36D691E400F@bag.python.org> Author: georg.brandl Date: Tue Mar 6 13:16:52 2007 New Revision: 54158 Modified: python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Python/pythonrun.c Log: Bug #1674503: close the file opened by execfile() in an error condition. (backport) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 13:16:52 2007 @@ -12,6 +12,8 @@ Core and builtins ----------------- +- Bug #1674503: close the file opened by execfile() in an error condition. + - Patch #1674228: when assigning a slice (old-style), check for the sq_ass_slice instead of the sq_slice slot. Modified: python/branches/release25-maint/Python/pythonrun.c ============================================================================== --- python/branches/release25-maint/Python/pythonrun.c (original) +++ python/branches/release25-maint/Python/pythonrun.c Tue Mar 6 13:16:52 2007 @@ -1244,12 +1244,12 @@ 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; From python-checkins at python.org Tue Mar 6 13:17:51 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 13:17:51 +0100 (CET) Subject: [Python-checkins] r54159 - in python/trunk: Misc/NEWS Python/pythonrun.c Message-ID: <20070306121751.4F5701E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 13:17:50 2007 New Revision: 54159 Modified: python/trunk/Misc/NEWS python/trunk/Python/pythonrun.c Log: Bug #1674503: close the file opened by execfile() in an error condition. Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 13:17:50 2007 @@ -12,6 +12,8 @@ Core and builtins ----------------- +- Bug #1674503: close the file opened by execfile() in an error condition. + - Patch #1674228: when assigning a slice (old-style), check for the sq_ass_slice instead of the sq_slice slot. Modified: python/trunk/Python/pythonrun.c ============================================================================== --- python/trunk/Python/pythonrun.c (original) +++ python/trunk/Python/pythonrun.c Tue Mar 6 13:17:50 2007 @@ -1251,12 +1251,12 @@ 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; From buildbot at python.org Tue Mar 6 13:31:23 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 12:31:23 +0000 Subject: [Python-checkins] buildbot warnings in PPC64 Debian trunk Message-ID: <20070306123123.884991E400D@bag.python.org> The Buildbot has detected a new failure of PPC64 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/PPC64%2520Debian%2520trunk/builds/117 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 13:50:00 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 12:50:00 +0000 Subject: [Python-checkins] buildbot warnings in S-390 Debian trunk Message-ID: <20070306125000.EFA631E400D@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%2520Debian%2520trunk/builds/717 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 13:55:02 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 12:55:02 +0000 Subject: [Python-checkins] buildbot warnings in x86 mvlgcc trunk Message-ID: <20070306125502.7041D1E400E@bag.python.org> The Buildbot has detected a new failure of x86 mvlgcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520mvlgcc%2520trunk/builds/292 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 14:15:46 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 13:15:46 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo 2.5 Message-ID: <20070306131546.8C8381E4018@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%25202.5/builds/255 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 695, in send self.connect() File "/home/buildslave/python-trunk/2.5.norwitz-x86/build/Lib/httplib.py", line 1130, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 14:32:52 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 14:32:52 +0100 (CET) Subject: [Python-checkins] r54160 - python/trunk/Modules/_collectionsmodule.c Message-ID: <20070306133252.BDDEB1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 14:32:52 2007 New Revision: 54160 Modified: python/trunk/Modules/_collectionsmodule.c Log: Fix another reincarnation of bug #1576657 in defaultdict. Modified: python/trunk/Modules/_collectionsmodule.c ============================================================================== --- python/trunk/Modules/_collectionsmodule.c (original) +++ python/trunk/Modules/_collectionsmodule.c Tue Mar 6 14:32:52 2007 @@ -1075,7 +1075,7 @@ 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 @@ 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); From python-checkins at python.org Tue Mar 6 14:33:07 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 14:33:07 +0100 (CET) Subject: [Python-checkins] r54161 - python/branches/release25-maint/Modules/collectionsmodule.c Message-ID: <20070306133307.6C12A1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 14:33:07 2007 New Revision: 54161 Modified: python/branches/release25-maint/Modules/collectionsmodule.c Log: Fix another reincarnation of bug #1576657 in defaultdict. (backport from rev. 54160) Modified: python/branches/release25-maint/Modules/collectionsmodule.c ============================================================================== --- python/branches/release25-maint/Modules/collectionsmodule.c (original) +++ python/branches/release25-maint/Modules/collectionsmodule.c Tue Mar 6 14:33:07 2007 @@ -1075,7 +1075,7 @@ 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 @@ 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); From python-checkins at python.org Tue Mar 6 14:35:02 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 14:35:02 +0100 (CET) Subject: [Python-checkins] r54162 - python/trunk/Lib/test/test_defaultdict.py Message-ID: <20070306133502.6DD951E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 14:35:00 2007 New Revision: 54162 Modified: python/trunk/Lib/test/test_defaultdict.py Log: A test case for the defaultdict KeyError bug. Modified: python/trunk/Lib/test/test_defaultdict.py ============================================================================== --- python/trunk/Lib/test/test_defaultdict.py (original) +++ python/trunk/Lib/test/test_defaultdict.py Tue Mar 6 14:35:00 2007 @@ -132,6 +132,15 @@ self.assertEqual(d2.default_factory, list) self.assertEqual(d2, d1) + def test_keyerror_without_factory(self): + d1 = defaultdict() + try: + d1[(1,)] + except KeyError, err: + self.assertEqual(err.message, (1,)) + else: + self.fail("expected KeyError") + def test_main(): test_support.run_unittest(TestDefaultDict) From python-checkins at python.org Tue Mar 6 14:35:09 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 14:35:09 +0100 (CET) Subject: [Python-checkins] r54163 - python/branches/release25-maint/Lib/test/test_defaultdict.py Message-ID: <20070306133509.F3E151E4014@bag.python.org> Author: georg.brandl Date: Tue Mar 6 14:35:08 2007 New Revision: 54163 Modified: python/branches/release25-maint/Lib/test/test_defaultdict.py Log: A test case for the defaultdict KeyError bug. (backport from rev. 54162) Modified: python/branches/release25-maint/Lib/test/test_defaultdict.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_defaultdict.py (original) +++ python/branches/release25-maint/Lib/test/test_defaultdict.py Tue Mar 6 14:35:08 2007 @@ -132,6 +132,15 @@ self.assertEqual(d2.default_factory, list) self.assertEqual(d2, d1) + def test_keyerror_without_factory(self): + d1 = defaultdict() + try: + d1[(1,)] + except KeyError, err: + self.assertEqual(err.message, (1,)) + else: + self.fail("expected KeyError") + def test_main(): test_support.run_unittest(TestDefaultDict) From python-checkins at python.org Tue Mar 6 14:37:50 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 14:37:50 +0100 (CET) Subject: [Python-checkins] r54164 - in python/trunk: Doc/lib/libdoctest.tex Lib/doctest.py Misc/NEWS Message-ID: <20070306133750.C40061E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 14:37:45 2007 New Revision: 54164 Modified: python/trunk/Doc/lib/libdoctest.tex python/trunk/Lib/doctest.py python/trunk/Misc/NEWS Log: Patch #1663234: you can now run doctest on test files and modules using "python -m doctest [-v] filename ...". Modified: python/trunk/Doc/lib/libdoctest.tex ============================================================================== --- python/trunk/Doc/lib/libdoctest.tex (original) +++ python/trunk/Doc/lib/libdoctest.tex Tue Mar 6 14:37:45 2007 @@ -201,6 +201,19 @@ \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 @@ 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}. Modified: python/trunk/Lib/doctest.py ============================================================================== --- python/trunk/Lib/doctest.py (original) +++ python/trunk/Lib/doctest.py Tue Mar 6 14:37:45 2007 @@ -2630,8 +2630,23 @@ } def _test(): - r = unittest.TextTestRunner() - r.run(DocTestSuite()) + testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-'] + if len(testfiles) > 0: + for filename in testfiles: + if filename.endswith(".py"): + # This 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() Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 14:37:45 2007 @@ -141,6 +141,9 @@ Library ------- +- Patch #1663234: you can now run doctest on test files and modules + using "python -m doctest [-v] filename ...". + - Patch #1121142: Implement ZipFile.open. - Taught setup.py how to locate Berkeley DB on Macs using MacPorts. From buildbot at python.org Tue Mar 6 15:29:36 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 14:29:36 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20070306142936.D41871E400E@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1969 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 864, in endheaders self._send_output() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 736, in _send_output self.send(msg) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 695, in send self.connect() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 1152, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 15:33:34 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 14:33:34 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable trunk Message-ID: <20070306143334.674C11E400D@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/120 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 15:43:01 2007 From: python-checkins at python.org (martin.v.loewis) Date: Tue, 6 Mar 2007 15:43:01 +0100 (CET) Subject: [Python-checkins] r54165 - in python/trunk: Doc/lib/libhtmlparser.tex Lib/HTMLParser.py Lib/test/test_htmlparser.py Misc/NEWS Message-ID: <20070306144301.A47BA1E400E@bag.python.org> Author: martin.v.loewis Date: Tue Mar 6 15:43:00 2007 New Revision: 54165 Modified: python/trunk/Doc/lib/libhtmlparser.tex python/trunk/Lib/HTMLParser.py python/trunk/Lib/test/test_htmlparser.py python/trunk/Misc/NEWS Log: Patch #912410: Replace HTML entity references for attribute values in HTMLParser. Modified: python/trunk/Doc/lib/libhtmlparser.tex ============================================================================== --- python/trunk/Doc/lib/libhtmlparser.tex (original) +++ python/trunk/Doc/lib/libhtmlparser.tex Tue Mar 6 15:43:00 2007 @@ -75,14 +75,18 @@ 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{}, 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{}, 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} Modified: python/trunk/Lib/HTMLParser.py ============================================================================== --- python/trunk/Lib/HTMLParser.py (original) +++ python/trunk/Lib/HTMLParser.py Tue Mar 6 15:43:00 2007 @@ -358,12 +358,30 @@ 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.iteritems(): + 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) Modified: python/trunk/Lib/test/test_htmlparser.py ============================================================================== --- python/trunk/Lib/test/test_htmlparser.py (original) +++ python/trunk/Lib/test/test_htmlparser.py Tue Mar 6 15:43:00 2007 @@ -309,6 +309,11 @@ ("endtag", "script"), ]) + def test_entityrefs_in_attributes(self): + self._run_check("", [ + ("starttag", "html", [("foo", u"\u20AC&aa&unsupported;")]) + ]) + def test_main(): test_support.run_unittest(HTMLParserTestCase) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 15:43:00 2007 @@ -141,6 +141,9 @@ Library ------- +- Patch #912410: Replace HTML entity references for attribute values + in HTMLParser. + - Patch #1663234: you can now run doctest on test files and modules using "python -m doctest [-v] filename ...". From python-checkins at python.org Tue Mar 6 16:41:43 2007 From: python-checkins at python.org (skip.montanaro) Date: Tue, 6 Mar 2007 16:41:43 +0100 (CET) Subject: [Python-checkins] r54166 - in python/trunk: Misc/NEWS setup.py Message-ID: <20070306154143.1B7091E4015@bag.python.org> Author: skip.montanaro Date: Tue Mar 6 16:41:38 2007 New Revision: 54166 Modified: python/trunk/Misc/NEWS python/trunk/setup.py Log: patch 1673619 - identify extension modules which cannot be built Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 16:41:38 2007 @@ -141,6 +141,9 @@ Library ------- +- Patch #1673619: setup.py identifies extension modules it doesn't know how + to build and those it knows how to build but that fail to build. + - Patch #912410: Replace HTML entity references for attribute values in HTMLParser. Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Tue Mar 6 16:41:38 2007 @@ -91,10 +91,14 @@ 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 @@ 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(cmp=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 @@ except (CCompilerError, DistutilsError), 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 @@ try: imp.load_dynamic(ext.name, ext_filename) except ImportError, 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 @@ 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 @@ ] 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)) @@ -387,6 +420,8 @@ # 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) @@ -419,6 +454,11 @@ 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']) ) @@ -435,11 +475,15 @@ # 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 @@ -466,6 +510,8 @@ 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') @@ -503,6 +549,9 @@ 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. @@ -511,6 +560,8 @@ else: libs = [] exts.append( Extension('crypt', ['cryptmodule.c'], libraries=libs) ) + else: + missing.append('crypt') # CSV files exts.append( Extension('_csv', ['_csv.c']) ) @@ -543,6 +594,8 @@ 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 @@ -576,6 +629,7 @@ 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']) ) @@ -585,12 +639,14 @@ 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 @@ -743,6 +799,7 @@ db_incs = None dblibs = [] dblib_dir = None + missing.append('_bsddb') # The sqlite interface sqlite_setup_debug = False # verbose debug prints from this script? @@ -835,6 +892,8 @@ 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 @@ -857,6 +916,10 @@ 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']: @@ -882,11 +945,15 @@ 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']: @@ -895,6 +962,8 @@ # 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']: @@ -904,6 +973,10 @@ 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. @@ -932,13 +1005,16 @@ 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: @@ -974,6 +1050,12 @@ 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')): @@ -984,6 +1066,8 @@ 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 # @@ -1021,14 +1105,20 @@ 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: @@ -1036,6 +1126,10 @@ 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) @@ -1044,14 +1138,20 @@ 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")): @@ -1140,6 +1240,11 @@ # 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. From buildbot at python.org Tue Mar 6 16:47:46 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 15:47:46 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable trunk Message-ID: <20070306154747.132021E400D@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/122 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 16:48:34 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 15:48:34 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20070306154834.6CD1B1E400D@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1971 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 16:48:42 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 15:48:42 +0000 Subject: [Python-checkins] buildbot failure in x86 mvlgcc trunk Message-ID: <20070306154842.7D5281E400E@bag.python.org> The Buildbot has detected a new failure of x86 mvlgcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520mvlgcc%2520trunk/builds/295 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 16:49:01 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 15:49:01 +0000 Subject: [Python-checkins] buildbot failure in PPC64 Debian trunk Message-ID: <20070306154901.DDDCB1E400F@bag.python.org> The Buildbot has detected a new failure of PPC64 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/PPC64%2520Debian%2520trunk/builds/121 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 16:50:05 2007 From: python-checkins at python.org (guido.van.rossum) Date: Tue, 6 Mar 2007 16:50:05 +0100 (CET) Subject: [Python-checkins] r54167 - in python/trunk: Lib/test/test_datetime.py Misc/NEWS Modules/datetimemodule.c Message-ID: <20070306155005.33C821E4012@bag.python.org> Author: guido.van.rossum Date: Tue Mar 6 16:50:01 2007 New Revision: 54167 Modified: python/trunk/Lib/test/test_datetime.py python/trunk/Misc/NEWS python/trunk/Modules/datetimemodule.c Log: Patch #1646728: datetime.fromtimestamp fails with negative fractional times. With unittest. Somebody please backport to 2.5. Modified: python/trunk/Lib/test/test_datetime.py ============================================================================== --- python/trunk/Lib/test/test_datetime.py (original) +++ python/trunk/Lib/test/test_datetime.py Tue Mar 6 16:50:01 2007 @@ -1425,6 +1425,15 @@ self.assertRaises(ValueError, self.theclass.utcfromtimestamp, insane) + def test_negative_float_fromtimestamp(self): + # 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): + d = self.theclass.utcfromtimestamp(-1.05) + self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) + def test_utcnow(self): import time Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 16:50:01 2007 @@ -404,6 +404,9 @@ Extension Modules ----------------- +- Patch #1646728: datetime.fromtimestamp fails with negative + fractional times. With unittest. + - Patch #1490190: posixmodule now includes os.chflags() and os.lchflags() functions on platforms where the underlying system calls are available. Modified: python/trunk/Modules/datetimemodule.c ============================================================================== --- python/trunk/Modules/datetimemodule.c (original) +++ python/trunk/Modules/datetimemodule.c Tue Mar 6 16:50:01 2007 @@ -3683,6 +3683,12 @@ 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. */ From buildbot at python.org Tue Mar 6 16:53:33 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 15:53:33 +0000 Subject: [Python-checkins] buildbot failure in alpha Tru64 5.1 trunk Message-ID: <20070306155333.55E8B1E400D@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%2520trunk/builds/1458 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 17:24:13 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 16:24:13 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian trunk Message-ID: <20070306162413.C86791E400E@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%2520Debian%2520trunk/builds/720 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum,martin.v.loewis,skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 17:24:13 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 16:24:13 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu trunk trunk Message-ID: <20070306162413.C642C1E400D@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%2520Ubuntu%2520trunk%2520trunk/builds/429 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum,skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 17:25:37 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 16:25:37 +0000 Subject: [Python-checkins] buildbot failure in sparc solaris10 gcc trunk Message-ID: <20070306162537.7FDAB1E4011@bag.python.org> The Buildbot has detected a new failure of sparc solaris10 gcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/sparc%2520solaris10%2520gcc%2520trunk/builds/1811 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum,skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 17:45:13 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 16:45:13 +0000 Subject: [Python-checkins] buildbot warnings in x86 XP trunk Message-ID: <20070306164513.D8E691E4017@bag.python.org> The Buildbot has detected a new failure of x86 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%2520trunk/builds/242 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum,skip.montanaro Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_datetime ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 17:49:31 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 16:49:31 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 trunk Message-ID: <20070306164932.015191E400D@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%2520trunk/builds/1792 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum,skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 18:43:50 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 17:43:50 +0000 Subject: [Python-checkins] buildbot failure in hppa Ubuntu dapper trunk Message-ID: <20070306174350.AFDCE1E400D@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper trunk. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%2520trunk/builds/83 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl,guido.van.rossum,martin.v.loewis,skip.montanaro BUILD FAILED: failed compile sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 18:46:24 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 18:46:24 +0100 (CET) Subject: [Python-checkins] r54168 - in python/branches/release25-maint: Lib/test/test_datetime.py Misc/NEWS Modules/datetimemodule.c Message-ID: <20070306174624.B099F1E4016@bag.python.org> Author: georg.brandl Date: Tue Mar 6 18:46:17 2007 New Revision: 54168 Modified: python/branches/release25-maint/Lib/test/test_datetime.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Modules/datetimemodule.c Log: Patch #1646728: datetime.fromtimestamp fails with negative fractional times. With unittest. (backport from rev. 54167 by Guido) Modified: python/branches/release25-maint/Lib/test/test_datetime.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_datetime.py (original) +++ python/branches/release25-maint/Lib/test/test_datetime.py Tue Mar 6 18:46:17 2007 @@ -1425,6 +1425,15 @@ self.assertRaises(ValueError, self.theclass.utcfromtimestamp, insane) + def test_negative_float_fromtimestamp(self): + # 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): + d = self.theclass.utcfromtimestamp(-1.05) + self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) + def test_utcnow(self): import time Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 18:46:17 2007 @@ -118,6 +118,9 @@ Extension Modules ----------------- +- Patch #1646728: datetime.fromtimestamp fails with negative + fractional times. With unittest. + - Patch #1494140: Add documentation for the new struct.Struct object. - Patch #1657276: Make NETLINK_DNRTMSG conditional. Modified: python/branches/release25-maint/Modules/datetimemodule.c ============================================================================== --- python/branches/release25-maint/Modules/datetimemodule.c (original) +++ python/branches/release25-maint/Modules/datetimemodule.c Tue Mar 6 18:46:17 2007 @@ -3686,6 +3686,12 @@ 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. */ From python-checkins at python.org Tue Mar 6 18:49:15 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 18:49:15 +0100 (CET) Subject: [Python-checkins] r54169 - python/trunk/setup.py Message-ID: <20070306174915.0D0C61E4010@bag.python.org> Author: georg.brandl Date: Tue Mar 6 18:49:14 2007 New Revision: 54169 Modified: python/trunk/setup.py Log: Fix cmp vs. key argument for list.sort. Modified: python/trunk/setup.py ============================================================================== --- python/trunk/setup.py (original) +++ python/trunk/setup.py Tue Mar 6 18:49:14 2007 @@ -187,7 +187,7 @@ longest = max(longest, max([len(name) for name in self.failed])) def print_three_column(lst): - lst.sort(cmp=str.lower) + lst.sort(key=str.lower) # guarantee zip() doesn't drop anything while len(lst) % 3: lst.append("") From python-checkins at python.org Tue Mar 6 19:21:33 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:21:33 +0100 (CET) Subject: [Python-checkins] r54170 - python/trunk/Lib/doctest.py Message-ID: <20070306182133.B9FF11E4017@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:21:32 2007 New Revision: 54170 Modified: python/trunk/Lib/doctest.py Log: Small nit, found by Neal. Modified: python/trunk/Lib/doctest.py ============================================================================== --- python/trunk/Lib/doctest.py (original) +++ python/trunk/Lib/doctest.py Tue Mar 6 19:21:32 2007 @@ -2631,10 +2631,10 @@ def _test(): testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-'] - if len(testfiles) > 0: + if testfiles: for filename in testfiles: if filename.endswith(".py"): - # This is a module -- insert its dir into sys.path and try to + # 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) From python-checkins at python.org Tue Mar 6 19:30:02 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:30:02 +0100 (CET) Subject: [Python-checkins] r54171 - python/trunk/Doc/ref/ref3.tex Message-ID: <20070306183002.BFCDA1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:29:58 2007 New Revision: 54171 Modified: python/trunk/Doc/ref/ref3.tex Log: Patch #1602128: clarify that richcmp methods can return NotImplemented and should return True or False otherwise. Modified: python/trunk/Doc/ref/ref3.tex ============================================================================== --- python/trunk/Doc/ref/ref3.tex (original) +++ python/trunk/Doc/ref/ref3.tex Tue Mar 6 19:29:58 2007 @@ -1282,10 +1282,14 @@ \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}} @@ -1299,9 +1303,7 @@ \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} From python-checkins at python.org Tue Mar 6 19:30:15 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:30:15 +0100 (CET) Subject: [Python-checkins] r54172 - python/branches/release25-maint/Doc/ref/ref3.tex Message-ID: <20070306183015.7538A1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:30:12 2007 New Revision: 54172 Modified: python/branches/release25-maint/Doc/ref/ref3.tex Log: Patch #1602128: clarify that richcmp methods can return NotImplemented and should return True or False otherwise. (backport from rev. 54171) Modified: python/branches/release25-maint/Doc/ref/ref3.tex ============================================================================== --- python/branches/release25-maint/Doc/ref/ref3.tex (original) +++ python/branches/release25-maint/Doc/ref/ref3.tex Tue Mar 6 19:30:12 2007 @@ -1282,10 +1282,14 @@ \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}} @@ -1299,9 +1303,7 @@ \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} From python-checkins at python.org Tue Mar 6 19:41:22 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:41:22 +0100 (CET) Subject: [Python-checkins] r54173 - in python/trunk: Lib/test/test_builtin.py Misc/NEWS Objects/longobject.c Message-ID: <20070306184122.28C411E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:41:12 2007 New Revision: 54173 Modified: python/trunk/Lib/test/test_builtin.py python/trunk/Misc/NEWS python/trunk/Objects/longobject.c Log: Patch #1638879: don't accept strings with embedded NUL bytes in long(). Modified: python/trunk/Lib/test/test_builtin.py ============================================================================== --- python/trunk/Lib/test/test_builtin.py (original) +++ python/trunk/Lib/test/test_builtin.py Tue Mar 6 19:41:12 2007 @@ -1017,6 +1017,11 @@ self.assertRaises(ValueError, long, '53', 40) self.assertRaises(TypeError, long, 1, 12) + # SF patch #1638879: embedded NULs were not detected with + # explicit base + self.assertRaises(ValueError, long, '123\0', 10) + self.assertRaises(ValueError, long, '123\x00 245', 20) + self.assertEqual(long('100000000000000000000000000000000', 2), 4294967296) self.assertEqual(long('102002022201221111211', 3), 4294967296) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 19:41:12 2007 @@ -12,6 +12,8 @@ Core and builtins ----------------- +- Patch #1638879: don't accept strings with embedded NUL bytes in long(). + - Bug #1674503: close the file opened by execfile() in an error condition. - Patch #1674228: when assigning a slice (old-style), check for the Modified: python/trunk/Objects/longobject.c ============================================================================== --- python/trunk/Objects/longobject.c (original) +++ python/trunk/Objects/longobject.c Tue Mar 6 19:41:12 2007 @@ -3287,8 +3287,25 @@ return PyLong_FromLong(0L); if (base == -909) return PyNumber_Long(x); - else if (PyString_Check(x)) + else if (PyString_Check(x)) { + /* 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 long() with base %d: %s", + base, PyString_AS_STRING(srepr)); + Py_DECREF(srepr); + return NULL; + } return PyLong_FromString(PyString_AS_STRING(x), NULL, base); + } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(x)) return PyLong_FromUnicode(PyUnicode_AS_UNICODE(x), From python-checkins at python.org Tue Mar 6 19:44:45 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:44:45 +0100 (CET) Subject: [Python-checkins] r54174 - in python/branches/release25-maint: Lib/test/test_builtin.py Misc/NEWS Objects/longobject.c Message-ID: <20070306184445.E57241E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:44:35 2007 New Revision: 54174 Modified: python/branches/release25-maint/Lib/test/test_builtin.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Objects/longobject.c Log: Patch #1638879: don't accept strings with embedded NUL bytes in long(). (backport from rev. 54173) Modified: python/branches/release25-maint/Lib/test/test_builtin.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_builtin.py (original) +++ python/branches/release25-maint/Lib/test/test_builtin.py Tue Mar 6 19:44:35 2007 @@ -1017,6 +1017,11 @@ self.assertRaises(ValueError, long, '53', 40) self.assertRaises(TypeError, long, 1, 12) + # SF patch #1638879: embedded NULs were not detected with + # explicit base + self.assertRaises(ValueError, long, '123\0', 10) + self.assertRaises(ValueError, long, '123\x00 245', 20) + self.assertEqual(long('100000000000000000000000000000000', 2), 4294967296) self.assertEqual(long('102002022201221111211', 3), 4294967296) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 19:44:35 2007 @@ -12,6 +12,8 @@ Core and builtins ----------------- +- Patch #1638879: don't accept strings with embedded NUL bytes in long(). + - Bug #1674503: close the file opened by execfile() in an error condition. - Patch #1674228: when assigning a slice (old-style), check for the Modified: python/branches/release25-maint/Objects/longobject.c ============================================================================== --- python/branches/release25-maint/Objects/longobject.c (original) +++ python/branches/release25-maint/Objects/longobject.c Tue Mar 6 19:44:35 2007 @@ -3287,8 +3287,25 @@ return PyLong_FromLong(0L); if (base == -909) return PyNumber_Long(x); - else if (PyString_Check(x)) + else if (PyString_Check(x)) { + /* 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 long() with base %d: %s", + base, PyString_AS_STRING(srepr)); + Py_DECREF(srepr); + return NULL; + } return PyLong_FromString(PyString_AS_STRING(x), NULL, base); + } #ifdef Py_USING_UNICODE else if (PyUnicode_Check(x)) return PyLong_FromUnicode(PyUnicode_AS_UNICODE(x), From python-checkins at python.org Tue Mar 6 19:47:36 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:47:36 +0100 (CET) Subject: [Python-checkins] r54175 - python/trunk/README Message-ID: <20070306184736.E071B1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:47:31 2007 New Revision: 54175 Modified: python/trunk/README Log: Patch #1673121: update README wrt. OSX default shell. Modified: python/trunk/README ============================================================================== --- python/trunk/README (original) +++ python/trunk/README Tue Mar 6 19:47:31 2007 @@ -570,9 +570,9 @@ 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 From python-checkins at python.org Tue Mar 6 19:47:42 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 19:47:42 +0100 (CET) Subject: [Python-checkins] r54176 - python/branches/release25-maint/README Message-ID: <20070306184742.0C8CB1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:47:40 2007 New Revision: 54176 Modified: python/branches/release25-maint/README Log: Patch #1673121: update README wrt. OSX default shell. (backport from rev. 54175) Modified: python/branches/release25-maint/README ============================================================================== --- python/branches/release25-maint/README (original) +++ python/branches/release25-maint/README Tue Mar 6 19:47:40 2007 @@ -583,9 +583,9 @@ 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 From buildbot at python.org Tue Mar 6 19:55:41 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 18:55:41 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 trunk Message-ID: <20070306185541.6A0201E4024@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%2520trunk/builds/1460 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 20:00:02 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 20:00:02 +0100 (CET) Subject: [Python-checkins] r54177 - in python/trunk: Lib/test/test_operator.py Misc/NEWS Modules/operator.c Message-ID: <20070306190002.D971D1E400D@bag.python.org> Author: georg.brandl Date: Tue Mar 6 19:59:11 2007 New Revision: 54177 Modified: python/trunk/Lib/test/test_operator.py python/trunk/Misc/NEWS python/trunk/Modules/operator.c Log: Patch #1654417: make operator.{get,set,del}slice use the full range of Py_ssize_t. Modified: python/trunk/Lib/test/test_operator.py ============================================================================== --- python/trunk/Lib/test/test_operator.py (original) +++ python/trunk/Lib/test/test_operator.py Tue Mar 6 19:59:11 2007 @@ -143,6 +143,8 @@ 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_div(self): self.failUnlessRaises(TypeError, operator.div, 5) @@ -170,6 +172,8 @@ 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) @@ -318,6 +322,8 @@ 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) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 19:59:11 2007 @@ -406,6 +406,9 @@ Extension Modules ----------------- +- Patch #1654417: make operator.{get,set,del}slice use the full range + of Py_ssize_t. + - Patch #1646728: datetime.fromtimestamp fails with negative fractional times. With unittest. Modified: python/trunk/Modules/operator.c ============================================================================== --- python/trunk/Modules/operator.c (original) +++ python/trunk/Modules/operator.c Tue Mar 6 19:59:11 2007 @@ -168,43 +168,41 @@ 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 From python-checkins at python.org Tue Mar 6 20:00:10 2007 From: python-checkins at python.org (georg.brandl) Date: Tue, 6 Mar 2007 20:00:10 +0100 (CET) Subject: [Python-checkins] r54178 - in python/branches/release25-maint: Lib/test/test_operator.py Misc/NEWS Modules/operator.c Message-ID: <20070306190010.EA5061E4018@bag.python.org> Author: georg.brandl Date: Tue Mar 6 20:00:09 2007 New Revision: 54178 Modified: python/branches/release25-maint/Lib/test/test_operator.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Modules/operator.c Log: Patch #1654417: make operator.{get,set,del}slice use the full range of Py_ssize_t. (backport from rev. 54177) Modified: python/branches/release25-maint/Lib/test/test_operator.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_operator.py (original) +++ python/branches/release25-maint/Lib/test/test_operator.py Tue Mar 6 20:00:09 2007 @@ -143,6 +143,8 @@ 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_div(self): self.failUnlessRaises(TypeError, operator.div, 5) @@ -170,6 +172,8 @@ 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) @@ -318,6 +322,8 @@ 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) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 20:00:09 2007 @@ -120,6 +120,9 @@ Extension Modules ----------------- +- Patch #1654417: make operator.{get,set,del}slice use the full range + of Py_ssize_t. + - Patch #1646728: datetime.fromtimestamp fails with negative fractional times. With unittest. Modified: python/branches/release25-maint/Modules/operator.c ============================================================================== --- python/branches/release25-maint/Modules/operator.c (original) +++ python/branches/release25-maint/Modules/operator.c Tue Mar 6 20:00:09 2007 @@ -168,43 +168,41 @@ 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 From buildbot at python.org Tue Mar 6 20:05:20 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 19:05:20 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20070306190523.175AE1E400D@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1975 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings failed slave lost sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 20:08:32 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 19:08:32 +0000 Subject: [Python-checkins] buildbot warnings in x86 XP 2.5 Message-ID: <20070306190832.EE7121E4018@bag.python.org> The Buildbot has detected a new failure of x86 XP 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%25202.5/builds/142 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_datetime ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\2.5.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\2.5.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\2.5.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\2.5.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function sincerely, -The Buildbot From buildbot at python.org Tue Mar 6 20:52:04 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 19:52:04 +0000 Subject: [Python-checkins] buildbot warnings in x86 mvlgcc trunk Message-ID: <20070306195204.2AE421E4014@bag.python.org> The Buildbot has detected a new failure of x86 mvlgcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520mvlgcc%2520trunk/builds/300 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 21:39:00 2007 From: python-checkins at python.org (walter.doerwald) Date: Tue, 6 Mar 2007 21:39:00 +0100 (CET) Subject: [Python-checkins] r54180 - in python/trunk: Lib/test/test_curses.py Modules/_cursesmodule.c Message-ID: <20070306203900.2880E1E4015@bag.python.org> Author: walter.doerwald Date: Tue Mar 6 21:38:57 2007 New Revision: 54180 Modified: python/trunk/Lib/test/test_curses.py python/trunk/Modules/_cursesmodule.c Log: Patch for bug #1633621: if curses.resizeterm() or curses.resize_term() is called, update _curses.LINES, _curses.COLS, curses.LINES and curses.COLS. Modified: python/trunk/Lib/test/test_curses.py ============================================================================== --- python/trunk/Lib/test/test_curses.py (original) +++ python/trunk/Lib/test/test_curses.py Tue Mar 6 21:38:57 2007 @@ -241,12 +241,21 @@ 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() Modified: python/trunk/Modules/_cursesmodule.c ============================================================================== --- python/trunk/Modules/_cursesmodule.c (original) +++ python/trunk/Modules/_cursesmodule.c Tue Mar 6 21:38:57 2007 @@ -2196,19 +2196,72 @@ } } +/* 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 +2273,19 @@ 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 */ From python-checkins at python.org Tue Mar 6 21:46:32 2007 From: python-checkins at python.org (walter.doerwald) Date: Tue, 6 Mar 2007 21:46:32 +0100 (CET) Subject: [Python-checkins] r54181 - in python/branches/release25-maint: Lib/test/test_curses.py Modules/_cursesmodule.c Message-ID: <20070306204632.5C6231E400D@bag.python.org> Author: walter.doerwald Date: Tue Mar 6 21:46:26 2007 New Revision: 54181 Modified: python/branches/release25-maint/Lib/test/test_curses.py python/branches/release25-maint/Modules/_cursesmodule.c Log: Backport checkin: Patch for bug #1633621: if curses.resizeterm() or curses.resize_term() is called, update _curses.LINES, _curses.COLS, curses.LINES and curses.COLS. Modified: python/branches/release25-maint/Lib/test/test_curses.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_curses.py (original) +++ python/branches/release25-maint/Lib/test/test_curses.py Tue Mar 6 21:46:26 2007 @@ -241,12 +241,21 @@ 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() Modified: python/branches/release25-maint/Modules/_cursesmodule.c ============================================================================== --- python/branches/release25-maint/Modules/_cursesmodule.c (original) +++ python/branches/release25-maint/Modules/_cursesmodule.c Tue Mar 6 21:46:26 2007 @@ -2196,19 +2196,72 @@ } } +/* 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 +2273,19 @@ 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 */ From buildbot at python.org Tue Mar 6 21:54:14 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 20:54:14 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 trunk Message-ID: <20070306205414.EE75C1E4019@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%2520trunk/builds/1462 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: walter.doerwald Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 22:15:24 2007 From: python-checkins at python.org (walter.doerwald) Date: Tue, 6 Mar 2007 22:15:24 +0100 (CET) Subject: [Python-checkins] r54182 - python/trunk/Misc/NEWS Message-ID: <20070306211524.92B8C1E400E@bag.python.org> Author: walter.doerwald Date: Tue Mar 6 22:15:24 2007 New Revision: 54182 Modified: python/trunk/Misc/NEWS Log: Document change to curses. Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Tue Mar 6 22:15:24 2007 @@ -484,6 +484,8 @@ - Added support for linking the bsddb module against BerkeleyDB 4.5.x. +- Bug #1633621: if curses.resizeterm() or curses.resize_term() is called, + update _curses.LINES, _curses.COLS, curses.LINES and curses.COLS. Tests ----- From python-checkins at python.org Tue Mar 6 22:16:32 2007 From: python-checkins at python.org (walter.doerwald) Date: Tue, 6 Mar 2007 22:16:32 +0100 (CET) Subject: [Python-checkins] r54183 - python/branches/release25-maint/Misc/NEWS Message-ID: <20070306211632.EDFEE1E400D@bag.python.org> Author: walter.doerwald Date: Tue Mar 6 22:16:32 2007 New Revision: 54183 Modified: python/branches/release25-maint/Misc/NEWS Log: Document curses changes. Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Tue Mar 6 22:16:32 2007 @@ -191,6 +191,9 @@ - Bug #1552726: fix polling at the interpreter prompt when certain versions of the readline library are in use. +- Bug #1633621: if curses.resizeterm() or curses.resize_term() is called, + update _curses.LINES, _curses.COLS, curses.LINES and curses.COLS. + Library ------- From buildbot at python.org Tue Mar 6 22:20:18 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 21:20:18 +0000 Subject: [Python-checkins] buildbot warnings in x86 XP trunk Message-ID: <20070306212018.950351E400D@bag.python.org> The Buildbot has detected a new failure of x86 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%2520trunk/builds/246 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: walter.doerwald Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_datetime ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTime) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_fromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1431, in test_negative_float_fromtimestamp self.theclass.fromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function ====================================================================== ERROR: test_negative_float_utcfromtimestamp (test.test_datetime.TestDateTimeTZ) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot_py25\trunk.mcintyre-windows\build\lib\test\test_datetime.py", line 1434, in test_negative_float_utcfromtimestamp d = self.theclass.utcfromtimestamp(-1.05) ValueError: timestamp out of range for platform localtime()/gmtime() function sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 23:31:21 2007 From: python-checkins at python.org (brett.cannon) Date: Tue, 6 Mar 2007 23:31:21 +0100 (CET) Subject: [Python-checkins] r54184 - sandbox/trunk/pep0 Message-ID: <20070306223121.8617B1E400D@bag.python.org> Author: brett.cannon Date: Tue Mar 6 23:31:18 2007 New Revision: 54184 Added: sandbox/trunk/pep0/ Log: Place in the sandbox to develop a script to auto-generate PEP 0 (PEP index). From buildbot at python.org Tue Mar 6 23:43:07 2007 From: buildbot at python.org (buildbot at python.org) Date: Tue, 06 Mar 2007 22:43:07 +0000 Subject: [Python-checkins] buildbot warnings in hppa Ubuntu dapper 2.5 Message-ID: <20070306224307.6796F1E400F@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%25202.5/builds/43 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: walter.doerwald Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Tue Mar 6 23:43:19 2007 From: python-checkins at python.org (brett.cannon) Date: Tue, 6 Mar 2007 23:43:19 +0100 (CET) Subject: [Python-checkins] r54185 - sandbox/trunk/pep0/TODO sandbox/trunk/pep0/pep0.py sandbox/trunk/pep0/test_pep0.py Message-ID: <20070306224319.9866F1E400E@bag.python.org> Author: brett.cannon Date: Tue Mar 6 23:43:15 2007 New Revision: 54185 Added: sandbox/trunk/pep0/TODO (contents, props changed) sandbox/trunk/pep0/pep0.py (contents, props changed) sandbox/trunk/pep0/test_pep0.py (contents, props changed) Log: Initial checkin. No where near complete, but can at least parse metadata headers of PEPs. Added: sandbox/trunk/pep0/TODO ============================================================================== --- (empty file) +++ sandbox/trunk/pep0/TODO Tue Mar 6 23:43:15 2007 @@ -0,0 +1,25 @@ +In script: +* Read PEPs as UTF-8, not ASCII. +* Output static text for PEP 0. + + Store Owners list in a data structure. + - Support nicknames for PEP listing (e.g., "Guido van Rossum" -> + "GvR"). + - Care about making sure that email addresses are up-to-date between + PEPs and PEP 0? + - Worth keeping emails in both PEP 0 and individual PEPs, or just make + PEP 0 master and leave out of PEPs so that single place can be + maintained and considered up-to-date? + + Store Key in data structure for easy mapping? + - Would allow for easy validation that metadata is correct in PEPs. +* Output PEP 0 with numerical PEP index. +* Output PEP 0 with special sections. + +For PEPs: +* Define (and enforce) consistency in Author field. +* Empty PEPs are not in any way identified within the PEPs themselves. + + Get ride of section and just consider rejected? + - No longer accept empty PEPs, right? +* Meta-PEPs are not noted as such within the PEPs. + + Add a "Meta" option for "Type"? +* Fix inconsistent usage of Status field. + + Some PEPs just say "Standard"; missing "Track". Added: sandbox/trunk/pep0/pep0.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep0/pep0.py Tue Mar 6 23:43:15 2007 @@ -0,0 +1,125 @@ +"""Auto-generate PEP 0 (PEP index). """ +from __future__ import with_statement +import os + +def consume_headers(directory='.'): + """Pull out metadata for every PEP in the specified directory and return + them in a list sorted by PEP name.""" + peps = [] + for file_name in os.listdir(directory): + if file_name.startswith('pep-') and file_name.endswith('.txt'): + peps.append(consume_pep(os.path.join(directory, file_name))) + peps.sort(key=lambda pep: pep['PEP']) + return peps + +def consume_pep(path): + """Consume the specified file as a PEP to get its metadata.""" + pep_info = {} + with open(path, 'rU') as pep_file: + try: + for line in pep_file: + if line == '\n': + break + elif line[1].isspace(): + type_ = parse_metadata(pep_info, line, type_) + else: + type_ = parse_metadata(pep_info, line) + except Exception: + print "In", pep_file + raise + return pep_info + +def parse_metadata(pep_info, line, previous_type=None): + """Parse the given line for PEP metadata, adding on to existing metadata if + previous_type is specified, returning the last type of metadata handled.""" + if previous_type: + type_ = previous_type + data = line + else: + type_, data = line.split(':', 1) + type_ = type_.strip() + data = data.strip() + handler = handlers.get(type_, handle_generic) + result = handler(data) + if previous_type: + previous_data = pep_info[type_] + if not isinstance(previous_data, list): + previous_data = [previous_data] + pep_info[type_] = previous_data + previous_data.extend(result) + else: + pep_info[type_] = result + return type_ + +def handle_generic(data): + """Default handler for PEP metadata.""" + return data + +def handle_pep_num(data): + """Return the integer for the PEP number.""" + return int(data) + +def handle_author(data): + """Return a list of author names.""" + if '<' in data: + author, email = data.split('<', 1) + elif '(' in data: + email, author = data.split('(', 1) + author = author[:author.index(')')] + else: + author = data + return [author_name.strip() for author_name in author.split(',') if + author_name] + +def handle_csv(data): + """Handle the Post-History.""" + return [value.strip() for value in data.split(',') if value] + +handlers = {'Author': handle_author, + 'PEP': handle_pep_num, + 'Post-History': handle_csv, + } + +def sort_peps(peps): + """Sort PEPs into meta, informational, accepted, open, finished, empty, + and essentially dead.""" + meta = [] + info = [] + accepted = [] + open_ = [] + finished = [] + empty = [] + dead = [] + for pep in peps: + # XXX not all meta PEPs are process PEPs. + if pep['Type'] == 'Process': + meta.append(pep) + elif pep['Type'] == 'Informational': + info.append(pep) + elif pep['Status'] == 'Accepted': + accepted.append(pep) + elif pep['Status'] == 'Draft': + open_.append(pep) + elif pep['Status'] == 'Final': + finished.append(pep) + # XXX empty + elif pep['Status'] in ('Rejected', 'Withdrawn', 'Deferred', + 'Incomplete'): + dead.append(pep) + return meta, info, accepted, open_, finished, empty, dead + +if __name__ == '__main__': + from sys import argv + + if not argv[1:]: + directory = '.' + else: + directory = argv[1] + peps = consume_headers(directory) + + status = set() + type_ = set() + for pep in peps: + status.add(pep['Status']) + type_.add(pep['Type']) + meta, info, accepted, open_, done, empty, dead = sort_peps(peps) Added: sandbox/trunk/pep0/test_pep0.py ============================================================================== --- (empty file) +++ sandbox/trunk/pep0/test_pep0.py Tue Mar 6 23:43:15 2007 @@ -0,0 +1,187 @@ +from __future__ import with_statement +import unittest +from test import test_support +import pep0 +from contextlib import contextmanager +import os + +class HandlerTests(unittest.TestCase): + + def test_handle_generic(self): + # Identity function. + for data in ('spam', 'spam,', '', 'spam, monty'): + self.failUnlessEqual(pep0.handle_generic(data), data) + + def test_pep_num(self): + # String -> int. + num = 42 + string_rep = str(num) + self.failUnlessEqual(pep0.handle_pep_num(string_rep), num) + + def test_handle_csv(self): + # Split on commas. + data = ['a', 'b', 'c'] + string_rep = ','.join(data) + self.failUnlessEqual(pep0.handle_csv(string_rep), data) + string_rep = ', '.join(data) + self.failUnlessEqual(pep0.handle_csv(string_rep), data) + string_rep = ', '.join(data) + ',' + got = pep0.handle_csv(string_rep) + self.failUnlessEqual(got, data, + '%r != %r (using %r)' % + (got, data, string_rep)) + + def test_handle_author(self): + # Handle the various ways authors can be specified: + # Name , + # email (name), + # Name. + author = "Guido van Rossum" + str_rep = "%s " % author + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + str_rep += ',' + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + str_rep = "email (%s)" % author + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + str_rep += ',' + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + str_rep = author + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + str_rep += ',' + self.failUnlessEqual(pep0.handle_author(str_rep), [author]) + authors = ["Guido van Rossum", "Brett Cannon"] + str_rep = ', '.join(authors) + self.failUnlessEqual(pep0.handle_author(str_rep), authors) + + +class ParseMetaDataTests(unittest.TestCase): + + def test_parse_known_formats(self): + # Handle specific metadata format types. + assert "PEP" in pep0.handlers + line = "PEP: 42" + info = {} + handled = pep0.parse_metadata(info, line) + self.failUnlessEqual(handled, "PEP") + self.failUnless("PEP" in info) + self.failUnlessEqual(info['PEP'], 42) + + def test_parse_unknown_formats(self): + # If a format is not known then it should just be returned with + # whitespace stripped. + info = {} + type_ = "Spam" + data = "Monty Python" + line = "%s: %s" % (type_, data) + handled = pep0.parse_metadata(info, line) + self.failUnlessEqual(handled, type_) + self.failUnless(type_ in info) + self.failUnlessEqual(info[type_], data) + + def test_multiline_formats(self): + # Deal with formats that can span multiple lines (e.g., authors and + # Post-History). + type_ = 'Prev' + info = {type_ : [1]} + data = 'A' + handled = pep0.parse_metadata(info, data, type_) + self.failUnlessEqual(handled, type_) + self.failUnlessEqual(len(info[type_]), 2) + self.failUnlessEqual(info[type_][1], data) + + + at contextmanager +def test_file(path): + try: + open_file = open(path, 'w') + yield open_file + finally: + open_file.close() + if os.path.exists(path): + os.unlink(path) + + +class ConsumePepTests(unittest.TestCase): + + def test_single_line(self): + # Test a PEP that only has a single line of metadata. + type_ = 'Spam' + data = 'Monty' + with test_file(test_support.TESTFN) as pep_file: + pep_file.write('%s: %s\n' % (type_, data)) + pep_file.write('\n') + pep_file.write('The PEP.') + pep_file.close() + metadata = pep0.consume_pep(test_support.TESTFN) + self.failUnless(type_ in metadata) + self.failUnlessEqual(metadata[type_], data) + + def test_multi_line_authors(self): + # Make sure that metadata that takes multiple lines works when it + # is expected (e.g., Author and Post-History). + authors = ['A', 'B'] + output = 'Author: ' + authors[0] + '\n' + output += ' ' + authors[1] + '\n' + with test_file(test_support.TESTFN) as pep_file: + pep_file.write(output) + pep_file.write('\n') + pep_file.write('The PEP.') + pep_file.close() + metadata = pep0.consume_pep(test_support.TESTFN) + self.failUnless('Author' in metadata) + self.failUnlessEqual(metadata['Author'], authors) + + def test_multi_line_post_history(self): + # Post-History entries on multiple lines is fine. + dates = ['04-Jul-1776', '23-Oct-2007', '01-Jan-2001'] + output = 'Post-History: ' + ', '.join(dates[:2]) + '\n' + output += ' ' + dates[2] + '\n' + with test_file(test_support.TESTFN) as pep_file: + pep_file.write(output) + pep_file.write('\n') + pep_file.write('The PEP.') + pep_file.close() + metadata = pep0.consume_pep(test_support.TESTFN) + self.failUnless('Post-History' in metadata) + self.failUnlessEqual(metadata['Post-History'], dates) + + def test_utf8(self): + # Need to handle text as UTF-8. + # XXX + pass + + +class PepSortTests(unittest.TestCase): + + def test_meta(self): + pass + + def test_info(self): + pass + + def test_accepted(self): + pass + + def test_open(self): + pass + + def test_finished(self): + pass + + def test_empty(self): + pass + + def test_dead(self): + pass + + +def test_main(): + test_support.run_unittest( + HandlerTests, + ParseMetaDataTests, + ConsumePepTests, + ) + + +if __name__ == '__main__': + test_main() From python-checkins at python.org Wed Mar 7 00:01:35 2007 From: python-checkins at python.org (barry.warsaw) Date: Wed, 7 Mar 2007 00:01:35 +0100 (CET) Subject: [Python-checkins] r54186 - peps/trunk/pep-0000.txt peps/trunk/pep-0364.txt Message-ID: <20070306230135.571711E400D@bag.python.org> Author: barry.warsaw Date: Wed Mar 7 00:01:20 2007 New Revision: 54186 Added: peps/trunk/pep-0364.txt Modified: peps/trunk/pep-0000.txt Log: PEP 364, Transitioning to the Py3K Standard Library Modified: peps/trunk/pep-0000.txt ============================================================================== --- peps/trunk/pep-0000.txt (original) +++ peps/trunk/pep-0000.txt Wed Mar 7 00:01:20 2007 @@ -109,6 +109,7 @@ S 355 Path - Object oriented filesystem paths Lindqvist S 358 The "bytes" Object Schemenauer, GvR S 362 Function Signature Object Cannon, Seo + S 364 Transitioning to the Py3K Standard Library Warsaw S 754 IEEE 754 Floating Point Special Values Warnes S 3101 Advanced String Formatting Talin S 3108 Standard Library Reorganization Cannon @@ -440,6 +441,7 @@ I 361 Python 2.6 Release Schedule Norwitz, et al S 362 Function Signature Object Cannon, Seo SR 363 Syntax For Dynamic Attribute Access North + S 364 Transitioning to the Py3K Standard Library Warsaw SR 666 Reject Foolish Indentation Creighton S 754 IEEE 754 Floating Point Special Values Warnes P 3000 Python 3000 GvR Added: peps/trunk/pep-0364.txt ============================================================================== --- (empty file) +++ peps/trunk/pep-0364.txt Wed Mar 7 00:01:20 2007 @@ -0,0 +1,269 @@ +PEP: 364 +Title: Transitioning to the Py3K Standard Library +Version: $Revision$ +Last-Modified: $Date$ +Author: Barry A. Warsaw +Status: Active +Type: Standards Track +Content-Type: text/x-rst +Created: 01-Mar-2007 +Python-Version: 2.6 +Post-History: + + +Abstract +======== + +PEP 3108 describes the reorganization of the Python standard library +for the Python 3.0 release [1]_. This PEP describes a +mechanism for transitioning from the Python 2.x standard library to +the Python 3.0 standard library. This transition will allow and +encourage Python programmers to use the new Python 3.0 library names +starting with Python 2.6, while maintaining the old names for backward +compatibility. In this way, a Python programmer will be able to write +forward compatible code without sacrificing interoperability with +existing Python programs. + + +Rationale +========= + +PEP 3108 presents a rationale for Python standard library (stdlib) +reorganization. The reader is encouraged to consult that PEP for +details about why and how the library will be reorganized. Should +PEP 3108 be accepted in part or in whole, then it is advantageous to +allow Python programmers to begin the transition to the new stdlib +module names in Python 2.x, so that they can write forward compatible +code starting with Python 2.6. + +Note that PEP 3108 proposes to remove some "silly old stuff", +i.e. modules that are no longer useful or necessary. The PEP you are +reading does not address this because there are no forward +compatibility issues for modules that are to be removed, except to +stop using such modules. + +This PEP concerns only the mechanism by which mappings from old stdlib +names to new stdlib names are maintained. Please consult PEP 3108 for +all specific module renaming proposals. Specifically see the section +titled ``Modules to Rename`` for guidelines on the old name to new +name mappings. The few examples in this PEP are given for +illustrative purposes only and should not be used for specific +renaming recommendations. + + +Supported Renamings +=================== + +There are at least 4 use cases explicitly supported by this PEP: + +- Simple top-level package name renamings, such as ``StringIO`` to + ``stringio``; + +- Sub-package renamings where the package name may or may not be + renamed, such as ``email.MIMEText`` to ``email.mime.text``; + +- Extension module renaming, such as ``cStringIO`` to ``cstringio``; + +- Third party renaming of any of the above. + +Two use cases supported by this PEP include renaming simple top-level +modules, such as ``StringIO``, as well as modules within packages, +such as ``email.MIMEText``. + +In the former case, PEP 3108 currently recommends ``StringIO`` be +renamed to ``stringio``, following PEP 8 recommendations [2]_. + +In the latter case, the email 4.0 package distributed with Python 2.5 +already renamed ``email.MIMEText`` to ``email.mime.text``, although it +did so in a one-off, uniquely hackish way inside the email package. +The mechanism described in this PEP is general enough to handle all +module renamings, obviating the need for the Python 2.5 hack (except +for backward compatibility with earlier Python versions). + +An additional use case is to support the renaming of C extension +modules. As long as the new name for the C module is importable, it +can be remapped to the new name. E.g. ``cStringIO`` renamed to +``cstringio``. + +Third party package renaming is also supported, via several public +interfaces accessible by any Python module. + +Remappings are not performed recursively. + + +.mv files +========= + +Remapping files are called ``.mv`` files; the suffix was chosen to be +evocative of the Unix mv(1) command. An ``.mv`` file is a simple +line-oriented text file. All blank lines and lines that start with a +# are ignored. All other lines must contain two whitespace separated +fields. The first field is the old module name, and the second field +is the new module name. Both module names must be specified using +their full dotted-path names. Here is an example ``.mv`` file from +Python 2.6:: + + # Map the various string i/o libraries to their new names + StringIO stringio + cStringIO cstringio + +``.mv`` files can appear anywhere in the file system, and there is a +programmatic interface provided to parse them, and register the +remappings inside them. By default, when Python starts up, all the +``.mv`` files in the ``oldlib`` package are read, and their remappings +are automatically registered. This is where all the module remappings +should be specified for top-level Python 2.x standard library modules. + + +Implementation Specification +============================ + +This section provides the full specification for how module renamings +in Python 2.x are implemented. The central mechanism relies on +various import hooks as described in PEP 302 [3]_. Specifically +``sys.path_importer_cache``, ``sys.path``, and ``sys.meta_path`` are +all employed to provide the necessary functionality. + +When Python's import machinery is initialized, the oldlib package is +imported. Inside oldlib there is a class called ``OldStdlibLoader``. +This class implements the PEP 302 interface and is automatically +instantiated, with zero arguments. The constructor reads all the +``.mv`` files from the oldlib package directory, automatically +registering all the remappings found in those ``.mv`` files. This is +how the Python 2.x standard library is remapped. + +The OldStdlibLoader class should not be instantiated by other Python +modules. Instead, you can access the global OldStdlibLoader instance +via the ``sys.stdlib_remapper`` instance. Use this instance if you want +programmatic access to the remapping machinery. + +One important implementation detail: as needed by the PEP 302 API, a +magic string is added to sys.path, and module __path__ attributes in +order to hook in our remapping loader. This magic string is currently +```` and some changes were necessary to Python's site.py file +in order to treat all sys.path entries starting with ``<`` as +special. Specifically, no attempt is made to make them absolute file +names (since they aren't file names at all). + +In order for the remapping import hooks to work, the module or package +must be physically located under its new name. This is because the +import hooks catch only modules that are not already imported, and +cannot be imported by Python's built-in import rules. Thus, if a +module has been moved, say from Lib/StringIO.py to Lib/stringio.py, +and the former's ``.pyc`` file has been removed, then without the +remapper, this would fail:: + + import StringIO + +Instead, with the remapper, this failing import will be caught, the +old name will be looked up in the registered remappings, and in this +case, the new name ``stringio`` will be found. The remapper then +attempts to import the new name, and if that succeeds, it binds the +resulting module into sys.modules, under both the old and new names. +Thus, the above import will result in entries in sys.modules for +'StringIO' and 'stringio', and both will point to the exact same +module object. + +Note that no way to disable the remapping machinery is proposed, short +of moving all the ``.mv`` files away or programmatically removing them +in some custom start up code. In Python 3.0, the remappings will be +eliminated, leaving only the "new" names. + + +Programmatic Interface +====================== + +Several methods are added to the ``sys.stdlib_remapper`` object, which +third party packages can use to register their own remappings. Note +however that in all cases, there is one and only one mapping from an +old name to a new name. If two ``.mv`` files contain different +mappings for an old name, or if a programmatic call is made with an +old name that is already remapped, the previous mapping is lost. This +will not affect any already imported modules. + +The following methods are available on the ``sys.stdlib_remapper`` +object: + +- ``read_mv_file(filename)`` -- Read the given file and register all + remappings found in the file. + +- ``read_directory_mv_files(dirname, suffix='.mv')`` -- List the given + directory, reading all files in that directory that have the + matching suffix (``.mv`` by default). For each parsed file, + register all the remappings found in that file. + +- ``set_mapping(oldname, newname)`` -- Register a new mapping from an + old module name to a new module name. Both must be the full + dotted-path name to the module. newname may be ``None`` in which + case any existing mapping for oldname will be removed (it is not an + error if there is no existing mapping). + +- ``get_mapping(oldname, default=None)`` -- Return any registered + newname for the given oldname. If there is no registered remapping, + default is returned. + + +Open Issues +=========== + + - Should there be a command line switch and/or environment variable to + disable all remappings? + + - Should remappings occur recursively? + + - Should we automatically parse package directories for .mv files when + the package's __init__.py is loaded? This would allow packages to + easily include .mv files for their own remappings. Compare what the + email package currently has to do if we place its ``.mv`` file in + the email package instead of in the oldlib package:: + + # Expose old names + import os, sys + sys.stdlib_remapper.read_directory_mv_files(os.path.dirname(__file__)) + + I think we should automatically read a package's directory for any + ``.mv`` files it might contain. + + +Reference Implementation +======================== + +A reference implementation, in the form of a patch against the current +(as of this writing) state of the Python 2.6 svn trunk, is available +as SourceForge patch #1675334 [4]_. Note that this patch includes a +rename of ``cStringIO`` to ``cstringio``, but this is primarily for +illustrative and unit testing purposes. Should the patch be accepted, +we might want to split this change off into other PEP 3108 changes. + + +References +========== + +.. [1] PEP 3108, Standard Library Reorganization, Cannon + (http://www.python.org/dev/peps/pep-3108) + +.. [2] PEP 8, Style Guide for Python Code, GvR, Warsaw + (http://www.python.org/dev/peps/pep-0008) + +.. [3] PEP 302, New Import Hooks, JvR, Moore + (http://www.python.org/dev/peps/pep-0302) + +.. [4] Reference implementation on SourceForge + (https://sourceforge.net/tracker/index.php?func=detail&aid=1675334&group_id=5470&atid=305470) + + +Copyright +========= + +This document has been placed in the public domain. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: From python-checkins at python.org Wed Mar 7 00:27:38 2007 From: python-checkins at python.org (ka-ping.yee) Date: Wed, 7 Mar 2007 00:27:38 +0100 (CET) Subject: [Python-checkins] r54187 - peps/trunk/pep-3114.txt Message-ID: <20070306232738.C524D1E400D@bag.python.org> Author: ka-ping.yee Date: Wed Mar 7 00:27:37 2007 New Revision: 54187 Added: peps/trunk/pep-3114.txt Log: Add PEP 3114: renaming .next() to .__next__(). Added: peps/trunk/pep-3114.txt ============================================================================== --- (empty file) +++ peps/trunk/pep-3114.txt Wed Mar 7 00:27:37 2007 @@ -0,0 +1,210 @@ +PEP: 3114 +Title: Renaming iterator.next() to iterator.__next__() +Version: $Revision$ +Last-Modified: $Date$ +Author: Ka-Ping Yee +Status: Accepted +Type: Standards Track +Python-version: 3.0 +Content-Type: text/x-rst +Created: 04-Mar-2007 + + +Abstract +======== + +The iterator protocol in Python 2.x consists of two methods: +``__iter__()`` called on an iterable object to yield an iterator, and +``next()`` called on an iterator object to yield the next item in the +sequence. Using a ``for`` loop to iterate over an iterable object +implicitly calls both of these methods. This PEP proposes that the +``next`` method be renamed to ``__next__``, consistent with all the +other protocols in Python in which a method is implicitly called as +part of a language-level protocol, and that a built-in function named +``next`` be introduced to invoke ``__next__`` method, consistent with +the manner in which other protocols are explicitly invoked. + + +Names With Double Underscores +============================= + +In Python, double underscores before and after a name are used to +distinguish names that belong to the language itself. Attributes and +methods that are implicitly used or created by the interpreter employ +this naming convention; some examples are: + +* ``__file__`` - an attribute automatically created by the interpreter + +* ``__dict__`` - an attribute with special meaning to the interpreter + +* ``__init__`` - a method implicitly called by the interpreter + +Note that this convention applies to methods such as ``__init__`` that +are explicitly defined by the programmer, as well as attributes such as +``__file__`` that can only be accessed by naming them explicitly, so it +includes names that are used *or* created by the interpreter. + +(Not all things that are called "protocols" are made of methods with +double-underscore names. For example, the ``__contains__`` method has +double underscores because the language construct ``x in y`` implicitly +calls ``__contains__``. But even though the ``read`` method is part of +the file protocol, it does not have double underscores because there is +no language construct that implicitly invokes ``x.read()``.) + +The use of double underscores creates a separate namespace for names +that are part of the Python language definition, so that programmers +are free to create variables, attributes, and methods that start with +letters, without fear of silently colliding with names that have a +language-defined purpose. (Colliding with reserved keywords is still +a concern, but at least this will immediately yield a syntax error.) + +The naming of the ``next`` method on iterators is an exception to +this convention. Code that nowhere contains an explicit call to a +``next`` method can nonetheless be silently affected by the presence +of such a method. Therefore, this PEP proposes that iterators should +have a ``__next__`` method instead of a ``next`` method (with no +change in semantics). + + +Double-Underscore Methods and Built-In Functions +================================================ + +The Python language defines several protocols that are implemented or +customized by defining methods with double-underscore names. In each +case, the protocol is provided by an internal method implemented as a +C function in the interpreter. For objects defined in Python, this +C function supports customization by implicitly invoking a Python method +with a double-underscore name (it often does a little bit of additional +work beyond just calling the Python method.) + +Sometimes the protocol is invoked by a syntactic construct: + +* ``x[y]`` --> internal ``tp_getitem`` --> ``x.__getitem__(y)`` + +* ``x + y`` --> internal ``nb_add`` --> ``x.__add__(y)`` + +* ``-x`` --> internal ``nb_negative`` --> ``x.__neg__()`` + +Sometimes there is no syntactic construct, but it is still useful to be +able to explicitly invoke the protocol. For such cases Python offers a +built-in function of the same name but without the double underscores. + +* ``len(x)`` --> internal ``sq_length`` --> ``x.__len__()`` + +* ``hash(x)`` --> internal ``tp_hash`` --> ``x.__hash__()`` + +* ``iter(x)`` --> internal ``tp_iter`` --> ``x.__iter__()`` + +Following this pattern, the natural way to handle ``next`` is to add a +``next`` built-in function that behaves in exactly the same fashion. + +* ``next(x)`` --> internal ``tp_iternext`` --> ``x.__next__()`` + +Further, it is proposed that the ``next`` built-in function accept a +sentinel value as an optional second argument, following the style of +the ``getattr`` and ``iter`` built-in functions. When called with two +arguments, ``next`` catches the StopIteration exception and returns +the sentinel value instead of propagating the exception. This creates +a nice duality between ``iter`` and ``next``: + + iter(function, sentinel) <--> next(iterator, sentinel) + + +Previous Proposals +================== + +This proposal is not a new idea. The idea proposed here was supported +by the BDFL on python-dev [1]_ and is even mentioned in the original +iterator PEP, PEP 234:: + + (In retrospect, it might have been better to go for __next__() + and have a new built-in, next(it), which calls it.__next__(). + But alas, it's too late; this has been deployed in Python 2.2 + since December 2001.) + + +Objections +========== + +There have been a few objections to the addition of more built-ins. +In particular, Martin von Loewis writes [2]_:: + + I dislike the introduction of more builtins unless they have a true + generality (i.e. are likely to be needed in many programs). For this + one, I think the normal usage of __next__ will be with a for loop, so + I don't think one would often need an explicit next() invocation. + + It is also not true that most protocols are explicitly invoked through + builtin functions. Instead, most protocols are can be explicitly invoked + through methods in the operator module. So following tradition, it + should be operator.next. + + ... + + As an alternative, I propose that object grows a .next() method, + which calls __next__ by default. + + +Transition Plan +=============== + +(This section is likely to be updated.) + +Two additional transformations will be added to the 2to3 translation +tool [3]_: + +* Method definitions named ``next`` will be renamed to ``__next__``. + +* Explicit calls to the ``next`` method will be replaced with calls + to the built-in ``next`` function. For example, ``x.next()`` will + become ``next(x)``. + +If the module being processed already contains a binding for the name +``next``, the second transformation will not be done; instead, calls to +``x.next()`` will be replaced with ``x.__next__()`` and a warning will +be emitted. (Collin Winter has looked into this [4]_ and found that +it's difficult to make the second transformation depend on the presence +of a module-level binding; warning about the existence of bindings to +``next`` should be possible, though.) + + +Approval +======== + +This PEP was accepted by Guido on March 6, 2007 [5]_. + + +References +========== + +.. [1] Single- vs. Multi-pass iterability (Guido van Rossum) + http://mail.python.org/pipermail/python-dev/2002-July/026814.html + +.. [2] PEP: rename it.next() to it.__next__()... (Martin von Loewis) + http://mail.python.org/pipermail/python-3000/2007-March/005965.html + +.. [3] 2to3 refactoring tool + http://svn.python.org/view/sandbox/trunk/2to3/ + +.. [4] PEP: rename it.next() to it.__next__()... (Collin Winter) + http://mail.python.org/pipermail/python-3000/2007-March/006020.html + +.. [5] PEP: rename it.next() to it.__next__()... (Guido van Rossum) + http://mail.python.org/pipermail/python-3000/2007-March/006027.html + + +Copyright +========= + +This document has been placed in the public domain. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: From python-checkins at python.org Wed Mar 7 01:34:53 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 01:34:53 +0100 (CET) Subject: [Python-checkins] r54188 - in python/trunk: Include/pydebug.h Misc/NEWS Modules/main.c Python/pythonrun.c Message-ID: <20070307003453.8AFCA1E400D@bag.python.org> Author: georg.brandl Date: Wed Mar 7 01:34:46 2007 New Revision: 54188 Modified: python/trunk/Include/pydebug.h python/trunk/Misc/NEWS python/trunk/Modules/main.c python/trunk/Python/pythonrun.c Log: Variant of patch #697613: don't exit the interpreter on a SystemExit exception if the -i command line option or PYTHONINSPECT environment variable is given, but break into the interactive interpreter just like on other exceptions or normal program exit. (backport) Modified: python/trunk/Include/pydebug.h ============================================================================== --- python/trunk/Include/pydebug.h (original) +++ python/trunk/Include/pydebug.h Wed Mar 7 01:34:46 2007 @@ -8,6 +8,7 @@ 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; Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 01:34:46 2007 @@ -12,6 +12,11 @@ Core and builtins ----------------- +- Variant of patch #697613: don't exit the interpreter on a SystemExit + exception if the -i command line option or PYTHONINSPECT environment + variable is given, but break into the interactive interpreter just like + on other exceptions or normal program exit. + - Patch #1638879: don't accept strings with embedded NUL bytes in long(). - Bug #1674503: close the file opened by execfile() in an error condition. Modified: python/trunk/Modules/main.c ============================================================================== --- python/trunk/Modules/main.c (original) +++ python/trunk/Modules/main.c Wed Mar 7 01:34:46 2007 @@ -216,13 +216,11 @@ 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; @@ -297,8 +295,7 @@ /* NOTREACHED */ case 'i': - inspect++; - saw_inspect_flag = 1; + Py_InspectFlag++; Py_InteractiveFlag++; break; @@ -369,9 +366,9 @@ 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; @@ -499,7 +496,7 @@ 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"); @@ -518,6 +515,7 @@ } else { if (filename == NULL && stdin_is_interactive) { + Py_InspectFlag = 0; /* do exit on SystemExit */ RunStartupFile(&cf); } /* XXX */ @@ -530,16 +528,18 @@ /* 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, "", &cf) != 0; + } WaitForThreadShutdown(); Modified: python/trunk/Python/pythonrun.c ============================================================================== --- python/trunk/Python/pythonrun.c (original) +++ python/trunk/Python/pythonrun.c Wed Mar 7 01:34:46 2007 @@ -69,6 +69,7 @@ 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 */ @@ -1019,6 +1020,11 @@ 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); if (Py_FlushLine()) PyErr_Clear(); From python-checkins at python.org Wed Mar 7 01:40:29 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 01:40:29 +0100 (CET) Subject: [Python-checkins] r54189 - in python/trunk: Misc/NEWS Python/pythonrun.c Message-ID: <20070307004029.8E1B91E400D@bag.python.org> Author: georg.brandl Date: Wed Mar 7 01:40:28 2007 New Revision: 54189 Modified: python/trunk/Misc/NEWS python/trunk/Python/pythonrun.c Log: Patch #703779: unset __file__ in __main__ after running a file. This makes the filenames the warning module prints much more sensible when a PYTHONSTARTUP file is used. Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 01:40:28 2007 @@ -12,6 +12,10 @@ Core and builtins ----------------- +- Patch #703779: unset __file__ in __main__ after running a file. This + makes the filenames the warning module prints much more sensible when + a PYTHONSTARTUP file is used. + - Variant of patch #697613: don't exit the interpreter on a SystemExit exception if the -i command line option or PYTHONINSPECT environment variable is given, but break into the interactive interpreter just like Modified: python/trunk/Python/pythonrun.c ============================================================================== --- python/trunk/Python/pythonrun.c (original) +++ python/trunk/Python/pythonrun.c Wed Mar 7 01:40:28 2007 @@ -849,6 +849,7 @@ { PyObject *m, *d, *v; const char *ext; + int set_file_name = 0, ret; m = PyImport_AddModule("__main__"); if (m == NULL) @@ -862,6 +863,7 @@ Py_DECREF(f); return -1; } + set_file_name = 1; Py_DECREF(f); } ext = filename + strlen(filename) - 4; @@ -871,7 +873,8 @@ 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) @@ -883,12 +886,17 @@ } if (v == NULL) { PyErr_Print(); - return -1; + ret = -1; + goto done; } Py_DECREF(v); if (Py_FlushLine()) PyErr_Clear(); - return 0; + ret = 0; + done: + if (set_file_name && PyDict_DelItemString(d, "__file__")) + PyErr_Clear(); + return ret; } int From buildbot at python.org Wed Mar 7 02:10:58 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 01:10:58 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable trunk Message-ID: <20070307011058.B6D981E401B@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/128 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl,walter.doerwald Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 02:58:18 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 01:58:18 +0000 Subject: [Python-checkins] buildbot warnings in g4 osx.4 trunk Message-ID: <20070307015818.385851E4002@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%2520trunk/builds/1797 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 03:19:21 2007 From: python-checkins at python.org (ka-ping.yee) Date: Wed, 7 Mar 2007 03:19:21 +0100 (CET) Subject: [Python-checkins] r54191 - peps/trunk/pep-3114.txt Message-ID: <20070307021921.139D11E4002@bag.python.org> Author: ka-ping.yee Date: Wed Mar 7 03:19:15 2007 New Revision: 54191 Modified: peps/trunk/pep-3114.txt Log: Update transition plan (according to e-mail from Collin Winter). Modified: peps/trunk/pep-3114.txt ============================================================================== --- peps/trunk/pep-3114.txt (original) +++ peps/trunk/pep-3114.txt Wed Mar 7 03:19:15 2007 @@ -148,8 +148,6 @@ Transition Plan =============== -(This section is likely to be updated.) - Two additional transformations will be added to the 2to3 translation tool [3]_: @@ -159,13 +157,20 @@ to the built-in ``next`` function. For example, ``x.next()`` will become ``next(x)``. -If the module being processed already contains a binding for the name -``next``, the second transformation will not be done; instead, calls to -``x.next()`` will be replaced with ``x.__next__()`` and a warning will -be emitted. (Collin Winter has looked into this [4]_ and found that -it's difficult to make the second transformation depend on the presence -of a module-level binding; warning about the existence of bindings to -``next`` should be possible, though.) +Collin Winter looked into the possibility of automatically deciding +whether to perform the second transformation depending on the presence +of a module-level binding to ``next`` [4]_ and found that it would be +"ugly and slow". Instead, the translation tool will emit warnings +upon detecting such a binding. Collin has proposed warnings for the +following conditions [5]_: + +* Module-level assignments to ``next``. + +* Module-level definitions of a function named ``next``. + +* Module-level imports of the name ``next``. + +* Assignments to ``__builtin__.next``. Approval @@ -189,7 +194,10 @@ .. [4] PEP: rename it.next() to it.__next__()... (Collin Winter) http://mail.python.org/pipermail/python-3000/2007-March/006020.html -.. [5] PEP: rename it.next() to it.__next__()... (Guido van Rossum) +.. [5] PEP 3113 transition plan + http://mail.python.org/pipermail/python-3000/2007-March/006044.html + +.. [6] PEP: rename it.next() to it.__next__()... (Guido van Rossum) http://mail.python.org/pipermail/python-3000/2007-March/006027.html From python-checkins at python.org Wed Mar 7 05:21:25 2007 From: python-checkins at python.org (george.yoshida) Date: Wed, 7 Mar 2007 05:21:25 +0100 (CET) Subject: [Python-checkins] r54192 - python/trunk/Doc/lib/libzipfile.tex Message-ID: <20070307042125.2E1E91E4003@bag.python.org> Author: george.yoshida Date: Wed Mar 7 05:21:18 2007 New Revision: 54192 Modified: python/trunk/Doc/lib/libzipfile.tex Log: add versionadded info Modified: python/trunk/Doc/lib/libzipfile.tex ============================================================================== --- python/trunk/Doc/lib/libzipfile.tex (original) +++ python/trunk/Doc/lib/libzipfile.tex Wed Mar 7 05:21:18 2007 @@ -165,6 +165,8 @@ by the ZipExtFile, allowing it to operate independently of the ZipFile. \end{notice} + + \versionadded{2.6} \end{methoddesc} \begin{methoddesc}{printdir}{} From python-checkins at python.org Wed Mar 7 05:46:28 2007 From: python-checkins at python.org (patrick.maupin) Date: Wed, 7 Mar 2007 05:46:28 +0100 (CET) Subject: [Python-checkins] r54193 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070307044628.DC8751E4003@bag.python.org> Author: patrick.maupin Date: Wed Mar 7 05:46:21 2007 New Revision: 54193 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Found/fixed bug with useall and no keyword dictionary Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Wed Mar 7 05:46:21 2007 @@ -350,6 +350,8 @@ self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1) self.formatEquals(result, s, 'a', 'b', bar=bar, foo=1, sam=27) self.formatRaises(ValueError, s2, 'a', 'b', bar=bar, foo=1, sam=27) + self.formatRaises(ValueError, '{!useall}', 1) + self.formatRaises(ValueError, '{!useall}', foo=1) def test_comments(self): self.formatEquals('Hi there','''{#Comment}Hi{# Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Wed Mar 7 05:46:21 2007 @@ -2113,7 +2113,7 @@ PyObject *used = fs->keyword_arg_set; if (result != NULL) { num_unused = fs->positional_arg_set; - if (!num_unused) { + if (!num_unused && (used != NULL)) { num_unused = PySet_GET_SIZE(used); i = 0; while (num_unused && (ignore[i] != NULL)) { @@ -2132,7 +2132,7 @@ result = SetError(fs, "Not all arguments consumed"); } } - Py_DECREF(used); + Py_XDECREF(used); return result; } @@ -2151,12 +2151,16 @@ fs.max_recursion = 4; fs.do_markup = do_markup; + /* XXX -- I really want to say fs.keywords = keywords, + but this exposes a different set of potential bugs... */ + fs.keywords = (keywords != NULL && PyDict_Size(keywords)) + ? keywords : NULL; fs.keywords = keywords; result = build_string(&fs); Py_XDECREF(fs.keywords_tuple); Py_XDECREF(fs.hookfunc); - return (fs.keyword_arg_set != NULL) + return ((fs.keyword_arg_set != NULL) || fs.positional_arg_set) ? check_args_consumed(&fs, result) : result; } From python-checkins at python.org Wed Mar 7 08:39:08 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 08:39:08 +0100 (CET) Subject: [Python-checkins] r54195 - python/trunk/Lib/urllib2.py Message-ID: <20070307073908.7C2DA1E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 08:39:06 2007 New Revision: 54195 Modified: python/trunk/Lib/urllib2.py Log: Patch #812285: allow multiple auth schemes in AbstractBasicAuthHandler. Modified: python/trunk/Lib/urllib2.py ============================================================================== --- python/trunk/Lib/urllib2.py (original) +++ python/trunk/Lib/urllib2.py Wed Mar 7 08:39:06 2007 @@ -767,11 +767,10 @@ 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" From python-checkins at python.org Wed Mar 7 08:39:17 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 08:39:17 +0100 (CET) Subject: [Python-checkins] r54196 - python/branches/release25-maint/Lib/urllib2.py Message-ID: <20070307073917.23AC11E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 08:39:13 2007 New Revision: 54196 Modified: python/branches/release25-maint/Lib/urllib2.py Log: Patch #812285: allow multiple auth schemes in AbstractBasicAuthHandler. (backport from rev. 54195) Modified: python/branches/release25-maint/Lib/urllib2.py ============================================================================== --- python/branches/release25-maint/Lib/urllib2.py (original) +++ python/branches/release25-maint/Lib/urllib2.py Wed Mar 7 08:39:13 2007 @@ -766,11 +766,10 @@ 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" From python-checkins at python.org Wed Mar 7 09:31:53 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 09:31:53 +0100 (CET) Subject: [Python-checkins] r54197 - in python/trunk: Lib/glob.py Lib/test/test_glob.py Misc/NEWS Message-ID: <20070307083153.578C41E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 09:31:51 2007 New Revision: 54197 Modified: python/trunk/Lib/glob.py python/trunk/Lib/test/test_glob.py python/trunk/Misc/NEWS Log: Patch #1001604: glob.glob() now returns unicode filenames if it was given a unicode argument and os.listdir() returns unicode filenames. Modified: python/trunk/Lib/glob.py ============================================================================== --- python/trunk/Lib/glob.py (original) +++ python/trunk/Lib/glob.py Wed Mar 7 09:31:51 2007 @@ -1,8 +1,9 @@ """Filename globbing utility.""" +import sys import os -import fnmatch import re +import fnmatch __all__ = ["glob", "iglob"] @@ -48,13 +49,15 @@ def glob1(dirname, pattern): if not dirname: dirname = os.curdir + if isinstance(pattern, unicode) and not isinstance(dirname, unicode): + dirname = unicode(dirname, sys.getfilesystemencoding()) 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 == '': Modified: python/trunk/Lib/test/test_glob.py ============================================================================== --- python/trunk/Lib/test/test_glob.py (original) +++ python/trunk/Lib/test/test_glob.py Wed Mar 7 09:31:51 2007 @@ -52,6 +52,16 @@ 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'])) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 09:31:51 2007 @@ -152,6 +152,9 @@ Library ------- +- Patch #1001604: glob.glob() now returns unicode filenames if it was + given a unicode argument and os.listdir() returns unicode filenames. + - Patch #1673619: setup.py identifies extension modules it doesn't know how to build and those it knows how to build but that fail to build. From python-checkins at python.org Wed Mar 7 09:32:26 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 09:32:26 +0100 (CET) Subject: [Python-checkins] r54198 - in python/branches/release25-maint: Lib/glob.py Lib/test/test_glob.py Misc/NEWS Message-ID: <20070307083226.905111E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 09:32:24 2007 New Revision: 54198 Modified: python/branches/release25-maint/Lib/glob.py python/branches/release25-maint/Lib/test/test_glob.py python/branches/release25-maint/Misc/NEWS Log: Patch #1001604: glob.glob() now returns unicode filenames if it was given a unicode argument and os.listdir() returns unicode filenames. (backport from rev. 54197) Modified: python/branches/release25-maint/Lib/glob.py ============================================================================== --- python/branches/release25-maint/Lib/glob.py (original) +++ python/branches/release25-maint/Lib/glob.py Wed Mar 7 09:32:24 2007 @@ -1,8 +1,9 @@ """Filename globbing utility.""" +import sys import os -import fnmatch import re +import fnmatch __all__ = ["glob", "iglob"] @@ -48,13 +49,15 @@ def glob1(dirname, pattern): if not dirname: dirname = os.curdir + if isinstance(pattern, unicode) and not isinstance(dirname, unicode): + dirname = unicode(dirname, sys.getfilesystemencoding()) 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 == '': Modified: python/branches/release25-maint/Lib/test/test_glob.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_glob.py (original) +++ python/branches/release25-maint/Lib/test/test_glob.py Wed Mar 7 09:32:24 2007 @@ -52,6 +52,16 @@ 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'])) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Wed Mar 7 09:32:24 2007 @@ -197,6 +197,9 @@ Library ------- +- Patch #1001604: glob.glob() now returns unicode filenames if it was + given a unicode argument and os.listdir() returns unicode filenames. + - Patch #685268: Consider a package's __path__ in imputil. - Patch 1463026: Support default namespace in XMLGenerator. From buildbot at python.org Wed Mar 7 09:40:03 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 08:40:03 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.5 Message-ID: <20070307084003.EDAC11E4010@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%25202.5/builds/190 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl,walter.doerwald Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_operator ====================================================================== ERROR: test_delslice (test.test_operator.OperatorTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/net/ringneck/scratch1/nnorwitz/python/2.5.norwitz-tru64/build/Lib/test/test_operator.py", line 146, in test_delslice operator.delslice(a, 0, test_support.MAX_Py_ssize_t) OverflowError: signed integer is greater than maximum ====================================================================== ERROR: test_getslice (test.test_operator.OperatorTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/net/ringneck/scratch1/nnorwitz/python/2.5.norwitz-tru64/build/Lib/test/test_operator.py", line 175, in test_getslice b = operator.getslice(a, 0, test_support.MAX_Py_ssize_t) OverflowError: signed integer is greater than maximum ====================================================================== ERROR: test_setslice (test.test_operator.OperatorTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/net/ringneck/scratch1/nnorwitz/python/2.5.norwitz-tru64/build/Lib/test/test_operator.py", line 325, in test_setslice operator.setslice(a, 0, test_support.MAX_Py_ssize_t, []) OverflowError: signed integer is greater than maximum sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 10:09:49 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 10:09:49 +0100 (CET) Subject: [Python-checkins] r54199 - in python/trunk: Lib/test/test_unittest.py Lib/unittest.py Misc/NEWS Message-ID: <20070307090949.BF7081E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 10:09:40 2007 New Revision: 54199 Modified: python/trunk/Lib/test/test_unittest.py python/trunk/Lib/unittest.py python/trunk/Misc/NEWS Log: Patches #1550273, #1550272: fix a few bugs in unittest and add a comprehensive test suite for the module. Modified: python/trunk/Lib/test/test_unittest.py ============================================================================== --- python/trunk/Lib/test/test_unittest.py (original) +++ python/trunk/Lib/test/test_unittest.py Wed Mar 7 10:09:40 2007 @@ -1,31 +1,2289 @@ """Test script for unittest. -This just includes tests for new features. We really need a -full set of tests. +By Collin Winter + +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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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") + + +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 + -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 - """ + # "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) + + # 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__': - test_main() +if __name__ == "__main__": + test_main() Modified: python/trunk/Lib/unittest.py ============================================================================== --- python/trunk/Lib/unittest.py (original) +++ python/trunk/Lib/unittest.py Wed Mar 7 10:09:40 2007 @@ -25,7 +25,7 @@ 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 @@ -107,7 +107,7 @@ 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" @@ -235,6 +235,18 @@ 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(str(hash(type(self))) + str(hash(self._testMethodName))) + def __str__(self): return "%s (%s)" % (self._testMethodName, _strclass(self.__class__)) @@ -291,10 +303,7 @@ 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.""" @@ -401,6 +410,14 @@ __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) @@ -436,7 +453,7 @@ """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. """ @@ -463,6 +480,23 @@ 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(''.join(str(hash(x)) for x in [ + type(self), self.__setUpFunc, self.__tearDownFunc, self.__testFunc, + self.__description])) + def __str__(self): return "%s (%s)" % (_strclass(self.__class__), self.__testFunc.__name__) @@ -482,7 +516,7 @@ 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 @@ -536,18 +570,23 @@ 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 @@ -562,10 +601,6 @@ 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 Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 10:09:40 2007 @@ -152,6 +152,9 @@ Library ------- +- Patches #1550273, #1550272: fix a few bugs in unittest and add a + comprehensive test suite for the module. + - Patch #1001604: glob.glob() now returns unicode filenames if it was given a unicode argument and os.listdir() returns unicode filenames. From python-checkins at python.org Wed Mar 7 10:17:43 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 10:17:43 +0100 (CET) Subject: [Python-checkins] r54200 - python/branches/release25-maint/Doc/lib/libunittest.tex Message-ID: <20070307091743.64C551E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 10:17:42 2007 New Revision: 54200 Modified: python/branches/release25-maint/Doc/lib/libunittest.tex Log: Typo fix. Modified: python/branches/release25-maint/Doc/lib/libunittest.tex ============================================================================== --- python/branches/release25-maint/Doc/lib/libunittest.tex (original) +++ python/branches/release25-maint/Doc/lib/libunittest.tex Wed Mar 7 10:17:42 2007 @@ -540,7 +540,7 @@ \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 From python-checkins at python.org Wed Mar 7 10:21:12 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 10:21:12 +0100 (CET) Subject: [Python-checkins] r54201 - in python/trunk: Doc/lib/libunittest.tex Lib/unittest.py Misc/NEWS Message-ID: <20070307092112.81BA71E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 10:21:06 2007 New Revision: 54201 Modified: python/trunk/Doc/lib/libunittest.tex python/trunk/Lib/unittest.py python/trunk/Misc/NEWS Log: Patch #787789: allow to pass custom TestRunner instances to unittest's main() function. Modified: python/trunk/Doc/lib/libunittest.tex ============================================================================== --- python/trunk/Doc/lib/libunittest.tex (original) +++ python/trunk/Doc/lib/libunittest.tex Wed Mar 7 10:21:06 2007 @@ -290,6 +290,7 @@ 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 @@ \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 @@ 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 Modified: python/trunk/Lib/unittest.py ============================================================================== --- python/trunk/Lib/unittest.py (original) +++ python/trunk/Lib/unittest.py Wed Mar 7 10:21:06 2007 @@ -776,7 +776,8 @@ 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:]: @@ -826,9 +827,16 @@ 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 Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 10:21:06 2007 @@ -152,6 +152,9 @@ Library ------- +- Patch #787789: allow to pass custom TestRunner instances to unittest's + main() function. + - Patches #1550273, #1550272: fix a few bugs in unittest and add a comprehensive test suite for the module. From python-checkins at python.org Wed Mar 7 10:34:45 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 10:34:45 +0100 (CET) Subject: [Python-checkins] r54202 - python/trunk/Doc/lib/libshutil.tex Message-ID: <20070307093445.EE1CB1E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 10:34:45 2007 New Revision: 54202 Modified: python/trunk/Doc/lib/libshutil.tex Log: Patch #1669331: clarify shutil.copyfileobj() behavior wrt. file position. Modified: python/trunk/Doc/lib/libshutil.tex ============================================================================== --- python/trunk/Doc/lib/libshutil.tex (original) +++ python/trunk/Doc/lib/libshutil.tex Wed Mar 7 10:34:45 2007 @@ -34,7 +34,9 @@ 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} From python-checkins at python.org Wed Mar 7 10:34:52 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 10:34:52 +0100 (CET) Subject: [Python-checkins] r54203 - python/branches/release25-maint/Doc/lib/libshutil.tex Message-ID: <20070307093452.BBF561E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 10:34:52 2007 New Revision: 54203 Modified: python/branches/release25-maint/Doc/lib/libshutil.tex Log: Patch #1669331: clarify shutil.copyfileobj() behavior wrt. file position. (backport from rev. 54202) Modified: python/branches/release25-maint/Doc/lib/libshutil.tex ============================================================================== --- python/branches/release25-maint/Doc/lib/libshutil.tex (original) +++ python/branches/release25-maint/Doc/lib/libshutil.tex Wed Mar 7 10:34:52 2007 @@ -34,7 +34,9 @@ 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} From buildbot at python.org Wed Mar 7 10:48:42 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 09:48:42 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable 2.5 Message-ID: <20070307094842.85E911E4003@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%25202.5/builds/50 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socketserver Traceback (most recent call last): File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 81, in run svr = svrcls(self.__addr, self.__hdlrcls) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 330, in __init__ self.server_bind() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 341, in server_bind self.socket.bind(self.server_address) File "", line 1, in bind error: (98, 'Address already in use') Traceback (most recent call last): File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 466, in process_request_thread self.handle_error(request, client_address) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 463, in process_request_thread self.finish_request(request, client_address) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 254, in finish_request self.RequestHandlerClass(request, client_address, self) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/SocketServer.py", line 521, in __init__ self.handle() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 21, in handle time.sleep(DELAY) AttributeError: 'NoneType' object has no attribute 'sleep' Traceback (most recent call last): File "./Lib/test/regrtest.py", line 554, in runtest_inner indirect_test() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 209, in test_main testall() File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 192, in testall testloop(socket.AF_INET, tcpservers, MyStreamHandler, teststream) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 141, in testloop testfunc(proto, addr) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 64, in teststream buf = data = receive(s, 100) File "/home/pybot/buildarea/2.5.klose-debian-ppc/build/Lib/test/test_socketserver.py", line 46, in receive return sock.recv(n) error: (104, 'Connection reset by peer') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 11:05:07 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 10:05:07 +0000 Subject: [Python-checkins] buildbot warnings in ia64 Ubuntu trunk trunk Message-ID: <20070307100508.27C6F1E400D@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%2520Ubuntu%2520trunk%2520trunk/builds/436 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 11:30:19 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 10:30:19 +0000 Subject: [Python-checkins] buildbot warnings in S-390 Debian trunk Message-ID: <20070307103020.3117C1E401C@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%2520Debian%2520trunk/builds/727 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 11:52:34 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 10:52:34 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable trunk Message-ID: <20070307105234.6EC4D1E4016@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/133 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/bsddb/test/test_thread.py", line 281, in readerThread rec = dbutils.DeadlockWrap(c.next, max_retries=10) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/bsddb/dbutils.py", line 62, in DeadlockWrap return function(*_args, **_kwargs) DBLockDeadlockError: (-30995, 'DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock') Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/bsddb/test/test_thread.py", line 245, in writerThread self.assertEqual(data, self.makeData(key)) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/unittest.py", line 343, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: None != '0091-0091-0091-0091-0091' Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/bsddb/test/test_thread.py", line 260, in writerThread self.assertEqual(data, self.makeData(key)) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/unittest.py", line 343, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: None != '2000-2000-2000-2000-2000' Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 460, in __bootstrap self.run() File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/threading.py", line 440, in run self.__target(*self.__args, **self.__kwargs) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/bsddb/test/test_thread.py", line 260, in writerThread self.assertEqual(data, self.makeData(key)) File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/unittest.py", line 343, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: None != '1000-1000-1000-1000-1000' 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 12:04:37 2007 From: python-checkins at python.org (martin.v.loewis) Date: Wed, 7 Mar 2007 12:04:37 +0100 (CET) Subject: [Python-checkins] r54204 - in python/trunk: Doc/lib/libposixpath.tex Lib/genericpath.py Lib/macpath.py Lib/ntpath.py Lib/posixpath.py Lib/test/test_macpath.py Lib/test/test_ntpath.py Lib/test/test_posixpath.py Misc/NEWS Message-ID: <20070307110437.139941E4003@bag.python.org> Author: martin.v.loewis Date: Wed Mar 7 12:04:33 2007 New Revision: 54204 Modified: python/trunk/Doc/lib/libposixpath.tex python/trunk/Lib/genericpath.py python/trunk/Lib/macpath.py python/trunk/Lib/ntpath.py python/trunk/Lib/posixpath.py python/trunk/Lib/test/test_macpath.py python/trunk/Lib/test/test_ntpath.py python/trunk/Lib/test/test_posixpath.py python/trunk/Misc/NEWS Log: Bug #1115886: os.path.splitext('.cshrc') gives now ('.cshrc', ''). Modified: python/trunk/Doc/lib/libposixpath.tex ============================================================================== --- python/trunk/Doc/lib/libposixpath.tex (original) +++ python/trunk/Doc/lib/libposixpath.tex Wed Mar 7 12:04:33 2007 @@ -234,7 +234,12 @@ 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} Modified: python/trunk/Lib/genericpath.py ============================================================================== --- python/trunk/Lib/genericpath.py (original) +++ python/trunk/Lib/genericpath.py Wed Mar 7 12:04:33 2007 @@ -75,3 +75,32 @@ 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, '' Modified: python/trunk/Lib/macpath.py ============================================================================== --- python/trunk/Lib/macpath.py (original) +++ python/trunk/Lib/macpath.py Wed Mar 7 12:04:33 2007 @@ -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 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 Modified: python/trunk/Lib/ntpath.py ============================================================================== --- python/trunk/Lib/ntpath.py (original) +++ python/trunk/Lib/ntpath.py Wed Mar 7 12:04:33 2007 @@ -8,6 +8,7 @@ import os import stat import sys +import genericpath from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", @@ -182,16 +183,8 @@ # 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. Modified: python/trunk/Lib/posixpath.py ============================================================================== --- python/trunk/Lib/posixpath.py (original) +++ python/trunk/Lib/posixpath.py Wed Mar 7 12:04:33 2007 @@ -12,6 +12,7 @@ import os import stat +import genericpath from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", @@ -88,14 +89,8 @@ # 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. Modified: python/trunk/Lib/test/test_macpath.py ============================================================================== --- python/trunk/Lib/test/test_macpath.py (original) +++ python/trunk/Lib/test/test_macpath.py Wed Mar 7 12:04:33 2007 @@ -48,7 +48,7 @@ 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(""), ('', '')) Modified: python/trunk/Lib/test/test_ntpath.py ============================================================================== --- python/trunk/Lib/test/test_ntpath.py (original) +++ python/trunk/Lib/test/test_ntpath.py Wed Mar 7 12:04:33 2007 @@ -18,13 +18,14 @@ 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')) Modified: python/trunk/Lib/test/test_posixpath.py ============================================================================== --- python/trunk/Lib/test/test_posixpath.py (original) +++ python/trunk/Lib/test/test_posixpath.py Wed Mar 7 12:04:33 2007 @@ -43,15 +43,27 @@ 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): Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Wed Mar 7 12:04:33 2007 @@ -152,6 +152,8 @@ Library ------- +- Bug #1115886: os.path.splitext('.cshrc') gives now ('.cshrc', ''). + - Patch #787789: allow to pass custom TestRunner instances to unittest's main() function. From buildbot at python.org Wed Mar 7 12:07:23 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 11:07:23 +0000 Subject: [Python-checkins] buildbot warnings in PPC64 Debian trunk Message-ID: <20070307110723.ED4421E4003@bag.python.org> The Buildbot has detected a new failure of PPC64 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/PPC64%2520Debian%2520trunk/builds/131 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 12:23:05 2007 From: python-checkins at python.org (eric.smith) Date: Wed, 7 Mar 2007 12:23:05 +0100 (CET) Subject: [Python-checkins] r54205 - sandbox/trunk/pep3101/test_simpleformat.py Message-ID: <20070307112305.703EF1E4003@bag.python.org> Author: eric.smith Date: Wed Mar 7 12:23:01 2007 New Revision: 54205 Modified: sandbox/trunk/pep3101/test_simpleformat.py Log: Added test cases for invalid types passed to formatters. Added test case for deriving from long and making sure correct __format__ is called. This is a future-proofness test in case __format__ gets implemented by long. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Wed Mar 7 12:23:01 2007 @@ -140,7 +140,7 @@ self.formatEqualsWithUnicode("'abcdefg", "{0:8r}", "abcdefg") def test_decimal_specifiers(self): - self.assertRaises(TypeError, "{0:d}", "non-number") + self.formatRaises(TypeError, "{0:d}", "non-number") self.formatEqualsWithUnicode("0", "{0:d}", 0) self.formatEqualsWithUnicode("0", "{0:d}", long(0)) @@ -182,7 +182,7 @@ def test_octal_specifiers(self): n = int("31415", 8) - self.assertRaises(TypeError, "{0:o", "non-number") + self.formatRaises(TypeError, "{0:o}", "non-number") self.formatEqualsWithUnicode("0", "{0:o}", 0) self.formatEqualsWithUnicode("31415", "{0:o}", n) @@ -225,16 +225,28 @@ #self.formatRaises(TypeError, "{0:c}", 3.14) def test_exponent_specifiers(self): + self.formatRaises(TypeError, "{0:e}", "a string") + self.formatRaises(TypeError, "{0:E}", "a string") + self.formatRaises(TypeError, "{0:e}", 1j) + self.formatRaises(TypeError, "{0:E}", 1j) self.formatEqualsWithUnicodeUC("3.141500e+00", "{0:e}", 3.1415) self.formatEqualsWithUnicodeUC("3.1415000000e+00", "{0:.10e}", 3.1415) def test_fixed_specifiers(self): + self.formatRaises(TypeError, "{0:f}", "a string") + self.formatRaises(TypeError, "{0:F}", "a string") + self.formatRaises(TypeError, "{0:f}", 1j) + self.formatRaises(TypeError, "{0:F}", 1j) self.formatEqualsWithUnicode("3.141500", "{0:f}", 3.1415) self.formatEqualsWithUnicode("3.1415000000", "{0:.10f}", 3.1415) self.formatEqualsWithUnicode("3.1415e+200", "{0:f}", 3.1415e200) self.formatEqualsWithUnicode("3.1415e+200", "{0:F}", 3.1415e200) def test_general_specifiers(self): + self.formatRaises(TypeError, "{0:g}", "a string") + self.formatRaises(TypeError, "{0:G}", "a string") + self.formatRaises(TypeError, "{0:g}", 1j) + self.formatRaises(TypeError, "{0:G}", 1j) self.formatEqualsWithUnicodeUC("3.1415", "{0:g}", 3.1415) self.formatEqualsWithUnicodeUC("3.1415", "{0:.10g}", 3.1415) self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200) @@ -311,6 +323,24 @@ self.formatRaises(ValueError, "{0:k}", 0) self.formatRaises(ValueError, "{0:4<10.20K}", 0) + def test_derive_from_long(self): + # make sure classes that inherit from long and don't specify + # __format__ get long's default format + class SimpleLong(long): + pass + + l = SimpleLong(2**100) + self.formatEquals("1" + "0" * 100, "{0:b}", l) + + # make sure that classes that inherit from long and do specify + # __format__ get it called + class FormatLong(long): + def __format__(self, specifiers): + return "how now brown cow" + + l = FormatLong(2**100) + self.formatEquals("how now brown cow", "{0:b}", l) + def test_name_mapper(self): mydict = dict(foo=1, bar=2) dict2 = mydict, dict(foobar=3) From python-checkins at python.org Wed Mar 7 12:37:43 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 12:37:43 +0100 (CET) Subject: [Python-checkins] r54206 - in python/trunk/Lib/test: output/test_pty test_pty.py Message-ID: <20070307113743.D2A211E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 12:37:42 2007 New Revision: 54206 Removed: python/trunk/Lib/test/output/test_pty Modified: python/trunk/Lib/test/test_pty.py Log: Patch #1675471: convert test_pty to unittest. Deleted: /python/trunk/Lib/test/output/test_pty ============================================================================== --- /python/trunk/Lib/test/output/test_pty Wed Mar 7 12:37:42 2007 +++ (empty file) @@ -1,3 +0,0 @@ -test_pty -I wish to buy a fish license. -For my pet fish, Eric. Modified: python/trunk/Lib/test/test_pty.py ============================================================================== --- python/trunk/Lib/test/test_pty.py (original) +++ python/trunk/Lib/test/test_pty.py Wed Mar 7 12:37:42 2007 @@ -1,5 +1,8 @@ -import pty, os, sys, signal -from test.test_support import verbose, TestFailed, TestSkipped +import pty +import os +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 +14,7 @@ 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 +36,122 @@ return data + # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. +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) + ##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_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) +def test_main(verbose=None): + run_unittest(PtyTest) -# pty.fork() passed. +if __name__ == "__main__": + test_main() From python-checkins at python.org Wed Mar 7 12:54:54 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 12:54:54 +0100 (CET) Subject: [Python-checkins] r54207 - in python/trunk/Lib: test/test_unittest.py unittest.py Message-ID: <20070307115454.BC2E51E4003@bag.python.org> Author: georg.brandl Date: Wed Mar 7 12:54:49 2007 New Revision: 54207 Modified: python/trunk/Lib/test/test_unittest.py python/trunk/Lib/unittest.py Log: Add some sanity checks to unittest.TestSuite's addTest(s) methods. Fixes #878275. Modified: python/trunk/Lib/test/test_unittest.py ============================================================================== --- python/trunk/Lib/test/test_unittest.py (original) +++ python/trunk/Lib/test/test_unittest.py Wed Mar 7 12:54:49 2007 @@ -1580,6 +1580,19 @@ 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): Modified: python/trunk/Lib/unittest.py ============================================================================== --- python/trunk/Lib/unittest.py (original) +++ python/trunk/Lib/unittest.py Wed Mar 7 12:54:49 2007 @@ -428,9 +428,18 @@ 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) From python-checkins at python.org Wed Mar 7 12:55:27 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 12:55:27 +0100 (CET) Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py Message-ID: <20070307115527.E88C41E400D@bag.python.org> Author: georg.brandl Date: Wed Mar 7 12:55:25 2007 New Revision: 54208 Modified: python/branches/release25-maint/Lib/unittest.py Log: backport rev. 54207: add a few sanity checks in unittest.TestSuite.addTest(s). Modified: python/branches/release25-maint/Lib/unittest.py ============================================================================== --- python/branches/release25-maint/Lib/unittest.py (original) +++ python/branches/release25-maint/Lib/unittest.py Wed Mar 7 12:55:25 2007 @@ -411,9 +411,18 @@ 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) From buildbot at python.org Wed Mar 7 13:26:10 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 12:26:10 +0000 Subject: [Python-checkins] buildbot warnings in sparc solaris10 gcc trunk Message-ID: <20070307122610.D25741E4003@bag.python.org> The Buildbot has detected a new failure of sparc solaris10 gcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/sparc%2520solaris10%2520gcc%2520trunk/builds/1821 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 2 tests failed: test_posixpath test_pty ====================================================================== FAIL: test_basic (test.test_pty.PtyTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/opt/users/buildbot/slave/trunk.loewis-sun/build/Lib/test/test_pty.py", line 76, in test_basic normalize_output(s1)) AssertionError: 'I wish to buy a fish license.\n' != '' sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 13:26:30 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 12:26:30 +0000 Subject: [Python-checkins] buildbot warnings in ia64 Ubuntu trunk trunk Message-ID: <20070307122631.0DF8B1E4003@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%2520Ubuntu%2520trunk%2520trunk/builds/438 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl,martin.v.loewis Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 13:41:17 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 12:41:17 +0000 Subject: [Python-checkins] buildbot warnings in x86 mvlgcc trunk Message-ID: <20070307124117.C3E901E4016@bag.python.org> The Buildbot has detected a new failure of x86 mvlgcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520mvlgcc%2520trunk/builds/310 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 13:58:10 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 12:58:10 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.5 Message-ID: <20070307125811.4A5A01E4003@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%25202.5/builds/192 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: 2 tests failed: test_signal test_socket sincerely, -The Buildbot From buildbot at python.org Wed Mar 7 14:12:20 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 13:12:20 +0000 Subject: [Python-checkins] buildbot warnings in hppa Ubuntu dapper trunk Message-ID: <20070307131220.BAFFD1E4003@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper trunk. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%2520trunk/builds/90 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 16:16:32 2007 From: python-checkins at python.org (guido.van.rossum) Date: Wed, 7 Mar 2007 16:16:32 +0100 (CET) Subject: [Python-checkins] r54209 - python/trunk/Lib/test/test_datetime.py Message-ID: <20070307151632.BE5351E400D@bag.python.org> Author: guido.van.rossum Date: Wed Mar 7 16:16:29 2007 New Revision: 54209 Modified: python/trunk/Lib/test/test_datetime.py Log: Windows doesn't support negative timestamps. Skip the tests involving them if os.name == "nt". Modified: python/trunk/Lib/test/test_datetime.py ============================================================================== --- python/trunk/Lib/test/test_datetime.py (original) +++ python/trunk/Lib/test/test_datetime.py Wed Mar 7 16:16:29 2007 @@ -3,6 +3,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +import os import sys import pickle import cPickle @@ -1426,11 +1427,17 @@ 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)) From buildbot at python.org Wed Mar 7 16:39:43 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 15:39:43 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20070307153943.D648F1E4003@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1987 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 16:46:37 2007 From: python-checkins at python.org (eric.smith) Date: Wed, 7 Mar 2007 16:46:37 +0100 (CET) Subject: [Python-checkins] r54210 - sandbox/trunk/pep3101/test_simpleformat.py Message-ID: <20070307154637.75ECF1E4003@bag.python.org> Author: eric.smith Date: Wed Mar 7 16:46:33 2007 New Revision: 54210 Modified: sandbox/trunk/pep3101/test_simpleformat.py Log: Added more extensive binary formatting (':b') tests. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Wed Mar 7 16:46:33 2007 @@ -276,6 +276,16 @@ self.formatEqualsWithUnicode("-" + "1" * 100, "{0:b}", -(2**100 - 1)) self.formatEqualsWithUnicode("(" + "1" * 100 + ")", "{0:()b}", -(2**100 - 1)) self.formatEqualsWithUnicode("(" + " " * 98 + "1" * 100 + ")", "{0:=()200b}", -(2**100 - 1)) + for n in range(1, 100): + # build an n bit integer, alternating ones and zeros + # as n gets large, these will become longs + s = "10" * (n / 2) + (n % 2 and "1" or "") + i = int(s, 2) + self.formatEquals(s, "{0:b}", i) + + # make sure negative also works, hoping to catch an + # overflow off the end of the digits + self.formatEquals("(" + s + ")", "{0:()b}", -i) def test_number_specifier(self): def test(value): From python-checkins at python.org Wed Mar 7 17:12:12 2007 From: python-checkins at python.org (georg.brandl) Date: Wed, 7 Mar 2007 17:12:12 +0100 (CET) Subject: [Python-checkins] r54211 - python/branches/release25-maint/Lib/test/test_datetime.py Message-ID: <20070307161212.BEE721E4004@bag.python.org> Author: georg.brandl Date: Wed Mar 7 17:12:05 2007 New Revision: 54211 Modified: python/branches/release25-maint/Lib/test/test_datetime.py Log: Backport skipping fromtimestamp(negative value) tests on Windows (from rev. 54209) Modified: python/branches/release25-maint/Lib/test/test_datetime.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_datetime.py (original) +++ python/branches/release25-maint/Lib/test/test_datetime.py Wed Mar 7 17:12:05 2007 @@ -3,6 +3,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +import os import sys import pickle import cPickle @@ -1426,11 +1427,17 @@ 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)) From buildbot at python.org Wed Mar 7 18:38:41 2007 From: buildbot at python.org (buildbot at python.org) Date: Wed, 07 Mar 2007 17:38:41 +0000 Subject: [Python-checkins] buildbot warnings in hppa Ubuntu dapper 2.5 Message-ID: <20070307173841.C2EC21E4004@bag.python.org> The Buildbot has detected a new failure of hppa Ubuntu dapper 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/hppa%2520Ubuntu%2520dapper%25202.5/builds/47 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: georg.brandl Build had warnings: warnings test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Wed Mar 7 23:07:13 2007 From: python-checkins at python.org (eric.smith) Date: Wed, 7 Mar 2007 23:07:13 +0100 (CET) Subject: [Python-checkins] r54212 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070307220713.583131E4014@bag.python.org> Author: eric.smith Date: Wed Mar 7 23:07:08 2007 New Revision: 54212 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Removed longintrepr.h. I no longer look at PyLong internals; instead everything works through functions exported by PyLong, although some begin with underscores like _PyLong_Sign(). The only code this affected is the base 2 output for longs in _format_long_binary(). Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Wed Mar 7 23:07:08 2007 @@ -20,9 +20,6 @@ #define C_UNICODE 1 #endif -/* we need access to a PyLongObject's internals */ -#include "longintrepr.h" - #if C_UNICODE #define CH_TYPE Py_UNICODE #define CH_TYPE_ISDECIMAL Py_UNICODE_ISDECIMAL @@ -1115,21 +1112,33 @@ return p_digits; } +/* call _PyLong_AsByteArray to get the bytes, then do the formatting. + note that for negative numbers, the byte array will be two's + complement, and we need to undo that. */ static int -_format_long_binary(PyObject *v, FmtState *fs, const InternalFormatSpec *format) -{ - /* we know that v is a PyLongObject */ +_format_long_binary(PyObject *v, FmtState *fs, const +InternalFormatSpec *format) { /* we know that v is a PyLongObject */ PyLongObject* l = (PyLongObject*)v; IntegerFieldWidths spec; CH_TYPE *pbuf; CH_TYPE *start; - char sign = _PyLong_Sign(v) >= 0 ? '\0' : '-'; + + size_t bytes_required; + int is_negative = _PyLong_Sign(v) < 0; + unsigned char* byte_buffer; + unsigned char* start_byte_buffer; + char sign = is_negative ? '-' : '\0'; Py_ssize_t n_digits = _PyLong_NumBits(v); - Py_ssize_t i; + int is_zero = n_digits == 0; + int carry_bit; - /* special case for zero */ - if (l->ob_size == 0) + /* check for error getting number of bits */ + if (n_digits < 0) + return 0; + + /* special case for zero. it takes one digit, despite having zero bits */ + if (is_zero) n_digits = 1; _calc_integer_widths(&spec, sign, n_digits, format); @@ -1143,24 +1152,71 @@ format->fill_char == '\0' ? ' ' : format->fill_char); /* degenerate case for zero. handle it and get out */ - if (l->ob_size == 0) { + if (is_zero) { *pbuf = '0'; return 1; } - /* finally, fill in the digits, starting at the right and working left */ + /* copy the bytes into the output buffer. we know they'll fit, + because we have enough space for a textual representation */ + + /* how many bytes this long will require. we allocate on extra + bit (by not subtracting 1 from the number of bits) because the + sign bit might be needed */ + bytes_required = n_digits / 8 + 1; + byte_buffer = (unsigned char*)start; /* where to put the bytes */ + start_byte_buffer = byte_buffer; + /* use little endian so we can start at the lsb and overwrite as we + go higher */ + if (_PyLong_AsByteArray(l, byte_buffer, bytes_required, 0, 1) < 0) + return 0; + + /* adjust byte_buffer to point to the end of the string of digits */ + byte_buffer += bytes_required - 1; + /* and adjust the output pointer to point to the end of the + CH_TYPE output buffer */ pbuf = start + n_digits - 1; - for (i = 0; i < ABS(l->ob_size); i++) { - Py_ssize_t j; - digit d = l->ob_digit[i]; - for (j = 0; j < SHIFT; j++, d >>= 1) { - if (d & 1) + carry_bit = 1; /* two's complement setup */ + + /* finally, fill in the digits, starting at the right and working + left */ + while (1) { + int j; + unsigned char byte = *byte_buffer; + + /* this test is just trying to make sure we don't go past the + beginning of the byte buffer. IIRC, it's illegal to + decrement a pointer past the beginning of what it points + to. not that it probably matters, but let's be as + conformant as possible */ + if (byte_buffer != start_byte_buffer) + byte_buffer--; + + /* two's complement algorithm */ + if (is_negative) { + byte = ~byte; + if (byte == 0xff && carry_bit) { + /* this is the only case where the carry bit will be + set */ + byte = 0; + carry_bit = 1; + } else { + /* we know adding won't overflow */ + byte += carry_bit; + carry_bit = 0; + } + } + + /* loop over each bit. this could be sped up with a table + lookup, but the mid-byte termination makes it complex */ + for (j = 0; j < 8; j++, byte >>= 1) { + if (byte & 1) *pbuf = '1'; else *pbuf = '0'; - /* see if we're done mid-digit */ + /* see if we're done mid-byte */ if (pbuf == start) goto DONE; pbuf--; From buildbot at python.org Thu Mar 8 01:32:00 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 00:32:00 +0000 Subject: [Python-checkins] buildbot warnings in MIPS Debian trunk Message-ID: <20070308003201.5930F1E4002@bag.python.org> The Buildbot has detected a new failure of MIPS Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/MIPS%2520Debian%2520trunk/builds/659 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: georg.brandl,guido.van.rossum,martin.v.loewis Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Thu Mar 8 04:03:01 2007 From: python-checkins at python.org (eric.smith) Date: Thu, 8 Mar 2007 04:03:01 +0100 (CET) Subject: [Python-checkins] r54217 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070308030301.6F6E81E4002@bag.python.org> Author: eric.smith Date: Thu Mar 8 04:02:55 2007 New Revision: 54217 Modified: sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c Log: Fixed a comment that got stepped on due to reformatting. Improved some other comments. Added test cases, primarily focusing on longs. Modified: sandbox/trunk/pep3101/test_simpleformat.py ============================================================================== --- sandbox/trunk/pep3101/test_simpleformat.py (original) +++ sandbox/trunk/pep3101/test_simpleformat.py Thu Mar 8 04:02:55 2007 @@ -171,6 +171,11 @@ # XXX I'm not sure this is correct, maybe it should be " (123)" self.formatEqualsWithUnicode("( 123)", "{0:=()10d}", -123) + self.formatEqualsWithUnicode(" (123)", "{0:>()10d}", -123) + self.formatEqualsWithUnicode("(123) ", "{0:<()10d}", -123) + self.formatEqualsWithUnicode("( 123)", "{0:=()10d}", long(-123)) + self.formatEqualsWithUnicode(" (123)", "{0:>()10d}", long(-123)) + self.formatEqualsWithUnicode("(123) ", "{0:<()10d}", long(-123)) self.formatEqualsWithUnicode("1" + "0" * 100, "{0:d}", 10**100) self.formatEqualsWithUnicode("-1" + "0" * 100, "{0:d}", -10**100) @@ -179,6 +184,11 @@ self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) self.formatEqualsWithUnicode("( 1" + "0" * 100 + ")", "{0:()110d}", -10**100) + for base in (0, 10**10, 10**100): + for i in range(base+1, base+2000): + self.formatEqualsWithUnicode(str(i), "{0:d}", i) + self.formatEqualsWithUnicode("(" + str(i) + ")", "{0:()d}", -i) + def test_octal_specifiers(self): n = int("31415", 8) @@ -199,17 +209,25 @@ self.formatRaises(TypeError, "{0:x}", "non-number") self.formatEqualsWithUnicodeUC("0", "{0:x}", 0) + self.formatEqualsWithUnicodeUC("0", "{0:x}", long(0)) self.formatEqualsWithUnicodeUC("beef", "{0:x}", n) self.formatEqualsWithUnicodeUC("-beef", "{0:x}", -n) n = int("deadbeef", 16) self.formatEqualsWithUnicodeUC("deadbeef", "{0:x}", n) self.formatEqualsWithUnicodeUC("-deadbeef", "{0:x}", -n) + self.formatEqualsWithUnicodeUC("(deadbeef)", "{0:()x}", -n) + self.formatEqualsWithUnicodeUC("( deadbeef)", "{0:()11x}", -n) + self.formatEqualsWithUnicodeUC(" (deadbeef)", "{0:>()11x}", -n) + self.formatEqualsWithUnicodeUC(" (deadbeef)", "{0:>()11x}", long(-n)) + self.formatEqualsWithUnicodeUC("(deadbeef) ", "{0:<()11x}", -n) + self.formatEqualsWithUnicodeUC("(deadbeef) ", "{0:<()11x}", long(-n)) def test_char_specifiers(self): self.formatEqualsWithUnicode("A", "{0:c}", "A") self.formatEqualsWithUnicode("8", "{0:c}", "8") self.formatEqualsWithUnicode(";", "{0:c}", ";") + self.formatEqualsWithUnicode(";", "{0:c}", ord(";")) self.formatEqualsWithUnicode(";", "{0:c}", long(ord(";"))) self.formatEquals(u"f", u"{0:c}", u"f") @@ -276,16 +294,19 @@ self.formatEqualsWithUnicode("-" + "1" * 100, "{0:b}", -(2**100 - 1)) self.formatEqualsWithUnicode("(" + "1" * 100 + ")", "{0:()b}", -(2**100 - 1)) self.formatEqualsWithUnicode("(" + " " * 98 + "1" * 100 + ")", "{0:=()200b}", -(2**100 - 1)) - for n in range(1, 100): + for n in range(1, 1000): # build an n bit integer, alternating ones and zeros # as n gets large, these will become longs s = "10" * (n / 2) + (n % 2 and "1" or "") i = int(s, 2) - self.formatEquals(s, "{0:b}", i) + self.formatEqualsWithUnicode(s, "{0:b}", i) # make sure negative also works, hoping to catch an - # overflow off the end of the digits - self.formatEquals("(" + s + ")", "{0:()b}", -i) + # overflow off the end of the digits. an internal + # implementation detail is that the parens are inserted + # into the output buffer before the digits are, so any + # error in writing the digits might write over the parens + self.formatEqualsWithUnicode("(" + s + ")", "{0:()b}", -i) def test_number_specifier(self): def test(value): Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 8 04:02:55 2007 @@ -67,8 +67,8 @@ /* MAXLEN_INT_STRING is the maximum length of an integer represented - * as a string. The analysis in stringobject.c shows that 24 is the - * worst case. Allocate more, just in case. */ + * as a string, in base 10. The analysis in stringobject.c shows that + * 24 is the worst case. Allocate more, just in case. */ /* fmt = '%#.' + `prec` + 'l' + `type` worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine) + 1 + 1 = 24 */ @@ -1117,7 +1117,9 @@ complement, and we need to undo that. */ static int _format_long_binary(PyObject *v, FmtState *fs, const -InternalFormatSpec *format) { /* we know that v is a PyLongObject */ + InternalFormatSpec *format) +{ + /* we know that v is a PyLongObject */ PyLongObject* l = (PyLongObject*)v; IntegerFieldWidths spec; @@ -1129,7 +1131,9 @@ unsigned char* byte_buffer; unsigned char* start_byte_buffer; char sign = is_negative ? '-' : '\0'; - Py_ssize_t n_digits = _PyLong_NumBits(v); + Py_ssize_t n_digits = _PyLong_NumBits(v); /* same number of + output digits as we + have binary bits */ int is_zero = n_digits == 0; int carry_bit; From nnorwitz at gmail.com Thu Mar 8 05:54:23 2007 From: nnorwitz at gmail.com (Neal Norwitz) Date: Wed, 7 Mar 2007 20:54:23 -0800 Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py In-Reply-To: <20070307115527.E88C41E400D@bag.python.org> References: <20070307115527.E88C41E400D@bag.python.org> Message-ID: I'm not sure it's the best thing to backport this. I agree with the change, but worry that people writing bad unit tests will suddenly have it break. They might have the proper signature, even if they don't inherit from the right classes. I guess the only part I'm worried about is the second if in addTest. The callable and basestring checks will just error out earlier which definitely seems reasonable. If you really feel strongly that this should go in, it should have a NEWS entry. n -- On 3/7/07, georg.brandl wrote: > Author: georg.brandl > Date: Wed Mar 7 12:55:25 2007 > New Revision: 54208 > > Modified: > python/branches/release25-maint/Lib/unittest.py > Log: > backport rev. 54207: add a few sanity checks in unittest.TestSuite.addTest(s). > > > Modified: python/branches/release25-maint/Lib/unittest.py > ============================================================================== > --- python/branches/release25-maint/Lib/unittest.py (original) > +++ python/branches/release25-maint/Lib/unittest.py Wed Mar 7 12:55:25 2007 > @@ -411,9 +411,18 @@ > 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) > > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > From python-checkins at python.org Thu Mar 8 06:03:49 2007 From: python-checkins at python.org (patrick.maupin) Date: Thu, 8 Mar 2007 06:03:49 +0100 (CET) Subject: [Python-checkins] r54218 - sandbox/trunk/pep3101/unicodeformat.c Message-ID: <20070308050349.2913B1E4004@bag.python.org> Author: patrick.maupin Date: Thu Mar 8 06:03:44 2007 New Revision: 54218 Modified: sandbox/trunk/pep3101/unicodeformat.c Log: Better exception handling will require the fmtstr object, so I merged the SubString and SubstrObj structures. (I guess that was a premature optimization :) Also added comment headers to functions that didn't have them. Modified: sandbox/trunk/pep3101/unicodeformat.c ============================================================================== --- sandbox/trunk/pep3101/unicodeformat.c (original) +++ sandbox/trunk/pep3101/unicodeformat.c Thu Mar 8 06:03:44 2007 @@ -89,22 +89,15 @@ #include "pep3101.h" /* - A SubString is a string between two unicode pointers. -*/ -typedef struct { - CH_TYPE *ptr; - CH_TYPE *end; -} SubString; - -/* - A SubStringObj is like a SubString, but also has an associated - object (which may be null). + A SubString consists of the characters between two string or + unicode pointers. It may also have an associated object, + or the object may be null. */ typedef struct { CH_TYPE *ptr; CH_TYPE *end; PyObject *obj; -} SubStringObj; +} SubString; /* A MarkupEscapeHandler allows us to handle different escape @@ -127,8 +120,9 @@ PyObject *args; /* keywords passed to PyString_FormatMethod or PyUnicode_FormatMethod */ PyObject *keywords; - /* arg_param_offset is 1 if not called as a method */ + /* Total number of positional arguments */ int num_args; + /* arg_param_offset is 1 if not called as a method */ int arg_param_offset; /* Function to call to perform markup */ MarkupEscapeHandler do_markup; @@ -137,12 +131,12 @@ /* Used for error reporting */ CH_TYPE *fmtstart, *fieldstart; /* Output string we are constructing, including current and end pointers*/ - SubStringObj outstr; + SubString outstr; /* Field Specifier, after the colon in {1:{2}} This may or may not have a valid object (the field specifier might just be a substring of the fmtstr. If it does not have its own object, the .obj struct member will be NULL */ - SubStringObj fieldspec; + SubString fieldspec; /* size_increment is used for optimizing string growth */ int size_increment; /* max_recursion is used to limit the ability of a malicious string @@ -166,6 +160,9 @@ int syntaxmode; /* Support for hooking render calls */ PyObject *hookfunc; + /* hookalways is true if we should call the hook function + on every single field, false to just call it on fields + with a specifier ending in 'p' */ int hookalways; } FmtState; @@ -277,6 +274,12 @@ return 1; } +/* + get_locals_globals builds a tuple out of our locals() + and globals() dictionaries. It only does this if we + have no positional arguments. (It will not be called + unless we have no keyword arguments.) +*/ static int get_locals_globals(FmtState *fs) { @@ -352,6 +355,11 @@ return SetError(fs, "name lookup failed"); } +/* + read_parameter reads a single parameter out of our "namespace". + + Returns object, or NULL on error. +*/ static PyObject * read_parameter(FmtState *fs, const char *keyword) { @@ -364,6 +372,11 @@ return result; } +/* + read_bool_parameter uses read_parameter to retrieve + a parameter and check it for "truth" Returns + 0 for false, 1 for true, -1 for error. +*/ static int read_bool_parameter(FmtState *fs, const char *keyword) { @@ -377,6 +390,11 @@ return result; } +/* + read_allow_under_parameter determines if leading underscores + are allowed, and saves the value so it doesn't need to + be called again later. +*/ static int read_allow_under_parameter(FmtState *fs) { @@ -385,6 +403,10 @@ return result; } +/* + read_hook_parameter retrieves the hook function, and saves the + object so it doesn't need to be called again later. +*/ static int read_hook_parameter(FmtState *fs) { @@ -398,11 +420,11 @@ /*********** Output string management functions ****************/ /************************************************************************/ -/* Fill in a SubStringObj from a Python string */ -Py_LOCAL_INLINE(SubStringObj) -make_substrobj(PyObject *obj) +/* Fill in a SubString from a Python string */ +Py_LOCAL_INLINE(SubString) +make_substr(PyObject *obj) { - SubStringObj s; + SubString s; s.obj = obj; s.ptr = STROBJ_AS_PTR(obj); s.end = STROBJ_GET_SIZE(obj) + s.ptr; @@ -657,7 +679,7 @@ myobject = recurse_format(fs); if (myobject == NULL) return 0; - fs->fieldspec = make_substrobj(myobject); + fs->fieldspec = make_substr(myobject); fs->fmtstr = savefmt; } return 1; @@ -1786,6 +1808,12 @@ return format_function(fieldobj, &format)(fieldobj, fs, &format); } +/* + get_field_spec_obj creates a string object for the fieldspec, + if necessary. (The field spec might already be an object, + or it might just be a substring of the format string, in + which case we need to copy it into a real string object.) +*/ static PyObject * get_field_spec_obj(FmtState *fs) { @@ -1799,8 +1827,8 @@ } /* - object_self_render is invoked to format an object with a defined __format__ - attribute. + object_self_render is invoked to format an object with a defined + __format__ attribute. */ static int object_self_render(FmtState *fs, PyObject *__format__) @@ -1899,6 +1927,11 @@ return result; } +/* + check_keyword compares a string against an expected + string, a character at a time, so that it works on + unicode vs. regular strings. +*/ static int check_keyword(const CH_TYPE *ptr, const char *expected, int count) { @@ -1908,6 +1941,9 @@ return 1; } +/* + process_metadata handles string metadata, e.g. "{!keyword}" +*/ static int process_metadata(FmtState *fs) { @@ -1983,8 +2019,8 @@ /************************************************************************/ /* - do_markup is the main program loop. It rummages through - the format string, looking for escapes to markup, and + do_markup is the top-level loop for the format() function. It + searches through the format string for escapes to markup codes, and calls other functions to move non-markup text to the output, and to perform the markup to the output. */ @@ -2095,7 +2131,7 @@ fs->fmtstr.end - fs->fmtstr.ptr + INITIAL_SIZE_INCREMENT); if (myobj == NULL) return NULL; - fs->outstr = make_substrobj(myobj); + fs->outstr = make_substr(myobj); fs->size_increment = INITIAL_SIZE_INCREMENT; ok = fs->do_markup(fs); @@ -2119,7 +2155,7 @@ recurse_format(FmtState *fs) { PyObject *result; - SubStringObj saveoutstr = fs->outstr; + SubString saveoutstr = fs->outstr; int saveincrement = fs->size_increment; if (--(fs->max_recursion) < 0) return SetError(fs, "Max string recursion exceeded"); @@ -2155,11 +2191,14 @@ fs->arg_param_offset = 1; fs->num_args -= 1; } - fs->fmtstr.ptr = fs->fmtstart = STROBJ_AS_PTR(self); - fs->fmtstr.end = fs->fmtstr.ptr + STROBJ_GET_SIZE(self); + fs->fmtstr = make_substr(self); return 1; } +/* + if {!useall} is set, check_args_consumed will be called to verify + that all arguments have been used during string processing. +*/ static PyObject * check_args_consumed(FmtState *fs, PyObject *result) { From g.brandl at gmx.net Thu Mar 8 08:47:54 2007 From: g.brandl at gmx.net (Georg Brandl) Date: Thu, 08 Mar 2007 08:47:54 +0100 Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py In-Reply-To: References: <20070307115527.E88C41E400D@bag.python.org> Message-ID: Neal Norwitz schrieb: > I'm not sure it's the best thing to backport this. I agree with the > change, but worry that people writing bad unit tests will suddenly > have it break. They might have the proper signature, even if they > don't inherit from the right classes. > > I guess the only part I'm worried about is the second if in addTest. > The callable and basestring checks will just error out earlier which > definitely seems reasonable. But this + if (isinstance(test, (type, types.ClassType)) and + issubclass(test, (TestCase, TestSuite))): + raise TypeError("TestCases and TestSuites must be instantiated " + "before passing them to addTest()") does allow for tests that don't inherit from TestCase/Suite. It only checks if the test is a TestCase, which is the common case, whether is instantiated. Georg From python-checkins at python.org Thu Mar 8 14:42:48 2007 From: python-checkins at python.org (martin.v.loewis) Date: Thu, 8 Mar 2007 14:42:48 +0100 (CET) Subject: [Python-checkins] r54219 - python/trunk/Doc/lib/liburlparse.tex Message-ID: <20070308134248.BBD2B1E4002@bag.python.org> Author: martin.v.loewis Date: Thu Mar 8 14:42:43 2007 New Revision: 54219 Modified: python/trunk/Doc/lib/liburlparse.tex Log: Add missing ) in parenthical remark. Modified: python/trunk/Doc/lib/liburlparse.tex ============================================================================== --- python/trunk/Doc/lib/liburlparse.tex (original) +++ python/trunk/Doc/lib/liburlparse.tex Thu Mar 8 14:42:43 2007 @@ -157,7 +157,7 @@ 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} From jimjjewett at gmail.com Thu Mar 8 18:35:04 2007 From: jimjjewett at gmail.com (Jim Jewett) Date: Thu, 8 Mar 2007 12:35:04 -0500 Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py In-Reply-To: References: <20070307115527.E88C41E400D@bag.python.org> Message-ID: On 3/8/07, Georg Brandl wrote: > Neal Norwitz schrieb: > > I'm not sure it's the best thing to backport this. I agree with the > > change, but worry that people writing bad unit tests will suddenly > > have it break. They might have the proper signature, even if they > > don't inherit from the right classes. > + if (isinstance(test, (type, types.ClassType)) and > + issubclass(test, (TestCase, TestSuite))): > + raise TypeError("TestCases and TestSuites must be instantiated > " > + "before passing them to addTest()") > does allow for tests that don't inherit from TestCase/Suite. It only checks > if the test is a TestCase, which is the common case, whether is > instantiated. When trying to emulate prototype-based OO (like javascript), it is common to have classes that are intended to be singletons; sometimes it isn't clear what is a class method and what is an instance method of the single instance. It is possible to create a class that itself has the right signature. I make no claims about sanity, but it is possible, so it is a concern in bugfix releases. Of course, it is also possible to create your own test runner, which might not even insist on callability. That said, it may well be that no one has done this, and that someone is going along with untested code because the checks aren't there ... so I'm not sure what to do. A what's new entry might be enough. Something along the line of: """ unittest now verifies more of its assumptions. If your tests start to fail with 2.5.1, it *might* be a problem with the tests themselves -- and you should verify that they were ever run. """ -jJ From python-checkins at python.org Thu Mar 8 18:49:10 2007 From: python-checkins at python.org (georg.brandl) Date: Thu, 8 Mar 2007 18:49:10 +0100 (CET) Subject: [Python-checkins] r54220 - python/trunk/Doc/lib/liburlparse.tex Message-ID: <20070308174910.062611E4002@bag.python.org> Author: georg.brandl Date: Thu Mar 8 18:49:06 2007 New Revision: 54220 Modified: python/trunk/Doc/lib/liburlparse.tex Log: Fix #1676656: \em is different from \emph... Modified: python/trunk/Doc/lib/liburlparse.tex ============================================================================== --- python/trunk/Doc/lib/liburlparse.tex (original) +++ python/trunk/Doc/lib/liburlparse.tex Thu Mar 8 18:49:06 2007 @@ -168,7 +168,7 @@ If you do not want that behavior, preprocess the \var{url} with \function{urlsplit()} and \function{urlunsplit()}, -removing possible \em{scheme} and \em{netloc} parts. +removing possible \emph{scheme} and \emph{netloc} parts. \end{funcdesc} \begin{funcdesc}{urldefrag}{url} From buildbot at python.org Thu Mar 8 18:49:10 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 17:49:10 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 2.5 Message-ID: <20070308174910.350361E4004@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%25202.5/builds/251 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Jaren': Ethan Build Source Stamp: [branch Jonatan] Curtis Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Thu Mar 8 18:49:19 2007 From: python-checkins at python.org (georg.brandl) Date: Thu, 8 Mar 2007 18:49:19 +0100 (CET) Subject: [Python-checkins] r54221 - python/branches/release25-maint/Doc/lib/liburlparse.tex Message-ID: <20070308174919.0957C1E4002@bag.python.org> Author: georg.brandl Date: Thu Mar 8 18:49:17 2007 New Revision: 54221 Modified: python/branches/release25-maint/Doc/lib/liburlparse.tex Log: Fix #1676656: \em is different from \emph... (backport from rev. 54220) Modified: python/branches/release25-maint/Doc/lib/liburlparse.tex ============================================================================== --- python/branches/release25-maint/Doc/lib/liburlparse.tex (original) +++ python/branches/release25-maint/Doc/lib/liburlparse.tex Thu Mar 8 18:49:17 2007 @@ -168,7 +168,7 @@ If you do not want that behavior, preprocess the \var{url} with \function{urlsplit()} and \function{urlunsplit()}, -removing possible \em{scheme} and \em{netloc} parts. +removing possible \emph{scheme} and \emph{netloc} parts. \end{funcdesc} \begin{funcdesc}{urldefrag}{url} From buildbot at python.org Thu Mar 8 18:55:54 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 17:55:54 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo 2.5 Message-ID: <20070308175554.806591E4005@bag.python.org> The Buildbot has detected a new failure of x86 gentoo 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%25202.5/builds/265 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: The web-page 'force build' button was pressed by 'Kendrick': Isidro Build Source Stamp: [branch Javen] Ethen Blamelist: BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Thu Mar 8 19:37:31 2007 From: python-checkins at python.org (georg.brandl) Date: Thu, 8 Mar 2007 19:37:31 +0100 (CET) Subject: [Python-checkins] r54222 - python/trunk/Misc/NEWS Message-ID: <20070308183731.EAE891E4002@bag.python.org> Author: georg.brandl Date: Thu Mar 8 19:37:31 2007 New Revision: 54222 Modified: python/trunk/Misc/NEWS Log: Add a NEWS entry for rev. 54207,8. Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Thu Mar 8 19:37:31 2007 @@ -154,6 +154,12 @@ - Bug #1115886: os.path.splitext('.cshrc') gives now ('.cshrc', ''). +- unittest now verifies more of its assumptions. In particular, TestCase + and TestSuite subclasses (not instances) are no longer accepted in + TestSuite.addTest(). This should cause no incompatibility since it + never made sense with ordinary subclasses -- the failure just occurred + later, with a more cumbersome exception. + - Patch #787789: allow to pass custom TestRunner instances to unittest's main() function. From python-checkins at python.org Thu Mar 8 19:37:36 2007 From: python-checkins at python.org (georg.brandl) Date: Thu, 8 Mar 2007 19:37:36 +0100 (CET) Subject: [Python-checkins] r54223 - python/branches/release25-maint/Misc/NEWS Message-ID: <20070308183736.52E961E4002@bag.python.org> Author: georg.brandl Date: Thu Mar 8 19:37:35 2007 New Revision: 54223 Modified: python/branches/release25-maint/Misc/NEWS Log: Add a NEWS entry for rev. 54207,8. (backport from rev. 54222) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Thu Mar 8 19:37:35 2007 @@ -197,6 +197,12 @@ Library ------- +- unittest now verifies more of its assumptions. In particular, TestCase + and TestSuite subclasses (not instances) are no longer accepted in + TestSuite.addTest(). This should cause no incompatibility since it + never made sense with ordinary subclasses -- the failure just occurred + later, with a more cumbersome exception. + - Patch #1001604: glob.glob() now returns unicode filenames if it was given a unicode argument and os.listdir() returns unicode filenames. From g.brandl at gmx.net Thu Mar 8 19:37:31 2007 From: g.brandl at gmx.net (Georg Brandl) Date: Thu, 08 Mar 2007 19:37:31 +0100 Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py In-Reply-To: References: <20070307115527.E88C41E400D@bag.python.org> Message-ID: Jim Jewett schrieb: > On 3/8/07, Georg Brandl wrote: >> Neal Norwitz schrieb: >> > I'm not sure it's the best thing to backport this. I agree with the >> > change, but worry that people writing bad unit tests will suddenly >> > have it break. They might have the proper signature, even if they >> > don't inherit from the right classes. > >> + if (isinstance(test, (type, types.ClassType)) and >> + issubclass(test, (TestCase, TestSuite))): >> + raise TypeError("TestCases and TestSuites must be instantiated >> " >> + "before passing them to addTest()") > >> does allow for tests that don't inherit from TestCase/Suite. It only checks >> if the test is a TestCase, which is the common case, whether is >> instantiated. > > When trying to emulate prototype-based OO (like javascript), it is > common to have classes that are intended to be singletons; sometimes > it isn't clear what is a class method and what is an instance method > of the single instance. > > It is possible to create a class that itself has the right signature. > I make no claims about sanity, but it is possible, so it is a concern > in bugfix releases. > > Of course, it is also possible to create your own test runner, which > might not even insist on callability. > > That said, it may well be that no one has done this, and that someone > is going along with untested code because the checks aren't there ... > so I'm not sure what to do. A what's new entry might be enough. > Something along the line of: > > """ > unittest now verifies more of its assumptions. If your tests start to > fail with 2.5.1, it *might* be a problem with the tests themselves -- > and you should verify that they were ever run. > """ Agreed. I added a NEWS item. Georg From nnorwitz at gmail.com Thu Mar 8 19:39:59 2007 From: nnorwitz at gmail.com (Neal Norwitz) Date: Thu, 8 Mar 2007 10:39:59 -0800 Subject: [Python-checkins] r54208 - python/branches/release25-maint/Lib/unittest.py In-Reply-To: References: <20070307115527.E88C41E400D@bag.python.org> Message-ID: On 3/8/07, Georg Brandl wrote: > > Agreed. I added a NEWS item. Thanks! That's good enough for me. n From python-checkins at python.org Thu Mar 8 20:23:26 2007 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 8 Mar 2007 20:23:26 +0100 (CET) Subject: [Python-checkins] r54224 - in python/branches/release25-maint: Lib/sched.py Misc/NEWS Message-ID: <20070308192326.C83401E4002@bag.python.org> Author: raymond.hettinger Date: Thu Mar 8 20:23:25 2007 New Revision: 54224 Modified: python/branches/release25-maint/Lib/sched.py python/branches/release25-maint/Misc/NEWS Log: SF 1676321: empty() returned wrong result Modified: python/branches/release25-maint/Lib/sched.py ============================================================================== --- python/branches/release25-maint/Lib/sched.py (original) +++ python/branches/release25-maint/Lib/sched.py Thu Mar 8 20:23:25 2007 @@ -72,7 +72,7 @@ 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. Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Thu Mar 8 20:23:25 2007 @@ -197,6 +197,8 @@ Library ------- +- Bugs #1676321: the empty() function in sched.py returned the wrong result + - unittest now verifies more of its assumptions. In particular, TestCase and TestSuite subclasses (not instances) are no longer accepted in TestSuite.addTest(). This should cause no incompatibility since it From python-checkins at python.org Thu Mar 8 20:24:27 2007 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 8 Mar 2007 20:24:27 +0100 (CET) Subject: [Python-checkins] r54225 - python/trunk/Lib/sched.py Message-ID: <20070308192427.CC7051E4002@bag.python.org> Author: raymond.hettinger Date: Thu Mar 8 20:24:27 2007 New Revision: 54225 Modified: python/trunk/Lib/sched.py Log: SF 1676321: empty() returned wrong result Modified: python/trunk/Lib/sched.py ============================================================================== --- python/trunk/Lib/sched.py (original) +++ python/trunk/Lib/sched.py Thu Mar 8 20:24:27 2007 @@ -72,7 +72,7 @@ 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. From python-checkins at python.org Thu Mar 8 20:58:15 2007 From: python-checkins at python.org (collin.winter) Date: Thu, 8 Mar 2007 20:58:15 +0100 (CET) Subject: [Python-checkins] r54227 - python/trunk/Lib/test/regrtest.py Message-ID: <20070308195815.B5EBD1E4002@bag.python.org> Author: collin.winter Date: Thu Mar 8 20:58:14 2007 New Revision: 54227 Modified: python/trunk/Lib/test/regrtest.py Log: Backported r54226 from p3yk: Move test_unittest, test_doctest and test_doctest2 higher up in the testing order. Modified: python/trunk/Lib/test/regrtest.py ============================================================================== --- python/trunk/Lib/test/regrtest.py (original) +++ python/trunk/Lib/test/regrtest.py Thu Mar 8 20:58:14 2007 @@ -474,6 +474,9 @@ 'test_builtin', 'test_exceptions', 'test_types', + 'test_unittest', + 'test_doctest', + 'test_doctest2', ] NOTTESTS = [ From python-checkins at python.org Thu Mar 8 20:58:48 2007 From: python-checkins at python.org (collin.winter) Date: Thu, 8 Mar 2007 20:58:48 +0100 (CET) Subject: [Python-checkins] r54228 - python/branches/release25-maint/Lib/test/regrtest.py Message-ID: <20070308195848.9CE3C1E4002@bag.python.org> Author: collin.winter Date: Thu Mar 8 20:58:46 2007 New Revision: 54228 Modified: python/branches/release25-maint/Lib/test/regrtest.py Log: Backported r54226 from p3yk: Move test_unittest, test_doctest and test_doctest2 higher up in the testing order. Modified: python/branches/release25-maint/Lib/test/regrtest.py ============================================================================== --- python/branches/release25-maint/Lib/test/regrtest.py (original) +++ python/branches/release25-maint/Lib/test/regrtest.py Thu Mar 8 20:58:46 2007 @@ -474,6 +474,9 @@ 'test_builtin', 'test_exceptions', 'test_types', + 'test_unittest', + 'test_doctest', + 'test_doctest2', ] NOTTESTS = [ From python-checkins at python.org Thu Mar 8 22:30:58 2007 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 8 Mar 2007 22:30:58 +0100 (CET) Subject: [Python-checkins] r54229 - in python/branches/release25-maint: Lib/difflib.py Misc/NEWS Message-ID: <20070308213058.9FE7B1E401C@bag.python.org> Author: raymond.hettinger Date: Thu Mar 8 22:30:55 2007 New Revision: 54229 Modified: python/branches/release25-maint/Lib/difflib.py python/branches/release25-maint/Misc/NEWS Log: SF #1637850: make_table in difflib did not work with unicode Modified: python/branches/release25-maint/Lib/difflib.py ============================================================================== --- python/branches/release25-maint/Lib/difflib.py (original) +++ python/branches/release25-maint/Lib/difflib.py Thu Mar 8 22:30:55 2007 @@ -1945,8 +1945,7 @@ fromlist,tolist,flaglist,next_href,next_id = self._convert_flags( fromlist,tolist,flaglist,context,numlines) - import cStringIO - s = cStringIO.StringIO() + s = [] fmt = ' %s%s' + \ '%s%s\n' for i in range(len(flaglist)): @@ -1954,9 +1953,9 @@ # mdiff yields None on separator lines skip the bogus ones # generated for the first line if i > 0: - s.write(' \n \n') + s.append(' \n \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 = '%s%s%s%s' % ( @@ -1968,7 +1967,7 @@ header_row = '' table = self._table_template % dict( - data_rows=s.getvalue(), + data_rows=''.join(s), header_row=header_row, prefix=self._prefix[1]) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Thu Mar 8 22:30:55 2007 @@ -197,6 +197,8 @@ Library ------- +- Bug #1637850: make_table in difflib did not work with unicode + - Bugs #1676321: the empty() function in sched.py returned the wrong result - unittest now verifies more of its assumptions. In particular, TestCase From python-checkins at python.org Thu Mar 8 22:33:50 2007 From: python-checkins at python.org (raymond.hettinger) Date: Thu, 8 Mar 2007 22:33:50 +0100 (CET) Subject: [Python-checkins] r54230 - python/trunk/Lib/difflib.py Message-ID: <20070308213350.2F8491E4011@bag.python.org> Author: raymond.hettinger Date: Thu Mar 8 22:33:47 2007 New Revision: 54230 Modified: python/trunk/Lib/difflib.py Log: SF #1637850: make_table in difflib did not work with unicode Modified: python/trunk/Lib/difflib.py ============================================================================== --- python/trunk/Lib/difflib.py (original) +++ python/trunk/Lib/difflib.py Thu Mar 8 22:33:47 2007 @@ -1945,8 +1945,7 @@ fromlist,tolist,flaglist,next_href,next_id = self._convert_flags( fromlist,tolist,flaglist,context,numlines) - import cStringIO - s = cStringIO.StringIO() + s = [] fmt = ' %s%s' + \ '%s%s\n' for i in range(len(flaglist)): @@ -1954,9 +1953,9 @@ # mdiff yields None on separator lines skip the bogus ones # generated for the first line if i > 0: - s.write(' \n \n') + s.append(' \n \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 = '%s%s%s%s' % ( @@ -1968,7 +1967,7 @@ header_row = '' table = self._table_template % dict( - data_rows=s.getvalue(), + data_rows=''.join(s), header_row=header_row, prefix=self._prefix[1]) From buildbot at python.org Thu Mar 8 22:48:04 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 21:48:04 +0000 Subject: [Python-checkins] buildbot warnings in alpha Tru64 5.1 2.5 Message-ID: <20070308214804.AB1CD1E4002@bag.python.org> The Buildbot has detected a new failure of alpha Tru64 5.1 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/alpha%2520Tru64%25205.1%25202.5/builds/195 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: collin.winter Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket ====================================================================== FAIL: testInterruptedTimeout (test.test_socket.TCPTimeoutTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/net/ringneck/scratch1/nnorwitz/python/2.5.norwitz-tru64/build/Lib/test/test_socket.py", line 872, in testInterruptedTimeout self.fail("got Alarm in wrong place") AssertionError: got Alarm in wrong place sincerely, -The Buildbot From buildbot at python.org Thu Mar 8 23:11:12 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 22:11:12 +0000 Subject: [Python-checkins] buildbot warnings in g4 osx.4 trunk Message-ID: <20070308221112.944C51E4002@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%2520osx.4%2520trunk/builds/1806 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: collin.winter Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Thu Mar 8 23:14:31 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 22:14:31 +0000 Subject: [Python-checkins] buildbot warnings in x86 mvlgcc trunk Message-ID: <20070308221431.3932B1E4002@bag.python.org> The Buildbot has detected a new failure of x86 mvlgcc trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520mvlgcc%2520trunk/builds/314 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: raymond.hettinger Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_posixpath make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Thu Mar 8 23:15:43 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 22:15:43 +0000 Subject: [Python-checkins] buildbot warnings in S-390 Debian 2.5 Message-ID: <20070308221544.160851E4011@bag.python.org> The Buildbot has detected a new failure of S-390 Debian 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%2520Debian%25202.5/builds/203 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: collin.winter Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 557, in runtest_inner indirect_test() File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/httplib.py", line 856, in endheaders self._send_output() File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/httplib.py", line 728, in _send_output self.send(msg) File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/httplib.py", line 695, in send self.connect() File "/home/pybot/buildarea/2.5.klose-debian-s390/build/Lib/httplib.py", line 1130, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Thu Mar 8 23:16:28 2007 From: python-checkins at python.org (collin.winter) Date: Thu, 8 Mar 2007 23:16:28 +0100 (CET) Subject: [Python-checkins] r54232 - python/trunk/Lib/tempfile.py Message-ID: <20070308221628.711B51E4015@bag.python.org> Author: collin.winter Date: Thu Mar 8 23:16:25 2007 New Revision: 54232 Modified: python/trunk/Lib/tempfile.py Log: Patch #1668482: don't use '-' in mkstemp Modified: python/trunk/Lib/tempfile.py ============================================================================== --- python/trunk/Lib/tempfile.py (original) +++ python/trunk/Lib/tempfile.py Thu Mar 8 23:16:25 2007 @@ -114,7 +114,7 @@ characters = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "0123456789-_") + "0123456789_") def __init__(self): self.mutex = _allocate_lock() From buildbot at python.org Thu Mar 8 23:43:17 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 22:43:17 +0000 Subject: [Python-checkins] buildbot warnings in ppc Debian unstable trunk Message-ID: <20070308224317.D99681E4002@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%2520Debian%2520unstable%2520trunk/builds/140 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: raymond.hettinger Build had warnings: warnings test Excerpt from the test logfile: 2 tests failed: test_posixpath test_socket_ssl make: *** [buildbottest] Error 1 sincerely, -The Buildbot From nnorwitz at gmail.com Thu Mar 8 23:44:37 2007 From: nnorwitz at gmail.com (Neal Norwitz) Date: Thu, 8 Mar 2007 17:44:37 -0500 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20070308224437.GA4446@python.psfb.org> test_grammar test_opcodes test_operations test_builtin test_exceptions test_types test_unittest test_doctest test_doctest2 test_MimeWriter test_StringIO test___all__ test___future__ test__locale test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat WARNING: failed to listen on port 54322, trying another WARNING: failed to listen on port 9907, trying another test_atexit test_audioop test_augassign test_base64 test_bastion test_bigaddrspace test_bigmem test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 test_bufio test_bz2 test_cProfile test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd_line test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_collections test_colorsys test_commands test_compare test_compile test_compiler testCompileLibrary still working, be patient... test_complex test_complex_args test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_difflib test_dircache test_dis test_distutils test_dl test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_float test_fnmatch test_fork1 test_format test_fpformat test_frozen test_funcattrs test_functools test_future test_gc test_gdbm test_generators test_genericpath test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hexoct test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_imageop test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_largefile test_list test_locale test_logging test_long test_long_future test_longexp test_macfs test_macfs skipped -- No module named macfs test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_modulefinder test_multibytecodec test_multibytecodec_support test_multifile test_mutants test_netrc test_new test_nis test_normalization test_ntpath test_old_mailbox test_openpty test_operator test_optparse test_os test_parser test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pkg test_pkgimport test_platform test_plistlib test_plistlib skipped -- No module named plistlib test_poll test_popen [7321 refs] [7321 refs] [7321 refs] test_popen2 test_posix test_posixpath test_pow test_pprint test_profile test_profilehooks test_pty test_pwd test_pyclbr test_pyexpat test_queue test_quopri [7696 refs] [7696 refs] test_random test_re test_repr test_resource test_rfc822 test_rgbimg test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site test_slice test_socket test_socket_ssl test test_socket_ssl crashed -- : [Errno socket error] (110, 'Connection timed out') test_socketserver test_softspace test_sort test_sqlite test_startfile test_startfile skipped -- cannot import name startfile test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structmembers test_structseq test_subprocess [7316 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7317 refs] [8865 refs] [7532 refs] [7317 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] [7316 refs] . [7316 refs] [7316 refs] this bit of output is from a test of stdout in a different process ... [7316 refs] [7316 refs] [7532 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [7316 refs] [7316 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_tempfile [7323 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_ucn test_unary test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_uuid WARNING: uuid.getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._ifconfig_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._unixdll_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_wsgiref test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipfile64 test_zipfile64 skipped -- test requires loads of disk-space bytes and a long time to run test_zipimport test_zlib 299 tests OK. 1 test failed: test_socket_ssl 21 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_gl test_imgfile test_ioctl test_macfs test_macostools test_pep277 test_plistlib test_scriptpackages test_startfile test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound test_zipfile64 1 skip unexpected on linux2: test_ioctl [473993 refs] From buildbot at python.org Thu Mar 8 23:51:06 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 22:51:06 +0000 Subject: [Python-checkins] buildbot warnings in x86 gentoo trunk Message-ID: <20070308225106.273E91E4002@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520gentoo%2520trunk/builds/1991 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: collin.winter Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl Traceback (most recent call last): File "./Lib/test/regrtest.py", line 557, in runtest_inner indirect_test() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 124, in test_main test_basic() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_socket_ssl.py", line 30, in test_basic f = urllib.urlopen('https://sf.net') File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 82, in urlopen return opener.open(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 190, in open return getattr(self, name)(url) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/urllib.py", line 412, in open_https h.endheaders() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 864, in endheaders self._send_output() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 736, in _send_output self.send(msg) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 695, in send self.connect() File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/httplib.py", line 1152, in connect sock.connect((self.host, self.port)) File "", line 1, in connect IOError: [Errno socket error] (110, 'Connection timed out') make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Mar 9 00:07:24 2007 From: buildbot at python.org (buildbot at python.org) Date: Thu, 08 Mar 2007 23:07:24 +0000 Subject: [Python-checkins] buildbot warnings in x86 XP 2.5 Message-ID: <20070308230725.241071E4002@bag.python.org> The Buildbot has detected a new failure of x86 XP 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%2520XP%25202.5/builds/151 Buildbot URL: http://www.python.org/dev/buildbot/all/ Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: raymond.hettinger Build had warnings: warnings test Excerpt from the test logfile: 1 test failed: test_socket_ssl sincerely, -The Buildbot From python-checkins at python.org Fri Mar 9 00:58:19 2007 From: python-checkins at python.org (brett.cannon) Date: Fri, 9 Mar 2007 00:58:19 +0100 (CET) Subject: [Python-checkins] r54233 - in python/trunk: Doc/lib/libtest.tex Lib/test/test_socket_ssl.py Lib/test/test_support.py Misc/NEWS Message-ID: <20070308235819.E7BD11E4002@bag.python.org> Author: brett.cannon Date: Fri Mar 9 00:58:11 2007 New Revision: 54233 Modified: python/trunk/Doc/lib/libtest.tex python/trunk/Lib/test/test_socket_ssl.py python/trunk/Lib/test/test_support.py python/trunk/Misc/NEWS Log: Introduce test.test_support.TransientResource. It's a context manager to surround calls to resources that may or may not be available. Specifying the expected exception and attributes to be raised if the resource is not available prevents overly broad catching of exceptions. This is meant to help suppress spurious failures by raising test.test_support.ResourceDenied if the exception matches. It would probably be good to go through the various network tests and surround the calls to catch connection timeouts (as done with test_socket_ssl in this commit). Modified: python/trunk/Doc/lib/libtest.tex ============================================================================== --- python/trunk/Doc/lib/libtest.tex (original) +++ python/trunk/Doc/lib/libtest.tex Fri Mar 9 00:58:11 2007 @@ -285,6 +285,14 @@ 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. Modified: python/trunk/Lib/test/test_socket_ssl.py ============================================================================== --- python/trunk/Lib/test/test_socket_ssl.py (original) +++ python/trunk/Lib/test/test_socket_ssl.py Fri Mar 9 00:58:11 2007 @@ -27,7 +27,8 @@ print "didn't raise TypeError" socket.RAND_add("this is a random string", 75.0) - f = urllib.urlopen('https://sf.net') + with test_support.TransientResource(IOError, errno=errno.ETIMEDOUT): + f = urllib.urlopen('https://sf.net') buf = f.read() f.close() Modified: python/trunk/Lib/test/test_support.py ============================================================================== --- python/trunk/Lib/test/test_support.py (original) +++ python/trunk/Lib/test/test_support.py Fri Mar 9 00:58:11 2007 @@ -312,6 +312,31 @@ 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.iteritems(): + if not hasattr(value, attr): + break + if getattr(value, attr) != attr_value: + break + else: + raise ResourceDenied("an optional resource is not available") + #======