From perky at users.sourceforge.net Mon Aug 1 07:26:46 2005 From: perky at users.sourceforge.net (perky@users.sourceforge.net) Date: Sun, 31 Jul 2005 22:26:46 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.36,1.37 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29494/Objects Modified Files: setobject.c Log Message: Fix build on gcc: PySetIter_Type should be static in definition part also. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.36 retrieving revision 1.37 diff -u -d -r1.36 -r1.37 --- setobject.c 31 Jul 2005 15:36:06 -0000 1.36 +++ setobject.c 1 Aug 2005 05:26:41 -0000 1.37 @@ -602,7 +602,7 @@ return NULL; } -PyTypeObject PySetIter_Type = { +static PyTypeObject PySetIter_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "setiterator", /* tp_name */ From rhettinger at users.sourceforge.net Mon Aug 1 23:39:30 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 14:39:30 -0700 Subject: [Python-checkins] python/dist/src/Lib/test test_set.py,1.18,1.19 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3433/Lib/test Modified Files: test_set.py Log Message: * Improve code for the empty frozenset singleton: - Handle both frozenset() and frozenset([]). - Do not use singleton for frozenset subclasses. - Finalize the singleton. - Add test cases. * Factor-out set_update_internal() from set_update(). Simplifies the code for several internal callers. * Factor constant expressions out of loop in set_merge_internal(). * Minor comment touch-ups. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- test_set.py 5 Jul 2005 05:34:43 -0000 1.18 +++ test_set.py 1 Aug 2005 21:39:28 -0000 1.19 @@ -391,6 +391,15 @@ s.__init__(self.otherword) self.assertEqual(s, set(self.word)) + def test_singleton_empty_frozenset(self): + f = frozenset() + efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''), + frozenset(), frozenset([]), frozenset(()), frozenset(''), + frozenset(xrange(0)), frozenset(frozenset()), + frozenset(f), f] + # All of the empty frozensets should have just one id() + self.assertEqual(len(set(map(id, efs))), 1) + def test_constructor_identity(self): s = self.thetype(range(3)) t = self.thetype(s) @@ -456,6 +465,17 @@ t = self.thetype(s) self.assertEqual(s, t) + def test_singleton_empty_frozenset(self): + Frozenset = self.thetype + f = frozenset() + F = Frozenset() + efs = [Frozenset(), Frozenset([]), Frozenset(()), Frozenset(''), + Frozenset(), Frozenset([]), Frozenset(()), Frozenset(''), + Frozenset(xrange(0)), Frozenset(Frozenset()), + Frozenset(frozenset()), f, F, Frozenset(f), Frozenset(F)] + # All empty frozenset subclass instances should have different ids + self.assertEqual(len(set(map(id, efs))), len(efs)) + # Tests taken from test_sets.py ============================================= empty_set = set() From rhettinger at users.sourceforge.net Mon Aug 1 23:39:30 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 14:39:30 -0700 Subject: [Python-checkins] python/dist/src/Include pythonrun.h, 2.66, 2.67 setobject.h, 2.6, 2.7 Message-ID: Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3433/Include Modified Files: pythonrun.h setobject.h Log Message: * Improve code for the empty frozenset singleton: - Handle both frozenset() and frozenset([]). - Do not use singleton for frozenset subclasses. - Finalize the singleton. - Add test cases. * Factor-out set_update_internal() from set_update(). Simplifies the code for several internal callers. * Factor constant expressions out of loop in set_merge_internal(). * Minor comment touch-ups. Index: pythonrun.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/pythonrun.h,v retrieving revision 2.66 retrieving revision 2.67 diff -u -d -r2.66 -r2.67 --- pythonrun.h 27 May 2005 15:23:13 -0000 2.66 +++ pythonrun.h 1 Aug 2005 21:39:27 -0000 2.67 @@ -115,6 +115,7 @@ PyAPI_FUNC(void) PyCFunction_Fini(void); PyAPI_FUNC(void) PyTuple_Fini(void); PyAPI_FUNC(void) PyList_Fini(void); +PyAPI_FUNC(void) PySet_Fini(void); PyAPI_FUNC(void) PyString_Fini(void); PyAPI_FUNC(void) PyInt_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); Index: setobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/setobject.h,v retrieving revision 2.6 retrieving revision 2.7 diff -u -d -r2.6 -r2.7 --- setobject.h 31 Jul 2005 01:16:36 -0000 2.6 +++ setobject.h 1 Aug 2005 21:39:27 -0000 2.7 @@ -42,8 +42,7 @@ /* table points to smalltable for small tables, else to * additional malloc'ed memory. table is never NULL! This rule - * saves repeated runtime null-tests in the workhorse getitem and - * setitem calls. + * saves repeated runtime null-tests. */ setentry *table; setentry *(*lookup)(PySetObject *so, PyObject *key, long hash); From rhettinger at users.sourceforge.net Mon Aug 1 23:39:31 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 14:39:31 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.37,1.38 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3433/Objects Modified Files: setobject.c Log Message: * Improve code for the empty frozenset singleton: - Handle both frozenset() and frozenset([]). - Do not use singleton for frozenset subclasses. - Finalize the singleton. - Add test cases. * Factor-out set_update_internal() from set_update(). Simplifies the code for several internal callers. * Factor constant expressions out of loop in set_merge_internal(). * Minor comment touch-ups. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.37 retrieving revision 1.38 diff -u -d -r1.37 -r1.38 --- setobject.c 1 Aug 2005 05:26:41 -0000 1.37 +++ setobject.c 1 Aug 2005 21:39:28 -0000 1.38 @@ -311,9 +311,6 @@ return 0; } -/*** Internal functions (derived from public dictionary api functions ) ***/ - - /* CAUTION: set_add_internal() must guarantee that it won't resize the table */ static int set_add_internal(register PySetObject *so, PyObject *key) @@ -435,10 +432,10 @@ /* * Iterate over a set table. Use like so: * - * int i; + * int pos; * PyObject *key; - * i = 0; # important! i should not otherwise be changed by you - * while (set_next_internal(yourset, &i, &key)) { + * pos = 0; # important! pos should not otherwise be changed by you + * while (set_next_internal(yourset, &pos, &key)) { * Refer to borrowed reference in key. * } * @@ -467,14 +464,13 @@ return 1; } -/* Methods */ - static int set_merge_internal(PySetObject *so, PyObject *otherset) { - register PySetObject *other; + PySetObject *other; register int i; - setentry *entry; + register setentry *entry, *othertable; + register int othermask; assert (PyAnySet_Check(so)); assert (PyAnySet_Check(otherset)); @@ -491,8 +487,10 @@ if (set_table_resize(so, (so->used + other->used)*2) != 0) return -1; } - for (i = 0; i <= other->mask; i++) { - entry = &other->table[i]; + othermask = other->mask; + othertable = other->table; + for (i = 0; i <= othermask; i++) { + entry = &othertable[i]; if (entry->key != NULL && entry->key != dummy) { Py_INCREF(entry->key); @@ -517,9 +515,9 @@ return key != NULL && key != dummy; } -static PyTypeObject PySetIter_Type; /* Forward */ +/***** Set iterator types **********************************************/ -/* Set iterator types */ +static PyTypeObject PySetIter_Type; /* Forward */ typedef struct { PyObject_HEAD @@ -647,41 +645,52 @@ All rights reserved. */ -static PyObject * -set_update(PySetObject *so, PyObject *other) +static int +set_len(PyObject *so) +{ + return ((PySetObject *)so)->used; +} + +static int +set_update_internal(PySetObject *so, PyObject *other) { PyObject *key, *it; - if (PyAnySet_Check(other)) { - if (set_merge_internal(so, other) == -1) - return NULL; - Py_RETURN_NONE; - } + if (PyAnySet_Check(other)) + return set_merge_internal(so, other); if (PyDict_Check(other)) { PyObject *key, *value; int pos = 0; while (PyDict_Next(other, &pos, &key, &value)) { if (set_add_internal(so, key) == -1) - return NULL; + return -1; } - Py_RETURN_NONE; + return 0; } it = PyObject_GetIter(other); if (it == NULL) - return NULL; + return -1; while ((key = PyIter_Next(it)) != NULL) { if (set_add_internal(so, key) == -1) { Py_DECREF(it); Py_DECREF(key); - return NULL; + return -1; } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) + return -1; + return 0; +} + +static PyObject * +set_update(PySetObject *so, PyObject *other) +{ + if (set_update_internal(so, other) == -1) return NULL; Py_RETURN_NONE; } @@ -692,7 +701,6 @@ static PyObject * make_new_set(PyTypeObject *type, PyObject *iterable) { - PyObject *tmp; register PySetObject *so = NULL; if (dummy == NULL) { /* Auto-initialize dummy */ @@ -712,37 +720,51 @@ so->weakreflist = NULL; if (iterable != NULL) { - tmp = set_update(so, iterable); - if (tmp == NULL) { + if (set_update_internal(so, iterable) == -1) { Py_DECREF(so); return NULL; } - Py_DECREF(tmp); } return (PyObject *)so; } +/* The empty frozenset is a singleton */ +static PyObject *emptyfrozenset = NULL; + static PyObject * frozenset_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *iterable = NULL; - static PyObject *emptyfrozenset = NULL; + PyObject *iterable = NULL, *result; if (!PyArg_UnpackTuple(args, type->tp_name, 0, 1, &iterable)) return NULL; - if (iterable == NULL) { - if (type == &PyFrozenSet_Type) { - if (emptyfrozenset == NULL) - emptyfrozenset = make_new_set(type, NULL); - Py_INCREF(emptyfrozenset); - return emptyfrozenset; + + if (type != &PyFrozenSet_Type) + return make_new_set(type, iterable); + + if (iterable != NULL) { + /* frozenset(f) is idempotent */ + if (PyFrozenSet_CheckExact(iterable)) { + Py_INCREF(iterable); + return iterable; } - } else if (PyFrozenSet_CheckExact(iterable)) { - Py_INCREF(iterable); - return iterable; + result = make_new_set(type, iterable); + if (result == NULL || set_len(result)) + return result; + Py_DECREF(result); } - return make_new_set(type, iterable); + /* The empty frozenset is a singleton */ + if (emptyfrozenset == NULL) + emptyfrozenset = make_new_set(type, NULL); + Py_XINCREF(emptyfrozenset); + return emptyfrozenset; +} + +void +PySet_Fini(void) +{ + Py_XDECREF(emptyfrozenset); } static PyObject * @@ -786,12 +808,6 @@ return 0; } -static int -set_len(PyObject *so) -{ - return ((PySetObject *)so)->used; -} - /* set_swap_bodies() switches the contents of any two sets by moving their internal data pointers and, if needed, copying the internal smalltables. Semantically equivalent to: @@ -892,17 +908,14 @@ set_union(PySetObject *so, PyObject *other) { PySetObject *result; - PyObject *rv; result = (PySetObject *)set_copy(so); if (result == NULL) return NULL; - rv = set_update(result, other); - if (rv == NULL) { + if (set_update_internal(result, other) == -1) { Py_DECREF(result); return NULL; } - Py_DECREF(rv); return (PyObject *)result; } @@ -924,16 +937,12 @@ static PyObject * set_ior(PySetObject *so, PyObject *other) { - PyObject *result; - if (!PyAnySet_Check(other)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } - result = set_update(so, other); - if (result == NULL) + if (set_update_internal(so, other) == -1) return NULL; - Py_DECREF(result); Py_INCREF(so); return (PyObject *)so; } @@ -1553,7 +1562,6 @@ set_init(PySetObject *self, PyObject *args, PyObject *kwds) { PyObject *iterable = NULL; - PyObject *result; if (!PyAnySet_Check(self)) return -1; @@ -1563,12 +1571,7 @@ self->hash = -1; if (iterable == NULL) return 0; - result = set_update(self, iterable); - if (result != NULL) { - Py_DECREF(result); - return 0; - } - return -1; + return set_update_internal(self, iterable); } static PySequenceMethods set_as_sequence = { From rhettinger at users.sourceforge.net Mon Aug 1 23:39:33 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 14:39:33 -0700 Subject: [Python-checkins] python/dist/src/Python pythonrun.c,2.214,2.215 Message-ID: Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3433/Python Modified Files: pythonrun.c Log Message: * Improve code for the empty frozenset singleton: - Handle both frozenset() and frozenset([]). - Do not use singleton for frozenset subclasses. - Finalize the singleton. - Add test cases. * Factor-out set_update_internal() from set_update(). Simplifies the code for several internal callers. * Factor constant expressions out of loop in set_merge_internal(). * Minor comment touch-ups. Index: pythonrun.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/pythonrun.c,v retrieving revision 2.214 retrieving revision 2.215 diff -u -d -r2.214 -r2.215 --- pythonrun.c 27 May 2005 15:23:20 -0000 2.214 +++ pythonrun.c 1 Aug 2005 21:39:29 -0000 2.215 @@ -420,6 +420,7 @@ PyCFunction_Fini(); PyTuple_Fini(); PyList_Fini(); + PySet_Fini(); PyString_Fini(); PyInt_Fini(); PyFloat_Fini(); From pje at users.sourceforge.net Tue Aug 2 02:46:50 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:46:50 -0700 Subject: [Python-checkins] python/dist/src/Python ceval.c, 2.424, 2.425 compile.c, 2.350, 2.351 exceptions.c, 1.49, 1.50 graminit.c, 2.39, 2.40 Message-ID: Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Python Modified Files: ceval.c compile.c exceptions.c graminit.c Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.424 retrieving revision 2.425 diff -u -d -r2.424 -r2.425 --- ceval.c 25 Jun 2005 08:23:40 -0000 2.424 +++ ceval.c 2 Aug 2005 00:46:45 -0000 2.425 @@ -499,7 +499,14 @@ /* Interpreter main loop */ PyObject * -PyEval_EvalFrame(PyFrameObject *f) +PyEval_EvalFrame(PyFrameObject *f) { + /* This is for backward compatibility with extension modules that + used this API; core interpreter code should call PyEval_EvalFrameEx() */ + return PyEval_EvalFrameEx(f, 0); +} + +PyObject * +PyEval_EvalFrameEx(PyFrameObject *f, int throw) { #ifdef DXPAIRS int lastopcode = 0; @@ -747,6 +754,11 @@ x = Py_None; /* Not a reference, just anything non-NULL */ w = NULL; + if (throw) { /* support for generator.throw() */ + why = WHY_EXCEPTION; + goto on_error; + } + for (;;) { #ifdef WITH_TSC if (inst1 == 0) { @@ -2733,7 +2745,7 @@ return PyGen_New(f); } - retval = PyEval_EvalFrame(f); + retval = PyEval_EvalFrameEx(f,0); fail: /* Jump here from prelude on failure */ @@ -3636,7 +3648,7 @@ Py_INCREF(*stack); fastlocals[i] = *stack++; } - retval = PyEval_EvalFrame(f); + retval = PyEval_EvalFrameEx(f,0); assert(tstate != NULL); ++tstate->recursion_depth; Py_DECREF(f); Index: compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.350 retrieving revision 2.351 diff -u -d -r2.350 -r2.351 --- compile.c 25 Jun 2005 08:23:41 -0000 2.350 +++ compile.c 2 Aug 2005 00:46:46 -0000 2.351 @@ -2145,6 +2145,7 @@ else { com_test(c, t); com_addbyte(c, YIELD_VALUE); + com_addbyte(c, POP_TOP); com_pop(c, 1); } @@ -2193,6 +2194,7 @@ else { com_test(c, t); com_addbyte(c, YIELD_VALUE); + com_addbyte(c, POP_TOP); com_pop(c, 1); } com_addfwref(c, JUMP_FORWARD, &anchor); @@ -2354,6 +2356,10 @@ } } + +/* forward reference */ +static void com_yield_expr(struct compiling *c, node *n); + static void com_atom(struct compiling *c, node *n) { @@ -2369,7 +2375,10 @@ com_push(c, 1); } else - com_testlist_gexp(c, CHILD(n, 1)); + if (TYPE(CHILD(n, 1)) == yield_expr) + com_yield_expr(c, CHILD(n, 1)); + else + com_testlist_gexp(c, CHILD(n, 1)); break; case LSQB: /* '[' [listmaker] ']' */ if (TYPE(CHILD(n, 1)) == RSQB) { @@ -3436,7 +3445,11 @@ } n = CHILD(n, 0); break; - + case yield_expr: + com_error(c, PyExc_SyntaxError, + "assignment to yield expression not possible"); + return; + case test: case and_test: case not_test: @@ -3493,7 +3506,7 @@ } if (assigning > OP_APPLY) { com_error(c, PyExc_SyntaxError, - "augmented assign to tuple literal or generator expression not possible"); + "augmented assign to tuple literal, yield, or generator expression not possible"); return; } break; @@ -3729,27 +3742,42 @@ } static void -com_yield_stmt(struct compiling *c, node *n) +com_yield_expr(struct compiling *c, node *n) { int i; - REQ(n, yield_stmt); /* 'yield' testlist */ + REQ(n, yield_expr); /* 'yield' testlist */ if (!c->c_infunction) { com_error(c, PyExc_SyntaxError, "'yield' outside function"); } - for (i = 0; i < c->c_nblocks; ++i) { + /* for (i = 0; i < c->c_nblocks; ++i) { if (c->c_block[i] == SETUP_FINALLY) { com_error(c, PyExc_SyntaxError, "'yield' not allowed in a 'try' block " "with a 'finally' clause"); return; } + } */ + + if (NCH(n) < 2) { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); } - com_node(c, CHILD(n, 1)); + else + com_node(c, CHILD(n, 1)); com_addbyte(c, YIELD_VALUE); +} + +static void +com_yield_stmt(struct compiling *c, node *n) +{ + REQ(n, yield_stmt); /* yield_expr */ + com_node(c, CHILD(n, 0)); + com_addbyte(c, POP_TOP); com_pop(c, 1); } + static void com_raise_stmt(struct compiling *c, node *n) { @@ -4768,6 +4796,10 @@ /* Expression nodes */ + case yield_expr: + com_yield_expr(c, n); + break; + case testlist: case testlist1: case testlist_safe: @@ -5027,7 +5059,9 @@ REQ(CHILD(n, 1), gen_for); c->c_name = ""; + c->c_infunction = 1; com_gen_for(c, CHILD(n, 1), CHILD(n, 0), 1); + c->c_infunction = 0; com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_push(c, 1); @@ -6115,7 +6149,7 @@ #define symtable_add_use(ST, NAME) symtable_add_def((ST), (NAME), USE) -/* Look for a yield stmt under n. Return 1 if found, else 0. +/* Look for a yield stmt or expr under n. Return 1 if found, else 0. This hack is used to look inside "if 0:" blocks (which are normally ignored) in case those are the only places a yield occurs (so that this function is a generator). */ @@ -6137,6 +6171,7 @@ return 0; case yield_stmt: + case yield_expr: return GENERATOR; default: @@ -6247,8 +6282,10 @@ case del_stmt: symtable_assign(st, CHILD(n, 1), 0); break; - case yield_stmt: + case yield_expr: st->st_cur->ste_generator = 1; + if (NCH(n)==1) + break; n = CHILD(n, 1); goto loop; case expr_stmt: @@ -6341,9 +6378,15 @@ /* fall through */ case atom: - if (TYPE(n) == atom && TYPE(CHILD(n, 0)) == NAME) { - symtable_add_use(st, STR(CHILD(n, 0))); - break; + if (TYPE(n) == atom) { + if (TYPE(CHILD(n, 0)) == NAME) { + symtable_add_use(st, STR(CHILD(n, 0))); + break; + } + else if (TYPE(CHILD(n,0)) == LPAR) { + n = CHILD(n,1); + goto loop; + } } /* fall through */ default: @@ -6739,6 +6782,15 @@ symtable_add_def(st, STR(tmp), DEF_LOCAL | def_flag); } return; + + case yield_expr: + st->st_cur->ste_generator = 1; + if (NCH(n)==2) { + n = CHILD(n, 1); + goto loop; + } + return; + case dotted_as_name: if (NCH(n) == 3) symtable_add_def(st, STR(CHILD(n, 2)), Index: exceptions.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/exceptions.c,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- exceptions.c 25 Aug 2004 02:14:08 -0000 1.49 +++ exceptions.c 2 Aug 2005 00:46:46 -0000 1.50 @@ -57,6 +57,7 @@ |\n\ +-- SystemExit\n\ +-- StopIteration\n\ + +-- GeneratorExit\n\ +-- StandardError\n\ | |\n\ | +-- KeyboardInterrupt\n\ @@ -394,6 +395,7 @@ PyDoc_STRVAR(TypeError__doc__, "Inappropriate argument type."); PyDoc_STRVAR(StopIteration__doc__, "Signal the end from iterator.next()."); +PyDoc_STRVAR(GeneratorExit__doc__, "Request that a generator exit."); @@ -1583,6 +1585,7 @@ PyObject *PyExc_Exception; PyObject *PyExc_StopIteration; +PyObject *PyExc_GeneratorExit; PyObject *PyExc_StandardError; PyObject *PyExc_ArithmeticError; PyObject *PyExc_LookupError; @@ -1657,6 +1660,8 @@ {"Exception", &PyExc_Exception}, {"StopIteration", &PyExc_StopIteration, &PyExc_Exception, StopIteration__doc__}, + {"GeneratorExit", &PyExc_GeneratorExit, &PyExc_Exception, + GeneratorExit__doc__}, {"StandardError", &PyExc_StandardError, &PyExc_Exception, StandardError__doc__}, {"TypeError", &PyExc_TypeError, 0, TypeError__doc__}, Index: graminit.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/graminit.c,v retrieving revision 2.39 retrieving revision 2.40 diff -u -d -r2.39 -r2.40 --- graminit.c 5 Mar 2005 06:47:56 -0000 2.39 +++ graminit.c 2 Aug 2005 00:46:46 -0000 2.40 @@ -279,10 +279,12 @@ {25, 3}, {0, 1}, }; -static arc arcs_13_2[1] = { +static arc arcs_13_2[2] = { + {43, 4}, {9, 4}, }; -static arc arcs_13_3[1] = { +static arc arcs_13_3[2] = { + {43, 5}, {9, 5}, }; static arc arcs_13_4[1] = { @@ -295,13 +297,12 @@ static state states_13[6] = { {1, arcs_13_0}, {3, arcs_13_1}, - {1, arcs_13_2}, - {1, arcs_13_3}, + {2, arcs_13_2}, + {2, arcs_13_3}, {1, arcs_13_4}, {2, arcs_13_5}, }; static arc arcs_14_0[12] = { - {43, 1}, {44, 1}, {45, 1}, {46, 1}, @@ -313,6 +314,7 @@ {52, 1}, {53, 1}, {54, 1}, + {55, 1}, }; static arc arcs_14_1[1] = { {0, 1}, @@ -322,11 +324,11 @@ {1, arcs_14_1}, }; static arc arcs_15_0[1] = { - {55, 1}, + {56, 1}, }; static arc arcs_15_1[3] = { {26, 2}, - {56, 3}, + {57, 3}, {0, 1}, }; static arc arcs_15_2[2] = { @@ -367,10 +369,10 @@ {2, arcs_15_8}, }; static arc arcs_16_0[1] = { - {57, 1}, + {58, 1}, }; static arc arcs_16_1[1] = { - {58, 2}, + {59, 2}, }; static arc arcs_16_2[1] = { {0, 2}, @@ -381,7 +383,7 @@ {1, arcs_16_2}, }; static arc arcs_17_0[1] = { - {59, 1}, + {60, 1}, }; static arc arcs_17_1[1] = { {0, 1}, @@ -391,11 +393,11 @@ {1, arcs_17_1}, }; static arc arcs_18_0[5] = { - {60, 1}, {61, 1}, {62, 1}, {63, 1}, {64, 1}, + {65, 1}, }; static arc arcs_18_1[1] = { {0, 1}, @@ -405,7 +407,7 @@ {1, arcs_18_1}, }; static arc arcs_19_0[1] = { - {65, 1}, + {66, 1}, }; static arc arcs_19_1[1] = { {0, 1}, @@ -415,7 +417,7 @@ {1, arcs_19_1}, }; static arc arcs_20_0[1] = { - {66, 1}, + {67, 1}, }; static arc arcs_20_1[1] = { {0, 1}, @@ -425,7 +427,7 @@ {1, arcs_20_1}, }; static arc arcs_21_0[1] = { - {67, 1}, + {68, 1}, }; static arc arcs_21_1[2] = { {9, 2}, @@ -440,18 +442,14 @@ {1, arcs_21_2}, }; static arc arcs_22_0[1] = { - {68, 1}, + {43, 1}, }; static arc arcs_22_1[1] = { - {9, 2}, -}; -static arc arcs_22_2[1] = { - {0, 2}, + {0, 1}, }; -static state states_22[3] = { +static state states_22[2] = { {1, arcs_22_0}, {1, arcs_22_1}, - {1, arcs_22_2}, }; static arc arcs_23_0[1] = { {69, 1}, @@ -779,7 +777,7 @@ {93, 1}, }; static arc arcs_38_1[1] = { - {58, 2}, + {59, 2}, }; static arc arcs_38_2[1] = { {82, 3}, @@ -1034,7 +1032,7 @@ }; static arc arcs_50_1[3] = { {123, 0}, - {56, 0}, + {57, 0}, {0, 1}, }; static state states_50[2] = { @@ -1113,7 +1111,8 @@ {144, 5}, {145, 6}, }; -static arc arcs_55_1[2] = { +static arc arcs_55_1[3] = { + {43, 7}, {135, 7}, {15, 5}, }; @@ -1149,7 +1148,7 @@ }; static state states_55[11] = { {7, arcs_55_0}, - {2, arcs_55_1}, + {3, arcs_55_1}, {2, arcs_55_2}, {2, arcs_55_3}, {1, arcs_55_4}, @@ -1533,7 +1532,7 @@ {93, 1}, }; static arc arcs_71_1[1] = { - {58, 2}, + {59, 2}, }; static arc arcs_71_2[1] = { {82, 3}, @@ -1590,7 +1589,7 @@ {93, 1}, }; static arc arcs_74_1[1] = { - {58, 2}, + {59, 2}, }; static arc arcs_74_2[1] = { {82, 3}, @@ -1653,165 +1652,182 @@ {1, arcs_77_0}, {1, arcs_77_1}, }; -static dfa dfas[78] = { +static arc arcs_78_0[1] = { + {160, 1}, +}; +static arc arcs_78_1[2] = { + {9, 2}, + {0, 1}, +}; +static arc arcs_78_2[1] = { + {0, 2}, +}; +static state states_78[3] = { + {1, arcs_78_0}, + {2, arcs_78_1}, + {1, arcs_78_2}, +}; +static dfa dfas[79] = { {256, "single_input", 0, 3, states_0, - "\004\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"}, + "\004\050\014\000\000\000\000\025\074\205\011\162\000\002\000\140\010\111\023\002\001"}, {257, "file_input", 0, 2, states_1, - "\204\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"}, + "\204\050\014\000\000\000\000\025\074\205\011\162\000\002\000\140\010\111\023\002\001"}, {258, "eval_input", 0, 3, states_2, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {259, "decorator", 0, 7, states_3, - "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {260, "decorators", 0, 2, states_4, - "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {261, "funcdef", 0, 7, states_5, - "\000\010\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {262, "parameters", 0, 4, states_6, - "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {263, "varargslist", 0, 10, states_7, - "\000\040\010\060\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\040\010\060\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {264, "fpdef", 0, 4, states_8, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {265, "fplist", 0, 3, states_9, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {266, "stmt", 0, 2, states_10, - "\000\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"}, + "\000\050\014\000\000\000\000\025\074\205\011\162\000\002\000\140\010\111\023\002\001"}, {267, "simple_stmt", 0, 4, states_11, - "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\025\074\205\011\000\000\002\000\140\010\111\023\000\001"}, {268, "small_stmt", 0, 2, states_12, - "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\025\074\205\011\000\000\002\000\140\010\111\023\000\001"}, {269, "expr_stmt", 0, 6, states_13, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {270, "augassign", 0, 2, states_14, - "\000\000\000\000\000\370\177\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\360\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {271, "print_stmt", 0, 9, states_15, - "\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {272, "del_stmt", 0, 3, states_16, - "\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {273, "pass_stmt", 0, 2, states_17, - "\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {274, "flow_stmt", 0, 2, states_18, - "\000\000\000\000\000\000\000\000\076\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\074\000\000\000\000\000\000\000\000\000\000\000\001"}, {275, "break_stmt", 0, 2, states_19, - "\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000"}, {276, "continue_stmt", 0, 2, states_20, - "\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000"}, {277, "return_stmt", 0, 3, states_21, - "\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000"}, - {278, "yield_stmt", 0, 3, states_22, - "\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000"}, + {278, "yield_stmt", 0, 2, states_22, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, {279, "raise_stmt", 0, 7, states_23, - "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000"}, {280, "import_stmt", 0, 2, states_24, - "\000\000\000\000\000\000\000\000\000\005\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000"}, {281, "import_name", 0, 3, states_25, - "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000"}, {282, "import_from", 0, 7, states_26, - "\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000"}, {283, "import_as_name", 0, 4, states_27, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {284, "dotted_as_name", 0, 4, states_28, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {285, "import_as_names", 0, 3, states_29, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {286, "dotted_as_names", 0, 2, states_30, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {287, "dotted_name", 0, 2, states_31, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {288, "global_stmt", 0, 3, states_32, - "\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000"}, {289, "exec_stmt", 0, 7, states_33, - "\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"}, {290, "assert_stmt", 0, 5, states_34, - "\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000"}, {291, "compound_stmt", 0, 2, states_35, - "\000\010\004\000\000\000\000\000\000\000\000\162\000\000\000\000\000\000\000\002"}, + "\000\010\004\000\000\000\000\000\000\000\000\162\000\000\000\000\000\000\000\002\000"}, {292, "if_stmt", 0, 8, states_36, - "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"}, {293, "while_stmt", 0, 8, states_37, - "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000"}, {294, "for_stmt", 0, 10, states_38, - "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"}, {295, "try_stmt", 0, 10, states_39, - "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000"}, {296, "except_clause", 0, 5, states_40, - "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, {297, "suite", 0, 5, states_41, - "\004\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"}, + "\004\040\010\000\000\000\000\025\074\205\011\000\000\002\000\140\010\111\023\000\001"}, {298, "test", 0, 4, states_42, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {299, "and_test", 0, 2, states_43, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000\000"}, {300, "not_test", 0, 3, states_44, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000\000"}, {301, "comparison", 0, 2, states_45, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {302, "comp_op", 0, 4, states_46, - "\000\000\000\000\000\000\000\000\000\000\004\000\000\362\017\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\004\000\000\362\017\000\000\000\000\000\000"}, {303, "expr", 0, 2, states_47, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {304, "xor_expr", 0, 2, states_48, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {305, "and_expr", 0, 2, states_49, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {306, "shift_expr", 0, 2, states_50, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {307, "arith_expr", 0, 2, states_51, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {308, "term", 0, 2, states_52, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {309, "factor", 0, 3, states_53, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {310, "power", 0, 4, states_54, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000\000"}, {311, "atom", 0, 11, states_55, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000\000"}, {312, "listmaker", 0, 5, states_56, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {313, "testlist_gexp", 0, 5, states_57, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {314, "lambdef", 0, 5, states_58, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\020\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\020\000\000"}, {315, "trailer", 0, 7, states_59, - "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\001\000\000"}, + "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\001\000\000\000"}, {316, "subscriptlist", 0, 3, states_60, - "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000\000"}, {317, "subscript", 0, 7, states_61, - "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000\000"}, {318, "sliceop", 0, 3, states_62, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {319, "exprlist", 0, 3, states_63, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000\000"}, {320, "testlist", 0, 3, states_64, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {321, "testlist_safe", 0, 5, states_65, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {322, "dictmaker", 0, 5, states_66, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {323, "classdef", 0, 8, states_67, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000"}, {324, "arglist", 0, 8, states_68, - "\000\040\010\060\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\060\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {325, "argument", 0, 5, states_69, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {326, "list_iter", 0, 2, states_70, - "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000\000"}, {327, "list_for", 0, 6, states_71, - "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"}, {328, "list_if", 0, 4, states_72, - "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"}, {329, "gen_iter", 0, 2, states_73, - "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000\000"}, {330, "gen_for", 0, 6, states_74, - "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"}, {331, "gen_if", 0, 4, states_75, - "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"}, {332, "testlist1", 0, 2, states_76, - "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"}, + "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000\000"}, {333, "encoding_decl", 0, 2, states_77, - "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {334, "yield_expr", 0, 3, states_78, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, }; -static label labels[160] = { +static label labels[161] = { {0, "EMPTY"}, {256, 0}, {4, 0}, @@ -1855,6 +1871,7 @@ {289, 0}, {290, 0}, {270, 0}, + {334, 0}, {37, 0}, {38, 0}, {39, 0}, @@ -1880,7 +1897,6 @@ {1, "break"}, {1, "continue"}, {1, "return"}, - {1, "yield"}, {1, "raise"}, {281, 0}, {282, 0}, @@ -1972,10 +1988,11 @@ {329, 0}, {331, 0}, {333, 0}, + {1, "yield"}, }; grammar _PyParser_Grammar = { - 78, + 79, dfas, - {160, labels}, + {161, labels}, 256 }; From pje at users.sourceforge.net Tue Aug 2 02:47:14 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:14 -0700 Subject: [Python-checkins] python/dist/src/Grammar Grammar,1.53,1.54 Message-ID: Update of /cvsroot/python/python/dist/src/Grammar In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Grammar Modified Files: Grammar Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: Grammar =================================================================== RCS file: /cvsroot/python/python/dist/src/Grammar/Grammar,v retrieving revision 1.53 retrieving revision 1.54 diff -u -d -r1.53 -r1.54 --- Grammar 5 Mar 2005 06:47:56 -0000 1.53 +++ Grammar 2 Aug 2005 00:46:38 -0000 1.54 @@ -39,7 +39,7 @@ stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt -expr_stmt: testlist (augassign testlist | ('=' testlist)*) +expr_stmt: testlist (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist))*) augassign: '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' # For normal assignments, additional restrictions enforced by the interpreter print_stmt: 'print' ( [ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ] ) @@ -49,7 +49,7 @@ break_stmt: 'break' continue_stmt: 'continue' return_stmt: 'return' [testlist] -yield_stmt: 'yield' testlist +yield_stmt: yield_expr raise_stmt: 'raise' [test [',' test [',' test]]] import_stmt: import_name | import_from import_name: 'import' dotted_as_names @@ -86,7 +86,7 @@ term: factor (('*'|'/'|'%'|'//') factor)* factor: ('+'|'-'|'~') factor | power power: atom trailer* ['**' factor] -atom: '(' [testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+ +atom: '(' [yield_expr|testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+ listmaker: test ( list_for | (',' test)* [','] ) testlist_gexp: test ( gen_for | (',' test)* [','] ) lambdef: 'lambda' [varargslist] ':' test @@ -116,3 +116,6 @@ # not used in grammar, but may appear in "node" passed from Parser to Compiler encoding_decl: NAME + +yield_expr: 'yield' [testlist] + From pje at users.sourceforge.net Tue Aug 2 02:47:15 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:15 -0700 Subject: [Python-checkins] python/dist/src/Include ceval.h, 2.53, 2.54 graminit.h, 2.23, 2.24 pyerrors.h, 2.66, 2.67 Message-ID: Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Include Modified Files: ceval.h graminit.h pyerrors.h Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: ceval.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/ceval.h,v retrieving revision 2.53 retrieving revision 2.54 diff -u -d -r2.53 -r2.54 --- ceval.h 11 Oct 2004 02:40:35 -0000 2.53 +++ ceval.h 2 Aug 2005 00:46:42 -0000 2.54 @@ -65,6 +65,7 @@ PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *); PyAPI_FUNC(PyObject *) PyEval_EvalFrame(struct _frame *); +PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(struct _frame *f, int exc); /* this used to be handled on a per-thread basis - now just two globals */ PyAPI_DATA(volatile int) _Py_Ticker; Index: graminit.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/graminit.h,v retrieving revision 2.23 retrieving revision 2.24 diff -u -d -r2.23 -r2.24 --- graminit.h 31 Aug 2004 10:07:00 -0000 2.23 +++ graminit.h 2 Aug 2005 00:46:42 -0000 2.24 @@ -76,3 +76,4 @@ #define gen_if 331 #define testlist1 332 #define encoding_decl 333 +#define yield_expr 334 Index: pyerrors.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/pyerrors.h,v retrieving revision 2.66 retrieving revision 2.67 diff -u -d -r2.66 -r2.67 --- pyerrors.h 25 Aug 2004 02:14:06 -0000 2.66 +++ pyerrors.h 2 Aug 2005 00:46:42 -0000 2.67 @@ -25,6 +25,7 @@ PyAPI_DATA(PyObject *) PyExc_Exception; PyAPI_DATA(PyObject *) PyExc_StopIteration; +PyAPI_DATA(PyObject *) PyExc_GeneratorExit; PyAPI_DATA(PyObject *) PyExc_StandardError; PyAPI_DATA(PyObject *) PyExc_ArithmeticError; PyAPI_DATA(PyObject *) PyExc_LookupError; From pje at users.sourceforge.net Tue Aug 2 02:47:15 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:15 -0700 Subject: [Python-checkins] python/dist/src/Lib/compiler transformer.py, 1.51, 1.52 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/compiler In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Lib/compiler Modified Files: transformer.py Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: transformer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/compiler/transformer.py,v retrieving revision 1.51 retrieving revision 1.52 diff -u -d -r1.51 -r1.52 --- transformer.py 2 Jul 2005 18:37:41 -0000 1.51 +++ transformer.py 2 Aug 2005 00:46:43 -0000 1.52 @@ -403,7 +403,15 @@ return Return(self.com_node(nodelist[1]), lineno=nodelist[0][2]) def yield_stmt(self, nodelist): - return Yield(self.com_node(nodelist[1]), lineno=nodelist[0][2]) + expr = self.com_node(nodelist[0]) + return Discard(expr, lineno=expr.lineno) + + def yield_expr(self, nodelist): + if len(nodelist)>1: + value = nodelist[1] + else: + value = Const(None) + return Yield(self.com_node(value), lineno=nodelist[0][2]) def raise_stmt(self, nodelist): # raise: [test [',' test [',' test]]] @@ -1402,6 +1410,8 @@ if hasattr(symbol, 'yield_stmt'): _legal_node_types.append(symbol.yield_stmt) +if hasattr(symbol, 'yield_expr'): + _legal_node_types.append(symbol.yield_expr) _assign_types = [ symbol.test, From pje at users.sourceforge.net Tue Aug 2 02:47:15 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:15 -0700 Subject: [Python-checkins] python/dist/src/Lib symbol.py,1.18,1.19 Message-ID: Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Lib Modified Files: symbol.py Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: symbol.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/symbol.py,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- symbol.py 31 Aug 2004 10:07:00 -0000 1.18 +++ symbol.py 2 Aug 2005 00:46:42 -0000 1.19 @@ -88,6 +88,7 @@ gen_if = 331 testlist1 = 332 encoding_decl = 333 +yield_expr = 334 #--end constants-- sym_name = {} From pje at users.sourceforge.net Tue Aug 2 02:47:16 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:16 -0700 Subject: [Python-checkins] python/dist/src/Lib/test test_generators.py, 1.44, 1.45 test_genexps.py, 1.7, 1.8 test_parser.py, 1.22, 1.23 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Lib/test Modified Files: test_generators.py test_genexps.py test_parser.py Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: test_generators.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_generators.py,v retrieving revision 1.44 retrieving revision 1.45 diff -u -d -r1.44 -r1.45 --- test_generators.py 13 Sep 2004 01:07:12 -0000 1.44 +++ test_generators.py 2 Aug 2005 00:46:43 -0000 1.45 @@ -382,7 +382,7 @@ >>> type(i) >>> [s for s in dir(i) if not s.startswith('_')] -['gi_frame', 'gi_running', 'next'] +['close', 'gi_frame', 'gi_running', 'next', 'send', 'throw'] >>> print i.next.__doc__ x.next() -> the next value, or raise StopIteration >>> iter(i) is i @@ -421,6 +421,7 @@ ... self.name = name ... self.parent = None ... self.generator = self.generate() +... self.close = self.generator.close ... ... def generate(self): ... while not self.parent: @@ -482,6 +483,9 @@ A->A B->G C->A D->G E->G F->A G->G H->G I->A J->G K->A L->A M->G merged A into G A->G B->G C->G D->G E->G F->G G->G H->G I->G J->G K->G L->G M->G + +>>> for s in sets: s.close() # break cycles + """ # Emacs turd ' @@ -589,6 +593,7 @@ ... def __init__(self, g): ... self.sofar = [] ... self.fetch = g.next +... self.close = g.close ... ... def __getitem__(self, i): ... sofar, fetch = self.sofar, self.fetch @@ -619,6 +624,7 @@ [200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375, 384] [400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675] +>>> m235.close() Ye olde Fibonacci generator, LazyList style. @@ -642,6 +648,7 @@ >>> fib = LazyList(fibgen(1, 2)) >>> firstn(iter(fib), 17) [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584] +>>> fib.close() """ # syntax_tests mostly provokes SyntaxErrors. Also fiddling with #if 0 @@ -672,7 +679,7 @@ .. SyntaxError: 'return' with argument inside generator (, line 3) -This one is fine: +These are fine: >>> def f(): ... yield 1 @@ -683,9 +690,6 @@ ... yield 1 ... finally: ... pass -Traceback (most recent call last): - .. -SyntaxError: 'yield' not allowed in a 'try' block with a 'finally' clause (, line 3) >>> def f(): ... try: @@ -697,11 +701,6 @@ ... pass ... finally: ... pass -Traceback (most recent call last): - ... -SyntaxError: 'yield' not allowed in a 'try' block with a 'finally' clause (, line 6) - -But this is fine: >>> def f(): ... try: @@ -722,14 +721,16 @@ >>> def f(): ... yield -Traceback (most recent call last): -SyntaxError: invalid syntax +>>> type(f()) + + >>> def f(): ... if 0: ... yield -Traceback (most recent call last): -SyntaxError: invalid syntax +>>> type(f()) + + >>> def f(): ... if 0: @@ -805,7 +806,7 @@ ... if 0: ... yield 2 # because it's a generator Traceback (most recent call last): -SyntaxError: 'return' with argument inside generator (, line 8) +SyntaxError: 'return' with argument inside generator (, line 8) This one caused a crash (see SF bug 567538): @@ -1383,6 +1384,250 @@ """ +coroutine_tests = """\ +Sending a value into a started generator: + +>>> def f(): +... print (yield 1) +... yield 2 +>>> g = f() +>>> g.next() +1 +>>> g.send(42) +42 +2 + +Sending a value into a new generator produces a TypeError: + +>>> f().send("foo") +Traceback (most recent call last): +... +TypeError: can't send non-None value to a just-started generator + + +Yield by itself yields None: + +>>> def f(): yield +>>> list(f()) +[None] + + + +An obscene abuse of a yield expression within a generator expression: + +>>> list((yield 21) for i in range(4)) +[21, None, 21, None, 21, None, 21, None] + +And a more sane, but still weird usage: + +>>> def f(): list(i for i in [(yield 26)]) +>>> type(f()) + + + +Check some syntax errors for yield expressions: + +>>> f=lambda: (yield 1),(yield 2) +Traceback (most recent call last): + ... +SyntaxError: 'yield' outside function (, line 1) + +>>> def f(): return lambda x=(yield): 1 +Traceback (most recent call last): + ... +SyntaxError: 'return' with argument inside generator (, line 1) + +>>> def f(): x = yield = y +Traceback (most recent call last): + ... +SyntaxError: assignment to yield expression not possible (, line 1) + + +Now check some throw() conditions: + +>>> def f(): +... while True: +... try: +... print (yield) +... except ValueError,v: +... print "caught ValueError (%s)" % (v), +>>> import sys +>>> g = f() +>>> g.next() + +>>> g.throw(ValueError) # type only +caught ValueError () + +>>> g.throw(ValueError("xyz")) # value only +caught ValueError (xyz) + +>>> g.throw(ValueError, ValueError(1)) # value+matching type +caught ValueError (1) + +>>> g.throw(ValueError, TypeError(1)) # mismatched type, rewrapped +caught ValueError (1) + +>>> g.throw(ValueError(1), "foo") # bad args +Traceback (most recent call last): + ... +TypeError: instance exception may not have a separate value + +>>> g.throw(ValueError, "foo", 23) # bad args +Traceback (most recent call last): + ... +TypeError: throw() third argument must be a traceback object + +>>> def throw(g,exc): +... try: +... raise exc +... except: +... g.throw(*sys.exc_info()) +>>> throw(g,ValueError) # do it with traceback included +caught ValueError () + +>>> g.send(1) +1 + +>>> throw(g,TypeError) # terminate the generator +Traceback (most recent call last): + ... +TypeError + +>>> print g.gi_frame +None + +>>> g.send(2) +Traceback (most recent call last): + ... +StopIteration + +>>> g.throw(ValueError,6) # throw on closed generator +Traceback (most recent call last): + ... +ValueError: 6 + +>>> f().throw(ValueError,7) # throw on just-opened generator +Traceback (most recent call last): + ... +ValueError: 7 + + +Now let's try closing a generator: + +>>> def f(): +... try: yield +... except GeneratorExit: +... print "exiting" + +>>> g = f() +>>> g.next() +>>> g.close() +exiting +>>> g.close() # should be no-op now + +>>> f().close() # close on just-opened generator should be fine + +>>> def f(): yield # an even simpler generator +>>> f().close() # close before opening +>>> g = f() +>>> g.next() +>>> g.close() # close normally + +And finalization: + +>>> def f(): +... try: yield +... finally: +... print "exiting" + +>>> g = f() +>>> g.next() +>>> del g +exiting + + +Now let's try some ill-behaved generators: + +>>> def f(): +... try: yield +... except GeneratorExit: +... yield "foo!" +>>> g = f() +>>> g.next() +>>> g.close() +Traceback (most recent call last): + ... +RuntimeError: generator ignored GeneratorExit +>>> g.close() + + +Our ill-behaved code should be invoked during GC: + +>>> import sys, StringIO +>>> old, sys.stderr = sys.stderr, StringIO.StringIO() +>>> g = f() +>>> g.next() +>>> del g +>>> sys.stderr.getvalue().startswith( +... "Exception exceptions.RuntimeError: 'generator ignored GeneratorExit' in " +... ) +True +>>> sys.stderr = old + + +And errors thrown during closing should propagate: + +>>> def f(): +... try: yield +... except GeneratorExit: +... raise TypeError("fie!") +>>> g = f() +>>> g.next() +>>> g.close() +Traceback (most recent call last): + ... +TypeError: fie! + + +Ensure that various yield expression constructs make their +enclosing function a generator: + +>>> def f(): x += yield +>>> type(f()) + + +>>> def f(): x = yield +>>> type(f()) + + +>>> def f(): lambda x=(yield): 1 +>>> type(f()) + + +>>> def f(): x=(i for i in (yield) if (yield)) +>>> type(f()) + + +>>> def f(d): d[(yield "a")] = d[(yield "b")] = 27 +>>> data = [1,2] +>>> g = f(data) +>>> type(g) + +>>> g.send(None) +'a' +>>> data +[1, 2] +>>> g.send(0) +'b' +>>> data +[27, 2] +>>> try: g.send(1) +... except StopIteration: pass +>>> data +[27, 27] + +""" + __test__ = {"tut": tutorial_tests, "pep": pep_tests, "email": email_tests, @@ -1390,6 +1635,7 @@ "syntax": syntax_tests, "conjoin": conjoin_tests, "weakref": weakref_tests, + "coroutine": coroutine_tests, } # Magic test name that regrtest.py invokes *after* importing this module. Index: test_genexps.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_genexps.py,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- test_genexps.py 30 Sep 2004 22:29:03 -0000 1.7 +++ test_genexps.py 2 Aug 2005 00:46:43 -0000 1.8 @@ -130,7 +130,7 @@ >>> (y for y in (1,2)) += 10 Traceback (most recent call last): ... - SyntaxError: augmented assign to tuple literal or generator expression not possible + SyntaxError: augmented assign to tuple literal, yield, or generator expression not possible Index: test_parser.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_parser.py,v retrieving revision 1.22 retrieving revision 1.23 diff -u -d -r1.22 -r1.23 --- test_parser.py 20 Apr 2005 17:45:12 -0000 1.22 +++ test_parser.py 2 Aug 2005 00:46:43 -0000 1.23 @@ -29,11 +29,22 @@ def test_yield_statement(self): self.check_suite("def f(): yield 1") + self.check_suite("def f(): yield") + self.check_suite("def f(): x += yield") + self.check_suite("def f(): x = yield 1") + self.check_suite("def f(): x = y = yield 1") + self.check_suite("def f(): x = yield") + self.check_suite("def f(): x = y = yield") + self.check_suite("def f(): 1 + (yield)*2") + self.check_suite("def f(): (yield 1)*2") self.check_suite("def f(): return; yield 1") self.check_suite("def f(): yield 1; return") self.check_suite("def f():\n" " for x in range(30):\n" " yield x\n") + self.check_suite("def f():\n" + " if (yield):\n" + " yield x\n") def test_expressions(self): self.check_expr("foo(1)") From pje at users.sourceforge.net Tue Aug 2 02:47:18 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:18 -0700 Subject: [Python-checkins] python/dist/src/Modules gcmodule.c, 2.81, 2.82 parsermodule.c, 2.86, 2.87 Message-ID: Update of /cvsroot/python/python/dist/src/Modules In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Modules Modified Files: gcmodule.c parsermodule.c Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: gcmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/gcmodule.c,v retrieving revision 2.81 retrieving revision 2.82 diff -u -d -r2.81 -r2.82 --- gcmodule.c 18 Jun 2005 17:37:06 -0000 2.81 +++ gcmodule.c 2 Aug 2005 00:46:43 -0000 2.82 @@ -413,10 +413,8 @@ assert(delstr != NULL); return _PyInstance_Lookup(op, delstr) != NULL; } - else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) + else return op->ob_type->tp_del != NULL; - else - return 0; } /* Move the objects in unreachable with __del__ methods into `finalizers`. Index: parsermodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/parsermodule.c,v retrieving revision 2.86 retrieving revision 2.87 diff -u -d -r2.86 -r2.87 --- parsermodule.c 9 Apr 2005 02:30:14 -0000 2.86 +++ parsermodule.c 2 Aug 2005 00:46:44 -0000 2.87 @@ -859,7 +859,8 @@ VALIDATER(listmaker); VALIDATER(yield_stmt); VALIDATER(testlist1); VALIDATER(gen_for); VALIDATER(gen_iter); VALIDATER(gen_if); -VALIDATER(testlist_gexp); +VALIDATER(testlist_gexp); VALIDATER(yield_expr); +VALIDATER(yield_or_testlist); #undef VALIDATER @@ -1507,6 +1508,15 @@ static int +validate_yield_or_testlist(node *tree) +{ + if (TYPE(tree) == yield_expr) + return validate_yield_expr(tree); + else + return validate_testlist(tree); +} + +static int validate_expr_stmt(node *tree) { int j; @@ -1517,8 +1527,8 @@ if (res && nch == 3 && TYPE(CHILD(tree, 1)) == augassign) { - res = (validate_numnodes(CHILD(tree, 1), 1, "augassign") - && validate_testlist(CHILD(tree, 2))); + res = validate_numnodes(CHILD(tree, 1), 1, "augassign") + && validate_yield_or_testlist(CHILD(tree, 2)); if (res) { char *s = STR(CHILD(CHILD(tree, 1), 0)); @@ -1541,8 +1551,8 @@ } else { for (j = 1; res && (j < nch); j += 2) - res = (validate_equal(CHILD(tree, j)) - && validate_testlist(CHILD(tree, j + 1))); + res = validate_equal(CHILD(tree, j)) + && validate_yield_or_testlist(CHILD(tree, j + 1)); } return (res); } @@ -1649,15 +1659,31 @@ } -/* yield_stmt: 'yield' testlist +/* yield_expr: 'yield' [testlist] + */ +static int +validate_yield_expr(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, yield_expr) + && ((nch == 1) || (nch == 2)) + && validate_name(CHILD(tree, 0), "yield")); + + if (res && (nch == 2)) + res = validate_testlist(CHILD(tree, 1)); + + return (res); +} + + +/* yield_stmt: yield_expr */ static int validate_yield_stmt(node *tree) { return (validate_ntype(tree, yield_stmt) - && validate_numnodes(tree, 2, "yield_stmt") - && validate_name(CHILD(tree, 0), "yield") - && validate_testlist(CHILD(tree, 1))); + && validate_numnodes(tree, 1, "yield_stmt") + && validate_yield_expr(CHILD(tree, 0))); } @@ -2300,8 +2326,12 @@ res = ((nch <= 3) && (validate_rparen(CHILD(tree, nch - 1)))); - if (res && (nch == 3)) - res = validate_testlist_gexp(CHILD(tree, 1)); + if (res && (nch == 3)) { + if (TYPE(CHILD(tree, 1))==yield_expr) + res = validate_yield_expr(CHILD(tree, 1)); + else + res = validate_testlist_gexp(CHILD(tree, 1)); + } break; case LSQB: if (nch == 2) @@ -2914,6 +2944,9 @@ case testlist: res = validate_testlist(tree); break; + case yield_expr: + res = validate_yield_expr(tree); + break; case testlist1: res = validate_testlist1(tree); break; From pje at users.sourceforge.net Tue Aug 2 02:47:18 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Mon, 01 Aug 2005 17:47:18 -0700 Subject: [Python-checkins] python/dist/src/Objects genobject.c,1.4,1.5 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011/Objects Modified Files: genobject.c Log Message: PEP 342 implementation. Per Guido's comments, the generator throw() method still needs to support string exceptions, and allow None for the third argument. Documentation updates are needed, too. Index: genobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/genobject.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- genobject.c 1 Sep 2004 07:02:44 -0000 1.4 +++ genobject.c 2 Aug 2005 00:46:45 -0000 1.5 @@ -15,15 +15,31 @@ static void gen_dealloc(PyGenObject *gen) { + PyObject *self = (PyObject *) gen; + _PyObject_GC_UNTRACK(gen); + if (gen->gi_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) gen); - Py_DECREF(gen->gi_frame); + + + _PyObject_GC_TRACK(self); + + if (gen->gi_frame->f_stacktop!=NULL) { + /* Generator is paused, so we need to close */ + gen->ob_type->tp_del(self); + if (self->ob_refcnt > 0) + return; /* resurrected. :( */ + } + + _PyObject_GC_UNTRACK(self); + Py_XDECREF(gen->gi_frame); PyObject_GC_Del(gen); } + static PyObject * -gen_iternext(PyGenObject *gen) +gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { PyThreadState *tstate = PyThreadState_GET(); PyFrameObject *f = gen->gi_frame; @@ -34,8 +50,24 @@ "generator already executing"); return NULL; } - if (f->f_stacktop == NULL) + if ((PyObject *)f == Py_None || f->f_stacktop == NULL) { + /* Only set exception if called from send() */ + if (arg && !exc) PyErr_SetNone(PyExc_StopIteration); return NULL; + } + + if (f->f_lasti == -1) { + if (arg && arg != Py_None) { + PyErr_SetString(PyExc_TypeError, + "can't send non-None value to a just-started generator"); + return NULL; + } + } else { + /* Push arg onto the frame's value stack */ + result = arg ? arg : Py_None; + Py_INCREF(result); + *(f->f_stacktop++) = result; + } /* Generators always return to their most recent caller, not * necessarily their creator. */ @@ -44,7 +76,7 @@ f->f_back = tstate->frame; gen->gi_running = 1; - result = PyEval_EvalFrame(f); + result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It @@ -58,17 +90,199 @@ if (result == Py_None && f->f_stacktop == NULL) { Py_DECREF(result); result = NULL; + /* Set exception if not called by gen_iternext() */ + if (arg) PyErr_SetNone(PyExc_StopIteration); + } + + if (!result || f->f_stacktop == NULL) { + /* generator can't be rerun, so release the frame */ + Py_DECREF(f); + gen->gi_frame = (PyFrameObject *)Py_None; + Py_INCREF(Py_None); } return result; } +PyDoc_STRVAR(send_doc, +"send(arg) -> send 'arg' into generator, return next yielded value or raise StopIteration."); + +static PyObject * +gen_send(PyGenObject *gen, PyObject *arg) +{ + return gen_send_ex(gen, arg, 0); +} + +PyDoc_STRVAR(close_doc, +"close(arg) -> raise GeneratorExit inside generator."); + +static PyObject * +gen_close(PyGenObject *gen, PyObject *args) +{ + PyObject *retval; + PyErr_SetNone(PyExc_GeneratorExit); + retval = gen_send_ex(gen, Py_None, 1); + if (retval) { + Py_DECREF(retval); + PyErr_SetString(PyExc_RuntimeError, + "generator ignored GeneratorExit"); + return NULL; + } + if ( PyErr_ExceptionMatches(PyExc_StopIteration) + || PyErr_ExceptionMatches(PyExc_GeneratorExit) ) + { + PyErr_Clear(); /* ignore these errors */ + Py_INCREF(Py_None); + return Py_None; + } + return NULL; +} + +static void +gen_del(PyObject *self) +{ + PyObject *res; + PyObject *error_type, *error_value, *error_traceback; + PyGenObject *gen = (PyGenObject *)self; + + if ((PyObject *)gen->gi_frame == Py_None || gen->gi_frame->f_stacktop==NULL) + /* Generator isn't paused, so no need to close */ + return; + + /* Temporarily resurrect the object. */ + assert(self->ob_refcnt == 0); + self->ob_refcnt = 1; + + /* Save the current exception, if any. */ + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + res = gen_close((PyGenObject *)self, NULL); + + if (res == NULL) + PyErr_WriteUnraisable((PyObject *)self); + else + Py_DECREF(res); + + /* Restore the saved exception. */ + PyErr_Restore(error_type, error_value, error_traceback); + + /* Undo the temporary resurrection; can't use DECREF here, it would + * cause a recursive call. + */ + assert(self->ob_refcnt > 0); + if (--self->ob_refcnt == 0) + return; /* this is the normal path out */ + + /* close() resurrected it! Make it look like the original Py_DECREF + * never happened. + */ + { + int refcnt = self->ob_refcnt; + _Py_NewReference(self); + self->ob_refcnt = refcnt; + } + assert(!PyType_IS_GC(self->ob_type) || + _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); + + /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so + * we need to undo that. */ + _Py_DEC_REFTOTAL; + /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object + * chain, so no more to do there. + * If COUNT_ALLOCS, the original decref bumped tp_frees, and + * _Py_NewReference bumped tp_allocs: both of those need to be + * undone. + */ +#ifdef COUNT_ALLOCS + --self->ob_type->tp_frees; + --self->ob_type->tp_allocs; +#endif +} + + + +PyDoc_STRVAR(throw_doc, +"throw(typ[,val[,tb]]) -> raise exception in generator, return next yielded value or raise StopIteration."); + +static PyObject * +gen_throw(PyGenObject *gen, PyObject *args) +{ + PyObject *typ; + PyObject *tb = NULL; + PyObject *val = NULL; + + if (!PyArg_ParseTuple(args, "O|OO:throw", &typ, &val, &tb)) + return NULL; + + if (tb && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "throw() third argument must be a traceback object"); + return NULL; + } + + Py_INCREF(typ); + Py_XINCREF(val); + Py_XINCREF(tb); + + if (PyClass_Check(typ)) { + PyErr_NormalizeException(&typ, &val, &tb); + } + + else if (PyInstance_Check(typ)) { + /* Raising an instance. The value should be a dummy. */ + if (val && val != Py_None) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto failed_throw; + } + else { + /* Normalize to raise , */ + val = typ; + typ = (PyObject*) ((PyInstanceObject*)typ)->in_class; + Py_INCREF(typ); + } + } + else { + /* Not something you can raise. You get an exception + anyway, just not what you specified :-) */ + PyErr_Format(PyExc_TypeError, + "exceptions must be classes, or instances, not %s", + typ->ob_type->tp_name); + goto failed_throw; + } + + PyErr_Restore(typ,val,tb); + return gen_send_ex(gen, Py_None, 1); + +failed_throw: + /* Didn't use our arguments, so restore their original refcounts */ + Py_DECREF(typ); + Py_XDECREF(val); + Py_XDECREF(tb); + return NULL; +} + + +static PyObject * +gen_iternext(PyGenObject *gen) +{ + return gen_send_ex(gen, NULL, 0); +} + + static PyMemberDef gen_memberlist[] = { {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), RO}, {"gi_running", T_INT, offsetof(PyGenObject, gi_running), RO}, {NULL} /* Sentinel */ }; +static PyMethodDef gen_methods[] = { + {"send",(PyCFunction)gen_send, METH_O, send_doc}, + {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc}, + {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc}, + {NULL, NULL} /* Sentinel */ +}; + PyTypeObject PyGen_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ @@ -99,11 +313,26 @@ offsetof(PyGenObject, gi_weakreflist), /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)gen_iternext, /* tp_iternext */ - 0, /* tp_methods */ + gen_methods, /* tp_methods */ gen_memberlist, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ + + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + gen_del, /* tp_del */ }; PyObject * From montanaro at users.sourceforge.net Tue Aug 2 04:50:28 2005 From: montanaro at users.sourceforge.net (montanaro@users.sourceforge.net) Date: Mon, 01 Aug 2005 19:50:28 -0700 Subject: [Python-checkins] python/dist/src/Lib cgi.py,1.83,1.84 Message-ID: Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31219 Modified Files: cgi.py Log Message: Bring cgi.escape docstring slightly more in line with the library ref manual. Closes #1243553. Index: cgi.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/cgi.py,v retrieving revision 1.83 retrieving revision 1.84 diff -u -d -r1.83 -r1.84 --- cgi.py 8 Jan 2005 13:56:36 -0000 1.83 +++ cgi.py 2 Aug 2005 02:50:25 -0000 1.84 @@ -1041,7 +1041,9 @@ # ========= def escape(s, quote=None): - """Replace special characters '&', '<' and '>' by SGML entities.""" + '''Replace special characters "&", "<" and ">" to HTML-safe sequences. + If the optional flag quote is true, the quotation mark character (") + is also translated.''' s = s.replace("&", "&") # Must be done first! s = s.replace("<", "<") s = s.replace(">", ">") From montanaro at users.sourceforge.net Tue Aug 2 04:54:01 2005 From: montanaro at users.sourceforge.net (montanaro@users.sourceforge.net) Date: Mon, 01 Aug 2005 19:54:01 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libcgi.tex,1.45,1.46 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31743 Modified Files: libcgi.tex Log Message: Minor tweak as a side effect of fixing #1243553. The Unicode name for " is 'quotation mark', so I decided to use it instead of 'double-quote'. Index: libcgi.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libcgi.tex,v retrieving revision 1.45 retrieving revision 1.46 diff -u -d -r1.45 -r1.46 --- libcgi.tex 10 Jul 2004 11:15:56 -0000 1.45 +++ libcgi.tex 2 Aug 2005 02:53:59 -0000 1.46 @@ -404,7 +404,7 @@ \character{\&}, \character{<} and \character{>} in string \var{s} to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the optional flag \var{quote} is -true, the double-quote character (\character{"}) is also translated; +true, the quotation mark character (\character{"}) is also translated; this helps for inclusion in an HTML attribute value, as in \code{}. If the value to be quoted might include single- or double-quote characters, or both, consider using the From rhettinger at users.sourceforge.net Tue Aug 2 05:45:26 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 20:45:26 -0700 Subject: [Python-checkins] python/dist/src/Include setobject.h,2.7,2.8 Message-ID: Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6483/Include Modified Files: setobject.h Log Message: Model set.pop() after dict.popitem(). Index: setobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/setobject.h,v retrieving revision 2.7 retrieving revision 2.8 diff -u -d -r2.7 -r2.8 --- setobject.h 1 Aug 2005 21:39:27 -0000 2.7 +++ setobject.h 2 Aug 2005 03:45:16 -0000 2.8 @@ -13,6 +13,10 @@ 1. Unused: key == NULL 2. Active: key != NULL and key != dummy 3. Dummy: key == dummy + +Note: .pop() abuses the hash field of an Unused or Dummy slot to +hold a search finger. The hash field of Unused or Dummy slots has +no meaning otherwise. */ #define PySet_MINSIZE 8 From rhettinger at users.sourceforge.net Tue Aug 2 05:45:26 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Mon, 01 Aug 2005 20:45:26 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.38,1.39 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6483/Objects Modified Files: setobject.c Log Message: Model set.pop() after dict.popitem(). Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.38 retrieving revision 1.39 diff -u -d -r1.38 -r1.39 --- setobject.c 1 Aug 2005 21:39:28 -0000 1.38 +++ setobject.c 2 Aug 2005 03:45:14 -0000 1.39 @@ -1508,24 +1508,42 @@ set_pop(PySetObject *so) { PyObject *key; - int pos = 0; - int rv; + register setentry *entry; + register int i = 0; - if (!set_next_internal(so, &pos, &key)) { + assert (PyAnySet_Check(so)); + if (so->used == 0) { PyErr_SetString(PyExc_KeyError, "pop from an empty set"); return NULL; } - Py_INCREF(key); - rv = set_discard_internal(so, key); - if (rv == -1) { - Py_DECREF(key); - return NULL; - } else if (rv == DISCARD_NOTFOUND) { - Py_DECREF(key); - PyErr_SetObject(PyExc_KeyError, key); - return NULL; + /* Set entry to "the first" unused or dummy set entry. We abuse + * the hash field of slot 0 to hold a search finger: + * If slot 0 has a value, use slot 0. + * Else slot 0 is being used to hold a search finger, + * and we use its hash value as the first index to look. + */ + entry = &so->table[0]; + if (entry->key == NULL || entry->key == dummy) { + i = (int)entry->hash; + /* The hash field may be a real hash value, or it may be a + * legit search finger, or it may be a once-legit search + * finger that's out of bounds now because it wrapped around + * or the table shrunk -- simply make sure it's in bounds now. + */ + if (i > so->mask || i < 1) + i = 1; /* skip slot 0 */ + while ((entry = &so->table[i])->key == NULL || entry->key==dummy) { + i++; + if (i > so->mask) + i = 1; + } } + key = entry->key; + Py_INCREF(dummy); + entry->key = dummy; + so->used--; + so->table[0].hash = i + 1; /* next place to start */ return key; } From gregorykjohnson at users.sourceforge.net Tue Aug 2 05:49:04 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Mon, 01 Aug 2005 20:49:04 -0700 Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.2, 1.3 test_mailbox.py, 1.2, 1.3 libmailbox.tex, 1.3, 1.4 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6902 Modified Files: mailbox.py test_mailbox.py libmailbox.tex Log Message: * Implement mbox (needs more tests). * Implement mailbox locking. * Replace flush() on mailboxes with close(). Use close() in tests. * Fix numerous non-portable format assumptions by using os.linesep. * Refactor much test code. Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- mailbox.py 30 Jul 2005 22:49:08 -0000 1.2 +++ mailbox.py 2 Aug 2005 03:49:01 -0000 1.3 @@ -12,6 +12,10 @@ import email.Message import email.Generator import rfc822 +try: + import fnctl +except ImportError: + pass __all__ = [ 'open', 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', @@ -162,8 +166,8 @@ if bad_key: raise KeyError, "No message with key(s)" - def flush(self): - """Write any pending changes to disk.""" + def close(self): + """Close mailbox and write any pending changes to disk.""" raise NotImplementedError, "Method must be implemented by subclass" def _dump_message(self, message, target): @@ -301,8 +305,8 @@ self._refresh() return len(self._toc) - def flush(self): - """Write any pending changes to disk.""" + def close(self): + """Close mailbox and write any pending changes to disk.""" return # Maildir changes are always written immediately. def list_folders(self): @@ -396,6 +400,181 @@ raise KeyError, "No message with key '%s'" % key +class mbox(Mailbox): + """A classic mbox mailbox.""" + + def __init__(self, path, factory=None): + """Initialize an mbox instance.""" + Mailbox.__init__(self, path, factory) + try: + f = file(self._path, 'r+') + except IOError, e: + if e.errno == errno.ENOENT: + f = file(self._path, 'w+') + elif e.errno == errno.EACCES: + f = file(self._path, 'r') + else: + raise + try: + _lock_file(f) + except: + f.close() + raise + self._file = f + self._toc = None + self._next_key = 0 + + def add(self, message): + """Add message and return assigned key.""" + self._lookup() + self._toc[self._next_key] = self._append_message(message) + self._next_key += 1 + return self._next_key - 1 + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + del self._toc[key] + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + start, stop = self._append_message(message) + self._toc[key] = (start, stop) + + def get_message(self, key): + start, stop = self._lookup(key) + self._file.seek(start) + from_line = self._file.readline() + msg = mboxMessage(self._file.read(stop - self._file.tell())) + msg.set_from(from_line[5:-1]) + return msg + + def get_string(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + return self._file.read(stop - self._file.tell()) + + def get_file(self, key, from_=False): + """Return a file-like representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + return _PartialFile(self._file, self._file.tell(), stop) + + def iterkeys(self): + """Return an iterator over keys.""" + self._lookup() + for key in self._toc.keys(): + yield key + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._lookup() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._lookup() + return len(self._toc) + + def close(self): + """Close mailbox and write any pending changes to disk.""" + self._lookup() + f = file(self._path + '.tmp', 'w') + try: + new_toc = {} + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + if f.tell() != 0: + f.write(os.linesep) + new_start = f.tell() + while True: + buffer = self._file.read(min(4096, + stop - self._file.tell())) + if buffer == '': + break + f.write(buffer) + new_toc[key] = (new_start, f.tell()) + finally: + f.close() + try: + os.rename(self._path + '.tmp', self._path) + except oserror, e: + if e.errno == errno.eexist: + # xxx: is this is the exception Windows raises? + os.remove(self._path) + os.rename(self._path + '.tmp', self._path) + else: + raise + self._file.close() + if os.path.exists(self._path + '.lock'): + os.remove(self._path + '.lock') + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + prev_line = '' + while True: + pos = self._file.tell() + line = self._file.readline() + if line[:5] == 'From ': + starts.append(pos) + # The preceeding newline is part of the separator, e.g., + # "\nFrom .*\n", not part of the previous message. Ignore it. + if prev_line != '': + stops.append(pos - len(os.linesep)) + elif line == '': + stops.append(pos) + break + prev_line = line + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + def _lookup(self, key=None): + """Return (start, stop) for given key, or raise a KeyError.""" + if self._toc is None: + self._generate_toc() + if key is not None: + try: + return self._toc[key] + except IndexError: + raise KeyError, "No message with key '%s'" % key + + def _append_message(self, message): + """Append message to mailbox and return (start, stop) offset.""" + from_line = None + if isinstance(message, str) and message[:5] == 'From ': + newline = message.find(os.linesep) + if newline != -1: + from_line = message[:newline] + message = message[newline + len(os.linesep):] + else: + from_line = message + message = '' + elif isinstance(message, _mboxMMDFMessage): + from_line = 'From ' + message.get_from() + elif isinstance(message, email.Message.Message): + from_line = message.get_unixfrom() + if from_line is None: + from_line = 'From MAILER-DAEMON %s' % \ + time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime()) + self._file.seek(0, 2) + if self._file.tell() == 0: + start = self._file.tell() + self._file.write('%s%s' % (from_line, os.linesep)) + else: + start = self._file.tell() + 1 + self._file.write('%s%s%s' % (os.linesep, from_line, os.linesep)) + self._dump_message(message, self._file) + return (start, self._file.tell()) + + class Message(email.Message.Message): """Message with mailbox-format-specific properties.""" @@ -533,6 +712,7 @@ elif 'Return-Path' in message: # XXX: generate "From " line from Return-Path: and Received: pass + Message.__init__(self, message) def get_from(self): @@ -876,3 +1056,53 @@ class Error(Exception): """Raised for module-specific errors.""" + + +def _lock_file(f): + """Use various means to lock f (f.name should be absolute).""" + if 'fcntl' in globals(): + try: + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError, e: + if e.errno == errno.EAGAIN: + raise Error, 'Failed to acquire exclusive lock ' \ + 'using lockf: %s' % f.name + elif e.errno == errno.EBADF: + try: + fcntl.lockf(f, fcntl.LOCK_SH | fntl.LOCK_NB) + except IOError, e: + if e.errno == errno.EAGAIN: + raise Error, 'Failed to acquire shared lock ' \ + 'using lockf: %s' % f.name + else: + raise + else: + raise + try: + fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError, e: + if e.errno == errno.EWOULDBLOCK: + raise Error, 'Failed to aquire exclusive lock ' \ + 'using flock: %s' % f.name + else: + raise + tmp_name = f.name + '.%s.%s' % (socket.gethostname(), os.getpid()) + try: + file(tmp_name, 'w').close() + except IOError, e: + if e.errno == errno.EACCESS: + pass + else: + raise + else: + try: + if hasattr(os, 'link'): + os.link(tmp_name, f.name + '.lock') + os.unlink(tmp_name) + else: + os.rename(tmp_name, f.name + '.lock') + except OSError, e: + if e.errno == errno.EEXIST: + raise Error, 'Failed to acquire dot lock: %s' % f.name + else: + raise Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- test_mailbox.py 30 Jul 2005 22:49:08 -0000 1.2 +++ test_mailbox.py 2 Aug 2005 03:49:01 -0000 1.3 @@ -10,6 +10,7 @@ from test import test_support import unittest import mailbox +import glob class TestBase(unittest.TestCase): @@ -44,191 +45,172 @@ class TestMailbox(TestBase): _factory = None # Overridden by subclasses to reuse tests [...1009 lines suppressed...] +-- +Gregory K. Johnson +""", +"""H4sICM2D1UIAA3RleHQAC8nILFYAokSFktSKEoW0zJxUPa7wzJIMhZLyfIWczLzUYj0uAHTs +3FYlAAAA +""") def test_main(): - test_support.run_unittest(TestMaildir, TestMessage, TestMaildirMessage, - TestMboxMessage, TestMHMessage, TestBabylMessage, - TestMMDFMessage, TestMessageConversion, - TestProxyFile, TestPartialFile) + tests = (TestMaildir, TestMbox, TestMessage, TestMaildirMessage, + TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, + TestMessageConversion, TestProxyFile, TestPartialFile) + test_support.run_unittest(*tests) if __name__ == '__main__': Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- libmailbox.tex 30 Jul 2005 22:49:08 -0000 1.3 +++ libmailbox.tex 2 Aug 2005 03:49:01 -0000 1.4 @@ -242,10 +242,10 @@ are not supported.} \end{methoddesc} -\begin{methoddesc}{flush}{} -Write any pending changes to the filesystem. For some \class{Mailbox} -subclasses, this is done automatically and calling \method{flush()} has no -effect. More specific documentation is provided by each subclass. +\begin{methoddesc}{close}{} +Close the mailbox and write any pending changes to the filesystem. For some +\class{Mailbox} subclasses, changes are written immediately even without +calling this method. \end{methoddesc} @@ -316,9 +316,9 @@ these methods to manipulate the same mailbox simultaneously.} \end{methoddesc} -\begin{methoddesc}{flush}{} -All changes to Maildir mailboxes are immediately applied. This method does -nothing. +\begin{methoddesc}{close}{} +All changes to Maildir mailboxes are immediately applied even without calling +this method. \end{methoddesc} \begin{methoddesc}{get_file}{key} @@ -368,6 +368,9 @@ \begin{seealso} \seelink{http://www.qmail.org/man/man5/mbox.html}{mbox man page from qmail}{A specification of the format and its variations.} + \seelink{http://www.tin.org/bin/man.cgi?section=5\&topic=mbox}{mbox man + page from tin}{Another specification of the format, with details on + locking.} \seelink{http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html} {Configuring Netscape Mail on \UNIX{}: Why The Content-Length Format is Bad}{An argument for using the original mbox format rather than a @@ -451,8 +454,9 @@ XXX \end{methoddesc} -\begin{methoddesc}{flush}{} -All changes to MH mailboxes are immediately applied. This method does nothing. +\begin{methoddesc}{close}{} +All changes to MH mailboxes are immediately applied even without calling this +method. \end{methoddesc} \begin{classdesc}{MH}{path\optional{, factory}} From birkenfeld at users.sourceforge.net Tue Aug 2 12:28:18 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Tue, 02 Aug 2005 03:28:18 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libre.tex,1.113,1.114 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31597/Doc/lib Modified Files: libre.tex Log Message: [ 1243192 ] Incorrect documentation of re.UNICODE Index: libre.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libre.tex,v retrieving revision 1.113 retrieving revision 1.114 diff -u -d -r1.113 -r1.114 --- libre.tex 1 Jan 2005 00:28:37 -0000 1.113 +++ libre.tex 2 Aug 2005 10:28:08 -0000 1.114 @@ -342,17 +342,33 @@ at the beginning or end of a word. This is just the opposite of {}\code{\e b}, so is also subject to the settings of \code{LOCALE} and \code{UNICODE}. -\item[\code{\e d}]Matches any decimal digit; this is -equivalent to the set \regexp{[0-9]}. +\item[\code{\e d}]When the \constant{UNICODE} flag is not specified, matches +any decimal digit; this is equivalent to the set \regexp{[0-9]}. +With \constant{UNICODE}, it will match whatever is classified as a digit +in the Unicode character properties database. -\item[\code{\e D}]Matches any non-digit character; this is -equivalent to the set \regexp{[{\textasciicircum}0-9]}. +\item[\code{\e D}]When the \constant{UNICODE} flag is not specified, matches +any non-digit character; this is equivalent to the set +\regexp{[{\textasciicircum}0-9]}. With \constant{UNICODE}, it will match +anything other than character marked as digits in the Unicode character +properties database. -\item[\code{\e s}]Matches any whitespace character; this is +\item[\code{\e s}]When the \constant{LOCALE} and \constant{UNICODE} +flags are not specified, matches any whitespace character; this is equivalent to the set \regexp{[ \e t\e n\e r\e f\e v]}. +With \constant{LOCALE}, it will match this set plus whatever characters +are defined as space for the current locale. If \constant{UNICODE} is set, +this will match the characters \regexp{[ \e t\e n\e r\e f\e v]} plus +whatever is classified as space in the Unicode character properties +database. -\item[\code{\e S}]Matches any non-whitespace character; this is -equivalent to the set \regexp{[\textasciicircum\ \e t\e n\e r\e f\e v]}. +\item[\code{\e S}]When the \constant{LOCALE} and \constant{UNICODE} +flags are not specified, matches any non-whitespace character; this is +equivalent to the set \regexp{[\textasciicircum\ \e t\e n\e r\e f\e v]} +With \constant{LOCALE}, it will match any character not in this set, +and not defined as space in the current locale. If \constant{UNICODE} +is set, this will match anything other than \regexp{[ \e t\e n\e r\e f\e v]} +and characters marked as space in the Unicode character properties database. \item[\code{\e w}]When the \constant{LOCALE} and \constant{UNICODE} flags are not specified, matches any alphanumeric character and the @@ -468,8 +484,8 @@ \begin{datadesc}{L} \dataline{LOCALE} -Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, and -\regexp{\e B} dependent on the current locale. +Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, \regexp{\e B}, +\regexp{\e s} and \regexp{\e S} dependent on the current locale. \end{datadesc} \begin{datadesc}{M} @@ -493,8 +509,9 @@ \begin{datadesc}{U} \dataline{UNICODE} -Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, and -\regexp{\e B} dependent on the Unicode character properties database. +Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, \regexp{\e B}, +\regexp{\e d}, \regexp{\e D}, \regexp{\e s} and \regexp{\e S} +dependent on the Unicode character properties database. \versionadded{2.0} \end{datadesc} From birkenfeld at users.sourceforge.net Tue Aug 2 12:28:40 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Tue, 02 Aug 2005 03:28:40 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1327,1.1328 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31597/Misc Modified Files: NEWS Log Message: [ 1243192 ] Incorrect documentation of re.UNICODE Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1327 retrieving revision 1.1328 diff -u -d -r1.1327 -r1.1328 --- NEWS 31 Jul 2005 01:16:35 -0000 1.1327 +++ NEWS 2 Aug 2005 10:28:04 -0000 1.1328 @@ -433,6 +433,8 @@ Documentation ------------- +- Bug #1243192: re.UNICODE and re.LOCALE affect \d, \D, \s and \S. + - Bug #755617: Document the effects of os.chown() on Windows. - Patch #1180012: The documentation for modulefinder is now in the library reference. From birkenfeld at users.sourceforge.net Tue Aug 2 12:30:24 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Tue, 02 Aug 2005 03:30:24 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libre.tex, 1.112.2.1, 1.112.2.2 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31952/Doc/lib Modified Files: Tag: release24-maint libre.tex Log Message: backport [ 1243192 ] Incorrect documentation of re.UNICODE Index: libre.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libre.tex,v retrieving revision 1.112.2.1 retrieving revision 1.112.2.2 diff -u -d -r1.112.2.1 -r1.112.2.2 --- libre.tex 1 Jan 2005 00:34:53 -0000 1.112.2.1 +++ libre.tex 2 Aug 2005 10:30:08 -0000 1.112.2.2 @@ -342,17 +342,33 @@ at the beginning or end of a word. This is just the opposite of {}\code{\e b}, so is also subject to the settings of \code{LOCALE} and \code{UNICODE}. -\item[\code{\e d}]Matches any decimal digit; this is -equivalent to the set \regexp{[0-9]}. +\item[\code{\e d}]When the \constant{UNICODE} flag is not specified, matches +any decimal digit; this is equivalent to the set \regexp{[0-9]}. +With \constant{UNICODE}, it will match whatever is classified as a digit +in the Unicode character properties database. -\item[\code{\e D}]Matches any non-digit character; this is -equivalent to the set \regexp{[{\textasciicircum}0-9]}. +\item[\code{\e D}]When the \constant{UNICODE} flag is not specified, matches +any non-digit character; this is equivalent to the set +\regexp{[{\textasciicircum}0-9]}. With \constant{UNICODE}, it will match +anything other than character marked as digits in the Unicode character +properties database. -\item[\code{\e s}]Matches any whitespace character; this is +\item[\code{\e s}]When the \constant{LOCALE} and \constant{UNICODE} +flags are not specified, matches any whitespace character; this is equivalent to the set \regexp{[ \e t\e n\e r\e f\e v]}. +With \constant{LOCALE}, it will match this set plus whatever characters +are defined as space for the current locale. If \constant{UNICODE} is set, +this will match the characters \regexp{[ \e t\e n\e r\e f\e v]} plus +whatever is classified as space in the Unicode character properties +database. -\item[\code{\e S}]Matches any non-whitespace character; this is -equivalent to the set \regexp{[\textasciicircum\ \e t\e n\e r\e f\e v]}. +\item[\code{\e S}]When the \constant{LOCALE} and \constant{UNICODE} +flags are not specified, matches any non-whitespace character; this is +equivalent to the set \regexp{[\textasciicircum\ \e t\e n\e r\e f\e v]} +With \constant{LOCALE}, it will match any character not in this set, +and not defined as space in the current locale. If \constant{UNICODE} +is set, this will match anything other than \regexp{[ \e t\e n\e r\e f\e v]} +and characters marked as space in the Unicode character properties database. \item[\code{\e w}]When the \constant{LOCALE} and \constant{UNICODE} flags are not specified, matches any alphanumeric character and the @@ -468,8 +484,8 @@ \begin{datadesc}{L} \dataline{LOCALE} -Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, and -\regexp{\e B} dependent on the current locale. +Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, \regexp{\e B}, +\regexp{\e s} and \regexp{\e S} dependent on the current locale. \end{datadesc} \begin{datadesc}{M} @@ -493,8 +509,9 @@ \begin{datadesc}{U} \dataline{UNICODE} -Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, and -\regexp{\e B} dependent on the Unicode character properties database. +Make \regexp{\e w}, \regexp{\e W}, \regexp{\e b}, \regexp{\e B}, +\regexp{\e d}, \regexp{\e D}, \regexp{\e s} and \regexp{\e S} +dependent on the Unicode character properties database. \versionadded{2.0} \end{datadesc} From birkenfeld at users.sourceforge.net Tue Aug 2 12:30:24 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Tue, 02 Aug 2005 03:30:24 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.67, 1.1193.2.68 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31952/Misc Modified Files: Tag: release24-maint NEWS Log Message: backport [ 1243192 ] Incorrect documentation of re.UNICODE Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.67 retrieving revision 1.1193.2.68 diff -u -d -r1.1193.2.67 -r1.1193.2.68 --- NEWS 26 Jul 2005 23:59:58 -0000 1.1193.2.67 +++ NEWS 2 Aug 2005 10:30:08 -0000 1.1193.2.68 @@ -98,6 +98,8 @@ Documentation ------------- +- Bug #1243192: re.UNICODE and re.LOCALE affect \d, \D, \s and \S. + - Bug #755617: Document the effects of os.chown() on Windows. - Patch #1180012: The documentation for modulefinder is now in the library reference. From birkenfeld at users.sourceforge.net Tue Aug 2 16:00:01 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Tue, 02 Aug 2005 07:00:01 -0700 Subject: [Python-checkins] python/nondist/sandbox/path path.py,1.8,1.9 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/path In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3627 Modified Files: path.py Log Message: Use self.__class__ instead of hardcoded Path. Index: path.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/path/path.py,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- path.py 30 Jul 2005 12:14:14 -0000 1.8 +++ path.py 2 Aug 2005 13:59:58 -0000 1.9 @@ -59,21 +59,21 @@ The argument can be either a string or an existing Path object. """ if not args: - return Path(os.curdir) + return typ(os.curdir) for arg in args: if not isinstance(arg, basestring): - raise ValueError("Path() arguments must be Path, str or unicode") + raise ValueError("%s() arguments must be Path, str or unicode" % typ.__name__) if len(args) == 1: return _base.__new__(typ, *args) else: - return Path(os.path.join(*args)) + return typ(os.path.join(*args)) # Iterating over a string yields its parts def __iter__(self): return iter(self.parts()) def __repr__(self): - return 'Path(%s)' % repr(_base(self)) + return '%s(%r)' % (self.__class__.__name__, _base(self)) def base(self): return _base(self) @@ -82,12 +82,12 @@ # Caution: this is not a join! def __add__(self, other): if isinstance(other, basestring): - return Path(_base(self) + other) + return self.__class__(_base(self) + other) return NotImplemented def __radd__(self, other): if isinstance(other, basestring): - return Path(other + _base(self)) + return self.__class__(other + _base(self)) return NotImplemented # The / joins paths @@ -98,13 +98,13 @@ # Alternative constructor. - @staticmethod - def cwd(): + @classmethod + def cwd(cls): """ Return the current working directory as a path object. """ if os.path.supports_unicode_filenames: - return Path(os.getcwdu()) + return cls(os.getcwdu()) else: - return Path(os.getcwd()) + return cls(os.getcwd()) # --- Operations which return strings @@ -113,7 +113,7 @@ os.path.basename, None, None, """ The name of this file or directory without the full path. - For example, path('/usr/local/lib/libpython.so').basename == 'libpython.so' + For example, Path('/usr/local/lib/libpython.so').basename == 'libpython.so' """) def _get_namebase(self): @@ -132,8 +132,8 @@ _get_namebase, None, None, """ The same as Path.basename, but with one file extension stripped off. - For example, path('/home/guido/python.tar.gz').basename == 'python.tar.gz', - but path('/home/guido/python.tar.gz').namebase == 'python.tar' + For example, Path('/home/guido/python.tar.gz').basename == 'python.tar.gz', + but Path('/home/guido/python.tar.gz').namebase == 'python.tar' """) ext = property( @@ -149,22 +149,22 @@ # --- Operations which return Path objects def abspath(self): - return Path(os.path.abspath(self)) + return self.__class__(os.path.abspath(self)) def normcase(self): - return Path(os.path.normcase(self)) + return self.__class__(os.path.normcase(self)) def normpath(self): - return Path(os.path.normpath(self)) + return self.__class__(os.path.normpath(self)) def realpath(self): - return Path(os.path.realpath(self)) + return self.__class__(os.path.realpath(self)) def expanduser(self): - return Path(os.path.expanduser(self)) + return self.__class__(os.path.expanduser(self)) def expandvars(self): - return Path(os.path.expandvars(self)) + return self.__class__(os.path.expandvars(self)) def expand(self): """ Clean up a filename by calling expandvars(), @@ -176,7 +176,7 @@ return self.expandvars().expanduser().normpath() def _get_directory(self): - return Path(os.path.dirname(self)) + return self.__class__(os.path.dirname(self)) directory = property( _get_directory, None, None, @@ -198,7 +198,7 @@ def splitpath(self): """ p.splitpath() -> Return (p.directory, p.basename). """ parent, child = os.path.split(self) - return Path(parent), child + return self.__class__(parent), child def splitdrive(self): """ p.splitdrive() -> Return (Path(p.drive), ). @@ -208,7 +208,7 @@ is simply (Path(''), p). This is always the case on Unix. """ drive, rel = os.path.splitdrive(self) - return Path(drive), rel + return self.__class__(drive), rel def splitext(self): """ p.splitext() -> Return (p.stripext(), p.ext). @@ -220,16 +220,16 @@ last path segment. """ filename, ext = os.path.splitext(self) - return Path(filename), ext + return self.__class__(filename), ext if hasattr(os.path, 'splitunc'): def splitunc(self): unc, rest = os.path.splitunc(self) - return Path(unc), rest + return self.__class__(unc), rest def _get_uncshare(self): unc, r = os.path.splitunc(self) - return Path(unc) + return self.__class__(unc) uncshare = property( _get_uncshare, None, None, @@ -241,7 +241,7 @@ character (os.sep) if needed. Returns a new path object. """ - return Path(os.path.join(self, *args)) + return self.__class__(os.path.join(self, *args)) joinpath = joinwith @@ -271,7 +271,7 @@ """ Return this path as a relative path, based from the current working directory. """ - return Path.cwd().relpathto(self) + return self.__class__.cwd().relpathto(self) def relpathto(self, dest): """ Return a relative path from self to dest. @@ -281,7 +281,7 @@ dest.abspath(). """ origin = self.abspath() - dest = Path(dest).abspath() + dest = self.__class__(dest).abspath() orig_list = origin.normcase().parts() # Don't normcase dest! We want to preserve the case. @@ -306,15 +306,15 @@ segments += dest_list[i:] if len(segments) == 0: # If they happen to be identical, use os.curdir. - return Path(os.curdir) + return self.__class__(os.curdir) else: - return Path(os.path.join(*segments)) + return self.__class__(os.path.join(*segments)) # --- Listing, searching, walking, and matching def listdir(self): - return [Path(p) for p in os.listdir(self)] + return [self.__class__(p) for p in os.listdir(self)] def children(self, pattern=None): """ D.children() -> List of items in this directory, @@ -422,7 +422,7 @@ For example, path('/users').glob('*/bin/*') returns a list of all the files users have in their bin directories. """ - return map(Path, glob.glob(self / pattern)) + return map(self.__class__, glob.glob(self / pattern)) # --- Reading or writing an entire file at once. @@ -799,7 +799,7 @@ The result may be an absolute or a relative path. """ - return Path(os.readlink(self)) + return self.__class__(os.readlink(self)) def readlinkabs(self): """ Return the path to which this symbolic link points. @@ -833,4 +833,7 @@ if hasattr(os, 'startfile'): def startfile(self): os.startfile(self) - + + if hasattr(os, 'chdir'): + def chdir(self): + os.chdir(self) From akuchling at users.sourceforge.net Tue Aug 2 19:13:24 2005 From: akuchling at users.sourceforge.net (akuchling@users.sourceforge.net) Date: Tue, 02 Aug 2005 10:13:24 -0700 Subject: [Python-checkins] python/dist/src/Doc/whatsnew whatsnew25.tex, 1.14, 1.15 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/whatsnew In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21649 Modified Files: whatsnew25.tex Log Message: Add note Index: whatsnew25.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/whatsnew/whatsnew25.tex,v retrieving revision 1.14 retrieving revision 1.15 diff -u -d -r1.14 -r1.15 --- whatsnew25.tex 22 Jul 2005 18:39:19 -0000 1.14 +++ whatsnew25.tex 2 Aug 2005 17:13:21 -0000 1.15 @@ -100,6 +100,20 @@ %====================================================================== +\section{PEP 342: New Generator Features} + +XXX write this section + +\begin{seealso} + +\seepep{342}{Coroutines via Enhanced Generators}{PEP written by +Guido van Rossum and Phillip J. Eby; +implemented by Phillip J. Eby.} + +\end{seealso} + + +%====================================================================== \section{Other Language Changes} Here are all of the changes that Python 2.5 makes to the core Python From akuchling at users.sourceforge.net Tue Aug 2 19:20:38 2005 From: akuchling at users.sourceforge.net (akuchling@users.sourceforge.net) Date: Tue, 02 Aug 2005 10:20:38 -0700 Subject: [Python-checkins] python/dist/src/Doc/whatsnew whatsnew25.tex, 1.15, 1.16 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/whatsnew In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23280 Modified Files: whatsnew25.tex Log Message: Add example Index: whatsnew25.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/whatsnew/whatsnew25.tex,v retrieving revision 1.15 retrieving revision 1.16 diff -u -d -r1.15 -r1.16 --- whatsnew25.tex 2 Aug 2005 17:13:21 -0000 1.15 +++ whatsnew25.tex 2 Aug 2005 17:20:36 -0000 1.16 @@ -60,9 +60,21 @@ server_log = functional.partial(log, subsystem='server') \end{verbatim} -Here's another example, from a program that uses PyGTk. +Here's another example, from a program that uses PyGTk. Here a +context-sensitive pop-up menu is being constructed dynamically. The +callback provided for the menu option is a partially applied version +of the \method{open_item()} method, where the first argument has been +provided. -% XXX add example from my GTk programming +\begin{verbatim} +... +class Application: + def open_item(self, path): + ... + def init (self): + open_func = functional.partial(self.open_item, item_path) + popup_menu.append( ("Open", open_func, 1) ) +\end{verbatim} \begin{seealso} From gregorykjohnson at users.sourceforge.net Tue Aug 2 21:18:56 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Tue, 02 Aug 2005 12:18:56 -0700 Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.3, 1.4 test_mailbox.py, 1.3, 1.4 libmailbox.tex, 1.4, 1.5 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23886 Modified Files: mailbox.py test_mailbox.py libmailbox.tex Log Message: * Implement MMDF, refactoring much code from mbox * Improve locking for mbox and MMDF: * Use os.open() with O_EXCL for dot locking * Only lock while writing, as other mailbox manipulation programs do * Bring back flush() method in addition to close() * Add some mbox- and MMDF-specific tests Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- mailbox.py 2 Aug 2005 03:49:01 -0000 1.3 +++ mailbox.py 2 Aug 2005 19:18:53 -0000 1.4 @@ -166,8 +166,12 @@ if bad_key: raise KeyError, "No message with key(s)" + def flush(self): + """Write any pending changes to the disk.""" + raise NotImplementedError, "Method must be implemented by subclass" + def close(self): - """Close mailbox and write any pending changes to disk.""" + """Flush and close the mailbox.""" raise NotImplementedError, "Method must be implemented by subclass" def _dump_message(self, message, target): @@ -305,10 +309,14 @@ self._refresh() return len(self._toc) - def close(self): - """Close mailbox and write any pending changes to disk.""" + def flush(self): + """Write any pending changes to disk.""" return # Maildir changes are always written immediately. + def close(self): + """Flush and close the mailbox.""" + return + def list_folders(self): """Return a list of folder names.""" result = [] @@ -400,11 +408,11 @@ raise KeyError, "No message with key '%s'" % key -class mbox(Mailbox): - """A classic mbox mailbox.""" +class _mboxMMDF(Mailbox): + """An mbox or MMDF mailbox.""" def __init__(self, path, factory=None): - """Initialize an mbox instance.""" + """Initialize an mbox or MMDF mailbox.""" Mailbox.__init__(self, path, factory) try: f = file(self._path, 'r+') @@ -415,14 +423,11 @@ f = file(self._path, 'r') else: raise - try: - _lock_file(f) - except: - f.close() - raise self._file = f self._toc = None self._next_key = 0 + self._pending = False # No changes require rewriting the file. + self._mtime = os.fstat(self._file.fileno()).st_mtime def add(self, message): """Add message and return assigned key.""" @@ -434,24 +439,28 @@ def remove(self, key): """Remove the keyed message; raise KeyError if it doesn't exist.""" del self._toc[key] + self._pending = True def __setitem__(self, key, message): """Replace the keyed message; raise KeyError if it doesn't exist.""" self._lookup(key) start, stop = self._append_message(message) self._toc[key] = (start, stop) + self._pending = True def get_message(self, key): start, stop = self._lookup(key) + self._assert_mtime() self._file.seek(start) from_line = self._file.readline() - msg = mboxMessage(self._file.read(stop - self._file.tell())) + msg = self._message_factory(self._file.read(stop - self._file.tell())) msg.set_from(from_line[5:-1]) return msg def get_string(self, key, from_=False): """Return a string representation or raise a KeyError.""" start, stop = self._lookup(key) + self._assert_mtime() self._file.seek(start) if not from_: self._file.readline() @@ -460,6 +469,7 @@ def get_file(self, key, from_=False): """Return a file-like representation or raise a KeyError.""" start, stop = self._lookup(key) + self._assert_mtime() self._file.seek(start) if not from_: self._file.readline() @@ -481,60 +491,51 @@ self._lookup() return len(self._toc) - def close(self): - """Close mailbox and write any pending changes to disk.""" - self._lookup() - f = file(self._path + '.tmp', 'w') - try: - new_toc = {} - for key in sorted(self._toc.keys()): - start, stop = self._toc[key] - self._file.seek(start) - if f.tell() != 0: - f.write(os.linesep) - new_start = f.tell() - while True: - buffer = self._file.read(min(4096, - stop - self._file.tell())) - if buffer == '': - break - f.write(buffer) - new_toc[key] = (new_start, f.tell()) - finally: - f.close() - try: - os.rename(self._path + '.tmp', self._path) - except oserror, e: - if e.errno == errno.eexist: - # xxx: is this is the exception Windows raises? - os.remove(self._path) + def flush(self): + """Write any pending changes to disk.""" + if self._pending: + self._lookup() + dotlock_inode = _lock_file(self._file) + self._assert_mtime() + f = file(self._path + '.tmp', 'w') + try: + try: + new_toc = {} + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + self._pre_write_hook(f) + new_start = f.tell() + while True: + buffer = self._file.read( + min(4096, stop - self._file.tell())) + if buffer == '': + break + f.write(buffer) + new_toc[key] = (new_start, f.tell()) + self._post_write_hook(f) + finally: + f.close() + finally: + _unlock_file(self._file, dotlock_inode) + try: os.rename(self._path + '.tmp', self._path) - else: - raise - self._file.close() - if os.path.exists(self._path + '.lock'): - os.remove(self._path + '.lock') + except OSError, e: + if e.errno == errno.EEXIST: + # XXX: Is this the exception Windows raises? + os.remove(self._path) + os.rename(self._path + '.tmp', self._path) + else: + raise + self._file.flush() + self._set_mtime() + _unlock_file(self._file, dotlock_inode) + self._pending = False - def _generate_toc(self): - """Generate key-to-(start, stop) table of contents.""" - starts, stops = [], [] - self._file.seek(0) - prev_line = '' - while True: - pos = self._file.tell() - line = self._file.readline() - if line[:5] == 'From ': - starts.append(pos) - # The preceeding newline is part of the separator, e.g., - # "\nFrom .*\n", not part of the previous message. Ignore it. - if prev_line != '': - stops.append(pos - len(os.linesep)) - elif line == '': - stops.append(pos) - break - prev_line = line - self._toc = dict(enumerate(zip(starts, stops))) - self._next_key = len(self._toc) + def close(self): + """Flush and close the mailbox.""" + self.flush() + self._file.close() def _lookup(self, key=None): """Return (start, stop) for given key, or raise a KeyError.""" @@ -564,15 +565,113 @@ if from_line is None: from_line = 'From MAILER-DAEMON %s' % \ time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime()) - self._file.seek(0, 2) - if self._file.tell() == 0: + dotlock_inode = _lock_file(self._file) + self._assert_mtime() + try: + self._file.seek(0, 2) + self._pre_write_hook(self._file) start = self._file.tell() self._file.write('%s%s' % (from_line, os.linesep)) - else: - start = self._file.tell() + 1 - self._file.write('%s%s%s' % (os.linesep, from_line, os.linesep)) - self._dump_message(message, self._file) - return (start, self._file.tell()) + self._dump_message(message, self._file) + stop = self._file.tell() + self._post_write_hook(self._file) + self._file.flush() + self._set_mtime() + finally: + _unlock_file(self._file, dotlock_inode) + return (start, stop) + + def _assert_mtime(self): + """Raise an exception if the file has been externally modified.""" + if self._mtime != os.fstat(self._file.fileno()).st_mtime: + raise Error, 'External modifications detected: use refresh(): ' \ + '%r vs %r' % (self._mtime, + os.fstat(self._file.fileno()).st_mtime) + + def _set_mtime(self): + """Store the current mtime.""" + self._mtime = os.fstat(self._file.fileno()).st_mtime + + + +class mbox(_mboxMMDF): + """A classic mbox mailbox.""" + + def __init__(self, path, factory=None): + """Initialize an mbox mailbox.""" + self._message_factory = mboxMessage + _mboxMMDF.__init__(self, path, factory) + + def _pre_write_hook(self, f): + """Called by close before writing each message.""" + if f.tell() != 0: + f.write(os.linesep) + + def _post_write_hook(self, f): + """Called by close after writing each message.""" + return + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._assert_mtime() + self._file.seek(0) + prev_line = '' + while True: + pos = self._file.tell() + line = self._file.readline() + if line[:5] == 'From ': + starts.append(pos) + # The preceeding newline is part of the separator, e.g., + # "\nFrom .*\n", not part of the previous message. Ignore it. + if prev_line != '': + stops.append(pos - len(os.linesep)) + elif line == '': + stops.append(pos) + break + prev_line = line + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + +class MMDF(_mboxMMDF): + """An MMDF mailbox.""" + + def __init__(self, path, factory=None): + """Initialize an MMDF mailbox.""" + self._message_factory = MMDFMessage + _mboxMMDF.__init__(self, path, factory) + + def _pre_write_hook(self, f): + """Called by close before writing each message.""" + f.write('\001\001\001\001\n') + + def _post_write_hook(self, f): + """Called by close after writing each message.""" + f.write('\n\001\001\001\001\n') + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._assert_mtime() + self._file.seek(0) + while True: + line = self._file.readline() + if line[:4 + len(os.linesep)] == '\001\001\001\001' + os.linesep: + starts.append(self._file.tell()) + while True: + pos = self._file.tell() + line = self._file.readline() + if line == '\001\001\001\001' + os.linesep: + stops.append(pos - len(os.linesep)) + break + elif line == '': + stops.append(pos) + break + elif line == '': + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) class Message(email.Message.Message): @@ -1059,20 +1158,20 @@ def _lock_file(f): - """Use various means to lock f (f.name should be absolute).""" + """Use various means to lock file f. Return dotlock inode or None.""" if 'fcntl' in globals(): try: fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: if e.errno == errno.EAGAIN: - raise Error, 'Failed to acquire exclusive lock ' \ + raise Error, 'Exclusive lock cannot be acquired ' \ 'using lockf: %s' % f.name elif e.errno == errno.EBADF: try: fcntl.lockf(f, fcntl.LOCK_SH | fntl.LOCK_NB) except IOError, e: if e.errno == errno.EAGAIN: - raise Error, 'Failed to acquire shared lock ' \ + raise Error, 'Shared lock cannot be acquired ' \ 'using lockf: %s' % f.name else: raise @@ -1082,27 +1181,55 @@ fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: if e.errno == errno.EWOULDBLOCK: - raise Error, 'Failed to aquire exclusive lock ' \ + raise Error, 'Exclusive lock cannot be acquired ' \ 'using flock: %s' % f.name else: raise tmp_name = f.name + '.%s.%s' % (socket.gethostname(), os.getpid()) try: - file(tmp_name, 'w').close() + dotlock = os.open(tmp_name, os.O_WRONLY | os.O_EXCL | os.O_CREAT, 0600) + dotlock_inode = os.fstat(dotlock).st_ino + os.close(dotlock) except IOError, e: if e.errno == errno.EACCESS: - pass + pass # Without write access, just skip dotlocking. + elif e.errno == errno.EEXIST: + raise Error, 'Failed to create unique file ' \ + 'for dot lock: %s' % tmp_name + else: + raise + try: + if os.path.exists(f.name + '.lock') and \ + time.time() - os.getmtime(f.name + '.lock') > 300: + try: + os.remove(f.name + '.lock') + except IOError, e: + if e.errno == errno.EACCESS: + raise Error, 'Stale dot lock cannot be removed: %s' \ + % f.name + '.lock' + else: + raise + if hasattr(os, 'link'): + os.link(tmp_name, f.name + '.lock') + os.unlink(tmp_name) + else: + os.rename(tmp_name, f.name + '.lock') + except OSError, e: + if e.errno == errno.EEXIST: + raise Error, 'Dot lock exists and cannot be acquired: %s' % \ + f.name + '.lock' else: raise else: - try: - if hasattr(os, 'link'): - os.link(tmp_name, f.name + '.lock') - os.unlink(tmp_name) - else: - os.rename(tmp_name, f.name + '.lock') - except OSError, e: - if e.errno == errno.EEXIST: - raise Error, 'Failed to acquire dot lock: %s' % f.name - else: - raise + return dotlock_inode + return None + + +def _unlock_file(f, dotlock_inode): + """Unlock file f by various means, given a dotlock_inode (or None).""" + if 'fcntl' in globals(): + fcntl.lockf(f, fcntl.LOCK_UN) + fcntl.flock(f, fcntl.LOCK_UN) + if dotlock_inode is not None and \ + dotlock_inode == os.stat(f.name + '.lock').st_ino: + os.remove(f.name + '.lock') Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- test_mailbox.py 2 Aug 2005 03:49:01 -0000 1.3 +++ test_mailbox.py 2 Aug 2005 19:18:53 -0000 1.4 @@ -11,6 +11,10 @@ import unittest import mailbox import glob +try: + import fcntl +except ImportError: + pass class TestBase(unittest.TestCase): @@ -370,13 +374,20 @@ self.assert_(self._box.get_string(key2) == self._template % "changed 2") + def test_flush(self): + # Write changes to disk + self._test_flush_or_close(self._box.flush) + def test_close(self): # Close mailbox and flush changes to disk + self._test_flush_or_close(self._box.close) + + def _test_flush_or_close(self, method): contents = [self._template % i for i in xrange(3)] self._box.add(contents[0]) self._box.add(contents[1]) self._box.add(contents[2]) - self._box.close() + method() self._box = self._factory(self._path) keys = self._box.keys() self.assert_(len(keys) == 3) @@ -602,15 +613,62 @@ self.assert_(self._box._toc == {}) -class TestMbox(TestMailbox): - - _factory = lambda self, path, factory=None: mailbox.mbox(path, factory) +class _TestMboxMMDF(TestMailbox): def tearDown(self): self._delete_recursively(self._path) for lock_remnant in glob.glob(self._path + '.*'): os.remove(lock_remnant) + def test_add_from_string(self): + # Add a string starting with 'From ' to the mailbox + key = self._box.add('From foo at bar blah\nFrom: foo\n\n0') + self.assert_(self._box[key].get_from() == 'foo at bar blah') + self.assert_(self._box[key].get_payload() == '0') + + def test_add_mbox_or_mmdf_message(self): + # Add an mboxMessage or MMDFMessage + for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage): + msg = class_('From foo at bar blah\nFrom: foo\n\n0') + key = self._box.add(msg) + + def test_open_close_open(self): + # Open and inspect previously-created mailbox + values = [self._template % i for i in xrange(3)] + for value in values: + self._box.add(value) + self._box.close() + mtime = os.path.getmtime(self._path) + self._box = self._factory(self._path) + self.assert_(len(self._box) == 3) + for key in self._box.iterkeys(): + self.assert_(self._box.get_string(key) in values) + self._box.close() + self.assert_(mtime == os.path.getmtime(self._path)) + + def test_add_and_close(self): + # Verifying that closing a mailbox doesn't change added items + self._box.add(_sample_message) + for i in xrange(3): + self._box.add(self._template % i) + self._box.add(_sample_message) + self._box._file.flush() + self._box._file.seek(0) + contents = self._box._file.read() + self._box.close() + self.assert_(contents == file(self._path, 'r').read()) + self._box = self._factory(self._path) + + +class TestMbox(_TestMboxMMDF): + + _factory = lambda self, path, factory=None: mailbox.mbox(path, factory) + + +class TestMMDF(_TestMboxMMDF): + + _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory) + class TestMessage(TestBase): @@ -1382,7 +1440,7 @@ def test_main(): - tests = (TestMaildir, TestMbox, TestMessage, TestMaildirMessage, + tests = (TestMaildir, TestMbox, TestMMDF, TestMessage, TestMaildirMessage, TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, TestMessageConversion, TestProxyFile, TestPartialFile) test_support.run_unittest(*tests) Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- libmailbox.tex 2 Aug 2005 03:49:01 -0000 1.4 +++ libmailbox.tex 2 Aug 2005 19:18:54 -0000 1.5 @@ -242,10 +242,14 @@ are not supported.} \end{methoddesc} +\begin{methoddesc}{flush}{} +Write any pending changes to the filesystem. For some \class{Mailbox} +subclasses, changes are written immediately and this method does nothing. +\end{methoddesc} + \begin{methoddesc}{close}{} -Close the mailbox and write any pending changes to the filesystem. For some -\class{Mailbox} subclasses, changes are written immediately even without -calling this method. +Flush the mailbox and close any open files. For some \class{Mailbox} +subclasses, this method does nothing. \end{methoddesc} @@ -316,9 +320,14 @@ these methods to manipulate the same mailbox simultaneously.} \end{methoddesc} +\begin{methoddesc}{flush}{} +All changes to Maildir mailboxes are immediately applied, so this method does +nothing. +\end{methoddesc} + \begin{methoddesc}{close}{} -All changes to Maildir mailboxes are immediately applied even without calling -this method. +\class{Maildir} instances do not keep any open files, so this method does +nothing. \end{methoddesc} \begin{methoddesc}{get_file}{key} @@ -454,9 +463,13 @@ XXX \end{methoddesc} -\begin{methoddesc}{close}{} -All changes to MH mailboxes are immediately applied even without calling this -method. +\begin{methoddesc}{flush}{} +All changes to MH mailboxes are immediately applied, so this method does +nothing. +\end{methoddesc} + +\begin{methoddesc}{flush}{} +\class{MH} instances do not keep any open files, so this method does nothing. \end{methoddesc} \begin{classdesc}{MH}{path\optional{, factory}} From gregorykjohnson at users.sourceforge.net Tue Aug 2 23:46:16 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Tue, 02 Aug 2005 14:46:16 -0700 Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.4, 1.5 test_mailbox.py, 1.4, 1.5 libmailbox.tex, 1.5, 1.6 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29669 Modified Files: mailbox.py test_mailbox.py libmailbox.tex Log Message: * Implement MH (except sequences) * Use mailbox.Error instead of IOError for non-empty folder removal Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- mailbox.py 2 Aug 2005 19:18:53 -0000 1.4 +++ mailbox.py 2 Aug 2005 21:46:13 -0000 1.5 @@ -341,12 +341,12 @@ for entry in os.listdir(os.path.join(path, 'new')) + \ os.listdir(os.path.join(path, 'cur')): if len(entry) < 1 or entry[0] != '.': - raise IOError, "Folder '%s' contains message" % name + raise Error, "Folder '%s' contains message" % name for entry in os.listdir(path): if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ os.path.isdir(os.path.join(path, entry)): - raise IOError, "Folder '%s' contains subdirectory '%s'" % \ - (name, entry) + raise Error, "Folder '%s' contains subdirectory '%s'" % \ + (name, entry) for root, dirs, files in os.walk(path, topdown=False): for entry in files: os.remove(os.path.join(root, entry)) @@ -674,6 +674,176 @@ self._next_key = len(self._toc) +class MH(Mailbox): + """An MH mailbox.""" + + def __init__(self, path, factory=None): + """Initialize an MH instance.""" + Mailbox.__init__(self, path, factory) + if not os.path.exists(self._path): + os.mkdir(self._path, 0700) + + def add(self, message): + """Add message and return assigned key.""" + keys = self.keys() + if len(keys) == 0: + new_key = 1 + else: + new_key = max(keys) + 1 + fd = os.open(os.path.join(self._path, str(new_key)), + os.O_CREAT | os.O_EXCL | os.O_WRONLY) + f = os.fdopen(fd, 'w') + try: + try: + dotlock_inode = _lock_file(f) + try: + self._dump_message(message, f) + # XXX: update sequences based on message + f.flush() + finally: + _unlock_file(f, dotlock_inode) + finally: + f.close() + except: + os.remove(os.path.join(self._path, str(new_key))) + raise + return new_key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + try: + os.remove(os.path.join(self._path, str(key))) + except OSError, e: + if e.errno == errno.ENOENT: + raise KeyError, "No message with key '%s'" % key + else: + raise + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + try: + f_r = file(os.path.join(self._path, str(key)), 'r+') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError, "No message with key '%s'" % key + else: + raise + try: + dotlock_inode = _lock_file(f_r) + try: + f_w = file(os.path.join(self._path, str(key)), 'w') + try: + self._dump_message(message, f_w) + finally: + f_w.close() + finally: + _unlock_file(f_r, dotlock_inode) + finally: + f_r.close() + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + try: + f = file(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError, "No message with key '%s'" % key + else: + raise + try: + msg = MHMessage(f) + finally: + f.close() + # XXX: set sequences on msg + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + try: + f = file(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError, "No message with key '%s'" % key + else: + raise + try: + return f.read() + finally: + f.close() + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + try: + f = file(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError, "No message with key '%s'" % key + else: + raise + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + for x in sorted(int(x) for x in os.listdir(self._path) if x.isdigit()): + yield int(x) + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + return os.path.exists(os.path.join(self._path, str(key))) + + def __len__(self): + """Return a count of messages in the mailbox.""" + return len(list(self.iterkeys())) + + def flush(self): + """Write any pending changes to the disk.""" + return + + def close(self): + """Flush and close the mailbox.""" + return + + def list_folders(self): + """Return a list of folders in this mailbox.""" + result = [] + for entry in os.listdir(self._path): + if os.path.isdir(os.path.join(self._path, entry)): + result.append(entry) + return result + + def open_folder(self, name): + """Return an MH instance for folder name, creating it if necessary.""" + return MH(os.path.join(self._path, name)) + + def remove_folder(self, name): + """Delete folder name.""" + path = os.path.join(self._path, name) + entries = os.listdir(path) + if entries == ['.mh_sequences']: + os.remove(os.path.join(path, '.mh_sequences')) + elif entries == []: + pass + else: + raise Error, "Folder '%s' is not empty" % self._path + os.rmdir(path) + + def list_sequences(self, name): + """Return a list of the names of sequences defined in the mailbox.""" + raise NotImplementedError, 'Method not yet implemented' + + def get_sequence(self, name): + """Return a list of the keys in sequence 'name'.""" + raise NotImplementedError, 'Method not yet implemented' + + def set_sequence(self, name): + """Set the keys in sequence 'name'.""" + raise NotImplementedError, 'Method not yet implemented' + + def pack(self): + """Eliminate numbering gaps in message names. Invalidates keys.""" + raise NotImplementedError, 'Method not yet implemented' + + class Message(email.Message.Message): """Message with mailbox-format-specific properties.""" Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- test_mailbox.py 2 Aug 2005 19:18:53 -0000 1.4 +++ test_mailbox.py 2 Aug 2005 21:46:13 -0000 1.5 @@ -670,6 +670,11 @@ _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory) +class TestMH(TestMailbox): + + _factory = lambda self, path, factory=None: mailbox.MH(path, factory) + + class TestMessage(TestBase): _factory = mailbox.Message # Overridden by subclasses to reuse tests @@ -1440,9 +1445,10 @@ def test_main(): - tests = (TestMaildir, TestMbox, TestMMDF, TestMessage, TestMaildirMessage, - TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, - TestMessageConversion, TestProxyFile, TestPartialFile) + tests = (TestMaildir, TestMbox, TestMMDF, TestMH, TestMessage, + TestMaildirMessage, TestMboxMessage, TestMHMessage, + TestBabylMessage, TestMMDFMessage, TestMessageConversion, + TestProxyFile, TestPartialFile) test_support.run_unittest(*tests) Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- libmailbox.tex 2 Aug 2005 19:18:54 -0000 1.5 +++ libmailbox.tex 2 Aug 2005 21:46:13 -0000 1.6 @@ -291,8 +291,8 @@ \begin{methoddesc}{remove_folder}{name} Delete the folder whose name is \var{name}. If the folder contains any -messages, an \exception{IOError} exception will be raised and the folder will -not be deleted. +messages, a \exception{mailbox.Error} exception will be raised and the folder +will not be deleted. \end{methoddesc} \begin{methoddesc}{clean}{} @@ -404,13 +404,18 @@ \dfn{folders}) in addition to messages. Folders may be nested indefinitely. MH mailboxes support \dfn{sequences}, which are named lists used to logically -group messages without moving them to sub-folders. Some mail reading programs +group messages without moving them to sub-folders. Sequences are defined in a +file called \file{.mh_sequences} in each folder. Some mail reading programs (although not the standard \program{mh} and \program{nmh} implementations) use sequences to the same end as flags are used in other formats: unread messages are added to the "unseen" sequence, replied-to messages are added to the "replied" sequence, and important messages are added upon request to the "flagged" sequence. +\class{MH} manipulates MH mailboxes, but it does not attempt to emulate +\program{mh}. In particular, it does not access or modify \file{context} or +\file{.mh_profile} files. + \class{MH} instances have all of the methods of \class{Mailbox} in addition to the following: @@ -426,8 +431,8 @@ \begin{methoddesc}{remove_folder}{name} Delete the folder whose name is \var{name}. If the folder contains any -messages, an \exception{IOError} exception will be raised and the folder will -not be deleted. +messages, a \exception{mailbox.Error} exception will be raised and the folder +will not be deleted. \end{methoddesc} \begin{methoddesc}{list_sequences}{} @@ -468,14 +473,14 @@ nothing. \end{methoddesc} -\begin{methoddesc}{flush}{} +\begin{methoddesc}{close}{} \class{MH} instances do not keep any open files, so this method does nothing. \end{methoddesc} \begin{classdesc}{MH}{path\optional{, factory}} A subclass of \class{Mailbox} for mailboxes in MH format. Parameters \var{path} -and \var{factory} has the same meaning as with the module-level \method{open()} -function. +and \var{factory} have the same meaning as with the module-level +\method{open()} function. \end{classdesc} \class{MH} instances have all of the methods of \class{Mailbox} in addition to @@ -500,7 +505,7 @@ \begin{classdesc}{Babyl}{path\optional{, factory}} A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameters -\var{path} and \var{factory} has the same meaning as with the module-level +\var{path} and \var{factory} have the same meaning as with the module-level \method{open()} function. \end{classdesc} @@ -531,7 +536,7 @@ \begin{classdesc}{MMDF}{path\optional{, factory}} A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameters -\var{path} and \var{factory} has the same meaning as with the module-level +\var{path} and \var{factory} have the same meaning as with the module-level \method{open()} function. \end{classdesc} From birkenfeld at users.sourceforge.net Wed Aug 3 09:17:36 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Wed, 03 Aug 2005 00:17:36 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libfuncs.tex, 1.185, 1.186 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24435/Doc/lib Modified Files: libfuncs.tex Log Message: bug [ 1250306 ] incorrect description of range function Index: libfuncs.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libfuncs.tex,v retrieving revision 1.185 retrieving revision 1.186 diff -u -d -r1.185 -r1.186 --- libfuncs.tex 22 Jul 2005 18:39:19 -0000 1.185 +++ libfuncs.tex 3 Aug 2005 07:17:33 -0000 1.186 @@ -775,7 +775,7 @@ \var{start} + 2 * \var{step}, \ldots]}. If \var{step} is positive, the last element is the largest \code{\var{start} + \var{i} * \var{step}} less than \var{stop}; if \var{step} is negative, the last - element is the largest \code{\var{start} + \var{i} * \var{step}} + element is the smallest \code{\var{start} + \var{i} * \var{step}} greater than \var{stop}. \var{step} must not be zero (or else \exception{ValueError} is raised). Example: From birkenfeld at users.sourceforge.net Wed Aug 3 09:18:06 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Wed, 03 Aug 2005 00:18:06 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libfuncs.tex, 1.175.2.5, 1.175.2.6 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24581/Doc/lib Modified Files: Tag: release24-maint libfuncs.tex Log Message: backport bug [ 1250306 ] incorrect description of range function Index: libfuncs.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libfuncs.tex,v retrieving revision 1.175.2.5 retrieving revision 1.175.2.6 diff -u -d -r1.175.2.5 -r1.175.2.6 --- libfuncs.tex 22 Jul 2005 18:40:02 -0000 1.175.2.5 +++ libfuncs.tex 3 Aug 2005 07:18:04 -0000 1.175.2.6 @@ -737,7 +737,7 @@ \var{start} + 2 * \var{step}, \ldots]}. If \var{step} is positive, the last element is the largest \code{\var{start} + \var{i} * \var{step}} less than \var{stop}; if \var{step} is negative, the last - element is the largest \code{\var{start} + \var{i} * \var{step}} + element is the smallest \code{\var{start} + \var{i} * \var{step}} greater than \var{stop}. \var{step} must not be zero (or else \exception{ValueError} is raised). Example: From birkenfeld at users.sourceforge.net Wed Aug 3 09:30:13 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Wed, 03 Aug 2005 00:30:13 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1328,1.1329 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26243/Misc Modified Files: NEWS Log Message: patch [ 1105730 ] Faster commonprefix in macpath, ntpath, etc. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1328 retrieving revision 1.1329 diff -u -d -r1.1328 -r1.1329 --- NEWS 2 Aug 2005 10:28:04 -0000 1.1328 +++ NEWS 3 Aug 2005 07:30:10 -0000 1.1329 @@ -178,6 +178,9 @@ Library ------- +- Patch #1105730: Apply the new implementation of commonprefix in posixpath + to ntpath, macpath, os2emxpath and riscospath. + - Fix a problem in Tkinter introduced by SF patch #869468: delete bogus __hasattr__ and __delattr__ methods on class Tk that were breaking Tkdnd. From birkenfeld at users.sourceforge.net Wed Aug 3 09:30:14 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Wed, 03 Aug 2005 00:30:14 -0700 Subject: [Python-checkins] python/dist/src/Lib/plat-riscos riscospath.py, 1.11, 1.12 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/plat-riscos In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26243/Lib/plat-riscos Modified Files: riscospath.py Log Message: patch [ 1105730 ] Faster commonprefix in macpath, ntpath, etc. Index: riscospath.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/plat-riscos/riscospath.py,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- riscospath.py 30 Aug 2004 10:19:55 -0000 1.11 +++ riscospath.py 3 Aug 2005 07:30:12 -0000 1.12 @@ -168,23 +168,16 @@ return split(p)[0] -def commonprefix(ps): - """ - Return the longest prefix of all list elements. Purely string-based; does not - separate any path parts. Why am I in os.path? - """ - if len(ps)==0: - return '' - prefix= ps[0] - for p in ps[1:]: - prefix= prefix[:len(p)] - for i in range(len(prefix)): - if prefix[i] <> p[i]: - prefix= prefix[:i] - if i==0: - return '' - break - return prefix +def commonprefix(m): + "Given a list of pathnames, returns the longest common leading component" + if not m: return '' + s1 = min(m) + s2 = max(m) + n = min(len(s1), len(s2)) + for i in xrange(n): + if s1[i] != s2[i]: + return s1[:i] + return s1[:n] ## File access functions. Why are we in os.path? From birkenfeld at users.sourceforge.net Wed Aug 3 09:30:14 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Wed, 03 Aug 2005 00:30:14 -0700 Subject: [Python-checkins] python/dist/src/Lib macpath.py, 1.50, 1.51 ntpath.py, 1.61, 1.62 os2emxpath.py, 1.13, 1.14 Message-ID: Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26243/Lib Modified Files: macpath.py ntpath.py os2emxpath.py Log Message: patch [ 1105730 ] Faster commonprefix in macpath, ntpath, etc. Index: macpath.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/macpath.py,v retrieving revision 1.50 retrieving revision 1.51 diff -u -d -r1.50 -r1.51 --- macpath.py 30 Aug 2004 13:39:50 -0000 1.50 +++ macpath.py 3 Aug 2005 07:30:11 -0000 1.51 @@ -175,14 +175,14 @@ def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return '' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i+1] != item[:i+1]: - prefix = prefix[:i] - if i == 0: return '' - break - return prefix + s1 = min(m) + s2 = max(m) + n = min(len(s1), len(s2)) + for i in xrange(n): + if s1[i] != s2[i]: + return s1[:i] + return s1[:n] + def expandvars(path): """Dummy to retain interface-compatibility with other operating systems.""" Index: ntpath.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/ntpath.py,v retrieving revision 1.61 retrieving revision 1.62 diff -u -d -r1.61 -r1.62 --- ntpath.py 30 Aug 2004 10:19:55 -0000 1.61 +++ ntpath.py 3 Aug 2005 07:30:12 -0000 1.62 @@ -212,14 +212,13 @@ def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return '' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i+1] != item[:i+1]: - prefix = prefix[:i] - if i == 0: return '' - break - return prefix + s1 = min(m) + s2 = max(m) + n = min(len(s1), len(s2)) + for i in xrange(n): + if s1[i] != s2[i]: + return s1[:i] + return s1[:n] # Get size, mtime, atime of files. Index: os2emxpath.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/os2emxpath.py,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -r1.13 -r1.14 --- os2emxpath.py 30 Aug 2004 10:19:55 -0000 1.13 +++ os2emxpath.py 3 Aug 2005 07:30:12 -0000 1.14 @@ -173,14 +173,13 @@ def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return '' - prefix = m[0] - for item in m: - for i in range(len(prefix)): - if prefix[:i+1] != item[:i+1]: - prefix = prefix[:i] - if i == 0: return '' - break - return prefix + s1 = min(m) + s2 = max(m) + n = min(len(s1), len(s2)) + for i in xrange(n): + if s1[i] != s2[i]: + return s1[:i] + return s1[:n] # Get size, mtime, atime of files. From pje at users.sourceforge.net Wed Aug 3 15:18:53 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Wed, 03 Aug 2005 06:18:53 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.55, 1.56 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21843 Modified Files: pkg_resources.py Log Message: Fix a problem with zip paths reported by Ashley Walsh. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.55 retrieving revision 1.56 diff -u -d -r1.55 -r1.56 --- pkg_resources.py 31 Jul 2005 16:31:17 -0000 1.55 +++ pkg_resources.py 3 Aug 2005 13:18:50 -0000 1.56 @@ -1122,7 +1122,7 @@ for path in self.zipinfo: parts = path.split(os.sep) while parts: - parent = '/'.join(parts[:-1]) + parent = os.sep.join(parts[:-1]) if parent in ind: ind[parent].append(parts[-1]) break From doerwalter at users.sourceforge.net Wed Aug 3 19:09:31 2005 From: doerwalter at users.sourceforge.net (doerwalter@users.sourceforge.net) Date: Wed, 03 Aug 2005 10:09:31 -0700 Subject: [Python-checkins] python/dist/src/Lib/test test_textwrap.py, 1.28, 1.29 test_sys.py, 1.10, 1.11 test_isinstance.py, 1.9, 1.10 test_builtin.py, 1.41, 1.42 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18683/Lib/test Modified Files: test_textwrap.py test_sys.py test_isinstance.py test_builtin.py Log Message: Disable a few other tests, that can't work if Python is compiled without Unicode support. Index: test_textwrap.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_textwrap.py,v retrieving revision 1.28 retrieving revision 1.29 diff -u -d -r1.28 -r1.29 --- test_textwrap.py 5 Mar 2005 02:53:17 -0000 1.28 +++ test_textwrap.py 3 Aug 2005 17:09:02 -0000 1.29 @@ -328,17 +328,18 @@ self.check_wrap(text, 30, [" This is a sentence with", "leading whitespace."]) - def test_unicode(self): - # *Very* simple test of wrapping Unicode strings. I'm sure - # there's more to it than this, but let's at least make - # sure textwrap doesn't crash on Unicode input! - text = u"Hello there, how are you today?" - self.check_wrap(text, 50, [u"Hello there, how are you today?"]) - self.check_wrap(text, 20, [u"Hello there, how are", "you today?"]) - olines = self.wrapper.wrap(text) - assert isinstance(olines, list) and isinstance(olines[0], unicode) - otext = self.wrapper.fill(text) - assert isinstance(otext, unicode) + if test_support.have_unicode: + def test_unicode(self): + # *Very* simple test of wrapping Unicode strings. I'm sure + # there's more to it than this, but let's at least make + # sure textwrap doesn't crash on Unicode input! + text = u"Hello there, how are you today?" + self.check_wrap(text, 50, [u"Hello there, how are you today?"]) + self.check_wrap(text, 20, [u"Hello there, how are", "you today?"]) + olines = self.wrapper.wrap(text) + assert isinstance(olines, list) and isinstance(olines[0], unicode) + otext = self.wrapper.fill(text) + assert isinstance(otext, unicode) def test_split(self): # Ensure that the standard _split() method works as advertised Index: test_sys.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_sys.py,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- test_sys.py 15 Feb 2005 21:50:12 -0000 1.10 +++ test_sys.py 3 Aug 2005 17:09:03 -0000 1.11 @@ -247,7 +247,8 @@ self.assert_(isinstance(sys.executable, basestring)) self.assert_(isinstance(sys.hexversion, int)) self.assert_(isinstance(sys.maxint, int)) - self.assert_(isinstance(sys.maxunicode, int)) + if test.test_support.have_unicode: + self.assert_(isinstance(sys.maxunicode, int)) self.assert_(isinstance(sys.platform, basestring)) self.assert_(isinstance(sys.prefix, basestring)) self.assert_(isinstance(sys.version, basestring)) Index: test_isinstance.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_isinstance.py,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- test_isinstance.py 8 Jul 2004 04:22:19 -0000 1.9 +++ test_isinstance.py 3 Aug 2005 17:09:04 -0000 1.10 @@ -243,7 +243,8 @@ self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,)))) self.assertEqual(True, issubclass(int, (long, (float, int)))) - self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring)))) + if test_support.have_unicode: + self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring)))) def test_subclass_recursion_limit(self): # make sure that issubclass raises RuntimeError before the C stack is Index: test_builtin.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_builtin.py,v retrieving revision 1.41 retrieving revision 1.42 diff -u -d -r1.41 -r1.42 --- test_builtin.py 26 Apr 2005 03:45:26 -0000 1.41 +++ test_builtin.py 3 Aug 2005 17:09:04 -0000 1.42 @@ -582,14 +582,16 @@ self.assertRaises(TypeError, getattr, sys, 1) self.assertRaises(TypeError, getattr, sys, 1, "foo") self.assertRaises(TypeError, getattr) - self.assertRaises(UnicodeError, getattr, sys, unichr(sys.maxunicode)) + if have_unicode: + self.assertRaises(UnicodeError, getattr, sys, unichr(sys.maxunicode)) def test_hasattr(self): import sys self.assert_(hasattr(sys, 'stdout')) self.assertRaises(TypeError, hasattr, sys, 1) self.assertRaises(TypeError, hasattr) - self.assertRaises(UnicodeError, hasattr, sys, unichr(sys.maxunicode)) + if have_unicode: + self.assertRaises(UnicodeError, hasattr, sys, unichr(sys.maxunicode)) def test_hash(self): hash(None) @@ -1101,7 +1103,8 @@ if have_unicode: self.assertEqual(ord(unichr(sys.maxunicode)), sys.maxunicode) self.assertRaises(TypeError, ord, 42) - self.assertRaises(TypeError, ord, unicode("12")) + if have_unicode: + self.assertRaises(TypeError, ord, unicode("12")) def test_pow(self): self.assertEqual(pow(0,0), 1) @@ -1494,11 +1497,17 @@ def test_inputtypes(self): s = 'abracadabra' - for T in [unicode, list, tuple]: + types = [list, tuple] + if have_unicode: + types.insert(0, unicode) + for T in types: self.assertEqual(sorted(s), sorted(T(s))) s = ''.join(dict.fromkeys(s).keys()) # unique letters only - for T in [unicode, set, frozenset, list, tuple, dict.fromkeys]: + types = [set, frozenset, list, tuple, dict.fromkeys] + if have_unicode: + types.insert(0, unicode) + for T in types: self.assertEqual(sorted(s), sorted(T(s))) def test_baddecorator(self): From tim_one at users.sourceforge.net Wed Aug 3 20:33:09 2005 From: tim_one at users.sourceforge.net (tim_one@users.sourceforge.net) Date: Wed, 03 Aug 2005 11:33:09 -0700 Subject: [Python-checkins] python/dist/src/Python compile.c,2.351,2.352 Message-ID: Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6288/Python Modified Files: compile.c Log Message: com_yield_expr(): Squash new compiler wng about unreferenced local. Index: compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.351 retrieving revision 2.352 diff -u -d -r2.351 -r2.352 --- compile.c 2 Aug 2005 00:46:46 -0000 2.351 +++ compile.c 3 Aug 2005 18:33:05 -0000 2.352 @@ -3744,7 +3744,6 @@ static void com_yield_expr(struct compiling *c, node *n) { - int i; REQ(n, yield_expr); /* 'yield' testlist */ if (!c->c_infunction) { com_error(c, PyExc_SyntaxError, "'yield' outside function"); From gregorykjohnson at users.sourceforge.net Thu Aug 4 01:50:45 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Wed, 03 Aug 2005 16:50:45 -0700 Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.5, 1.6 libmailbox.tex, 1.6, 1.7 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23486 Modified Files: mailbox.py libmailbox.tex Log Message: * Implement sequences and pack() for MH: * Simplify interface. Use only get_sequences() and set_sequences(). * Overhaul locking (and carefulness of other processes in general): * Fix various places where locking was not properly conceived (especially when overwriting or replacing files). * Introduce _create_carefully() to avoid clobbering new files. * Use .lock.time.pid.hostname suffix for pre-dotlocks, like pine. * Don't steal stale dotlocks, even though interactive MUAs do. * Carefully release locks in case of exceptions (also avoids deadlocks if some locks are successfully acquired but not others). Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- mailbox.py 2 Aug 2005 21:46:13 -0000 1.5 +++ mailbox.py 3 Aug 2005 23:50:43 -0000 1.6 @@ -190,6 +190,8 @@ else: raise TypeError, "Invalid message type" + + class Maildir(Mailbox): """A qmail-style Maildir mailbox.""" @@ -434,6 +436,7 @@ self._lookup() self._toc[self._next_key] = self._append_message(message) self._next_key += 1 + self._pending = True return self._next_key - 1 def remove(self, key): @@ -493,12 +496,21 @@ def flush(self): """Write any pending changes to disk.""" - if self._pending: - self._lookup() - dotlock_inode = _lock_file(self._file) + if not self._pending: + return + self._lookup() + _lock_file(self._file, self._path) + try: + os.rename(self._path, self._path + '~') + _lock_file(self._file, self._path + '~', system=False) + except: + _unlock_file(self._file, self._path) + raise + try: self._assert_mtime() - f = file(self._path + '.tmp', 'w') + f = _create_carefully(self._path) try: + _lock_file(f, self._path, dot=False) try: new_toc = {} for key in sorted(self._toc.keys()): @@ -508,29 +520,25 @@ new_start = f.tell() while True: buffer = self._file.read( - min(4096, stop - self._file.tell())) + min(4096, stop - self._file.tell())) if buffer == '': break f.write(buffer) new_toc[key] = (new_start, f.tell()) self._post_write_hook(f) finally: - f.close() - finally: - _unlock_file(self._file, dotlock_inode) - try: - os.rename(self._path + '.tmp', self._path) - except OSError, e: - if e.errno == errno.EEXIST: - # XXX: Is this the exception Windows raises? - os.remove(self._path) - os.rename(self._path + '.tmp', self._path) - else: - raise - self._file.flush() - self._set_mtime() - _unlock_file(self._file, dotlock_inode) - self._pending = False + _unlock_file(f, self._path) + except: + f.close() + raise + finally: + _unlock_file(self._file, self._path + '~') + self._file.close() + self._toc = new_toc + self._file = f + self._set_mtime() + os.remove(self._path + '~') + self._pending = False def close(self): """Flush and close the mailbox.""" @@ -544,7 +552,7 @@ if key is not None: try: return self._toc[key] - except IndexError: + except KeyError: raise KeyError, "No message with key '%s'" % key def _append_message(self, message): @@ -565,9 +573,9 @@ if from_line is None: from_line = 'From MAILER-DAEMON %s' % \ time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime()) - dotlock_inode = _lock_file(self._file) - self._assert_mtime() + _lock_file(self._file, self._path) try: + self._assert_mtime() self._file.seek(0, 2) self._pre_write_hook(self._file) start = self._file.tell() @@ -578,15 +586,13 @@ self._file.flush() self._set_mtime() finally: - _unlock_file(self._file, dotlock_inode) + _unlock_file(self._file, self._path) return (start, stop) def _assert_mtime(self): """Raise an exception if the file has been externally modified.""" if self._mtime != os.fstat(self._file.fileno()).st_mtime: - raise Error, 'External modifications detected: use refresh(): ' \ - '%r vs %r' % (self._mtime, - os.fstat(self._file.fileno()).st_mtime) + raise Error, 'External modifications detected: use refresh()' def _set_mtime(self): """Store the current mtime.""" @@ -682,6 +688,8 @@ Mailbox.__init__(self, path, factory) if not os.path.exists(self._path): os.mkdir(self._path, 0700) + os.mknod(os.path.join(self._path, '.mh_sequences'), + 0600 | stat.S_IFREG) def add(self, message): """Add message and return assigned key.""" @@ -690,23 +698,18 @@ new_key = 1 else: new_key = max(keys) + 1 - fd = os.open(os.path.join(self._path, str(new_key)), - os.O_CREAT | os.O_EXCL | os.O_WRONLY) - f = os.fdopen(fd, 'w') + new_path = os.path.join(self._path, str(new_key)) + f = _create_carefully(new_path) try: + _lock_file(f, f.name) try: - dotlock_inode = _lock_file(f) - try: - self._dump_message(message, f) - # XXX: update sequences based on message - f.flush() - finally: - _unlock_file(f, dotlock_inode) + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, new_key) finally: - f.close() - except: - os.remove(os.path.join(self._path, str(new_key))) - raise + _unlock_file(f, f.name) + finally: + f.close() return new_key def remove(self, key): @@ -729,15 +732,17 @@ else: raise try: - dotlock_inode = _lock_file(f_r) + _lock_file(f_r, f_r.name) try: f_w = file(os.path.join(self._path, str(key)), 'w') try: self._dump_message(message, f_w) + if isinstance(message, MHMessage): + self._dump_sequences(message, key) finally: f_w.close() finally: - _unlock_file(f_r, dotlock_inode) + _unlock_file(f_r, f_r.name) finally: f_r.close() @@ -754,7 +759,9 @@ msg = MHMessage(f) finally: f.close() - # XXX: set sequences on msg + for name, key_list in self.get_sequences(): + if key in key_list: + msg.join_sequence(name) return msg def get_string(self, key): @@ -784,8 +791,8 @@ def iterkeys(self): """Return an iterator over keys.""" - for x in sorted(int(x) for x in os.listdir(self._path) if x.isdigit()): - yield int(x) + return iter(sorted(int(entry) for entry in os.listdir(self._path) + if entry.isdigit())) def has_key(self, key): """Return True if the keyed message exists, False otherwise.""" @@ -827,21 +834,106 @@ raise Error, "Folder '%s' is not empty" % self._path os.rmdir(path) - def list_sequences(self, name): - """Return a list of the names of sequences defined in the mailbox.""" - raise NotImplementedError, 'Method not yet implemented' - - def get_sequence(self, name): - """Return a list of the keys in sequence 'name'.""" - raise NotImplementedError, 'Method not yet implemented' + def get_sequences(self): + """Return a name-to-key-list dictionary to define each sequence.""" + results = {} + f = file(os.path.join(self._path, '.mh_sequences'), 'r') + try: + all_keys = set(self.keys()) + for line in f: + try: + name, contents = line.split(':') + keys = set() + for spec in contents.split(): + if spec.isdigit(): + keys.add(int(spec)) + else: + start, stop = (int(x) for x in spec.split('-')) + keys.update(range(start, stop + 1)) + results[name] = [key for key in sorted(keys) \ + if key in all_keys] + except ValueError: + raise Error, "Invalid sequence specification: '%s'" % \ + line.rstrip() + finally: + f.close() + return results - def set_sequence(self, name): - """Set the keys in sequence 'name'.""" - raise NotImplementedError, 'Method not yet implemented' + def set_sequences(self, sequences): + """Set sequences using the given name-to-key-list dictionary.""" + f = file(os.path.join(self._path, '.mh_sequences'), 'r+') + try: + _lock_file(f, f.name) + try: + # .truncate() is not portable, so re-open to truncate. + f_w = file(os.path.join(self._path, '.mh_sequences'), 'w') + try: + for name, keys in sequences.iteritems(): + if len(keys) == 0: + continue + f_w.write('%s:' % name) + prev = None + completing = False + for key in sorted(set(keys)): + if key - 1 == prev: + if not completing: + completing = True + f_w.write('-') + elif completing: + completing = False + f_w.write('%s %s' % (prev, key)) + else: + f_w.write(' %s' % key) + prev = key + if completing: + f_w.write('%s%s' % (prev, os.linesep)) + else: + f_w.write(os.linesep) + finally: + f_w.close() + finally: + _unlock_file(f, f.name) + finally: + f.close() def pack(self): - """Eliminate numbering gaps in message names. Invalidates keys.""" - raise NotImplementedError, 'Method not yet implemented' + """Re-name messages to eliminate numbering gaps. Invalidates keys.""" + sequences = self.get_sequences() + prev = 0 + changes = [] + for key in self.iterkeys(): + if key - 1 != prev: + changes.append((key, prev + 1)) + if hasattr(os, 'link'): + os.link(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + os.unlink(os.path.join(self._path, str(key))) + else: + os.rename(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + prev += 1 + if len(changes) == 0: + return + keys = self.keys() + for name, key_list in sequences.items(): + for old, new in changes: + if old in key_list: + key_list[key_list.index(old)] = new + self.set_sequences(sequences) + + def _dump_sequences(self, message, key): + """Inspect a new MHMessage and update sequences appropriately.""" + pending_sequences = message.list_sequences() + all_sequences = self.get_sequences() + for name, key_list in all_sequences.iteritems(): + if name in pending_sequences: + key_list.append(key) + elif key in key_list: + del key_list[key_list.index(key)] + for sequence in pending_sequences: + if sequence not in all_sequences: + all_sequences[sequence] = [new_key] + self.set_sequences(all_sequences) class Message(email.Message.Message): @@ -1327,79 +1419,71 @@ """Raised for module-specific errors.""" -def _lock_file(f): - """Use various means to lock file f. Return dotlock inode or None.""" - if 'fcntl' in globals(): - try: - fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError, e: - if e.errno == errno.EAGAIN: - raise Error, 'Exclusive lock cannot be acquired ' \ - 'using lockf: %s' % f.name - elif e.errno == errno.EBADF: - try: - fcntl.lockf(f, fcntl.LOCK_SH | fntl.LOCK_NB) - except IOError, e: - if e.errno == errno.EAGAIN: - raise Error, 'Shared lock cannot be acquired ' \ - 'using lockf: %s' % f.name - else: - raise - else: - raise - try: - fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError, e: - if e.errno == errno.EWOULDBLOCK: - raise Error, 'Exclusive lock cannot be acquired ' \ - 'using flock: %s' % f.name - else: - raise - tmp_name = f.name + '.%s.%s' % (socket.gethostname(), os.getpid()) - try: - dotlock = os.open(tmp_name, os.O_WRONLY | os.O_EXCL | os.O_CREAT, 0600) - dotlock_inode = os.fstat(dotlock).st_ino - os.close(dotlock) - except IOError, e: - if e.errno == errno.EACCESS: - pass # Without write access, just skip dotlocking. - elif e.errno == errno.EEXIST: - raise Error, 'Failed to create unique file ' \ - 'for dot lock: %s' % tmp_name - else: - raise +def _lock_file(f, path, system=True, dot=True): + """Lock file f using lockf, flock, and dot locking.""" + lockf_done = flock_done = dotlock_done = False try: - if os.path.exists(f.name + '.lock') and \ - time.time() - os.getmtime(f.name + '.lock') > 300: + if system and 'fcntl' in globals(): try: - os.remove(f.name + '.lock') + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + lockf_done = True + except IOError, e: + if e.errno == errno.EAGAIN: + raise Error, "lockf: lock unavailable: %s" % path + else: + raise + try: + fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + flock_done = True + except IOError, e: + if e.errno == errno.EWOULDBLOCK: + raise Error, "flock: lock unavailable: %s" % path + else: + raise + if dot: + tmp = '%s.lock.%s.%s.%s' % (path, int(time.time()), + socket.gethostname(), os.getpid()) + try: + os.close(os.open(tmp, os.O_WRONLY | os.O_EXCL | os.O_CREAT, + 0600)) except IOError, e: if e.errno == errno.EACCESS: - raise Error, 'Stale dot lock cannot be removed: %s' \ - % f.name + '.lock' + return # Without write access, just skip dotlocking. else: raise - if hasattr(os, 'link'): - os.link(tmp_name, f.name + '.lock') - os.unlink(tmp_name) - else: - os.rename(tmp_name, f.name + '.lock') - except OSError, e: - if e.errno == errno.EEXIST: - raise Error, 'Dot lock exists and cannot be acquired: %s' % \ - f.name + '.lock' - else: - raise - else: - return dotlock_inode - return None - + try: + if hasattr(os, 'link'): + os.link(tmp, path + '.lock') + os.unlink(tmp) + else: + os.rename(tmp, path + '.lock') + dotlock_done = True + except OSError, e: + if e.errno == errno.EEXIST: + try: + os.remove(tmp) + except: + pass + raise Error, 'dot lock unavailable: %s' % path + else: + raise + except: + if lockf_done: + fcntl.lockf(f, fcntl.LOCK_UN) + if flock_done: + fcntl.flock(f, fcntl.LOCK_UN) + if dotlock_done: + os.remove(path + '.lock') + raise -def _unlock_file(f, dotlock_inode): - """Unlock file f by various means, given a dotlock_inode (or None).""" - if 'fcntl' in globals(): +def _unlock_file(f, path, system=True, dot=True): + """Unlock file f using lockf, flock, and dot locking.""" + if system and 'fcntl' in globals(): fcntl.lockf(f, fcntl.LOCK_UN) fcntl.flock(f, fcntl.LOCK_UN) - if dotlock_inode is not None and \ - dotlock_inode == os.stat(f.name + '.lock').st_ino: - os.remove(f.name + '.lock') + if dot and os.path.exists(path + '.lock'): + os.remove(path + '.lock') + +def _create_carefully(path): + """Create a file if it doesn't exist and open for reading and writing.""" + return os.fdopen(os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR), 'r+') Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- libmailbox.tex 2 Aug 2005 21:46:13 -0000 1.6 +++ libmailbox.tex 3 Aug 2005 23:50:43 -0000 1.7 @@ -435,24 +435,21 @@ will not be deleted. \end{methoddesc} -\begin{methoddesc}{list_sequences}{} -Return a list of the names of sequences defined in the mailbox. If there are no -sequences, the empty list is returned. -\end{methoddesc} - -\begin{methoddesc}{get_sequence}{name} -Return a list of keys in sequence \var{name}. +\begin{methoddesc}{get_sequences}{} +Return a dictionary of sequence names mapped to key lists. If there are no +sequences, the empty dictionary is returned. \end{methoddesc} -\begin{methoddesc}{set_sequence}{name, value} -Set to \var{value} the list of keys in sequence \var{name}. The sequence will -be created if it does not exist. If \var{value} is the empty list, sequence -\var{name} will be removed. +\begin{methoddesc}{set_sequences}{sequences} +Re-define the sequences that exist in the mailbox based upon \var{sequences}, a +dictionary of names mapped to key lists like returned by +\method{get_sequences()}. \end{methoddesc} \begin{methoddesc}{pack}{} Renames messages in the mailbox as necessary to eliminate gaps in numbering. -Already-issued keys are invalidated by this operation. +Entries in the sequences list are updated correspondingly. Already-issued keys +are invalidated by this operation. \end{methoddesc} Some \class{Mailbox} methods implemented by \class{MH} deserve special remarks: From bcannon at users.sourceforge.net Thu Aug 4 05:19:00 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Wed, 03 Aug 2005 20:19:00 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,NONE,1.1 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5746 Added Files: pep-0348.txt Log Message: Initial checkin; essentially rev. 3; previous 2 sent to python-dev in July and August 2005, respectively. --- NEW FILE: pep-0348.txt --- PEP: 348 Title: Exception Reorganization for Python 3.0 Version: $Revision: 1.1 $ Last-Modified: $Date: 2005/08/04 03:18:57 $ Author: Brett Cannon Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 28-Jul-2005 Post-History: 03-Aug-2005 Abstract ======== Python, as of version 2.4, has 38 exceptions (including warnings) in the built-in namespace in a rather shallow hierarchy. This list of classes has grown over the years without a chance to learn from mistakes and cleaning up the hierarchy. This PEP proposes doing a reorganization for Python 3.0 when backwards-compatibility is not an issue. Along with this reorganization, adding a requirement that all objects passed to a ``raise`` statement must inherit from a specific superclass is proposed. Lastly, bare ``except`` clauses will catch only exceptions inheriting from StandardError. Rationale ========= Exceptions are a critical part of Python. While exceptions are traditionally used to signal errors in a program, they have also grown to be used for flow control for things such as iterators. There importance is great. But the organization of the exception hierarchy is suboptimal to serve the multiple uses of exceptions. Mostly for backwards-compatibility reasons, the hierarchy has stayed very flat and old exceptions who usefulness have not been proven have been left in. Making exceptions more hierarchical would help facilitate exception handling by making catching exceptions using inheritance much more logical. This should also help lead to less errors from being too broad in what exceptions are caught in an ``except`` clause. A required superclass for all exceptions is also being proposed [Summary2004-08-01]_. By requiring any object that is used in a ``raise`` statement to inherit from a specific superclass, certain attributes (such as those laid out in PEP 344 [PEP344]_) can be guaranteed to exist. This also will lead to the planned removal of string exceptions. Lastly, bare ``except`` clauses are to catch only exceptions that inherit from Exception [python-dev3]_. While currently used to catch all exceptions, that use is rather far reaching and typically not desired. Catching only exceptions inheriting from StandardError allows exceptions that should not be caught unless explicitly desired to continue to propagate up the execution stack. Philosophy of Reorganization ============================ There are several goals in this reorganization that defined the philosophy used to guide the work. One goal was to prune out unneeded exceptions. Extraneous exceptions should not be left in since it just serves to clutter the built-in namespace. Unneeded exceptions also dilute the importance of other exceptions by splitting uses between several exceptions when all uses should have been under a single exception. Another goal was to introduce any exceptions that were deemed needed to fill any holes in the hierarchy. Most new exceptions were done to flesh out the inheritance hierarchy to make it easier to catch a category of exceptions with a simpler ``except`` clause. Changing inheritance to make it more reasonable was a goal. As stated above, having proper inheritance allows for more accurate ``except`` statements when catching exceptions based on the inheritance tree. Lastly, any renaming to make an exception's use more obvious from its name was done. Having to look up what an exception is meant to be used for because the name does not proper reflect its usage is annoying and slows down debugging. Having a proper name also makes debugging easier on new programmers. But for simplicity of existing user's and for transitioning to Python 3.0, only exceptions whose names were fairly out of alignment with their stated purpose have been renamed. New Hierarchy ============= .. note:: exceptions flagged as "stricter inheritance" means that the class no longer inherits from a certain class; "broader inheritance" means a class has been added to the exception's inheritance tree :: BaseException +-- CriticalError (new) +-- KeyboardInterrupt (stricter inheritance) +-- MemoryError (stricter inheritance) +-- SystemError (stricter inheritance) +-- ControlFlowException (new) +-- GeneratorExit (defined in PEP 342 [PEP342]_) +-- StopIteration (broader inheritance) +-- SystemExit (broader inheritance) +-- Exception +-- ArithmeticError +-- DivideByZeroError +-- FloatingPointError +-- OverflowError +-- AssertionError +-- AttributeError +-- EnvironmentError +-- IOError +-- EOFError (broader inheritance) +-- OSError +-- ImportError +-- LookupError +-- IndexError +-- KeyError +-- NamespaceError (rename of NameError) +-- UnboundFreeError (new) +-- UnboundGlobalError (new) +-- UnboundLocalError +-- NotImplementedError (stricter inheritance) +-- SyntaxError +-- IndentationError +-- TabError +-- TypeError +-- UserError (rename of RuntimeError) +-- UnicodeError +-- UnicodeDecodeError +-- UnicodeEncodeError +-- UnicodeTranslateError +-- ValueError +-- Warning (broader inheritance, including subclasses) +-- AnyDeprecationWarning (new; broader inheritance for subclasses) +-- PendingDeprecationWarning +-- DeprecationWarning +-- FutureWarning +-- SyntaxWarning +-- SemanticsWarning (rename of RuntimeWarning) +-- UserWarning +-- WeakReferenceError (rename of ReferenceError) Differences Compared to Python 2.4 ================================== Changes to exceptions from Python 2.4 can take shape in three forms: removal, renaming, or change in the inheritance tree. There are also new exceptions introduced in the proposed hierarchy. In terms of new exceptions, almost all are to flesh out the inheritance tree. Those that are leaf classes are to alleaviate overloading the use of another exception. Inheritance change can be broader or more restrictive. The broader inheritance typically occurred to allow for a more reasonable superclass to group related exceptions together. Stricter inheritance happened when the pre-existing inheritance was deemed incorrect and needed correction. New Exceptions -------------- CriticalError ''''''''''''' The superclass for exceptions for which a severe error has occurred that one would not want to recover from. The name is meant to reflect that these exceptions are raised asynchronously by the interpreter when a critical event has occured that one would most likely want the interpreter to halt over. ControlFlowException '''''''''''''''''''' This exception exists as a superclass for all exceptions that directly deal with control flow. Inheriting from Exception instead of StandardError prevents them from being caught accidently when one wants to catch errors. The name, by not mentioning "Error", does not lead to one to confuse the subclasses as errors. UnboundGlobalError '''''''''''''''''' Raised when a global variable was not found. UnboundFreeError '''''''''''''''' Raised when a free variable is not found. AnyDeprecationWarning ''''''''''''''''''''' A common superclass for all deprecation-related exceptions. While having DeprecationWarning inherit from PendingDeprecationWarning was suggested because a DeprecationWarning can be viewed as a PendingDeprecationWarning that is happening now, the logic was not agreed upon by a majority. But since the exceptions are related, creating a common superclass is warranted. Removed Exceptions ------------------ WindowsError '''''''''''' Too OS-specific to be kept in the built-in exception hierarchy. Renamed Exceptions ------------------ RuntimeError '''''''''''' Renamed UserError. Meant for use as a generic exception to be used when one does not want to create a new exception class but do not want to raise an exception that might be caught based on inheritance, RuntimeError is poorly named. It's name in Python 2.4 seems to suggest an error that occurred at runtime, possibly an error in the VM. Renaming the exception to UserError more clearly states the purpose for the exception as quick-and-dirty error exception for the user to use. The name also keeps it in line with UserWarning. If a user wants an exception that is not to be used as an error, raising Exception directly should be sufficient as StandardError, as UserError inherits from, is only used for errors. ReferenceError '''''''''''''' Renamed WeakReferenceError. ReferenceError was added to the built-in exception hierarchy in Python 2.2 [exceptions-stdlib]_. Taken directly from the ``weakref`` module, its name comes directly from its original name when it resided in the module. Unfortunately its name does not suggest its connection to weak references and thus deserves a renaming. NameError ''''''''' Renamed NamespaceError. While NameError suggests its common use, it is not entirely apparent. Making it more of a superclass for namespace-related exceptions warrants a renaming to make it abundantly clear its use. Plus the documentation of the exception module[exceptions-stdlib]_ states that it is actually meant for global names and not for just any exception. RuntimeWarning '''''''''''''' Renamed SemanticsWarning. RuntimeWarning is to represent semantic changes coming in the future. But while saying that affects "runtime" is true, flat-out stating it is a semantic change is much clearer, eliminating any possible association of "runtime" with the virtual machine specifically. Changed Inheritance ------------------- KeyboardInterrupt, MemoryError, and SystemError ''''''''''''''''''''''''''''''''''''''''''''''' Inherit from CriticalError instead of StandardError. The three above-mentioned exceptions are not standard errors by any means. They are raised asynchronously by the interpreter when something specific has occurred. Thus they warrant not inheriting from StandardError but from an entirely separate exception that will not be caught by a bare ``except`` clause. NotImplementedError ''''''''''''''''''' Inherits from StandardError instead of RuntimeError (renamed UserError). Originally inheriting from RuntimeError, NotImplementedError does not have any direct relation to the exception meant for use in user code as a quick-and-dirty exception. Thus it now directly inherits from StandardError. EOFError '''''''' Subclasses IOError. Since an EOF comes from I/O it only makes sense that it be considered an I/O error. Warning ''''''' Inherits from StandardError instead of Exception. In order for Warning to be caught by bare ``except`` clauses, it needs to inherit from StandardError as specified in this PEP. Required Superclass for ``raise`` ================================= By requiring all objects passed to a ``raise`` statement inherit from a specific superclass, one is guaranteed that all exceptions will have certain attributes. If PEP 342 [PEP344]_ is accepted, the attributes outlined there will be guaranteed to be on all exceptions raised. This should help facilitate debugging by making the querying of information from exceptions much easier. The proposed hierarchy has Exception as the required class that one must inherit from. Implementation -------------- Enforcement is straight-forward. Modifying ``RAISE_VARARGS`` to do an inheritance check first before raising an exception should be enough. For the C API, all functions that set an exception will have the same inheritance check. Bare ``except`` Clauses Catching StandardError Only =================================================== While Python does have its "explicit is better than implicit" tenant, it is not necessary if a default behavior is reasonable. In the case of a bare ``except`` clause, changing the behavior makes it quite reasonable to have around. In Python 2.4, a bare ``except`` clause will catch all exceptions. Typically, though, this is not what is truly desired. More often than not one wants to catch all error exceptions that do not signify a bad state of the interpreter. In the new exception hierarchy this is embodied by StandardError. Thus bare ``except`` clauses will catch only exceptions inheriting from StandardError. Implementation -------------- In the compiler, when a bare ``except`` clause is reached, the code for ``except StandardError`` will be emitted. Transition Plan =============== Exception Hierarchy Changes --------------------------- New Exceptions '''''''''''''' New exceptions can simply be added to the built-in namespace. Any pre-existing objects with the same name will mask the new exceptions, preserving backwards-compatibility. Renamed Exceptions '''''''''''''''''' Renamed exceptions will directly subclass the new names. When the old exceptions are instantiated (which occurs when an exception is caught, either by a ``try`` statement or by propagating to the top of the execution stack), a PendingDeprecationWarning will be raised. This should properly preserve backwards-compatibility as old usage won't change and the new names can be used to also catch exceptions using the old name. The warning of the deprecation is also kept simple. New Inheritance for Old Exceptions '''''''''''''''''''''''''''''''''' Using multiple inheritance to our advantage, exceptions whose inheritance is now more resrictive can be made backwards-compatible. By inheriting from both the new superclasses as well as the original superclasses existing ``except`` clauses will continue to work as before while allowing the new inheritance to be used for new clauses. A PendingDeprecationWarning will be raised based on whether the bytecode ``COMPARE_OP(10)`` results in an exception being caught that would not have under the new hierarchy. This will require hard-coding in the implementation of the bytecode. Removed Exceptions '''''''''''''''''' Exceptions scheduled for removal will be transitioned much like the old names of renamed exceptions. Upon instantiation a PendingDeprecationWarning will be raised stating the the exception is due to be removed by Python 3.0 . Required Superclass for ``raise`` --------------------------------- A SemanticsWarning will be raised when an object is passed to ``raise`` that does not have the proper inheritance. Removal of Bare ``except`` Clauses ---------------------------------- A SemanticsWarning will be raised for all bare ``except`` clauses. Rejected Ideas ============== Threads on python-dev discussing this PEP can be found at [python-dev-thread1]_, [python-dev-thread2]_ KeyboardInterrupt inheriting from ControlFlowException ------------------------------------------------------ KeyboardInterrupt has been a contentious point within this hierarchy. Some view the exception as more control flow being caused by the user. But with its asynchronous cause thanks to the user being able to trigger the exception at any point in code it has a more proper place inheriting from CriticalException. It also keeps the name of the exception from being "CriticalError". Renaming Exception to Raisable, StandardError to Exception ---------------------------------------------------------- While the naming makes sense and emphasizes the required superclass as what must be inherited from for raising an object, the naming is not required. Keeping the existing names minimizes code change to use the new names. It has been argued that changing StandardError to Exception might actually reflect how users use Exception more accurately, that has not been considered a strong enough argument. It entails guessing as to what users think in general and does technically lead to a change that is not backwards-compatible. DeprecationWarning Inheriting From PendingDeprecationWarning ------------------------------------------------------------ Originally proposed because a DeprecationWarning can be viewed as a PendingDeprecationWarning that is being removed in the next version. But enough people thought the inheritance could logically work the other way the idea was dropped. AttributeError Inheriting From TypeError or NameError ----------------------------------------------------- Viewing attributes as part of the interface of a type caused the idea of inheriting from TypeError. But that partially defeats the thinking of duck typing and thus was dropped. Inheriting from NameError was suggested because objects can be viewed as having their own namespace that the attributes lived in and when they are not found it is a namespace failure. This was also dropped as a possibility since not everyone shared this view. Removal of EnvironmentError --------------------------- Originally proposed based on the idea that EnvironmentError was an unneeded distinction, the BDFL overruled this idea [python-dev4]_. Introduction of MacError and UnixError -------------------------------------- Proposed to add symmetry to WindowsError, the BDFL said they won't be used enough [python-dev4]_. The idea of then removing WindowsError was proposed and accepted as reasonable, thus completely negating the idea of adding these exceptions. SystemError Subclassing SystemExit ---------------------------------- Proposed because a SystemError is meant to lead to a system exit, the idea was removed since CriticalException signifies this better. ControlFlowException Under StandardError ---------------------------------------- It has been suggested that ControlFlowException inherit from StandardError. This idea has been rejected based on the thinking that control flow exceptions are typically not desired to be caught in a generic fashion as StandardError will usually be used. Removal of Bare ``except`` Clauses ---------------------------------- The suggestion has been made to remove bare ``except`` clauses in the name of "explicit is better than implicit". But Guido has said this is too weak of an argument since other things in Python has default behavior [python-dev3]_. Open Issues =========== Remove ControlFlowException? ---------------------------- It has been suggested that ControlFlowException is not needed. Since the desire to catch any control flow exception will be atypical, the suggestion is to just remove the exception and let the exceptions that inherited from it inherit directly from Exception. This still preserves the seperation from StandardError which is one of the driving factors behind the introduction of the exception. Acknowledgements ================ Thanks to Robert Brewer, Josiah Carlson, Nick Coghlan, Timothy Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing, James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull and everyone else I missed for participating in the discussion. References ========== .. [PEP342] PEP 342 (Coroutines via Enhanced Generators) (http://www.python.org/peps/pep-0342.html) .. [PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) (http://www.python.org/peps/pep-0344.html) .. [exceptionsmodules] 'exceptions' module (http://docs.python.org/lib/module-exceptions.html) .. [Summary2004-08-01] python-dev Summary (An exception is an exception, unless it doesn't inherit from Exception) (http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception) .. [Summary2004-09-01] python-dev Summary (Cleaning the Exception House) (http://www.python.org/dev/summary/2004-09-01_2004-09-15.html#cleaning-the-exception-house) .. [python-dev1] python-dev email (Exception hierarchy) (http://mail.python.org/pipermail/python-dev/2004-August/047908.html) .. [python-dev2] python-dev email (Dangerous exceptions) (http://mail.python.org/pipermail/python-dev/2004-September/048681.html) .. [python-dev3] python-dev email (PEP, take 2: Exception Reorganization for Python 3.0) (http://mail.python.org/pipermail/python-dev/2005-August/055116.html) .. [exceptions-stdlib] exceptions module (http://www.python.org/doc/2.4.1/lib/module-exceptions.html) .. [python-dev-thread1] python-dev thread (Pre-PEP: Exception Reorganization for Python 3.0) (http://mail.python.org/pipermail/python-dev/2005-July/055020.html , http://mail.python.org/pipermail/python-dev/2005-August/055065.html) .. [python-dev-thread2] python-dev thread (PEP, take 2: Exception Reorganization for Python 3.0) (http://mail.python.org/pipermail/python-dev/2005-August/055103.html) .. [python-dev4] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0) (http://mail.python.org/pipermail/python-dev/2005-July/055019.html) Copyright ========= This document has been placed in the public domain. From bcannon at users.sourceforge.net Thu Aug 4 05:33:06 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Wed, 03 Aug 2005 20:33:06 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.1,1.2 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9052 Modified Files: pep-0348.txt Log Message: Finish moving to BaseException/Exception naming. Also leave in StandardError so as to provide a base Error exception that inherits from Exception. Also allows Warning to inherit from Exception without being put at the same level as any *Error exceptions. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- pep-0348.txt 4 Aug 2005 03:18:57 -0000 1.1 +++ pep-0348.txt 4 Aug 2005 03:33:03 -0000 1.2 @@ -19,7 +19,7 @@ Along with this reorganization, adding a requirement that all objects passed to a ``raise`` statement must inherit from a specific superclass is proposed. Lastly, bare ``except`` clauses will catch only exceptions inheriting from -StandardError. +Exception. Rationale @@ -43,7 +43,7 @@ Exception [python-dev3]_. While currently used to catch all exceptions, that use is rather far reaching and typically not desired. -Catching only exceptions inheriting from StandardError allows exceptions that +Catching only exceptions inheriting from Exception allows exceptions that should not be caught unless explicitly desired to continue to propagate up the execution stack. @@ -66,6 +66,8 @@ Having to look up what an exception is meant to be used for because the name does not proper reflect its usage is annoying and slows down debugging. Having a proper name also makes debugging easier on new programmers. But for simplicity of existing user's and for transitioning to Python 3.0, only exceptions whose names were fairly out of alignment with their stated purpose have been renamed. +It was also made sure the exceptions dealing with errors had the "Error" +suffix. New Hierarchy ============= @@ -86,36 +88,37 @@ +-- StopIteration (broader inheritance) +-- SystemExit (broader inheritance) +-- Exception - +-- ArithmeticError - +-- DivideByZeroError - +-- FloatingPointError - +-- OverflowError - +-- AssertionError - +-- AttributeError - +-- EnvironmentError - +-- IOError - +-- EOFError (broader inheritance) - +-- OSError - +-- ImportError - +-- LookupError - +-- IndexError - +-- KeyError - +-- NamespaceError (rename of NameError) - +-- UnboundFreeError (new) - +-- UnboundGlobalError (new) - +-- UnboundLocalError - +-- NotImplementedError (stricter inheritance) - +-- SyntaxError - +-- IndentationError - +-- TabError - +-- TypeError - +-- UserError (rename of RuntimeError) - +-- UnicodeError - +-- UnicodeDecodeError - +-- UnicodeEncodeError - +-- UnicodeTranslateError - +-- ValueError - +-- Warning (broader inheritance, including subclasses) + +-- StandardError + +-- ArithmeticError + +-- DivideByZeroError + +-- FloatingPointError + +-- OverflowError + +-- AssertionError + +-- AttributeError + +-- EnvironmentError + +-- IOError + +-- EOFError (broader inheritance) + +-- OSError + +-- ImportError + +-- LookupError + +-- IndexError + +-- KeyError + +-- NamespaceError (rename of NameError) + +-- UnboundFreeError (new) + +-- UnboundGlobalError (new) + +-- UnboundLocalError + +-- NotImplementedError (stricter inheritance) + +-- SyntaxError + +-- IndentationError + +-- TabError + +-- TypeError + +-- UserError (rename of RuntimeError) + +-- UnicodeError + +-- UnicodeDecodeError + +-- UnicodeEncodeError + +-- UnicodeTranslateError + +-- ValueError + +-- Warning +-- AnyDeprecationWarning (new; broader inheritance for subclasses) +-- PendingDeprecationWarning +-- DeprecationWarning @@ -144,6 +147,12 @@ New Exceptions -------------- +BaseException +''''''''''''' + +The superclass that all exceptions must inherit from. + + CriticalError ''''''''''''' @@ -159,7 +168,7 @@ This exception exists as a superclass for all exceptions that directly deal with control flow. -Inheriting from Exception instead of StandardError prevents them from being caught accidently when one wants to catch errors. +Inheriting from BaseException instead of Exception prevents them from being caught accidently when one wants to catch errors. The name, by not mentioning "Error", does not lead to one to confuse the subclasses as errors. @@ -214,7 +223,7 @@ The name also keeps it in line with UserWarning. If a user wants an exception that is not to be used as an error, raising -Exception directly should be sufficient as StandardError, as UserError inherits +BaseException directly should be sufficient as Exception, as UserError inherits from, is only used for errors. @@ -255,11 +264,11 @@ KeyboardInterrupt, MemoryError, and SystemError ''''''''''''''''''''''''''''''''''''''''''''''' -Inherit from CriticalError instead of StandardError. +Inherit from CriticalError instead of Exception. The three above-mentioned exceptions are not standard errors by any means. They are raised asynchronously by the interpreter when something specific has -occurred. Thus they warrant not inheriting from StandardError but from an +occurred. Thus they warrant not inheriting from Exception but from an entirely separate exception that will not be caught by a bare ``except`` clause. @@ -267,11 +276,11 @@ NotImplementedError ''''''''''''''''''' -Inherits from StandardError instead of RuntimeError (renamed UserError). +Inherits from Exception instead of RuntimeError (renamed UserError). Originally inheriting from RuntimeError, NotImplementedError does not have any direct relation to the exception meant for use in user code as a -quick-and-dirty exception. Thus it now directly inherits from StandardError. +quick-and-dirty exception. Thus it now directly inherits from Exception. EOFError @@ -282,15 +291,6 @@ Since an EOF comes from I/O it only makes sense that it be considered an I/O error. -Warning -''''''' - -Inherits from StandardError instead of Exception. - -In order for Warning to be caught by bare ``except`` clauses, it needs to -inherit from StandardError as specified in this PEP. - - Required Superclass for ``raise`` ================================= @@ -298,7 +298,7 @@ If PEP 342 [PEP344]_ is accepted, the attributes outlined there will be guaranteed to be on all exceptions raised. This should help facilitate debugging by making the querying of information from exceptions much easier. -The proposed hierarchy has Exception as the required class that one must inherit from. +The proposed hierarchy has BaseException as the required class that one must inherit from. Implementation @@ -310,8 +310,8 @@ exception will have the same inheritance check. -Bare ``except`` Clauses Catching StandardError Only -=================================================== +Bare ``except`` Clauses Catching Exception Only +=============================================== While Python does have its "explicit is better than implicit" tenant, it is not necessary if a default behavior is reasonable. In the case of a bare @@ -322,15 +322,15 @@ though, this is not what is truly desired. More often than not one wants to catch all error exceptions that do not signify a bad state of the interpreter. In the new exception hierarchy this is -embodied by StandardError. Thus bare ``except`` clauses will catch only -exceptions inheriting from StandardError. +embodied by Exception. Thus bare ``except`` clauses will catch only +exceptions inheriting from Exception. Implementation -------------- In the compiler, when a bare ``except`` clause is reached, the code for -``except StandardError`` will be emitted. +``except Exception`` will be emitted. Transition Plan @@ -414,17 +414,14 @@ CriticalException. It also keeps the name of the exception from being "CriticalError". -Renaming Exception to Raisable, StandardError to Exception ----------------------------------------------------------- - -While the naming makes sense and emphasizes the required superclass as what -must be inherited from for raising an object, the naming is not required. -Keeping the existing names minimizes code change to use the new names. +Other Names for BaseException and Exception +------------------------------------------- -It has been argued that changing StandardError to Exception might actually -reflect how users use Exception more accurately, that has not been considered a -strong enough argument. It entails guessing as to what users think in general -and does technically lead to a change that is not backwards-compatible. +Alternative names for BaseException/Exception have been Raisable/Exception and +Exception/StandardError. The former has been rejected on the basis that +Raisable does not reflect how it is an exception well enough. The latter was +rejected based on the fact that it did not reflect current use as the chosen +names do. DeprecationWarning Inheriting From PendingDeprecationWarning @@ -472,12 +469,12 @@ removed since CriticalException signifies this better. -ControlFlowException Under StandardError ----------------------------------------- +ControlFlowException Under Exception +------------------------------------ -It has been suggested that ControlFlowException inherit from StandardError. +It has been suggested that ControlFlowException inherit from Exception. This idea has been rejected based on the thinking that control flow exceptions -are typically not desired to be caught in a generic fashion as StandardError +are typically not desired to be caught in a generic fashion as Exception will usually be used. @@ -498,8 +495,8 @@ It has been suggested that ControlFlowException is not needed. Since the desire to catch any control flow exception will be atypical, the suggestion is to just remove the exception and let the exceptions that -inherited from it inherit directly from Exception. This still preserves the -seperation from StandardError which is one of the driving factors behind the +inherited from it inherit directly from BaseException. This still preserves the +seperation from Exception which is one of the driving factors behind the introduction of the exception. From bcannon at users.sourceforge.net Thu Aug 4 05:35:22 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Wed, 03 Aug 2005 20:35:22 -0700 Subject: [Python-checkins] python/nondist/peps pep-0000.txt,1.335,1.336 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9941 Modified Files: pep-0000.txt Log Message: Add PEP 348. Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.335 retrieving revision 1.336 diff -u -d -r1.335 -r1.336 --- pep-0000.txt 28 Jun 2005 08:31:08 -0000 1.335 +++ pep-0000.txt 4 Aug 2005 03:35:19 -0000 1.336 @@ -103,6 +103,7 @@ S 341 Unifying try-except and try-finally Birkenfeld S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones + S 348 Exception Reorganization for Python 3.0 Cannon S 754 IEEE 754 Floating Point Special Values Warnes Finished PEPs (done, implemented in CVS) @@ -388,6 +389,7 @@ S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones SR 346 User Defined ("with") Statements Coghlan + S 348 Exception Reorganization for Python 3.0 Cannon SR 666 Reject Foolish Indentation Creighton S 754 IEEE 754 Floating Point Special Values Warnes I 3000 Python 3.0 Plans Kuchling, Cannon From bcannon at users.sourceforge.net Thu Aug 4 05:41:41 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Wed, 03 Aug 2005 20:41:41 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.2,1.3 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11161 Modified Files: pep-0348.txt Log Message: Change note on StopIteration and SystemExit on how they now have stricter inheritance thanks to no longer inheriting from Exception. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- pep-0348.txt 4 Aug 2005 03:33:03 -0000 1.2 +++ pep-0348.txt 4 Aug 2005 03:41:38 -0000 1.3 @@ -85,8 +85,8 @@ +-- SystemError (stricter inheritance) +-- ControlFlowException (new) +-- GeneratorExit (defined in PEP 342 [PEP342]_) - +-- StopIteration (broader inheritance) - +-- SystemExit (broader inheritance) + +-- StopIteration (stricter inheritance) + +-- SystemExit (stricter inheritance) +-- Exception +-- StandardError +-- ArithmeticError @@ -273,6 +273,15 @@ clause. +StopIteration and SystemExit +'''''''''''''''''''''''''''' + +Inherit from ControlFlowException instead of Exception. + +By having these exceptions no longer inherit from Exception they will not be +accidentally caught by a bare ``except`` clause. + + NotImplementedError ''''''''''''''''''' From loewis at users.sourceforge.net Thu Aug 4 20:42:29 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Thu, 04 Aug 2005 11:42:29 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,NONE,1.1 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15169 Added Files: pep-0347.txt Log Message: Add PEP 347. --- NEW FILE: pep-0347.txt --- (This appears to be a binary file; contents omitted.) From goodger at users.sourceforge.net Fri Aug 5 01:55:04 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Thu, 04 Aug 2005 16:55:04 -0700 Subject: [Python-checkins] python/nondist/peps pep-0000.txt,1.336,1.337 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22464 Modified Files: pep-0000.txt Log Message: added PEP 347 Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.336 retrieving revision 1.337 diff -u -d -r1.336 -r1.337 --- pep-0000.txt 4 Aug 2005 03:35:19 -0000 1.336 +++ pep-0000.txt 4 Aug 2005 23:55:01 -0000 1.337 @@ -103,6 +103,7 @@ S 341 Unifying try-except and try-finally Birkenfeld S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones + S 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon S 754 IEEE 754 Floating Point Special Values Warnes @@ -389,6 +390,7 @@ S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones SR 346 User Defined ("with") Statements Coghlan + S 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon SR 666 Reject Foolish Indentation Creighton S 754 IEEE 754 Floating Point Special Values Warnes From rhettinger at users.sourceforge.net Fri Aug 5 02:01:26 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Thu, 04 Aug 2005 17:01:26 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.39,1.40 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23091 Modified Files: setobject.c Log Message: * Move copyright notice to top and indicate derivation from sets.py and dictobject.c. * Have frozenset_hash() use entry->hash instead of re-computing each individual hash with PyObject_Hash(o); * Finalize the dummy entry before a system exit. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.39 retrieving revision 1.40 diff -u -d -r1.39 -r1.40 --- setobject.c 2 Aug 2005 03:45:14 -0000 1.39 +++ setobject.c 5 Aug 2005 00:01:15 -0000 1.40 @@ -1,15 +1,19 @@ +/* set object implementation + Written and maintained by Raymond D. Hettinger + Derived from Lib/sets.py and Objects/dictobject.c. -/* Set object implementation using a hash table - Functions adapted from dictobject.c + Copyright (c) 2003-5 Python Software Foundation. + All rights reserved. */ #include "Python.h" +#include "structmember.h" /* This must be >= 1. */ #define PERTURB_SHIFT 5 /* Object used as dummy key to fill deleted entries */ -static PyObject *dummy; /* Initialized by first call to make_new_set() */ +static PyObject *dummy = NULL; /* Initialized by first call to make_new_set() */ #define EMPTY_TO_MINSIZE(so) do { \ memset((so)->smalltable, 0, sizeof((so)->smalltable)); \ @@ -515,7 +519,7 @@ return key != NULL && key != dummy; } -/***** Set iterator types **********************************************/ +/***** Set iterator type ***********************************************/ static PyTypeObject PySetIter_Type; /* Forward */ @@ -558,7 +562,6 @@ static PySequenceMethods setiter_as_sequence = { (inquiry)setiter_len, /* sq_length */ - 0, /* sq_concat */ }; static PyObject *setiter_iternext(setiterobject *si) @@ -632,19 +635,6 @@ (iternextfunc)setiter_iternext, /* tp_iternext */ }; -/***** Derived functions (table accesses only done with above primitives *****/ - -#include "structmember.h" - -/* set object implementation - written and maintained by Raymond D. Hettinger - derived from sets.py written by Greg V. Wilson, Alex Martelli, - Guido van Rossum, Raymond Hettinger, and Tim Peters. - - Copyright (c) 2003-5 Python Software Foundation. - All rights reserved. -*/ - static int set_len(PyObject *so) { @@ -764,6 +754,7 @@ void PySet_Fini(void) { + Py_XDECREF(dummy); Py_XDECREF(emptyfrozenset); } @@ -1309,22 +1300,26 @@ static long frozenset_hash(PyObject *self) { - PyObject *key; PySetObject *so = (PySetObject *)self; - int pos = 0; long hash = 1927868237L; + int i, j; if (so->hash != -1) return so->hash; hash *= set_len(self) + 1; - while (set_next_internal(so, &pos, &key)) { + for (i=0, j=so->used ; j ; j--, i++) { + setentry *entry; + long h; + + while ((entry = &so->table[i])->key == NULL || entry->key==dummy) + i++; /* Work to increase the bit dispersion for closely spaced hash values. The is important because some use cases have many combinations of a small number of elements with nearby hashes so that many distinct combinations collapse to only a handful of distinct hash values. */ - long h = PyObject_Hash(key); + h = entry->hash; hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u; } hash = hash * 69069L + 907133923L; From goodger at users.sourceforge.net Fri Aug 5 02:16:53 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Thu, 04 Aug 2005 17:16:53 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.1,1.2 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26153 Modified Files: pep-0347.txt Log Message: light editing pass Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- pep-0347.txt 4 Aug 2005 18:42:26 -0000 1.1 +++ pep-0347.txt 5 Aug 2005 00:16:49 -0000 1.2 @@ -15,140 +15,165 @@ ======== The Python source code is currently managed in a CVS repository on -sourceforge.net. This PEP proposes to move it to a subversion repository -on svn.python.org. +sourceforge.net. This PEP proposes to move it to a subversion +repository on svn.python.org. + Rationale ========= -This change has two aspects: moving from CVS to subversion, and -moving from SourceForge to python.org. For each, a rationale will -be given. +This change has two aspects: moving from CVS to subversion, and moving +from SourceForge to python.org. For each, a rationale will be given. + Moving to Subversion -------------------- + CVS has a number of limitations that have been elimintation by -Subversion. For the development of Python, the most notable improvements -are: +Subversion. For the development of Python, the most notable +improvements are: + +- the ability to rename files and directories, and to remove + directories, while keeping the history of these files. -- ability to rename files and directories, and to remove directories, - while keeping the history of these files. - support for change sets (sets of correlated changes to multiple files) through global revision numbers. + - support for offline diffs, which is useful when creating patches. + Moving to python.org -------------------- + SourceForge has kindly provided an important infrastructure for the -past years. Unfortunately, the attention that SF received has also +past years. Unfortunately, the attention that SF received has also caused repeated overload situations in the past, to which the SF -operators could not always respond in a timely manner. In particular, +operators could not always respond in a timely manner. In particular, for CVS, they had to reduce the load on the primary CVS server by -introducing a second, read-only CVS server for anonymous access. -This server is regularly synchronized, but behind the the read-write -CVS repository between synchronizations. As a result, users without -commit access can see recent changes to the repository only with -a delay. +introducing a second, read-only CVS server for anonymous access. This +server is regularly synchronized, but lags behind the the read-write +CVS repository between synchronizations. As a result, users without +commit access can see recent changes to the repository only after a +delay. On python.org, it would be possible to make the repository accessible for anonymous access. + Migration Procedure =================== To move the Python CVS repository, the following steps need to be -executed. The steps are elaborated in more detail in the next -sections. +executed. The steps are elaborated upon in the following sections. + +1. Assign passwords for all current committers for use on + svn.python.org. User names on SF and svn.python.org should be + identical, unless some committer requests a different user name. -1. Assign passwords for all current committers for use on svn.python.org. - User names on SF and svn.python.org should be identical, unless some - committer requests a different user name. 2. At the beginning of the migration, announce that the repository on SourceForge closed. + 3. 24 hours after the last commit, download the CVS repository. -4. Convert the CVS repository into two subversion repositories, - one for distutils and one for Python. -5. Publish the repositories for write access for all committers, - and anonymous read access. + +4. Convert the CVS repository into two Subversion repositories, one + for Distutils and one for Python. + +5. Publish the repositories with write access for committers, and + read-only anonymous access. + 6. Disable CVS access on SF. + Assign Passwords -================ +---------------- Currently, access to Subversion on svn.python.org uses WebDAV over -https, using basic authentication. For this to work, authorized -users need to provide a password. This mechanism should be used, -atleast initially, for the Python CVS as well, since various committers -also have a username/password pair for the www SVN repository already. +https, using basic authentication. For this to work, authorized users +need to provide a password. This mechanism should be used, at least +initially, for the Python CVS as well, since various committers also +have a username/password pair for the www SVN repository already. Alternatives to password-based access include: - Subversion over SSH, using SSH key pairs. This would require - to give committers accounts on the machine, which currently is + committers to be given accounts on the machine, which currently is ruled out by the administration policy of svn.python.org. -- Subversion over WebDAV, using SSL client certificates. This - would work, but would require to administrate a certificate - authority. + +- Subversion over WebDAV, using SSL client certificates. This would + work, but would require to administrate a certificate authority. + Downloading the CVS Repository -============================== +------------------------------ The CVS repository can be downloaded from -http://cvs.sourceforge.net/cvstarballs/python-cvsroot.tar.bz2 + http://cvs.sourceforge.net/cvstarballs/python-cvsroot.tar.bz2 + +Since this tarball is generated only once a day, some time must pass +after the repository freeze before the tarball can be picked up. It +should be verified that the last commit, as recorded on the +python-commits mailing list, is indeed included in the tarball. -Since this tarball is generated only once a day, some time after -the repository freeze must pass before the tarball can be picked -up. It should be verified that the last commit, as recorded on -the python-commits mailing list, is indeed included in the tarball. Converting the CVS Repository -============================= +----------------------------- -The Python CVS repository contains two modules: distutils and -python. Keeping them together will produce quite long repository -URLs, so it is more convenient if the Python CVS and the distutils -CVS are converted into two separate repositories. +The Python CVS repository contains two modules: distutils and python. +Keeping them together will produce quite long repository URLs, so it +is more convenient if the Python CVS and the Distutils CVS are +converted into two separate repositories. -As the repository format, fsfs should be used (requires Subversion 1.1). -fsfs has the advantage of being more backup-friendly, as it allows to -backup a repository incrementally, without requiring to run any dump -commands. +Fsfs should be used as the repository format (requires Subversion +1.1). Fsfs has the advantage of being more backup-friendly, as it +allows incremental repository backups, without requiring any dump +commands to be run. -The conversion should be done using cvs2svn utility, available e.g. -in the cvs2svn Debian package. The command for converting the Python -repository is +The conversion should be done using the cvs2svn utility, available +e.g. in the cvs2svn Debian package. The command for converting the +Python repository is :: -cvs2svn -q --encoding=latin1 --force-branch=cnri-16-start ---force-branch=descr-branch --force-branch=release152p1-patches ---force-tag=r16b1 --fs-type=fsfs -s py.svn.new python/python + cvs2svn -q --encoding=latin1 --force-branch=cnri-16-start \ + --force-branch=descr-branch --force-branch=release152p1-patches \ + --force-tag=r16b1 --fs-type=fsfs -s py.svn.new python/python -The command to convert the distutils repository is +The command to convert the distutils repository is :: -cvs2svn -q --encoding=latin1 --fs-type=fsfs -s dist.svn.new python/distutils + cvs2svn -q --encoding=latin1 --fs-type=fsfs -s dist.svn.new python/distutils Sample results of this conversion are available at -http://www.dcl.hpi.uni-potsdam.de/python/ -http://www.dcl.hpi.uni-potsdam.de/distutils/ + | http://www.dcl.hpi.uni-potsdam.de/python/ + | http://www.dcl.hpi.uni-potsdam.de/distutils/ + Publish the Repositories -======================== +------------------------ The repositories should be published at https://svn.python.org/python -and https://svn.python.org/distutils. Read-write should be granted -through basic authentication to all current SF committers; read-only -access should be granted anonymously. As an option, websvn (available -e.g. from the Debian websvn package) could be provided. +and https://svn.python.org/distutils. Read-write access should be +granted through basic authentication to all current SF committers; +read-only anonymous access should also be granted. As an option, +websvn (available e.g. from the Debian websvn package) could be +provided. The current SF project admins should get write access to the password file, in order to create or delete new users. + Disable CVS -=========== +----------- -It appears that CVS cannot be disabled entirely. Only the user interface -can be removed from the project page; the repository itself remains -available. If desired, write access to the python and distutils modules -can be disabled through a commitinfo entry. +It appears that CVS cannot be disabled entirely. Only the user +interface can be removed from the project page; the repository itself +remains available. If desired, write access to the python and +distutils modules can be disabled through a CVS commitinfo entry. + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + End: From goodger at users.sourceforge.net Fri Aug 5 02:18:59 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Thu, 04 Aug 2005 17:18:59 -0700 Subject: [Python-checkins] python/nondist/peps pep-0000.txt,1.337,1.338 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27112 Modified Files: pep-0000.txt Log Message: changed PEP 347's type to reflect the PEP itself Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.337 retrieving revision 1.338 diff -u -d -r1.337 -r1.338 --- pep-0000.txt 4 Aug 2005 23:55:01 -0000 1.337 +++ pep-0000.txt 5 Aug 2005 00:18:51 -0000 1.338 @@ -103,7 +103,7 @@ S 341 Unifying try-except and try-finally Birkenfeld S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones - S 347 Migrating the Python CVS to Subversion von Löwis + I 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon S 754 IEEE 754 Floating Point Special Values Warnes @@ -390,7 +390,7 @@ S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones SR 346 User Defined ("with") Statements Coghlan - S 347 Migrating the Python CVS to Subversion von Löwis + I 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon SR 666 Reject Foolish Indentation Creighton S 754 IEEE 754 Floating Point Special Values Warnes From nascheme at users.sourceforge.net Fri Aug 5 04:59:02 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Thu, 04 Aug 2005 19:59:02 -0700 Subject: [Python-checkins] python/nondist/peps pep-0349.txt, NONE, 1.1 pep-0000.txt, 1.338, 1.339 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23371 Modified Files: pep-0000.txt Added Files: pep-0349.txt Log Message: Add PEP 349. --- NEW FILE: pep-0349.txt --- PEP: 349 Title: Generalised String Coercion Version: $Revision: 1.1 $ Last-Modified: $Date: 2005/08/05 02:59:00 $ Author: Neil Schemenauer Status: Draft Type: Standards Track Content-Type: text/plain Created: 02-Aug-2005 Post-History: Python-Version: 2.5 Abstract This PEP proposes the introduction of a new built-in function, text(), that provides a way of generating a string representation of an object. This function would make it easier to write library code that processes string data without forcing the use of a particular string type. Rationale Python has had a Unicode string type for some time now but use of it is not yet widespread. There is a large amount of Python code that assumes that string data is represented as str instances. The long term plan for Python is to phase out the str type and use unicode for all string data. Clearly, a smooth migration path must be provided. We need to upgrade existing libraries, written for str instances, to be made capable of operating in an all-unicode string world. We can't change to an all-unicode world until all essential libraries are made capable for it. Upgrading the libraries in one shot does not seem feasible. A more realistic strategy is to individually make the libraries capable of operating on unicode strings while preserving their current all-str environment behaviour. First, we need to be able to write code that can accept unicode instances without attempting to coerce them to str instances. Let us label such code as Unicode-safe. Unicode-safe libraries can be used in an all-unicode world. Second, we need to be able to write code that, when provided only str instances, will not create unicode results. Let us label such code as str-stable. Libraries that are str-stable can be used by libraries and applications that are not yet Unicode-safe. Sometimes it is simple to write code that is both str-stable and Unicode-safe. For example, the following function just works: def appendx(s): return s + 'x' That's not too surprising since the unicode type is designed to make the task easier. The principle is that when str and unicode instances meet, the result is a unicode instance. One notable difficulty arises when code requires a string representation of an object; an operation traditionally accomplished by using the str() built-in function. Using str() makes the code not Unicode-safe. Replacing a str() call with a unicode() call makes the code not str-stable. Using a string format almost accomplishes the goal but not quite. Consider the following code: def text(obj): return '%s' % obj It behaves as desired except if 'obj' is not a basestring instance and needs to return a Unicode representation of itself. In that case, the string format will attempt to coerce the result of __str__ to a str instance. Defining a __unicode__ method does not help since it will only be called if the right-hand operand is a unicode instance. Using a unicode instance for the right-hand operand does not work because the function is no longer str-stable (i.e. it will coerce everything to unicode). Specification A Python implementation of the text() built-in follows: def text(s): """Return a nice string representation of the object. The return value is a basestring instance. """ if isinstance(s, basestring): return s r = s.__str__() if not isinstance(s, basestring): raise TypeError('__str__ returned non-string') return r Note that it is currently possible, although not very useful, to write __str__ methods that return unicode instances. The %s format specifier for str objects would be changed to call text() on the argument. Currently it calls str() unless the argument is a unicode instance (in which case the object is substituted as is and the % operation returns a unicode instance). The following function would be added to the C API and would be the equivalent of the text() function: PyObject *PyObject_Text(PyObject *o); A reference implementation is available on Sourceforge [1] as a patch. Backwards Compatibility The change to the %s format specifier would result in some % operations returning a unicode instance rather than raising a UnicodeDecodeError exception. It seems unlikely that the change would break currently working code. Alternative Solutions Rather than adding the text() built-in, if PEP 246 were implemented then adapt(s, basestring) could be equivalent to text(s). The advantage would be one less built-in function. The problem is that PEP 246 is not implemented. Fredrik Lundh has suggested [2] that perhaps a new slot should be added (e.g. __text__), that could return any kind of string that's compatible with Python's text model. That seems like an attractive idea but many details would still need to be worked out. Instead of providing the text() built-in, the %s format specifier could be changed and a string format could be used instead of calling text(). However, it seems like the operation is important enough to justify a built-in. Instead of providing the text() built-in, the basestring type could be changed to provide the same functionality. That would possibly be confusing behaviour for an abstract base type. Some people have suggested [3] that an easier migration path would be to change the default encoding to be UTF-8. Code that is not Unicode safe would then encode Unicode strings as UTF-8 and operate on them as str instances, rather than raising a UnicodeDecodeError exception. Other code would assume that str instances were encoded using UTF-8 and decode them if necessary. While that solution may work for some applications, it seems unsuitable as a general solution. For example, some applications get string data from many different sources and assuming that all str instances were encoded using UTF-8 could easily introduce subtle bugs. References [1] http://www.python.org/sf/1159501 [2] http://mail.python.org/pipermail/python-dev/2004-September/048755.html [3] http://blog.ianbicking.org/illusive-setdefaultencoding.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 End: Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.338 retrieving revision 1.339 diff -u -d -r1.338 -r1.339 --- pep-0000.txt 5 Aug 2005 00:18:51 -0000 1.338 +++ pep-0000.txt 5 Aug 2005 02:59:00 -0000 1.339 @@ -105,6 +105,7 @@ S 345 Metadata for Python Software Packages 1.2 Jones I 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon + S 349 Generalized String Coercion Schemenauer S 754 IEEE 754 Floating Point Special Values Warnes Finished PEPs (done, implemented in CVS) @@ -392,6 +393,7 @@ SR 346 User Defined ("with") Statements Coghlan I 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon + S 349 Generalized String Coercion Schemenauer SR 666 Reject Foolish Indentation Creighton S 754 IEEE 754 Floating Point Special Values Warnes I 3000 Python 3.0 Plans Kuchling, Cannon From goodger at users.sourceforge.net Fri Aug 5 07:31:46 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Thu, 04 Aug 2005 22:31:46 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.3,1.4 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11195 Modified Files: pep-0348.txt Log Message: editing pass Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- pep-0348.txt 4 Aug 2005 03:41:38 -0000 1.3 +++ pep-0348.txt 5 Aug 2005 05:31:44 -0000 1.4 @@ -13,135 +13,157 @@ Abstract ======== -Python, as of version 2.4, has 38 exceptions (including warnings) in the built-in namespace in a rather shallow hierarchy. -This list of classes has grown over the years without a chance to learn from mistakes and cleaning up the hierarchy. -This PEP proposes doing a reorganization for Python 3.0 when backwards-compatibility is not an issue. -Along with this reorganization, adding a requirement that all objects passed to -a ``raise`` statement must inherit from a specific superclass is proposed. -Lastly, bare ``except`` clauses will catch only exceptions inheriting from -Exception. +Python, as of version 2.4, has 38 exceptions (including warnings) in +the built-in namespace in a rather shallow hierarchy. This list of +classes has grown over the years without a chance to learn from +mistakes and clean up the hierarchy. This PEP proposes doing a +reorganization for Python 3.0 when backwards-compatibility is not an +issue. Along with this reorganization, adding a requirement that all +objects passed to a ``raise`` statement must inherit from a specific +superclass is proposed. Lastly, bare ``except`` clauses will catch +only exceptions inheriting from Exception. Rationale ========= -Exceptions are a critical part of Python. -While exceptions are traditionally used to signal errors in a program, they have also grown to be used for flow control for things such as iterators. -There importance is great. +Exceptions are a critical part of Python. While exceptions are +traditionally used to signal errors in a program, they have also grown +to be used for flow control for things such as iterators. Their +importance is great. -But the organization of the exception hierarchy is suboptimal to serve the -multiple uses of exceptions. -Mostly for backwards-compatibility reasons, the hierarchy has stayed very flat and old exceptions who usefulness have not been proven have been left in. -Making exceptions more hierarchical would help facilitate exception handling by making catching exceptions using inheritance much more logical. -This should also help lead to less errors from being too broad in what exceptions are caught in an ``except`` clause. +But the organization of the exception hierarchy is suboptimal to serve +the multiple uses of exceptions. Mostly for backwards-compatibility +reasons, the hierarchy has stayed very flat and old exceptions whose +usefulness has not been proven have been left in. Making exceptions +more hierarchical would help facilitate exception handling by making +exception catching using inheritance much more logical. This should +also help lead to fewer errors from overly broad exception catching in +``except`` clauses. -A required superclass for all exceptions is also being proposed [Summary2004-08-01]_. -By requiring any object that is used in a ``raise`` statement to inherit from a specific superclass, certain attributes (such as those laid out in PEP 344 [PEP344]_) can be guaranteed to exist. -This also will lead to the planned removal of string exceptions. +A mandatory superclass for all exceptions is also being proposed +[#Summary2004-08-01]_. By requiring any object that is used in a +``raise`` statement to inherit from a specific superclass, certain +attributes (such as those laid out in PEP 344 [#PEP344]_) can be +guaranteed to exist. This also will lead to the planned removal of +string exceptions. -Lastly, bare ``except`` clauses are to catch only exceptions that inherit from -Exception [python-dev3]_. -While currently used to catch all exceptions, that use is rather far reaching -and typically not desired. -Catching only exceptions inheriting from Exception allows exceptions that -should not be caught unless explicitly desired to continue to propagate up the -execution stack. +Lastly, bare ``except`` clauses are to catch only exceptions that +inherit from ``Exception`` [#python-dev3]_. While currently used to +catch all exceptions, that use is too far-reaching and typically not +desired. Catching only exceptions that inherit from ``Exception`` +allows other exceptions (those that should not be caught unless +explicitly desired) to continue to propagate up the execution stack. Philosophy of Reorganization ============================ -There are several goals in this reorganization that defined the philosophy used to guide the work. -One goal was to prune out unneeded exceptions. -Extraneous exceptions should not be left in since it just serves to clutter the built-in namespace. -Unneeded exceptions also dilute the importance of other exceptions by splitting uses between several exceptions when all uses should have been under a single exception. +There are several goals in this reorganization that defined the +philosophy used to guide the work. One goal was to prune out unneeded +exceptions. Extraneous exceptions should not be left in since they +just serve to clutter the built-in namespace. Unneeded exceptions +also dilute the importance of other exceptions by splitting uses +between several exceptions when all uses should have been under a +single exception. -Another goal was to introduce any exceptions that were deemed needed to fill any holes in the hierarchy. -Most new exceptions were done to flesh out the inheritance hierarchy to make it easier to catch a category of exceptions with a simpler ``except`` clause. +Another goal was to introduce exceptions that were deemed necessary to +fill holes in the hierarchy. Most new exceptions were added to flesh +out the inheritance hierarchy to make it easier to catch a category of +exceptions with a simpler ``except`` clause. -Changing inheritance to make it more reasonable was a goal. -As stated above, having proper inheritance allows for more accurate ``except`` statements when catching exceptions based on the inheritance tree. +Changing inheritance to make the hierarchy more reasonable was a goal. +As stated above, having proper inheritance allows for more accurate +``except`` statements when catching exceptions based on the +inheritance tree. + +Lastly, some renaming was done to make the usage of certain exceptions +more obvious. Having to look up an exception due to the name not +accurately reflecting its intended use is annoying and slows down +debugging. Having accurate names also makes debugging easier for new +programmers. But for simplicity, for the convenience of existing +users, and for the sake of transitioning to Python 3.0, only +exceptions whose names were significantly out of alignment with their +stated purpose have been renamed. All exceptions dealing with errors +will be named with an "Error" suffix. -Lastly, any renaming to make an exception's use more obvious from its name was done. -Having to look up what an exception is meant to be used for because the name does not proper reflect its usage is annoying and slows down debugging. -Having a proper name also makes debugging easier on new programmers. -But for simplicity of existing user's and for transitioning to Python 3.0, only exceptions whose names were fairly out of alignment with their stated purpose have been renamed. -It was also made sure the exceptions dealing with errors had the "Error" -suffix. New Hierarchy ============= -.. note:: exceptions flagged as "stricter inheritance" means that the class no - longer inherits from a certain class; "broader inheritance" means a class has - been added to the exception's inheritance tree +.. Note:: Exceptions flagged with "stricter inheritance" will no + longer inherit from a certain class. A "broader inheritance" flag + means a class has been added to the exception's inheritance tree. -:: +.. parsed-literal:: - BaseException - +-- CriticalError (new) - +-- KeyboardInterrupt (stricter inheritance) - +-- MemoryError (stricter inheritance) - +-- SystemError (stricter inheritance) - +-- ControlFlowException (new) - +-- GeneratorExit (defined in PEP 342 [PEP342]_) - +-- StopIteration (stricter inheritance) - +-- SystemExit (stricter inheritance) - +-- Exception - +-- StandardError - +-- ArithmeticError - +-- DivideByZeroError - +-- FloatingPointError - +-- OverflowError - +-- AssertionError - +-- AttributeError - +-- EnvironmentError - +-- IOError - +-- EOFError (broader inheritance) - +-- OSError - +-- ImportError - +-- LookupError - +-- IndexError - +-- KeyError - +-- NamespaceError (rename of NameError) - +-- UnboundFreeError (new) - +-- UnboundGlobalError (new) - +-- UnboundLocalError - +-- NotImplementedError (stricter inheritance) - +-- SyntaxError - +-- IndentationError - +-- TabError - +-- TypeError - +-- UserError (rename of RuntimeError) - +-- UnicodeError - +-- UnicodeDecodeError - +-- UnicodeEncodeError - +-- UnicodeTranslateError - +-- ValueError - +-- Warning - +-- AnyDeprecationWarning (new; broader inheritance for subclasses) - +-- PendingDeprecationWarning - +-- DeprecationWarning - +-- FutureWarning - +-- SyntaxWarning - +-- SemanticsWarning (rename of RuntimeWarning) - +-- UserWarning - +-- WeakReferenceError (rename of ReferenceError) + BaseException + +-- CriticalError (new) + +-- KeyboardInterrupt (stricter inheritance) + +-- MemoryError (stricter inheritance) + +-- SystemError (stricter inheritance) + +-- ControlFlowException (new) + +-- GeneratorExit (defined in PEP 342 [#PEP342]_) + +-- StopIteration (stricter inheritance) + +-- SystemExit (stricter inheritance) + +-- Exception + +-- StandardError + +-- ArithmeticError + +-- DivideByZeroError + +-- FloatingPointError + +-- OverflowError + +-- AssertionError + +-- AttributeError + +-- EnvironmentError + +-- IOError + +-- EOFError (broader inheritance) + +-- OSError + +-- ImportError + +-- LookupError + +-- IndexError + +-- KeyError + +-- NamespaceError (renamed from NameError) + +-- UnboundFreeError (new) + +-- UnboundGlobalError (new) + +-- UnboundLocalError + +-- NotImplementedError (stricter inheritance) + +-- SyntaxError + +-- IndentationError + +-- TabError + +-- TypeError + +-- UserError (renamed from RuntimeError) + +-- UnicodeError + +-- UnicodeDecodeError + +-- UnicodeEncodeError + +-- UnicodeTranslateError + +-- ValueError + +-- Warning + +-- AnyDeprecationWarning (new; broader inheritance for subclasses) + +-- PendingDeprecationWarning + +-- DeprecationWarning + +-- FutureWarning + +-- SyntaxWarning + +-- SemanticsWarning (renamed from RuntimeWarning) + +-- UserWarning + +-- WeakReferenceError (renamed from ReferenceError) Differences Compared to Python 2.4 ================================== -Changes to exceptions from Python 2.4 can take shape in three forms: removal, renaming, or change in the inheritance tree. -There are also new exceptions introduced in the proposed hierarchy. +Changes to exceptions from Python 2.4 can take shape in three forms: +removal, renaming, or change of position in the hierarchy. There are +also new exceptions introduced in the proposed hierarchy. -In terms of new exceptions, almost all are to flesh out the inheritance tree. -Those that are leaf classes are to alleaviate overloading the use of another exception. +In terms of new exceptions, almost all are added to flesh out the +inheritance tree. Those that are leaf classes are added to alleviate +the overloading of another exception. -Inheritance change can be broader or more restrictive. The broader inheritance -typically occurred to allow for a more reasonable superclass to group related -exceptions together. Stricter inheritance happened when the pre-existing -inheritance was deemed incorrect and needed correction. +Positional changes result in either broader or more restrictive +inheritance. The broader inheritance typically occurred to allow for +a more reasonable superclass to group related exceptions together. +Stricter inheritance happened when the pre-existing inheritance was +deemed incorrect and needed correction. New Exceptions @@ -156,20 +178,20 @@ CriticalError ''''''''''''' -The superclass for exceptions for which a severe error has occurred that one -would not want to recover from. -The name is meant to reflect that these exceptions are raised asynchronously by -the interpreter when a critical event has occured that one would most likely -want the interpreter to halt over. +The superclass for severe error exceptions; typically, one would not +want to recover from such an exception. The name is meant to reflect +that these exceptions are raised asynchronously by the interpreter +when a critical event has occured. ControlFlowException '''''''''''''''''''' -This exception exists as a superclass for all exceptions that directly deal -with control flow. -Inheriting from BaseException instead of Exception prevents them from being caught accidently when one wants to catch errors. -The name, by not mentioning "Error", does not lead to one to confuse the subclasses as errors. +This exception exists as a superclass for all exceptions that directly +deal with control flow. Inheriting from BaseException instead of +Exception prevents them from being caught accidently when one wants to +catch errors. The name, by not mentioning "Error", does not lead to +one to confuse the subclasses as errors. UnboundGlobalError @@ -187,13 +209,12 @@ AnyDeprecationWarning ''''''''''''''''''''' -A common superclass for all deprecation-related exceptions. -While having DeprecationWarning inherit from PendingDeprecationWarning was -suggested because a DeprecationWarning can be viewed as a -PendingDeprecationWarning that is happening now, the logic was not agreed upon -by a majority. -But since the exceptions are related, creating a common superclass is -warranted. +A common superclass for all deprecation-related exceptions. While +having DeprecationWarning inherit from PendingDeprecationWarning was +suggested (a DeprecationWarning can be viewed as a +PendingDeprecationWarning that is happening now), the logic was not +agreed upon by a majority. But since the exceptions are related, +creating a common superclass is warranted. Removed Exceptions @@ -211,85 +232,91 @@ RuntimeError '''''''''''' -Renamed UserError. +Renamed to UserError. -Meant for use as a generic exception to be used when one does not want to -create a new exception class but do not want to raise an exception that might -be caught based on inheritance, RuntimeError is poorly named. -It's name in Python 2.4 seems to suggest an error that occurred at runtime, -possibly an error in the VM. -Renaming the exception to UserError more clearly states the purpose for -the exception as quick-and-dirty error exception for the user to use. -The name also keeps it in line with UserWarning. +Meant as a generic exception for use when neither a new exception +class nor inheritance-based exception catching is desired, +RuntimeError is poorly named. Its name in Python 2.4 seems to suggest +an error that occurred at runtime, possibly an error in the VM. +Renaming the exception to UserError more clearly states the purpose of +the exception as a quick-and-dirty error exception. The name also +keeps it in line with UserWarning. -If a user wants an exception that is not to be used as an error, raising -BaseException directly should be sufficient as Exception, as UserError inherits -from, is only used for errors. +If a user wants an non-error exception, raising BaseException directly +should be sufficient since Exception, which UserError inherits from, +is only used for errors. ReferenceError '''''''''''''' -Renamed WeakReferenceError. +Renamed to WeakReferenceError. -ReferenceError was added to the built-in exception hierarchy in Python 2.2 -[exceptions-stdlib]_. -Taken directly from the ``weakref`` module, its name comes directly from its original name when it resided in the module. -Unfortunately its name does not suggest its connection to weak references and thus deserves a renaming. +ReferenceError was added to the built-in exception hierarchy in Python +2.2 [#exceptions-stdlib]_. Its name comes directly from the time when +it resided in the ``weakref`` module. Unfortunately its name does not +suggest its connection to weak references and thus deserves a +renaming. NameError ''''''''' -Renamed NamespaceError. +Renamed to NamespaceError. While NameError suggests its common use, it is not entirely apparent. -Making it more of a superclass for namespace-related exceptions warrants a -renaming to make it abundantly clear its use. -Plus the documentation of the exception module[exceptions-stdlib]_ states that it is actually meant for global names and not for just any exception. +Making it a superclass for namespace-related exceptions warrants a +renaming to make its use abundantly clear. Plus the documentation of +the exception module [#exceptions-stdlib]_ states that it was actually +meant for global names and not for just any exception. RuntimeWarning '''''''''''''' -Renamed SemanticsWarning. +Renamed to SemanticsWarning. -RuntimeWarning is to represent semantic changes coming in the future. -But while saying that affects "runtime" is true, flat-out stating it is a semantic change is much clearer, eliminating any possible association of "runtime" with the virtual machine specifically. +RuntimeWarning is supposed to represent semantic changes coming in the +future. But while saying that it affects the "runtime" is true, +flat-out stating that it is a semantic change is much clearer, +eliminating any possible association of the term "runtime" with the +virtual machine. -Changed Inheritance -------------------- +Change of Position in the Exception Hierarchy +--------------------------------------------- KeyboardInterrupt, MemoryError, and SystemError ''''''''''''''''''''''''''''''''''''''''''''''' -Inherit from CriticalError instead of Exception. +Inherit from CriticalError instead of from Exception. -The three above-mentioned exceptions are not standard errors by any means. -They are raised asynchronously by the interpreter when something specific has +These three exceptions are not standard errors by any means. They are +raised asynchronously by the interpreter when something specific has occurred. Thus they warrant not inheriting from Exception but from an -entirely separate exception that will not be caught by a bare ``except`` -clause. +entirely separate exception that will not be caught by a bare +``except`` clause. StopIteration and SystemExit '''''''''''''''''''''''''''' -Inherit from ControlFlowException instead of Exception. +Inherit from ControlFlowException instead of from Exception. -By having these exceptions no longer inherit from Exception they will not be -accidentally caught by a bare ``except`` clause. +By having these exceptions no longer inherit from Exception they will +not be accidentally caught by a bare ``except`` clause. NotImplementedError ''''''''''''''''''' -Inherits from Exception instead of RuntimeError (renamed UserError). +Inherits from Exception instead of from RuntimeError (renamed to +UserError). -Originally inheriting from RuntimeError, NotImplementedError does not have any -direct relation to the exception meant for use in user code as a -quick-and-dirty exception. Thus it now directly inherits from Exception. +Originally inheriting from RuntimeError, NotImplementedError does not +have any direct relation to the exception meant for use in user code +as a quick-and-dirty exception. Thus it now directly inherits from +Exception. EOFError @@ -297,49 +324,53 @@ Subclasses IOError. -Since an EOF comes from I/O it only makes sense that it be considered an I/O error. +Since an EOF comes from I/O it only makes sense that it be considered +an I/O error. Required Superclass for ``raise`` ================================= -By requiring all objects passed to a ``raise`` statement inherit from a specific superclass, one is guaranteed that all exceptions will have certain attributes. -If PEP 342 [PEP344]_ is accepted, the attributes outlined there will be guaranteed to be on all exceptions raised. -This should help facilitate debugging by making the querying of information from exceptions much easier. +By requiring all objects passed to a ``raise`` statement to inherit +from a specific superclass, all exceptions are guaranteed to have +certain attributes. If PEP 344 [#PEP344]_ is accepted, the attributes +outlined there will be guaranteed to be on all exceptions raised. +This should help facilitate debugging by making the querying of +information from exceptions much easier. -The proposed hierarchy has BaseException as the required class that one must inherit from. +The proposed hierarchy has BaseException as the required base class. Implementation -------------- -Enforcement is straight-forward. -Modifying ``RAISE_VARARGS`` to do an inheritance check first before raising -an exception should be enough. For the C API, all functions that set an -exception will have the same inheritance check. +Enforcement is straightforward. Modifying ``RAISE_VARARGS`` to do an +inheritance check first before raising an exception should be enough. +For the C API, all functions that set an exception will have the same +inheritance check applied. -Bare ``except`` Clauses Catching Exception Only -=============================================== +Bare ``except`` Clauses Catching ``Exception`` Only +=================================================== -While Python does have its "explicit is better than implicit" tenant, it is not -necessary if a default behavior is reasonable. In the case of a bare -``except`` clause, changing the behavior makes it quite reasonable to have -around. +While Python does have its "explicit is better than implicit" tenant, +it is not necessary if there is a reasonable default behavior. +Changing the behavior of a bare ``except`` clause makes its existance +quite reasonable. -In Python 2.4, a bare ``except`` clause will catch all exceptions. Typically, -though, this is not what is truly desired. -More often than not one wants to catch all error exceptions that do not signify -a bad state of the interpreter. In the new exception hierarchy this is -embodied by Exception. Thus bare ``except`` clauses will catch only -exceptions inheriting from Exception. +In Python 2.4, a bare ``except`` clause will catch any and all +exceptions. Typically, though, this is not what is truly desired. +More often than not one wants to catch all error exceptions that do +not signify a "bad" interpreter state. In the new exception hierarchy +this is condition is embodied by Exception. Thus bare ``except`` +clauses will catch only exceptions inheriting from Exception. Implementation -------------- -In the compiler, when a bare ``except`` clause is reached, the code for -``except Exception`` will be emitted. +In the compiler, when a bare ``except`` clause is reached, the code +for ``except Exception`` will be emitted. Transition Plan @@ -351,53 +382,54 @@ New Exceptions '''''''''''''' -New exceptions can simply be added to the built-in namespace. -Any pre-existing objects with the same name will mask the new exceptions, +New exceptions can simply be added to the built-in namespace. Any +pre-existing objects with the same name will mask the new exceptions, preserving backwards-compatibility. Renamed Exceptions '''''''''''''''''' -Renamed exceptions will directly subclass the new names. -When the old exceptions are instantiated (which occurs when an exception is -caught, either by a ``try`` statement or by propagating to the top of the +Renamed exceptions will directly subclass the new names. When the old +exceptions are instantiated (which occurs when an exception is caught, +either by a ``try`` statement or by propagating to the top of the execution stack), a PendingDeprecationWarning will be raised. -This should properly preserve backwards-compatibility as old usage won't change -and the new names can be used to also catch exceptions using the old name. -The warning of the deprecation is also kept simple. +This should properly preserve backwards-compatibility as old usage +won't change and the new names can also be used to catch exceptions +using the old names. The warning of the deprecation is also kept +simple. New Inheritance for Old Exceptions '''''''''''''''''''''''''''''''''' -Using multiple inheritance to our advantage, exceptions whose inheritance is -now more resrictive can be made backwards-compatible. +Using multiple inheritance to our advantage, exceptions whose +inheritance is now more resrictive can be made backwards-compatible. By inheriting from both the new superclasses as well as the original -superclasses existing ``except`` clauses will continue to work as before while -allowing the new inheritance to be used for new clauses. +superclasses, existing ``except`` clauses will continue to work as +before while allowing the new inheritance to be used for new code. -A PendingDeprecationWarning will be raised based on whether the bytecode -``COMPARE_OP(10)`` results in an exception being caught that would not have -under the new hierarchy. This will require hard-coding in the implementation -of the bytecode. +A PendingDeprecationWarning will be raised based on whether the +bytecode ``COMPARE_OP(10)`` results in an exception being caught that +would not have under the new hierarchy. This will require hard-coding +in the implementation of the bytecode. Removed Exceptions '''''''''''''''''' -Exceptions scheduled for removal will be transitioned much like the old names -of renamed exceptions. -Upon instantiation a PendingDeprecationWarning will be raised stating the the -exception is due to be removed by Python 3.0 . +Exceptions scheduled for removal will be transitioned much like the +old names of renamed exceptions. Upon instantiation a +PendingDeprecationWarning will be raised stating the the exception is +due for removal in Python 3.0. Required Superclass for ``raise`` --------------------------------- -A SemanticsWarning will be raised when an object is passed to ``raise`` that -does not have the proper inheritance. +A SemanticsWarning will be raised when an object is passed to +``raise`` that does not have the proper inheritance. Removal of Bare ``except`` Clauses @@ -410,89 +442,91 @@ ============== Threads on python-dev discussing this PEP can be found at -[python-dev-thread1]_, [python-dev-thread2]_ +[#python-dev-thread1]_ and [#python-dev-thread2]_ KeyboardInterrupt inheriting from ControlFlowException ------------------------------------------------------ KeyboardInterrupt has been a contentious point within this hierarchy. -Some view the exception as more control flow being caused by the user. -But with its asynchronous cause thanks to the user being able to trigger the -exception at any point in code it has a more proper place inheriting from -CriticalException. It also keeps the name of the exception from being "CriticalError". +Some view the exception more as control flow being caused by the user. +But with its asynchronous cause (the user is able to trigger the +exception at any point in code) its proper place is inheriting from +CriticalException. It also keeps the name of the exception from being +"CriticalError". Other Names for BaseException and Exception ------------------------------------------- -Alternative names for BaseException/Exception have been Raisable/Exception and -Exception/StandardError. The former has been rejected on the basis that -Raisable does not reflect how it is an exception well enough. The latter was -rejected based on the fact that it did not reflect current use as the chosen -names do. +Alternative names for BaseException and Exception have been +Raisable/Exception and Exception/StandardError. The former +alternatives were rejected because "Raisable" does not reflect its +exception nature well enough. The latter alternatives were rejected +because they do not reflect current use. DeprecationWarning Inheriting From PendingDeprecationWarning ------------------------------------------------------------ -Originally proposed because a DeprecationWarning can be viewed as a -PendingDeprecationWarning that is being removed in the next version. -But enough people thought the inheritance could logically work the other way -the idea was dropped. +This was originally proposed because a DeprecationWarning can be +viewed as a PendingDeprecationWarning that is being removed in the +next version. But since enough people thought the inheritance could +logically work the other way around, the idea was dropped. AttributeError Inheriting From TypeError or NameError ----------------------------------------------------- -Viewing attributes as part of the interface of a type caused the idea of -inheriting from TypeError. -But that partially defeats the thinking of duck typing and thus was dropped. +Viewing attributes as part of the interface of a type caused the idea +of inheriting from TypeError. But that partially defeats the thinking +of duck typing and thus the idea was dropped. -Inheriting from NameError was suggested because objects can be viewed as having -their own namespace that the attributes lived in and when they are not found it -is a namespace failure. This was also dropped as a possibility since not -everyone shared this view. +Inheriting from NameError was suggested because objects can be viewed +as having their own namespace where the attributes live and when an +attribute is not found it is a namespace failure. This was also +dropped as a possibility since not everyone shared this view. Removal of EnvironmentError --------------------------- -Originally proposed based on the idea that EnvironmentError was an unneeded -distinction, the BDFL overruled this idea [python-dev4]_. +Originally proposed based on the idea that EnvironmentError was an +unneeded distinction, the BDFL overruled this idea [#python-dev4]_. Introduction of MacError and UnixError -------------------------------------- -Proposed to add symmetry to WindowsError, the BDFL said they won't be used -enough [python-dev4]_. The idea of then removing WindowsError was proposed and -accepted as reasonable, thus completely negating the idea of adding these -exceptions. +Proposed to add symmetry to WindowsError, the BDFL said they won't be +used enough [#python-dev4]_. The idea of then removing WindowsError +was proposed and accepted as reasonable, thus completely negating the +idea of adding these exceptions. SystemError Subclassing SystemExit ---------------------------------- -Proposed because a SystemError is meant to lead to a system exit, the idea was -removed since CriticalException signifies this better. +Proposed because a SystemError is meant to lead to a system exit, the +idea was removed since CriticalException indicates this better. ControlFlowException Under Exception ------------------------------------ -It has been suggested that ControlFlowException inherit from Exception. -This idea has been rejected based on the thinking that control flow exceptions -are typically not desired to be caught in a generic fashion as Exception -will usually be used. +It has been suggested that ControlFlowException should inherit from +Exception. This idea has been rejected based on the thinking that +control flow exceptions typically should not be caught by bare +``except`` clauses, whereas Exception subclasses should be. Removal of Bare ``except`` Clauses ---------------------------------- -The suggestion has been made to remove bare ``except`` clauses in the name of -"explicit is better than implicit". But Guido has said this is too weak of an -argument since other things in Python has default behavior [python-dev3]_. +The suggestion has been made to remove bare ``except`` clauses +altogether, in the name of "explicit is better than implicit". But +Guido has said this is too weak of an argument since other areas of +Python have default behavior [#python-dev3]_. Open Issues @@ -501,63 +535,76 @@ Remove ControlFlowException? ---------------------------- -It has been suggested that ControlFlowException is not needed. -Since the desire to catch any control flow exception will be atypical, the +It has been suggested that ControlFlowException is not needed. Since +the desire to catch any control flow exception will be atypical, the suggestion is to just remove the exception and let the exceptions that -inherited from it inherit directly from BaseException. This still preserves the -seperation from Exception which is one of the driving factors behind the -introduction of the exception. +inherited from it inherit directly from BaseException. This still +preserves the seperation from Exception which is one of the driving +factors behind the introduction of ControlFlowException. Acknowledgements ================ -Thanks to Robert Brewer, Josiah Carlson, Nick Coghlan, Timothy Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing, James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull and everyone else I missed for participating in the discussion. +Thanks to Robert Brewer, Josiah Carlson, Nick Coghlan, Timothy +Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing, +James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull and +everyone else I missed for participating in the discussion. References ========== -.. [PEP342] PEP 342 (Coroutines via Enhanced Generators) - (http://www.python.org/peps/pep-0342.html) - -.. [PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) - (http://www.python.org/peps/pep-0344.html) +.. [#PEP342] PEP 342 (Coroutines via Enhanced Generators) + (http://www.python.org/peps/pep-0342.html) -.. [exceptionsmodules] 'exceptions' module - (http://docs.python.org/lib/module-exceptions.html) +.. [#PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) + (http://www.python.org/peps/pep-0344.html) -.. [Summary2004-08-01] python-dev Summary (An exception is an exception, unless it doesn't inherit from Exception) - (http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception) +.. [#Summary2004-08-01] python-dev Summary (An exception is an + exception, unless it doesn't inherit from Exception) + (http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception) -.. [Summary2004-09-01] python-dev Summary (Cleaning the Exception House) - (http://www.python.org/dev/summary/2004-09-01_2004-09-15.html#cleaning-the-exception-house) +.. [#Summary2004-09-01] python-dev Summary (Cleaning the Exception House) + (http://www.python.org/dev/summary/2004-09-01_2004-09-15.html#cleaning-the-exception-house) -.. [python-dev1] python-dev email (Exception hierarchy) - (http://mail.python.org/pipermail/python-dev/2004-August/047908.html) +.. [#python-dev1] python-dev email (Exception hierarchy) + (http://mail.python.org/pipermail/python-dev/2004-August/047908.html) -.. [python-dev2] python-dev email (Dangerous exceptions) - (http://mail.python.org/pipermail/python-dev/2004-September/048681.html) +.. [#python-dev2] python-dev email (Dangerous exceptions) + (http://mail.python.org/pipermail/python-dev/2004-September/048681.html) -.. [python-dev3] python-dev email (PEP, take 2: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-August/055116.html) +.. [#python-dev3] python-dev email (PEP, take 2: Exception + Reorganization for Python 3.0) + (http://mail.python.org/pipermail/python-dev/2005-August/055116.html) -.. [exceptions-stdlib] exceptions module - (http://www.python.org/doc/2.4.1/lib/module-exceptions.html) +.. [#exceptions-stdlib] exceptions module + (http://docs.python.org/lib/module-exceptions.html) -.. [python-dev-thread1] python-dev thread (Pre-PEP: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-July/055020.html - , - http://mail.python.org/pipermail/python-dev/2005-August/055065.html) +.. [#python-dev-thread1] python-dev thread (Pre-PEP: Exception + Reorganization for Python 3.0) + (http://mail.python.org/pipermail/python-dev/2005-July/055020.html, + http://mail.python.org/pipermail/python-dev/2005-August/055065.html) -.. [python-dev-thread2] python-dev thread (PEP, take 2: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-August/055103.html) +.. [#python-dev-thread2] python-dev thread (PEP, take 2: Exception + Reorganization for Python 3.0) + (http://mail.python.org/pipermail/python-dev/2005-August/055103.html) -.. [python-dev4] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-July/055019.html) +.. [#python-dev4] python-dev email (Pre-PEP: Exception Reorganization + for Python 3.0) + (http://mail.python.org/pipermail/python-dev/2005-July/055019.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 + End: From loewis at users.sourceforge.net Fri Aug 5 09:26:35 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Fri, 05 Aug 2005 00:26:35 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.2,1.3 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26233 Modified Files: pep-0347.txt Log Message: Add copyright. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- pep-0347.txt 5 Aug 2005 00:16:49 -0000 1.2 +++ pep-0347.txt 5 Aug 2005 07:26:32 -0000 1.3 @@ -169,6 +169,11 @@ remains available. If desired, write access to the python and distutils modules can be disabled through a CVS commitinfo entry. +Copyright +--------- + +This document has been placed in the public domain. + .. Local Variables: From gregorykjohnson at users.sourceforge.net Fri Aug 5 18:34:48 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Fri, 05 Aug 2005 09:34:48 -0700 Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.6, 1.7 test_mailbox.py, 1.5, 1.6 libmailbox.tex, 1.7, 1.8 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27444 Modified Files: mailbox.py test_mailbox.py libmailbox.tex Log Message: * Implement Babyl, except labels. Needs more tests. * Introduce _singlefileMailbox class and refactor much of _mboxMMDF into it for use by Babyl. * Make various tweaks and rearrangements in mbox, MMDF, and _mboxMMDF. Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- mailbox.py 3 Aug 2005 23:50:43 -0000 1.6 +++ mailbox.py 5 Aug 2005 16:34:45 -0000 1.7 @@ -12,6 +12,7 @@ import email.Message import email.Generator import rfc822 +import StringIO try: import fnctl except ImportError: @@ -190,8 +191,6 @@ else: raise TypeError, "Invalid message type" - - class Maildir(Mailbox): """A qmail-style Maildir mailbox.""" @@ -410,11 +409,11 @@ raise KeyError, "No message with key '%s'" % key -class _mboxMMDF(Mailbox): - """An mbox or MMDF mailbox.""" +class _singlefileMailbox(Mailbox): + """A single-file mailbox.""" def __init__(self, path, factory=None): - """Initialize an mbox or MMDF mailbox.""" + """Initialize a single-file mailbox.""" Mailbox.__init__(self, path, factory) try: f = file(self._path, 'r+') @@ -447,37 +446,9 @@ def __setitem__(self, key, message): """Replace the keyed message; raise KeyError if it doesn't exist.""" self._lookup(key) - start, stop = self._append_message(message) - self._toc[key] = (start, stop) + self._toc[key] = self._append_message(message) self._pending = True - def get_message(self, key): - start, stop = self._lookup(key) - self._assert_mtime() - self._file.seek(start) - from_line = self._file.readline() - msg = self._message_factory(self._file.read(stop - self._file.tell())) - msg.set_from(from_line[5:-1]) - return msg - - def get_string(self, key, from_=False): - """Return a string representation or raise a KeyError.""" - start, stop = self._lookup(key) - self._assert_mtime() - self._file.seek(start) - if not from_: - self._file.readline() - return self._file.read(stop - self._file.tell()) - - def get_file(self, key, from_=False): - """Return a file-like representation or raise a KeyError.""" - start, stop = self._lookup(key) - self._assert_mtime() - self._file.seek(start) - if not from_: - self._file.readline() - return _PartialFile(self._file, self._file.tell(), stop) - def iterkeys(self): """Return an iterator over keys.""" self._lookup() @@ -512,11 +483,12 @@ try: _lock_file(f, self._path, dot=False) try: + self._pre_mailbox_hook(f) new_toc = {} for key in sorted(self._toc.keys()): - start, stop = self._toc[key] + start, stop = self._toc[key][:2] self._file.seek(start) - self._pre_write_hook(f) + self._pre_message_hook(f) new_start = f.tell() while True: buffer = self._file.read( @@ -524,8 +496,9 @@ if buffer == '': break f.write(buffer) - new_toc[key] = (new_start, f.tell()) - self._post_write_hook(f) + new_toc[key] = (new_start, f.tell()) + \ + self._toc[key][2:] # XXX: Wrong! + self._post_message_hook(f) finally: _unlock_file(f, self._path) except: @@ -536,17 +509,30 @@ self._file.close() self._toc = new_toc self._file = f + self._file.flush() self._set_mtime() os.remove(self._path + '~') self._pending = False + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + return + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + return + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + return + def close(self): """Flush and close the mailbox.""" self.flush() self._file.close() def _lookup(self, key=None): - """Return (start, stop) for given key, or raise a KeyError.""" + """Return (start, stop), possibly with more info, or raise KeyError.""" if self._toc is None: self._generate_toc() if key is not None: @@ -555,8 +541,64 @@ except KeyError: raise KeyError, "No message with key '%s'" % key + def _assert_mtime(self): + """Raise an exception if the file has been externally modified.""" + if self._mtime != os.fstat(self._file.fileno()).st_mtime: + raise Error, 'External modifications detected: use refresh()' + + def _set_mtime(self): + """Store the current mtime.""" + self._mtime = os.fstat(self._file.fileno()).st_mtime + def _append_message(self, message): - """Append message to mailbox and return (start, stop) offset.""" + """Append message to mailbox and return (start, stop, ...) offsets.""" + _lock_file(self._file, self._path) + try: + self._assert_mtime() + self._file.seek(0, 2) + self._pre_message_hook(self._file) + offsets = self._install_message(message) + self._post_message_hook(self._file) + self._file.flush() + self._set_mtime() + finally: + _unlock_file(self._file, self._path) + return offsets + + +class _mboxMMDF(_singlefileMailbox): + """An mbox or MMDF mailbox.""" + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._assert_mtime() + self._file.seek(start) + from_line = self._file.readline() + msg = self._message_factory(self._file.read(stop - self._file.tell())) + msg.set_from(from_line[5:-1]) + return msg + + def get_string(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._assert_mtime() + self._file.seek(start) + if not from_: + self._file.readline() + return self._file.read(stop - self._file.tell()) + + def get_file(self, key, from_=False): + """Return a file-like representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._assert_mtime() + self._file.seek(start) + if not from_: + self._file.readline() + return _PartialFile(self._file, self._file.tell(), stop) + + def _install_message(self, message): + """Format a message and blindly write to self._file.""" from_line = None if isinstance(message, str) and message[:5] == 'From ': newline = message.find(os.linesep) @@ -569,36 +611,16 @@ elif isinstance(message, _mboxMMDFMessage): from_line = 'From ' + message.get_from() elif isinstance(message, email.Message.Message): - from_line = message.get_unixfrom() + from_line = message.get_unixfrom() # May be None. if from_line is None: from_line = 'From MAILER-DAEMON %s' % \ time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime()) - _lock_file(self._file, self._path) - try: - self._assert_mtime() - self._file.seek(0, 2) - self._pre_write_hook(self._file) - start = self._file.tell() - self._file.write('%s%s' % (from_line, os.linesep)) - self._dump_message(message, self._file) - stop = self._file.tell() - self._post_write_hook(self._file) - self._file.flush() - self._set_mtime() - finally: - _unlock_file(self._file, self._path) + start = self._file.tell() + self._file.write('%s%s' % (from_line, os.linesep)) + self._dump_message(message, self._file) + stop = self._file.tell() return (start, stop) - def _assert_mtime(self): - """Raise an exception if the file has been externally modified.""" - if self._mtime != os.fstat(self._file.fileno()).st_mtime: - raise Error, 'External modifications detected: use refresh()' - - def _set_mtime(self): - """Store the current mtime.""" - self._mtime = os.fstat(self._file.fileno()).st_mtime - - class mbox(_mboxMMDF): """A classic mbox mailbox.""" @@ -608,34 +630,26 @@ self._message_factory = mboxMessage _mboxMMDF.__init__(self, path, factory) - def _pre_write_hook(self, f): - """Called by close before writing each message.""" + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" if f.tell() != 0: f.write(os.linesep) - def _post_write_hook(self, f): - """Called by close after writing each message.""" - return - def _generate_toc(self): """Generate key-to-(start, stop) table of contents.""" starts, stops = [], [] - self._assert_mtime() self._file.seek(0) - prev_line = '' + self._assert_mtime() while True: - pos = self._file.tell() + line_pos = self._file.tell() line = self._file.readline() if line[:5] == 'From ': - starts.append(pos) - # The preceeding newline is part of the separator, e.g., - # "\nFrom .*\n", not part of the previous message. Ignore it. - if prev_line != '': - stops.append(pos - len(os.linesep)) + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(line_pos) elif line == '': - stops.append(pos) + stops.append(line_pos) break - prev_line = line self._toc = dict(enumerate(zip(starts, stops))) self._next_key = len(self._toc) @@ -648,31 +662,35 @@ self._message_factory = MMDFMessage _mboxMMDF.__init__(self, path, factory) - def _pre_write_hook(self, f): - """Called by close before writing each message.""" + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" f.write('\001\001\001\001\n') - def _post_write_hook(self, f): - """Called by close after writing each message.""" + def _post_message_hook(self, f): + """Called after writing each message to file f.""" f.write('\n\001\001\001\001\n') def _generate_toc(self): """Generate key-to-(start, stop) table of contents.""" starts, stops = [], [] - self._assert_mtime() self._file.seek(0) + next_pos = 0 + self._assert_mtime() while True: + line_pos = next_pos line = self._file.readline() + next_pos = self._file.tell() if line[:4 + len(os.linesep)] == '\001\001\001\001' + os.linesep: - starts.append(self._file.tell()) + starts.append(next_pos) while True: - pos = self._file.tell() + line_pos = next_pos line = self._file.readline() + next_pos = self._file.tell() if line == '\001\001\001\001' + os.linesep: - stops.append(pos - len(os.linesep)) + stops.append(line_pos - len(os.linesep)) break elif line == '': - stops.append(pos) + stops.append(line_pos) break elif line == '': break @@ -912,6 +930,7 @@ os.rename(os.path.join(self._path, str(key)), os.path.join(self._path, str(prev + 1))) prev += 1 + self._next_key = prev + 1 if len(changes) == 0: return keys = self.keys() @@ -936,6 +955,160 @@ self.set_sequences(all_sequences) +class Babyl(_singlefileMailbox): + """An Rmail-style Babyl mailbox.""" + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._assert_mtime() + self._file.seek(start) + self._file.readline() # XXX: parse this '1,' line for labels + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': + break + original_headers.write(line) + visible_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + visible_headers.write(line) + body = self._file.read(stop - self._file.tell()) + msg = BabylMessage(original_headers.getvalue() + body) + msg.set_visible(visible_headers.getvalue()) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._assert_mtime() + self._file.seek(start) + self._file.readline() # Skip '1,' line. + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': + break + original_headers.write(line) + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + return original_headers.getvalue() + \ + self._file.read(stop - self._file.tell()) + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + return StringIO.StringIO(self.get_string(key)) + + def list_labels(self): + """Return a list of user-defined labels in the mailbox.""" + raise NotImplementedError, 'Method not yet implemented' + + def _generate_toc(self): + """Generate key-to-(start, stop, eooh, body) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + self._assert_mtime() + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == '\037\014' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(next_pos) + elif line == '\037' or line == '\037' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + elif line == '': + stops.append(line_pos - len(os.linesep)) + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + f.write('BABYL OPTIONS:%sVersion: 5%s\037' % (os.linesep, os.linesep)) + # XXX: write "Labels:" line too + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write('\014\n') + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write('\n\037') + + def _install_message(self, message): + """Write message contents and return (start, stop, ...).""" + start = self._file.tell() + self._file.write('1,,\n') # XXX: check for labels and add them + if isinstance(message, email.Message.Message): + pseudofile = StringIO.StringIO() + ps_generator = email.Generator.Generator(pseudofile, False, 0) + ps_generator.flatten(message) + pseudofile.seek(0) + while True: + line = pseudofile.readline() + self._file.write(line) + if line == os.linesep or line == '': + break + self._file.write('*** EOOH ***' + os.linesep) + if isinstance(message, BabylMessage): + generator = email.Generator.Generator(self._file, False, 0) + generator.flatten(message.get_visible()) + else: + pseudofile.seek(0) + while True: + line = pseudofile.readline() + self._file.write(line) + if line == os.linesep or line == '': + break + while True: + buffer = pseudofile.read(4069) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer) + elif isinstance(message, str): + body_start = message.find(os.linesep + os.linesep) + \ + 2 * len(os.linesep) + if body_start - 2 != -1: + self._file.write(message[:body_start]) + self._file.write('*** EOOH ***' + os.linesep) + self._file.write(message[:body_start]) + self._file.write(message[body_start:]) + else: + self._file.write('*** EOOH ***%s%s' % (os.linesep, os.linesep)) + self._file.write(message) + elif hasattr(message, 'readline'): + original_pos = message.tell() + first_pass = True + while True: + line = message.readline() + self._file.write(line) + if line == os.linesep or line == '': + self._file.write('*** EOOH ***' + os.linesep) + if first_pass: + first_pass = False + message.seek(original_pos) + else: + break + while True: + buffer = message.read(4096) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer) + else: + raise TypeError, "Invalid message type" + stop = self._file.tell() + return (start, stop) + + class Message(email.Message.Message): """Message with mailbox-format-specific properties.""" Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- test_mailbox.py 2 Aug 2005 21:46:13 -0000 1.5 +++ test_mailbox.py 5 Aug 2005 16:34:45 -0000 1.6 @@ -675,6 +675,11 @@ _factory = lambda self, path, factory=None: mailbox.MH(path, factory) +class TestBabyl(TestMailbox): + + _factory = lambda self, path, factory=None: mailbox.Babyl(path, factory) + + class TestMessage(TestBase): _factory = mailbox.Message # Overridden by subclasses to reuse tests @@ -1445,7 +1450,7 @@ def test_main(): - tests = (TestMaildir, TestMbox, TestMMDF, TestMH, TestMessage, + tests = (TestMaildir, TestMbox, TestMMDF, TestMH, TestBabyl, TestMessage, TestMaildirMessage, TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, TestMessageConversion, TestProxyFile, TestPartialFile) Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- libmailbox.tex 3 Aug 2005 23:50:43 -0000 1.7 +++ libmailbox.tex 5 Aug 2005 16:34:45 -0000 1.8 @@ -506,19 +506,34 @@ \method{open()} function. \end{classdesc} -Babyl is a single-file mailbox format invented for use with the Rmail mail -reading application that ships with Emacs. A Babyl mailbox begins with a -so-called options section that indicates the format of the mailbox. Messages -follow the options section, with the beginning and end of each message -indicated by control characters. Each message in a Babyl mailbox has an -accompanying list of \dfn{labels}, or short strings that record extra -information about the message. +Babyl is a single-file mailbox format invented for the \program{Rmail} mail +reading application included with Emacs. A Babyl mailbox begins with an options +section that indicates the format of the mailbox and contains a list of +user-defined labels that appear in the mailbox. Messages follow the options +section. The beginning of a message is indicated by a line containing exactly +two control characters, namely Control-Underscore +(\character{\textbackslash037}) followed by Control-L +(\character{\textbackslash014}). The end of a message is indicated by the start +of the next message or, in the case of the last message, a line containing only +a Control-Underscore (\character{\textbackslash037}) character. Each message in +a Babyl mailbox has an accompanying list of \dfn{labels}, or short strings that +record extra information about the message. + +\class{Babyl} instances have all of the methods of \class{Mailbox} in addition +to the following: + +\begin{methoddesc}{list_labels}{} +Return a list of all user-defined labels in the mailbox. +\end{methoddesc} Some \class{Mailbox} methods implemented by \class{Babyl} deserve special remarks: \begin{methoddesc}{get_file}{key} -XXX +In Babyl mailboxes, the headers of a message are not stored contiguously with +the body of the message. To generate a file-like representation, they are +copied together into a \class{StringIO} instance (from the \module{StringIO} +module), which may be used like a file. \end{methoddesc} \begin{seealso} @@ -941,7 +956,7 @@ Each message in a Babyl mailbox has two sets of headers, original headers and visible headers. Visible headers are typically a subset of the original -headers reformatted to be more attractive. By default, \program{rmail} displays +headers reformatted to be more attractive. By default, \program{Rmail} displays only visible headers. \class{BabylMessage} uses the original headers because they are more complete, though the visible headers may be accessed explicitly if desired. From rhettinger at users.sourceforge.net Fri Aug 5 19:19:57 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Fri, 05 Aug 2005 10:19:57 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.40,1.41 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3308/Objects Modified Files: setobject.c Log Message: * Improve a variable name: entry0 --> table. * Give set_lookkey_string() a fast alternate path when no dummy entries are present. * Have set_swap_bodies() reset the hash field to -1 whenever either of bodies is not a frozenset. Maintains the invariant of regular sets always having -1 in the hash field; otherwise, any mutation would make the hash value invalid. * Use an entry pointer to simplify the code in frozenset_hash(). Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.40 retrieving revision 1.41 diff -u -d -r1.40 -r1.41 --- setobject.c 5 Aug 2005 00:01:15 -0000 1.40 +++ setobject.c 5 Aug 2005 17:19:54 -0000 1.41 @@ -46,7 +46,7 @@ register unsigned int perturb; register setentry *freeslot; register unsigned int mask = so->mask; - setentry *entry0 = so->table; + setentry *table = so->table; register setentry *entry; register int restore_error; register int checked_error; @@ -55,7 +55,7 @@ PyObject *startkey; i = hash & mask; - entry = &entry0[i]; + entry = &table[i]; if (entry->key == NULL || entry->key == key) return entry; @@ -74,7 +74,7 @@ cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); if (cmp < 0) PyErr_Clear(); - if (entry0 == so->table && entry->key == startkey) { + if (table == so->table && entry->key == startkey) { if (cmp > 0) goto Done; } @@ -93,7 +93,7 @@ least likely outcome, so test for that last. */ for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { i = (i << 2) + i + perturb + 1; - entry = &entry0[i & mask]; + entry = &table[i & mask]; if (entry->key == NULL) { if (freeslot != NULL) entry = freeslot; @@ -114,7 +114,7 @@ cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); if (cmp < 0) PyErr_Clear(); - if (entry0 == so->table && entry->key == startkey) { + if (table == so->table && entry->key == startkey) { if (cmp > 0) break; } @@ -153,7 +153,7 @@ register unsigned int perturb; register setentry *freeslot; register unsigned int mask = so->mask; - setentry *entry0 = so->table; + setentry *table = so->table; register setentry *entry; /* Make sure this function doesn't have to handle non-string keys, @@ -165,31 +165,47 @@ return set_lookkey(so, key, hash); } i = hash & mask; - entry = &entry0[i]; + entry = &table[i]; if (entry->key == NULL || entry->key == key) return entry; - if (entry->key == dummy) - freeslot = entry; - else { - if (entry->hash == hash && _PyString_Eq(entry->key, key)) - return entry; - freeslot = NULL; - } + if (so->fill != so->used) { + if (entry->key == dummy) + freeslot = entry; + else { + if (entry->hash == hash && _PyString_Eq(entry->key, key)) + return entry; + freeslot = NULL; + } - /* In the loop, key == dummy is by far (factor of 100s) the - least likely outcome, so test for that last. */ - for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { - i = (i << 2) + i + perturb + 1; - entry = &entry0[i & mask]; - if (entry->key == NULL) - return freeslot == NULL ? entry : freeslot; - if (entry->key == key - || (entry->hash == hash - && entry->key != dummy - && _PyString_Eq(entry->key, key))) + /* In the loop, key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + entry = &table[i & mask]; + if (entry->key == NULL) + return freeslot == NULL ? entry : freeslot; + if (entry->key == key + || (entry->hash == hash + && entry->key != dummy + && _PyString_Eq(entry->key, key))) + return entry; + if (entry->key == dummy && freeslot == NULL) + freeslot = entry; + } + } else { + /* Simplified loop that can assume are no dummy entries */ + if (entry->hash == hash && _PyString_Eq(entry->key, key)) return entry; - if (entry->key == dummy && freeslot == NULL) - freeslot = entry; + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + entry = &table[i & mask]; + if (entry->key == NULL) + return entry; + if (entry->key == key + || (entry->hash == hash + && _PyString_Eq(entry->key, key))) + return entry; + } } } @@ -377,10 +393,8 @@ setentry small_copy[PySet_MINSIZE]; #ifdef Py_DEBUG int i, n; -#endif - assert (PyAnySet_Check(so)); -#ifdef Py_DEBUG + n = so->mask + 1; i = 0; #endif @@ -841,7 +855,13 @@ memcpy(b->smalltable, tab, sizeof(tab)); } - h = a->hash; a->hash = b->hash; b->hash = h; + if (PyType_IsSubtype(a->ob_type, &PyFrozenSet_Type) && + PyType_IsSubtype(b->ob_type, &PyFrozenSet_Type)) { + h = a->hash; a->hash = b->hash; b->hash = h; + } else { + a->hash = -1; + b->hash = -1; + } } static int @@ -1301,19 +1321,18 @@ frozenset_hash(PyObject *self) { PySetObject *so = (PySetObject *)self; - long hash = 1927868237L; - int i, j; + long h, hash = 1927868237L; + setentry *entry; + int i; if (so->hash != -1) return so->hash; hash *= set_len(self) + 1; - for (i=0, j=so->used ; j ; j--, i++) { - setentry *entry; - long h; - - while ((entry = &so->table[i])->key == NULL || entry->key==dummy) - i++; + entry = &so->table[0]; + for (i=so->used ; i ; entry++, i--) { + while (entry->key == NULL || entry->key==dummy) + entry++; /* Work to increase the bit dispersion for closely spaced hash values. The is important because some use cases have many combinations of a small number of elements with nearby From birkenfeld at users.sourceforge.net Fri Aug 5 23:02:00 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Fri, 05 Aug 2005 14:02:00 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libpoplib.tex,1.17,1.18 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19402/Doc/lib Modified Files: libpoplib.tex Log Message: bug [ 1252706 ] poplib list() docstring fix (and docs too) Index: libpoplib.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libpoplib.tex,v retrieving revision 1.17 retrieving revision 1.18 diff -u -d -r1.17 -r1.18 --- libpoplib.tex 11 Jan 2004 23:00:16 -0000 1.17 +++ libpoplib.tex 5 Aug 2005 21:01:57 -0000 1.18 @@ -108,8 +108,8 @@ \begin{methoddesc}{list}{\optional{which}} Request message list, result is in the form -\code{(\var{response}, ['mesg_num octets', ...])}. If \var{which} is -set, it is the message to list. +\code{(\var{response}, ['mesg_num octets', ...], \var{octets})}. +If \var{which} is set, it is the message to list. \end{methoddesc} \begin{methoddesc}{retr}{which} From birkenfeld at users.sourceforge.net Fri Aug 5 23:02:00 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Fri, 05 Aug 2005 14:02:00 -0700 Subject: [Python-checkins] python/dist/src/Lib poplib.py,1.23,1.24 Message-ID: Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19402/Lib Modified Files: poplib.py Log Message: bug [ 1252706 ] poplib list() docstring fix (and docs too) Index: poplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/poplib.py,v retrieving revision 1.23 retrieving revision 1.24 diff -u -d -r1.23 -r1.24 --- poplib.py 12 Feb 2004 17:35:06 -0000 1.23 +++ poplib.py 5 Aug 2005 21:01:58 -0000 1.24 @@ -219,7 +219,7 @@ """Request listing, return result. Result without a message number argument is in form - ['response', ['mesg_num octets', ...]]. + ['response', ['mesg_num octets', ...], octets]. Result when a message number argument is given is a single response: the "scan listing" for that message. From birkenfeld at users.sourceforge.net Fri Aug 5 23:02:45 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Fri, 05 Aug 2005 14:02:45 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libpoplib.tex, 1.17, 1.17.4.1 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19559/dist/src/Doc/lib Modified Files: Tag: release24-maint libpoplib.tex Log Message: backport patch [ 1252706 ] poplib list() docstring fix (and docs too) Index: libpoplib.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libpoplib.tex,v retrieving revision 1.17 retrieving revision 1.17.4.1 diff -u -d -r1.17 -r1.17.4.1 --- libpoplib.tex 11 Jan 2004 23:00:16 -0000 1.17 +++ libpoplib.tex 5 Aug 2005 21:02:43 -0000 1.17.4.1 @@ -108,8 +108,8 @@ \begin{methoddesc}{list}{\optional{which}} Request message list, result is in the form -\code{(\var{response}, ['mesg_num octets', ...])}. If \var{which} is -set, it is the message to list. +\code{(\var{response}, ['mesg_num octets', ...], \var{octets})}. +If \var{which} is set, it is the message to list. \end{methoddesc} \begin{methoddesc}{retr}{which} From birkenfeld at users.sourceforge.net Fri Aug 5 23:02:45 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Fri, 05 Aug 2005 14:02:45 -0700 Subject: [Python-checkins] python/dist/src/Lib poplib.py,1.23,1.23.4.1 Message-ID: Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19559/dist/src/Lib Modified Files: Tag: release24-maint poplib.py Log Message: backport patch [ 1252706 ] poplib list() docstring fix (and docs too) Index: poplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/poplib.py,v retrieving revision 1.23 retrieving revision 1.23.4.1 diff -u -d -r1.23 -r1.23.4.1 --- poplib.py 12 Feb 2004 17:35:06 -0000 1.23 +++ poplib.py 5 Aug 2005 21:02:43 -0000 1.23.4.1 @@ -219,7 +219,7 @@ """Request listing, return result. Result without a message number argument is in form - ['response', ['mesg_num octets', ...]]. + ['response', ['mesg_num octets', ...], octets]. Result when a message number argument is given is a single response: the "scan listing" for that message. From pje at users.sourceforge.net Sat Aug 6 04:30:55 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Fri, 05 Aug 2005 19:30:55 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.56, 1.57 setuptools.txt, 1.24, 1.25 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4617 Modified Files: pkg_resources.py setuptools.txt Log Message: Performance boosts: don't create environment during require()/resolve() if all requirements can be met with items already in the working set. Don't eagerly determine whether a path is a directory. Avoid redundant path operations, etc. These changes dropped the test suite runtime from over 3.4 seconds to around .34 seconds. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.56 retrieving revision 1.57 diff -u -d -r1.56 -r1.57 --- pkg_resources.py 3 Aug 2005 13:18:50 -0000 1.56 +++ pkg_resources.py 6 Aug 2005 02:30:52 -0000 1.57 @@ -460,8 +460,6 @@ already-installed distribution; it should return a ``Distribution`` or ``None``. """ - if env is None: - env = AvailableDistributions(self.entries) requirements = list(requirements)[::-1] # set up the stack processed = {} # set of processed requirements @@ -477,6 +475,8 @@ dist = best.get(req.key) if dist is None: # Find the best distribution and add it to the map + if env is None: + env = AvailableDistributions(self.entries) dist = best[req.key] = env.best_match(req, self, installer) if dist is None: raise DistributionNotFound(req) # XXX put more info here @@ -1232,8 +1232,6 @@ """PEP 302 Importer that wraps Python's "normal" import algorithm""" def __init__(self, path=None): - if path is not None and not os.path.isdir(path): - raise ImportError self.path = path def find_module(self, fullname, path=None): @@ -1269,6 +1267,8 @@ return mod + + def get_importer(path_item): """Retrieve a PEP 302 "importer" for the given path item @@ -1357,9 +1357,8 @@ def find_on_path(importer, path_item, only=False): """Yield distributions accessible on a sys.path directory""" - if not os.path.exists(path_item): - return path_item = normalize_path(path_item) + if os.path.isdir(path_item): if path_item.lower().endswith('.egg'): # unpacked egg @@ -1370,10 +1369,10 @@ ) else: # scan for .egg and .egg-info in directory - for entry in os.listdir(path_item): - fullpath = os.path.join(path_item, entry) + for entry in os.listdir(path_item): lower = entry.lower() if lower.endswith('.egg-info'): + fullpath = os.path.join(path_item, entry) if os.path.isdir(fullpath): # development egg metadata = PathMetadata(path_item, fullpath) @@ -1382,16 +1381,17 @@ path_item, metadata, project_name=dist_name ) elif not only and lower.endswith('.egg'): - for dist in find_distributions(fullpath): + for dist in find_distributions(os.path.join(path_item, entry)): yield dist elif not only and lower.endswith('.egg-link'): - for line in file(fullpath): + for line in file(os.path.join(path_item, entry)): if not line.strip(): continue for item in find_distributions(line.rstrip()): yield item register_finder(ImpWrapper,find_on_path) + _namespace_handlers = {} _namespace_packages = {} Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.24 retrieving revision 1.25 diff -u -d -r1.24 -r1.25 --- setuptools.txt 25 Jul 2005 03:12:51 -0000 1.24 +++ setuptools.txt 6 Aug 2005 02:30:52 -0000 1.25 @@ -1597,6 +1597,10 @@ containing ``setup.py``, not the highest revision number in the project. * Added ``eager_resources`` setup argument + + * Enhanced performance of ``require()`` and related operations when all + requirements are already in the working set, and enhanced performance of + directory scanning for distributions. * Fixed some problems using ``pkg_resources`` w/PEP 302 loaders other than ``zipimport``, and the previously-broken "eager resource" support. From nascheme at users.sourceforge.net Sat Aug 6 06:05:51 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Fri, 05 Aug 2005 21:05:51 -0700 Subject: [Python-checkins] python/nondist/peps pep-0349.txt,1.1,1.2 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16556 Modified Files: pep-0349.txt Log Message: Mention %s change in abstract. Fix bug in code (found by Osvaldo Santana Neto). Index: pep-0349.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0349.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- pep-0349.txt 5 Aug 2005 02:59:00 -0000 1.1 +++ pep-0349.txt 6 Aug 2005 04:05:48 -0000 1.2 @@ -15,9 +15,12 @@ This PEP proposes the introduction of a new built-in function, text(), that provides a way of generating a string representation - of an object. This function would make it easier to write library - code that processes string data without forcing the use of a - particular string type. + of an object without forcing the result to be a particular string + type. In addition, the behavior %s format specifier would be + changed to call text() on the argument. These two changes would + make it easier to write library code that can be used by + applications that use only the str type and by others that also use + the unicode type. Rationale @@ -90,7 +93,7 @@ if isinstance(s, basestring): return s r = s.__str__() - if not isinstance(s, basestring): + if not isinstance(r, basestring): raise TypeError('__str__ returned non-string') return r From rhettinger at users.sourceforge.net Sat Aug 6 07:43:41 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Fri, 05 Aug 2005 22:43:41 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.41,1.42 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29185 Modified Files: setobject.c Log Message: Factor away a redundant clear() function. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.41 retrieving revision 1.42 diff -u -d -r1.41 -r1.42 --- setobject.c 5 Aug 2005 17:19:54 -0000 1.41 +++ setobject.c 6 Aug 2005 05:43:39 -0000 1.42 @@ -384,7 +384,7 @@ return DISCARD_FOUND; } -static void +static int set_clear_internal(PySetObject *so) { setentry *entry, *table; @@ -406,7 +406,7 @@ /* This is delicate. During the process of clearing the set, * decrefs can cause the set to mutate. To avoid fatal confusion * (voice of experience), we have to make the set empty before - * clearing the slots, and never refer to anything via mp->ref while + * clearing the slots, and never refer to anything via so->ref while * clearing. */ fill = so->fill; @@ -445,6 +445,8 @@ if (table_is_malloced) PyMem_DEL(table); + so->hash = -1; + return 0; } /* @@ -1433,20 +1435,11 @@ set_clear(PySetObject *so) { set_clear_internal(so); - so->hash = -1; Py_RETURN_NONE; } PyDoc_STRVAR(clear_doc, "Remove all elements from this set."); -static int -set_tp_clear(PySetObject *so) -{ - set_clear_internal(so); - so->hash = -1; - return 0; -} - static PyObject * set_add(PySetObject *so, PyObject *key) { @@ -1727,7 +1720,7 @@ Py_TPFLAGS_BASETYPE, /* tp_flags */ set_doc, /* tp_doc */ (traverseproc)set_traverse, /* tp_traverse */ - (inquiry)set_tp_clear, /* tp_clear */ + (inquiry)set_clear_internal, /* tp_clear */ (richcmpfunc)set_richcompare, /* tp_richcompare */ offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */ (getiterfunc)set_iter, /* tp_iter */ @@ -1822,7 +1815,7 @@ Py_TPFLAGS_BASETYPE, /* tp_flags */ frozenset_doc, /* tp_doc */ (traverseproc)set_traverse, /* tp_traverse */ - 0, /* tp_clear */ + (inquiry)set_clear_internal, /* tp_clear */ (richcmpfunc)set_richcompare, /* tp_richcompare */ offsetof(PySetObject, weakreflist), /* tp_weaklistoffset */ (getiterfunc)set_iter, /* tp_iter */ From pje at users.sourceforge.net Sat Aug 6 18:26:46 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 09:26:46 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command sdist.py, 1.4, 1.5 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17185/setuptools/command Modified Files: sdist.py Log Message: Fix wrongly including files that Subversion has marked deleted. Index: sdist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/sdist.py,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- sdist.py 9 Jul 2005 04:21:22 -0000 1.4 +++ sdist.py 6 Aug 2005 16:26:43 -0000 1.5 @@ -84,7 +84,11 @@ (convert_path('CVS/Entries'), re_finder(re.compile(r"^\w?/([^/]+)/", re.M))), (convert_path('.svn/entries'), - re_finder(re.compile(r'name="([^"]+)"'), unescape)), + re_finder( + re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I), + unescape + ) + ), (convert_path('.svn/dir-props'), externals_finder), ] @@ -117,7 +121,3 @@ - - - - From pje at users.sourceforge.net Sat Aug 6 18:26:46 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 09:26:46 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools setuptools.txt, 1.25, 1.26 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17185 Modified Files: setuptools.txt Log Message: Fix wrongly including files that Subversion has marked deleted. Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.25 retrieving revision 1.26 diff -u -d -r1.25 -r1.26 --- setuptools.txt 6 Aug 2005 02:30:52 -0000 1.25 +++ setuptools.txt 6 Aug 2005 16:26:44 -0000 1.26 @@ -1601,6 +1601,9 @@ * Enhanced performance of ``require()`` and related operations when all requirements are already in the working set, and enhanced performance of directory scanning for distributions. + + * The ``sdist`` command now recognizes Subversion "deleted file" entries and + does not include them in source distributions. * Fixed some problems using ``pkg_resources`` w/PEP 302 loaders other than ``zipimport``, and the previously-broken "eager resource" support. From pje at users.sourceforge.net Sat Aug 6 19:54:58 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 10:54:58 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools EasyInstall.txt, 1.49, 1.50 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31364 Modified Files: EasyInstall.txt Log Message: Change dependency processing algorithm for less redundancy in the common case, and more thoroughness in the --always-copy case. Index: EasyInstall.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/EasyInstall.txt,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- EasyInstall.txt 25 Jul 2005 03:12:50 -0000 1.49 +++ EasyInstall.txt 6 Aug 2005 17:54:55 -0000 1.50 @@ -685,6 +685,11 @@ OS X upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a previous OS version to become obsolete. + * easy_install's dependency processing algorithms have changed. When using + ``--always-copy``, it now ensures that dependencies are copied too. When + not using ``--always-copy``, it tries to use a single resolution loop, + rather than recursing. + * Fixed installing extra ``.pyc`` or ``.pyo`` files for scripts with ``.py`` extensions. From pje at users.sourceforge.net Sat Aug 6 19:54:57 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 10:54:57 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command easy_install.py, 1.18, 1.19 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31364/setuptools/command Modified Files: easy_install.py Log Message: Change dependency processing algorithm for less redundancy in the common case, and more thoroughness in the --always-copy case. Index: easy_install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/easy_install.py,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- easy_install.py 24 Jul 2005 05:51:07 -0000 1.18 +++ easy_install.py 6 Aug 2005 17:54:55 -0000 1.19 @@ -97,6 +97,7 @@ self.delete_conflicting = None self.ignore_conflicts_at_my_risk = None self.site_dirs = None + self.installed_projects = {} def delete_blockers(self, blockers): for filename in blockers: @@ -120,7 +121,6 @@ - def finalize_options(self): # If a non-default installation directory was specified, default the # script directory to match it. @@ -222,7 +222,7 @@ for link in self.find_links: self.package_index.scan_url(link) for spec in self.args: - self.easy_install(spec) + self.easy_install(spec, True) if self.record: from distutils import file_util self.execute( @@ -285,7 +285,7 @@ - def easy_install(self, spec): + def easy_install(self, spec, deps=False): tmpdir = tempfile.mkdtemp(prefix="easy_install-") download = None @@ -295,12 +295,12 @@ # It's a url, download it to tmpdir and process self.not_editable(spec) download = self.package_index.download(spec, tmpdir) - return self.install_item(None, download, tmpdir, True) + return self.install_item(None, download, tmpdir, deps, True) elif os.path.exists(spec): # Existing file or directory, just process it directly self.not_editable(spec) - return self.install_item(None, spec, tmpdir, True) + return self.install_item(None, spec, tmpdir, deps, True) else: spec = parse_requirement_arg(spec) @@ -314,7 +314,7 @@ "Could not find distribution for %r" % spec ) - return self.install_item(spec, download, tmpdir) + return self.install_item(spec, download, tmpdir, deps) finally: if os.path.exists(tmpdir): @@ -326,46 +326,87 @@ - def install_item(self, spec, download, tmpdir, install_needed=False): + def install_item(self, spec, download, tmpdir, deps, install_needed=False): + # Installation is also needed if file in tmpdir or is not an egg install_needed = install_needed or os.path.dirname(download) == tmpdir install_needed = install_needed or not download.endswith('.egg') + log.info("Processing %s", os.path.basename(download)) + if install_needed or self.always_copy: dists = self.install_eggs(spec, download, tmpdir) for dist in dists: - self.process_distribution(spec, dist) + self.process_distribution(spec, dist, deps) else: dists = [self.check_conflicts(self.egg_distribution(download))] - self.process_distribution(spec, dists[0], "Using") + self.process_distribution(spec, dists[0], deps, "Using") + if spec is not None: for dist in dists: if dist in spec: return dist - def process_distribution(self, requirement, dist, *info): + + + + + + + + + + + + + + + + + + + + + def process_distribution(self, requirement, dist, deps=True, *info): self.update_pth(dist) self.package_index.add(dist) self.local_index.add(dist) self.install_egg_scripts(dist) + self.installed_projects[dist.key] = dist log.warn(self.installation_report(dist, *info)) + if requirement is None: requirement = dist.as_requirement() - if dist in requirement: + + if dist not in requirement: + return + + if deps or self.always_copy: log.info("Processing dependencies for %s", requirement) - try: - WorkingSet(self.shadow_path).resolve( - [requirement], self.local_index, self.easy_install - ) - except DistributionNotFound, e: - raise DistutilsError( - "Could not find required distribution %s" % e.args - ) - except VersionConflict, e: - raise DistutilsError( - "Installed distribution %s conflicts with requirement %s" - % e.args - ) + else: + return + + if self.always_copy: + # Recursively install *all* dependencies + for req in dist.requires(requirement.extras): + if req.key not in self.installed_projects: + self.easy_install(req) + return + + try: + WorkingSet(self.shadow_path).resolve( + [requirement], self.local_index, self.easy_install + ) + except DistributionNotFound, e: + raise DistutilsError( + "Could not find required distribution %s" % e.args + ) + except VersionConflict, e: + raise DistutilsError( + "Installed distribution %s conflicts with requirement %s" + % e.args + ) + def install_egg_scripts(self, dist): if self.exclude_scripts or not dist.metadata_isdir('scripts'): From pje at users.sourceforge.net Sat Aug 6 19:57:08 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 10:57:08 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.57, 1.58 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31572 Modified Files: pkg_resources.py Log Message: Allow specifying an environment and/or installer for entry-point loading. This will be used by setuptools to automatically install eggs that may be needed as part of a build process, or to invoke a particular command. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.57 retrieving revision 1.58 diff -u -d -r1.57 -r1.58 --- pkg_resources.py 6 Aug 2005 02:30:52 -0000 1.57 +++ pkg_resources.py 6 Aug 2005 17:56:58 -0000 1.58 @@ -1622,8 +1622,8 @@ def __repr__(self): return "EntryPoint.parse(%r)" % str(self) - def load(self, require=True): - if require: self.require() + def load(self, require=True, env=None, installer=None): + if require: self.require(env, installer) entry = __import__(self.module_name, globals(),globals(), ['__name__']) for attr in self.attrs: try: @@ -1632,11 +1632,11 @@ raise ImportError("%r has no %r attribute" % (entry,attr)) return entry - def require(self): + def require(self, env=None, installer=None): if self.extras and not self.dist: raise UnknownExtra("Can't require() without a distribution", self) map(working_set.add, - working_set.resolve(self.dist.requires(self.extras))) + working_set.resolve(self.dist.requires(self.extras),env,installer)) #@classmethod def parse(cls, src, dist=None): From rhettinger at users.sourceforge.net Sat Aug 6 20:31:27 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:31:27 -0700 Subject: [Python-checkins] python/dist/src/Include pyerrors.h,2.67,2.68 Message-ID: Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5206/Include Modified Files: pyerrors.h Log Message: * set_new() doesn't need to zero the structure a second time after tp_alloc has already done the job. * Use a macro form of PyErr_Occurred() inside the set_lookkey() function. Index: pyerrors.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/pyerrors.h,v retrieving revision 2.67 retrieving revision 2.68 diff -u -d -r2.67 -r2.68 --- pyerrors.h 2 Aug 2005 00:46:42 -0000 2.67 +++ pyerrors.h 6 Aug 2005 18:31:24 -0000 2.68 @@ -15,6 +15,12 @@ PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); +#ifdef Py_DEBUG +#define _PyErr_OCCURRED() PyErr_Occurred() +#else +#define _PyErr_OCCURRED() (_PyThreadState_Current->curexc_type) +#endif + /* Error testing and normalization */ PyAPI_FUNC(int) PyErr_GivenExceptionMatches(PyObject *, PyObject *); PyAPI_FUNC(int) PyErr_ExceptionMatches(PyObject *); From rhettinger at users.sourceforge.net Sat Aug 6 20:31:28 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:31:28 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.42,1.43 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5206/Objects Modified Files: setobject.c Log Message: * set_new() doesn't need to zero the structure a second time after tp_alloc has already done the job. * Use a macro form of PyErr_Occurred() inside the set_lookkey() function. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.42 retrieving revision 1.43 diff -u -d -r1.42 -r1.43 --- setobject.c 6 Aug 2005 05:43:39 -0000 1.42 +++ setobject.c 6 Aug 2005 18:31:24 -0000 1.43 @@ -66,7 +66,7 @@ if (entry->hash == hash) { /* error can't have been checked yet */ checked_error = 1; - if (PyErr_Occurred()) { + if (_PyErr_OCCURRED()) { restore_error = 1; PyErr_Fetch(&err_type, &err_value, &err_tb); } @@ -104,7 +104,7 @@ if (entry->hash == hash && entry->key != dummy) { if (!checked_error) { checked_error = 1; - if (PyErr_Occurred()) { + if (_PyErr_OCCURRED()) { restore_error = 1; PyErr_Fetch(&err_type, &err_value, &err_tb); @@ -720,7 +720,10 @@ if (so == NULL) return NULL; - EMPTY_TO_MINSIZE(so); + /* tp_alloc has already zeroed the structure */ + assert(so->table == NULL && so->fill == 0 && so->used == 0); + so->table = so->smalltable; + so->mask = PySet_MINSIZE - 1; so->lookup = set_lookkey_string; so->hash = -1; so->weakreflist = NULL; From pje at users.sourceforge.net Sat Aug 6 20:46:30 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:46:30 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command egg_info.py, 1.9, 1.10 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8052/setuptools/command Modified Files: egg_info.py Log Message: Enhanced setuptools infrastructure to support distutils extensions that can be plugged in at setup() time to define new setup() arguments or distutils commands. This allows modularization and reuse of distutils extensions in a way that was previously not possible. Index: egg_info.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/egg_info.py,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- egg_info.py 24 Jul 2005 22:47:06 -0000 1.9 +++ egg_info.py 6 Aug 2005 18:46:28 -0000 1.10 @@ -9,7 +9,6 @@ from distutils import log from pkg_resources import parse_requirements, safe_name, \ safe_version, yield_lines, EntryPoint -from setuptools.dist import iter_distribution_names class egg_info(Command): @@ -39,6 +38,7 @@ + def finalize_options (self): self.egg_name = safe_name(self.distribution.get_name()) self.egg_version = self.tagged_version() @@ -149,7 +149,7 @@ def write_toplevel_names(self): pkgs = dict.fromkeys( [k.split('.',1)[0] - for k in iter_distribution_names(self.distribution) + for k in self.distribution.iter_distribution_names() ] ) toplevel = os.path.join(self.egg_info, "top_level.txt") @@ -164,12 +164,8 @@ def write_or_delete_dist_arg(self, argname, filename=None): value = getattr(self.distribution, argname, None) - if value is None: - return - filename = filename or argname+'.txt' filename = os.path.join(self.egg_info,filename) - if value: log.info("writing %s", filename) if not self.dry_run: @@ -177,8 +173,12 @@ f.write('\n'.join(value)) f.write('\n') f.close() - elif os.path.exists(filename): + if value is None: + log.warn( + "%s not set in setup(), but %s exists", argname, filename + ) + return log.info("deleting %s", filename) if not self.dry_run: os.unlink(filename) From pje at users.sourceforge.net Sat Aug 6 20:46:30 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:46:30 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools.egg-info entry_points.txt, 1.1, 1.2 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8052/setuptools.egg-info Modified Files: entry_points.txt Log Message: Enhanced setuptools infrastructure to support distutils extensions that can be plugged in at setup() time to define new setup() arguments or distutils commands. This allows modularization and reuse of distutils extensions in a way that was previously not possible. Index: entry_points.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info/entry_points.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- entry_points.txt 24 Jul 2005 22:47:06 -0000 1.1 +++ entry_points.txt 6 Aug 2005 18:46:28 -0000 1.2 @@ -1,13 +1,22 @@ +[distutils.setup_keywords] +entry_points = setuptools.dist:check_entry_points +extras_require = setuptools.dist:check_extras +namespace_packages = setuptools.dist:check_nsp +test_suite = setuptools.dist:check_test_suite +eager_resources = setuptools.dist:assert_string_list +zip_safe = setuptools.dist:assert_bool + [distutils.commands] rotate = setuptools.command.rotate:rotate develop = setuptools.command.develop:develop setopt = setuptools.command.setopt:setopt +build_py = setuptools.command.build_py:build_py saveopts = setuptools.command.saveopts:saveopts egg_info = setuptools.command.egg_info:egg_info -depends = setuptools.command.depends:depends +easy_install = setuptools.command.easy_install:easy_install upload = setuptools.command.upload:upload alias = setuptools.command.alias:alias -easy_install = setuptools.command.easy_install:easy_install +depends = setuptools.command.depends:depends bdist_egg = setuptools.command.bdist_egg:bdist_egg install = setuptools.command.install:install test = setuptools.command.test:test From pje at users.sourceforge.net Sat Aug 6 20:46:29 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:46:29 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools setup.py, 1.34, 1.35 setuptools.txt, 1.26, 1.27 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8052 Modified Files: setup.py setuptools.txt Log Message: Enhanced setuptools infrastructure to support distutils extensions that can be plugged in at setup() time to define new setup() arguments or distutils commands. This allows modularization and reuse of distutils extensions in a way that was previously not possible. Index: setup.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setup.py,v retrieving revision 1.34 retrieving revision 1.35 diff -u -d -r1.34 -r1.35 --- setup.py 31 Jul 2005 16:31:18 -0000 1.34 +++ setup.py 6 Aug 2005 18:46:27 -0000 1.35 @@ -46,8 +46,18 @@ "%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals() for cmd in SETUP_COMMANDS if cmd!="build_py" or sys.version<"2.4" ], + "distutils.setup_keywords": [ + "eager_resources = setuptools.dist:assert_string_list", + "namespace_packages = setuptools.dist:check_nsp", + "extras_require = setuptools.dist:check_extras", + "entry_points = setuptools.dist:check_entry_points", + "test_suite = setuptools.dist:check_test_suite", + "zip_safe = setuptools.dist:assert_bool", + ] }, + setup_requires = ['setuptools>=0.6a0'], + classifiers = [f.strip() for f in """ Development Status :: 3 - Alpha Intended Audience :: Developers @@ -78,5 +88,3 @@ - - Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.26 retrieving revision 1.27 diff -u -d -r1.26 -r1.27 --- setuptools.txt 6 Aug 2005 16:26:44 -0000 1.26 +++ setuptools.txt 6 Aug 2005 18:46:27 -0000 1.27 @@ -64,7 +64,11 @@ ========================= Download `ez_setup.py`_ and run it; this will download and install the -appropriate egg for your Python version. +appropriate egg for your Python version. (Note: if you are behind +an NTLM-based firewall that prevents Python programs from accessing the net +directly, you may wish to install and use the `APS proxy server +`_, which lets you get past such firewalls in the same +way that your web browser(s) do.) .. _ez_setup.py: `bootstrap module`_ @@ -80,6 +84,16 @@ You can then install it using the usual "setup.py install" incantation. +Note that ``setuptools`` *must* be installed as an egg directory; it will not +operate correctly otherwise. If you are unable to install to a valid +``site-packages`` directory (e.g. a "non-root install"), you will therefore +need to manually add the setuptools egg to your ``PYTHONPATH``. (You won't +need to do this for every egg you install, because the ``pkg_resources`` module +can automatically find eggs and add them to ``sys.path`` at runtime. It's just +that the ``setuptools`` egg contains ``pkg_resources`` and therefore has to +be manually bootstrapped if you can't install it to a ``site-packages`` +directory.) + Basic Use ========= @@ -175,6 +189,15 @@ installed to support those features. See the section below on `Declaring Dependencies`_ for details and examples of the format of this argument. +``setup_requires`` + A string or list of strings specifying what other distributions need to + be present in order for the *setup script* to run. ``setuptools`` will + attempt to obtain these (even going so far as to download them using + ``EasyInstall``) before processing the rest of the setup script or commands. + This argument is needed if you are using distutils extensions as part of + your build process; for example, extensions that process setup() arguments + and turn them into EGG-INFO metadata files. + ``namespace_packages`` A list of strings naming the project's "namespace packages". A namespace package is a package that may be split across multiple project @@ -1517,19 +1540,28 @@ Extending and Reusing ``setuptools`` ------------------------------------ -Sorry, this section isn't written yet, and neither is a lot of what's below -this point, except for the change log. You might want to `subscribe to changes -in this page `_ to see when new documentation is -added or updated. +Creating ``distutils`` Extensions +================================= + +It can be hard to add new commands or setup arguments to the distutils. But +the ``setuptools`` package makes it a bit easier, by allowing you to distribute +a distutils extension as a separate project, and then have projects that need +the extension just refer to it in their ``setup_requires`` argument. + +With ``setuptools``, your distutils extension projects can hook in new +commands and ``setup()`` arguments just by defining "entry points". These +are mappings from command or argument names to a specification of where to +import a handler from. (See the section on `Dynamic Discovery of Services and +Plugins`_ above for some more background on entry points.) Adding Commands -=============== +--------------- -You can create add-on packages that extend setuptools with additional commands -by defining entry points in the ``distutils.commands`` group. For example, if -you wanted to add a ``foo`` command, you might add something like this to your -setup script:: +You can add new ``setup`` commands by defining entry points in the +``distutils.commands`` group. For example, if you wanted to add a ``foo`` +command, you might add something like this to your distutils extension +project's setup script:: setup( # ... @@ -1540,8 +1572,8 @@ }, ) -Assuming, of course, that the ``foo`` class in ``mypackage.some_module`` is -a ``setuptools.Command`` subclass. +(Assuming, of course, that the ``foo`` class in ``mypackage.some_module`` is +a ``setuptools.Command`` subclass.) Once a project containing such entry points has been activated on ``sys.path``, (e.g. by running "install" or "develop" with a site-packages installation @@ -1550,18 +1582,76 @@ to monkeypatch the ``distutils.command`` package to install your commands; ``setuptools`` automatically adds a wrapper to the distutils to search for entry points in the active distributions on ``sys.path``. In fact, this is -how setuptools' own commands are installed: the setuptools setup script defines -entry points for them. +how setuptools' own commands are installed: the setuptools project's setup +script defines entry points for them! + + +Adding ``setup()`` Arguments +---------------------------- + +Sometimes, your commands may need additional arguments to the ``setup()`` +script. You can enable this by defining entry points in the +``distutils.setup_keywords`` group. For example, if you wanted a ``setup()`` +argument called ``bar_baz``, you might add something like this to your +distutils extension project's setup script:: + + setup( + # ... + entry_points = { + "distutils.commands": [ + "foo = mypackage.some_module:foo", + ], + "distutils.setup_keywords": [ + "bar_baz = mypackage.some_module:validate_bar_baz", + ], + }, + ) + +The idea here is that the entry point defines a function that will be called +to validate the ``setup()`` argument, if it's supplied. The ``Distribution`` +object will have the initial value of the attribute set to ``None``, and the +validation function will only be called if the ``setup()`` call sets it to +a non-None value. Here's an example validation function:: + + def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + raise DistutilsSetupError( + "%r must be a boolean value (got %r)" % (attr,value) + ) + +Your function should accept three arguments: the ``Distribution`` object, +the attribute name, and the attribute value. It should raise a +``DistutilsSetupError`` (from the ``distutils.error`` module) if the argument +is invalid. Remember, your function will only be called with non-None values, +and the default value of arguments defined this way is always None. So, your +commands should always be prepared for the possibility that the attribute will +be ``None`` when they access it later. + +If more than one active distribution defines an entry point for the same +``setup()`` argument, *all* of them will be called. This allows multiple +distutils extensions to define a common argument, as long as they agree on +what values of that argument are valid. + +Also note that as with commands, it is not necessary to subclass or monkeypatch +the distutils ``Distribution`` class in order to add your arguments; it is +sufficient to define the entry points in your extension, as long as the setup +script lists your extension in its ``setup_requires`` argument. Subclassing ``Command`` ----------------------- +Sorry, this section isn't written yet, and neither is a lot of what's below +this point, except for the change log. You might want to `subscribe to changes +in this page `_ to see when new documentation is +added or updated. + XXX -Utility Modules -=============== +Reusing ``setuptools`` Code +=========================== ``ez_setup`` ------------ @@ -1604,14 +1694,24 @@ * The ``sdist`` command now recognizes Subversion "deleted file" entries and does not include them in source distributions. - + + * ``setuptools`` now embeds itself more thoroughly into the distutils, so that + other distutils extensions (e.g. py2exe, py2app) will subclass setuptools' + versions of things, rather than the native distutils ones. + * Fixed some problems using ``pkg_resources`` w/PEP 302 loaders other than ``zipimport``, and the previously-broken "eager resource" support. * Fixed ``pkg_resources.resource_exists()`` not working correctly, along with some other resource API bugs. - * Added ``entry_points`` argument to ``setup()`` + * Added ``entry_points`` and ``setup_requires`` arguments to ``setup()``; + ``setup_requires`` allows you to automatically find and download packages + that are needed in order to *build* your project (as opposed to running it). + + * ``setuptools`` now finds its commands and ``setup()`` argument validators + using entry points, so that they are extensible by third-party packages. + See `Creating distutils Extensions`_ above for more details. * Many ``pkg_resources`` API changes and enhancements: From pje at users.sourceforge.net Sat Aug 6 20:46:30 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:46:30 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools __init__.py, 1.21, 1.22 dist.py, 1.17, 1.18 extension.py, 1.1, 1.2 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8052/setuptools Modified Files: __init__.py dist.py extension.py Log Message: Enhanced setuptools infrastructure to support distutils extensions that can be plugged in at setup() time to define new setup() arguments or distutils commands. This allows modularization and reuse of distutils extensions in a way that was previously not possible. Index: __init__.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/__init__.py,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- __init__.py 18 Jul 2005 02:06:15 -0000 1.21 +++ __init__.py 6 Aug 2005 18:46:27 -0000 1.22 @@ -1,6 +1,6 @@ """Extensions to the 'distutils' for large or complex distributions""" +from setuptools.dist import Distribution, Feature, _get_unpatched import distutils.core, setuptools.command -from setuptools.dist import Distribution, Feature from setuptools.extension import Extension from setuptools.depends import Require from distutils.core import Command as _Command @@ -39,17 +39,9 @@ out = [item for item in out if not fnmatchcase(item,pat)] return out -def setup(**attrs): - """Do package setup - - This function takes the same arguments as 'distutils.core.setup()', except - that the default distribution class is 'setuptools.dist.Distribution'. See - that class' documentation for details on the new keyword arguments that it - makes available via this function. - """ - attrs.setdefault("distclass",Distribution) - return distutils.core.setup(**attrs) +setup = distutils.core.setup +_Command = _get_unpatched(_Command) class Command(_Command): __doc__ = _Command.__doc__ @@ -68,6 +60,14 @@ setattr(cmd,k,v) # update command with keywords return cmd +import distutils.core +distutils.core.Command = Command # we can't patch distutils.cmd, alas + + + + + + Index: dist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v retrieving revision 1.17 retrieving revision 1.18 diff -u -d -r1.17 -r1.18 --- dist.py 25 Jul 2005 03:12:51 -0000 1.17 +++ dist.py 6 Aug 2005 18:46:27 -0000 1.18 @@ -9,36 +9,118 @@ from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources - -def get_command_class(self, command): - """Pluggable version of get_command_class()""" - if command in self.cmdclass: - return self.cmdclass[command] +import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd +import os - for ep in pkg_resources.iter_entry_points('distutils.commands',command): - self.cmdclass[command] = cmdclass = ep.load() - return cmdclass - else: - return _old_get_command_class(self, command) +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded -def print_commands(self): - for ep in pkg_resources.iter_entry_points('distutils.commands'): - if ep.name not in self.cmdclass: - cmdclass = ep.load(False) # don't require extras, we're not running - self.cmdclass[ep.name] = cmdclass - return _old_print_commands(self) + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls -for meth in 'print_commands', 'get_command_class': - if getattr(_Distribution,meth).im_func.func_globals is not globals(): - globals()['_old_'+meth] = getattr(_Distribution,meth) - setattr(_Distribution, meth, globals()[meth]) +_Distribution = _get_unpatched(_Distribution) sequence = tuple, list + + + + + + +def assert_string_list(dist, attr, value): + """Verify that value is a string list or None""" + try: + assert ''.join(value)!=value + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be a list of strings (got %r)" % (attr,value) + ) + +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + assert_string_list(dist,attr,value) + + for nsp in value: + for name in dist.iter_distribution_names(): + if name.startswith(nsp+'.'): break + else: + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + for k,v in value.items(): + list(pkg_resources.parse_requirements(v)) + except (TypeError,ValueError,AttributeError): + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) + +def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + raise DistutilsSetupError( + "%r must be a boolean value (got %r)" % (attr,value) + ) + +def check_install_requires(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + except (TypeError,ValueError): + raise DistutilsSetupError( + "'install_requires' must be a string or list of strings " + "containing valid project/version requirement specifiers" + ) + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + pkg_resources.EntryPoint.parse_map(value) + except ValueError, e: + raise DistutilsSetupError(e) + + +def check_test_suite(dist, attr, value): + if not isinstance(value,basestring): + raise DistutilsSetupError("test_suite must be a string") + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -125,16 +207,19 @@ have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + self.features = {} - self.test_suite = None self.requires = [] - self.install_requires = [] - self.extras_require = {} self.dist_files = [] - self.zip_safe = None - self.namespace_packages = None - self.eager_resources = None - self.entry_points = None + + if attrs and 'setup_requires' in attrs: + # Make sure we have any eggs needed to interpret 'attrs' + self.fetch_build_eggs(attrs.pop('setup_requires')) + + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + if not hasattr(self,ep.name): + setattr(self,ep.name,None) + _Distribution.__init__(self,attrs) @@ -145,20 +230,17 @@ self._finalize_features() return result - def _feature_attrname(self,name): """Convert feature name to corresponding option attribute name""" return 'with_'+name.replace('-','_') - - - - - - - - - + def fetch_build_eggs(self, requires): + """Resolve pre-setup requirements""" + from pkg_resources import working_set, parse_requirements + for dist in working_set.resolve( + parse_requirements(requires), installer=self.fetch_build_egg + ): + working_set.add(dist) @@ -174,49 +256,34 @@ "setuptools. Please remove it from your setup script." ) - try: - list(pkg_resources.parse_requirements(self.install_requires)) - except (TypeError,ValueError): - raise DistutilsSetupError( - "'install_requires' must be a string or list of strings " - "containing valid project/version requirement specifiers" - ) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + value = getattr(self,ep.name,None) + if value is not None: + ep.require(installer=self.fetch_build_egg) + ep.load()(self, ep.name, value) + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" try: - for k,v in self.extras_require.items(): - list(pkg_resources.parse_requirements(v)) - except (TypeError,ValueError,AttributeError): - raise DistutilsSetupError( - "'extras_require' must be a dictionary whose values are " - "strings or lists of strings containing valid project/version " - "requirement specifiers." + cmd = self._egg_fetcher + except AttributeError: + from setuptools.command.easy_install import easy_install + cmd = easy_install( + self.__class__({'script_args':['easy_install']}), + args="x", install_dir=os.curdir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False ) + cmd.ensure_finalized() + cmd.zip_ok = None # override any setup.cfg setting for these + cmd.build_directory = None + self._egg_fetcher = cmd - for attr in 'namespace_packages','eager_resources': - value = getattr(self,attr,None) - if value is not None: - try: - assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) - ) + return cmd.easy_install(req) - for nsp in self.namespace_packages or (): - for name in iter_distribution_names(self): - if name.startswith(nsp+'.'): break - else: - raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp - ) - if self.entry_points is not None: - try: - pkg_resources.EntryPoint.parse_map(self.entry_points) - except ValueError, e: - raise DistutilsSetupError(e) def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -244,22 +311,7 @@ - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name,1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name,0) @@ -274,12 +326,42 @@ + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + for ep in pkg_resources.iter_entry_points('distutils.commands',command): + ep.require(installer=self.fetch_build_egg) + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + def print_commands(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load(False) # don't require extras, we're not running + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) @@ -572,25 +654,25 @@ return d -def iter_distribution_names(distribution): - """Yield all packages, modules, and extensions declared by distribution""" - - for pkg in distribution.packages or (): - yield pkg - - for module in distribution.py_modules or (): - yield module - - for ext in distribution.ext_modules or (): - if isinstance(ext,tuple): - name,buildinfo = ext - yield name - else: - yield ext.name + def iter_distribution_names(self): + """Yield all packages, modules, and extension names in distribution""" + for pkg in self.packages or (): + yield pkg + for module in self.py_modules or (): + yield module + for ext in self.ext_modules or (): + if isinstance(ext,tuple): + name,buildinfo = ext + yield name + else: + yield ext.name +# Install it throughout the distutils +for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = Distribution Index: extension.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/extension.py,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- extension.py 19 Mar 2004 20:53:14 -0000 1.1 +++ extension.py 6 Aug 2005 18:46:27 -0000 1.2 @@ -7,6 +7,9 @@ # Pyrex isn't around, so fix up the sources + from dist import _get_unpatched + _Extension = _get_unpatched(_Extension) + class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" @@ -21,7 +24,14 @@ sources.append(s) self.sources = sources + import sys, distutils.core, distutils.extension + distutils.core.Extension = Extension + distutils.extension.Extension = Extension + if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = Extension + else: # Pyrex is here, just use regular extension type Extension = _Extension + From rhettinger at users.sourceforge.net Sat Aug 6 20:57:21 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 06 Aug 2005 11:57:21 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.43,1.44 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10101 Modified Files: setobject.c Log Message: * Removed checked_error flag which no longer provides any benefit. * Have issubset() control its own loop instead of using set_next_internal(). Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- setobject.c 6 Aug 2005 18:31:24 -0000 1.43 +++ setobject.c 6 Aug 2005 18:57:13 -0000 1.44 @@ -49,7 +49,6 @@ setentry *table = so->table; register setentry *entry; register int restore_error; - register int checked_error; register int cmp; PyObject *err_type, *err_value, *err_tb; PyObject *startkey; @@ -59,13 +58,11 @@ if (entry->key == NULL || entry->key == key) return entry; - restore_error = checked_error = 0; + restore_error = 0; if (entry->key == dummy) freeslot = entry; else { if (entry->hash == hash) { - /* error can't have been checked yet */ - checked_error = 1; if (_PyErr_OCCURRED()) { restore_error = 1; PyErr_Fetch(&err_type, &err_value, &err_tb); @@ -102,13 +99,10 @@ if (entry->key == key) break; if (entry->hash == hash && entry->key != dummy) { - if (!checked_error) { - checked_error = 1; - if (_PyErr_OCCURRED()) { - restore_error = 1; - PyErr_Fetch(&err_type, &err_value, - &err_tb); - } + if (_PyErr_OCCURRED()) { + restore_error = 1; + PyErr_Fetch(&err_type, &err_value, + &err_tb); } startkey = entry->key; cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); @@ -1267,8 +1261,8 @@ set_issubset(PySetObject *so, PyObject *other) { PyObject *tmp, *result; - PyObject *key; - int pos = 0; + register setentry *entry; + register int i; if (!PyAnySet_Check(other)) { tmp = make_new_set(&PySet_Type, other); @@ -1281,8 +1275,11 @@ if (set_len((PyObject *)so) > set_len(other)) Py_RETURN_FALSE; - while (set_next_internal(so, &pos, &key)) { - if (!set_contains_internal((PySetObject *)other, key)) + entry = &so->table[0]; + for (i=so->used ; i ; entry++, i--) { + while (entry->key == NULL || entry->key==dummy) + entry++; + if (!set_contains_internal((PySetObject *)other, entry->key)) Py_RETURN_FALSE; } Py_RETURN_TRUE; From pje at users.sourceforge.net Sat Aug 6 21:29:51 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 12:29:51 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools setuptools.txt, 1.27, 1.28 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15840 Modified Files: setuptools.txt Log Message: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.27 retrieving revision 1.28 diff -u -d -r1.27 -r1.28 --- setuptools.txt 6 Aug 2005 18:46:27 -0000 1.27 +++ setuptools.txt 6 Aug 2005 19:29:48 -0000 1.28 @@ -1713,6 +1713,10 @@ using entry points, so that they are extensible by third-party packages. See `Creating distutils Extensions`_ above for more details. + * The vestigial ``depends`` command has been removed. It was never finished + or documented, and never would have worked without EasyInstall - which it + pre-dated and was never compatible with. + * Many ``pkg_resources`` API changes and enhancements: * Added ``EntryPoint``, ``get_entry_map``, ``load_entry_point``, and @@ -1918,9 +1922,7 @@ see the ``setuptools.dist.Distribution`` class. * Setup scripts using setuptools now always install using ``easy_install`` - internally, for ease of uninstallation and upgrading. Note: you *must* - remove any ``extra_path`` argument from your setup script, as it conflicts - with the proper functioning of the ``easy_install`` command. + internally, for ease of uninstallation and upgrading. * ``pkg_resources.AvailableDistributions.resolve()`` and related methods now accept an ``installer`` argument: a callable taking one argument, a From pje at users.sourceforge.net Sat Aug 6 21:29:51 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 12:29:51 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools dist.py, 1.18, 1.19 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15840/setuptools Modified Files: dist.py Log Message: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. Index: dist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v retrieving revision 1.18 retrieving revision 1.19 diff -u -d -r1.18 -r1.19 --- dist.py 6 Aug 2005 18:46:27 -0000 1.18 +++ dist.py 6 Aug 2005 19:29:49 -0000 1.19 @@ -207,9 +207,8 @@ have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - + self.requires = [] # XXX self.features = {} - self.requires = [] self.dist_files = [] if attrs and 'setup_requires' in attrs: @@ -244,18 +243,13 @@ + def finalize_options(self): _Distribution.finalize_options(self) if self.features: self._set_global_opts_from_features() - if self.extra_path: - raise DistutilsSetupError( - "The 'extra_path' parameter is not needed when using " - "setuptools. Please remove it from your setup script." - ) - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self,ep.name,None) if value is not None: @@ -285,6 +279,12 @@ + + + + + + def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -572,47 +572,6 @@ - def has_dependencies(self): - return not not self.requires - - def run_commands(self): - for cmd in self.commands: - if cmd=='install' and not cmd in self.have_run: - self.install_eggs() - else: - self.run_command(cmd) - - def install_eggs(self): - from setuptools.command.easy_install import easy_install - cmd = easy_install(self, args="x", ignore_conflicts_at_my_risk=1) - cmd.ensure_finalized() # finalize before bdist_egg munges install cmd - - self.run_command('bdist_egg') - args = [self.get_command_obj('bdist_egg').egg_output] - - if setuptools.bootstrap_install_from: - # Bootstrap self-installation of setuptools - args.insert(0, setuptools.bootstrap_install_from) - - cmd.args = args - cmd.run() - self.have_run['install'] = 1 - setuptools.bootstrap_install_from = None - - - - - - - - - - - - - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options From pje at users.sourceforge.net Sat Aug 6 21:29:51 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 12:29:51 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command __init__.py, 1.7, 1.8 install.py, 1.2, 1.3 depends.py, 1.4, NONE Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15840/setuptools/command Modified Files: __init__.py install.py Removed Files: depends.py Log Message: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. Index: __init__.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/__init__.py,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- __init__.py 24 Jul 2005 22:47:06 -0000 1.7 +++ __init__.py 6 Aug 2005 19:29:49 -0000 1.8 @@ -1,5 +1,5 @@ __all__ = [ - 'alias', 'bdist_egg', 'build_ext', 'build_py', 'depends', 'develop', + 'alias', 'bdist_egg', 'build_ext', 'build_py', 'develop', 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', 'sdist', 'setopt', 'test', 'upload', ] Index: install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/install.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- install.py 5 Apr 2004 20:02:45 -0000 1.2 +++ install.py 6 Aug 2005 19:29:49 -0000 1.3 @@ -1,9 +1,31 @@ +import setuptools from distutils.command.install import install as _install class install(_install): """Build dependencies before installation""" - def has_dependencies(self): - return self.distribution.has_dependencies() + def handle_extra_path(self): + # We always ignore extra_path, because we always install eggs + # (you can always use install_* commands directly if needed) + self.path_file = None + self.extra_dirs = '' + + def run(self): + from setuptools.command.easy_install import easy_install + cmd = easy_install( + self.distribution, args="x", ignore_conflicts_at_my_risk=1 + ) + cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + + self.run_command('bdist_egg') + args = [self.distribution.get_command_obj('bdist_egg').egg_output] + + if setuptools.bootstrap_install_from: + # Bootstrap self-installation of setuptools + args.insert(0, setuptools.bootstrap_install_from) + + cmd.args = args + cmd.run() + setuptools.bootstrap_install_from = None + - sub_commands = [('depends', has_dependencies)] + _install.sub_commands --- depends.py DELETED --- From pje at users.sourceforge.net Sat Aug 6 21:29:51 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 12:29:51 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/tests __init__.py, 1.8, 1.9 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15840/setuptools/tests Modified Files: __init__.py Log Message: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. Index: __init__.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests/__init__.py,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- __init__.py 18 Jul 2005 01:39:45 -0000 1.8 +++ __init__.py 6 Aug 2005 19:29:49 -0000 1.9 @@ -121,47 +121,6 @@ - def testDependsCmd(self): - path = convert_path('foo/bar/baz') - - dist = makeSetup( - script_args=['install','--install-lib',path] - ) - - cmd = dist.get_command_obj('depends') - cmd.ensure_finalized() - - self.assertEqual(cmd.temp, dist.get_command_obj('build').build_temp) - self.assertEqual(cmd.search_path, [path+os.path.sep,path]+sys.path) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class DistroTests(TestCase): def setUp(self): From pje at users.sourceforge.net Sat Aug 6 21:29:51 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 12:29:51 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools.egg-info entry_points.txt, 1.2, 1.3 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15840/setuptools.egg-info Modified Files: entry_points.txt Log Message: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. Index: entry_points.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info/entry_points.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- entry_points.txt 6 Aug 2005 18:46:28 -0000 1.2 +++ entry_points.txt 6 Aug 2005 19:29:49 -0000 1.3 @@ -13,10 +13,9 @@ build_py = setuptools.command.build_py:build_py saveopts = setuptools.command.saveopts:saveopts egg_info = setuptools.command.egg_info:egg_info -easy_install = setuptools.command.easy_install:easy_install upload = setuptools.command.upload:upload alias = setuptools.command.alias:alias -depends = setuptools.command.depends:depends +easy_install = setuptools.command.easy_install:easy_install bdist_egg = setuptools.command.bdist_egg:bdist_egg install = setuptools.command.install:install test = setuptools.command.test:test From pje at users.sourceforge.net Sat Aug 6 22:54:03 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 13:54:03 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.58, 1.59 api_tests.txt, 1.4, 1.5 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28726 Modified Files: pkg_resources.py api_tests.txt Log Message: Fix WorkingSet yielding the same distribution more than once if more than one path entry points to it. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.58 retrieving revision 1.59 diff -u -d -r1.58 -r1.59 --- pkg_resources.py 6 Aug 2005 17:56:58 -0000 1.58 +++ pkg_resources.py 6 Aug 2005 20:54:01 -0000 1.59 @@ -367,16 +367,6 @@ - def __iter__(self): - """Yield distributions for non-duplicate projects in the working set - - The yield order is the order in which the items' path entries were - added to the working set. - """ - for item in self.entries: - for key in self.entry_keys[item]: - yield self.by_key[key] - def find(self, req): """Find a distribution matching requirement `req` @@ -408,6 +398,29 @@ elif name in entries: yield entries[name] + + + + + + + + + + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + for key in self.entry_keys[item]: + if key not in seen: + seen[key]=1 + yield self.by_key[key] + def add(self, dist, entry=None): """Add `dist` to working set, associated with `entry` @@ -431,24 +444,11 @@ self.by_key[dist.key] = dist keys = self.entry_keys[entry] - if dist.key not in keys: keys.append(dist.key) self._added_new(dist) - - - - - - - - - - - - def resolve(self, requirements, env=None, installer=None): """List all distributions needed to (recursively) meet `requirements` Index: api_tests.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/api_tests.txt,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- api_tests.txt 21 Jul 2005 16:11:34 -0000 1.4 +++ api_tests.txt 6 Aug 2005 20:54:01 -0000 1.5 @@ -192,6 +192,7 @@ But even if a distribution is found under multiple path entries, it still only shows up once when iterating the working set: + >>> ws.add_entry(ws.entries[0]) >>> list(ws) [Bar 0.9 (http://example.com/something)] From pje at users.sourceforge.net Sat Aug 6 23:17:52 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 14:17:52 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools.egg-info entry_points.txt, 1.3, 1.4 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32399/setuptools.egg-info Modified Files: entry_points.txt Log Message: Allow distutils extensions to define new kinds of metadata that can be written to EGG-INFO. Extensible applications and frameworks can thus make it possible for plugin projects to supply setup() metadata that can then be used by the application or framework. Index: entry_points.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.egg-info/entry_points.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- entry_points.txt 6 Aug 2005 19:29:49 -0000 1.3 +++ entry_points.txt 6 Aug 2005 21:17:50 -0000 1.4 @@ -1,11 +1,21 @@ [distutils.setup_keywords] entry_points = setuptools.dist:check_entry_points extras_require = setuptools.dist:check_extras +install_requires = setuptools.dist:check_install_requires namespace_packages = setuptools.dist:check_nsp test_suite = setuptools.dist:check_test_suite eager_resources = setuptools.dist:assert_string_list zip_safe = setuptools.dist:assert_bool +[egg_info.writers] +requires.txt = setuptools.command.egg_info:write_requirements +PKG-INFO = setuptools.command.egg_info:write_pkg_info +eager_resources.txt = setuptools.command.egg_info:write_arg +top_level.txt = setuptools.command.egg_info:write_toplevel_names +namespace_packages.txt = setuptools.command.egg_info:write_arg +entry_points.txt = setuptools.command.egg_info:write_entries +depends.txt = setuptools.command.egg_info:warn_depends_obsolete + [distutils.commands] rotate = setuptools.command.rotate:rotate develop = setuptools.command.develop:develop From pje at users.sourceforge.net Sat Aug 6 23:17:52 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 14:17:52 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command egg_info.py, 1.10, 1.11 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32399/setuptools/command Modified Files: egg_info.py Log Message: Allow distutils extensions to define new kinds of metadata that can be written to EGG-INFO. Extensible applications and frameworks can thus make it possible for plugin projects to supply setup() metadata that can then be used by the application or framework. Index: egg_info.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/egg_info.py,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- egg_info.py 6 Aug 2005 18:46:28 -0000 1.10 +++ egg_info.py 6 Aug 2005 21:17:50 -0000 1.11 @@ -8,7 +8,7 @@ from distutils.errors import * from distutils import log from pkg_resources import parse_requirements, safe_name, \ - safe_version, yield_lines, EntryPoint + safe_version, yield_lines, EntryPoint, iter_entry_points class egg_info(Command): @@ -80,47 +80,55 @@ - def run(self): - # Make the .egg-info directory, then write PKG-INFO and requires.txt - self.mkpath(self.egg_info) - log.info("writing %s" % os.path.join(self.egg_info,'PKG-INFO')) - - if not self.dry_run: - metadata = self.distribution.metadata - metadata.version, oldver = self.egg_version, metadata.version - metadata.name, oldname = self.egg_name, metadata.name - try: - # write unescaped data to PKG-INFO, so older pkg_resources - # can still parse it - metadata.write_pkg_info(self.egg_info) - finally: - metadata.name, metadata.version = oldname, oldver - self.write_entry_points() - self.write_requirements() - self.write_toplevel_names() - self.write_or_delete_dist_arg('namespace_packages') - self.write_or_delete_dist_arg('eager_resources') - if os.path.exists(os.path.join(self.egg_info,'depends.txt')): - log.warn( - "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." - ) + def write_or_delete_file(self, what, filename, data): + """Write `data` to `filename` or delete if empty - def write_requirements(self): - dist = self.distribution - if not getattr(dist,'install_requires',None) and \ - not getattr(dist,'extras_require',None): return + If `data` is non-empty, this routine is the same as ``write_file()``. + If `data` is empty but not ``None``, this is the same as calling + ``delete_file(filename)`. If `data` is ``None``, then this is a no-op + unless `filename` exists, in which case a warning is issued about the + orphaned file. + """ + if data: + self.write_file(what, filename, data) + elif os.path.exists(filename): + if data is None: + log.warn( + "%s not set in setup(), but %s exists", what, filename + ) + return + else: + self.delete_file(filename) - requires = os.path.join(self.egg_info,"requires.txt") - log.info("writing %s", requires) + def write_file(self, what, filename, data): + """Write `data` to `filename` (if not a dry run) after announcing it + `what` is used in a log message to identify what is being written + to the file. + """ + log.info("writing %s to %s", what, filename) if not self.dry_run: - f = open(requires, 'wt') - f.write('\n'.join(yield_lines(dist.install_requires))) - for extra,reqs in dist.extras_require.items(): - f.write('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs)))) + f = open(filename, 'wb') + f.write(data) f.close() + def delete_file(self, filename): + """Delete `filename` (if not a dry run) after announcing it""" + log.info("deleting %s", filename) + if not self.dry_run: + os.unlink(filename) + + + + + def run(self): + # Make the .egg-info directory, then write PKG-INFO and requires.txt + self.mkpath(self.egg_info) + installer = self.distribution.fetch_build_egg + for ep in iter_entry_points('egg_info.writers'): + writer = ep.load(installer=installer) + writer(self, ep.name, os.path.join(self.egg_info,ep.name)) + def tagged_version(self): version = self.distribution.get_version() if self.tag_build: @@ -132,7 +140,6 @@ version += time.strftime("-%Y%m%d") return safe_version(version) - def get_svn_revision(self): stdin, stdout = os.popen4("svn info -R"); stdin.close() result = stdout.read(); stdout.close() @@ -146,60 +153,94 @@ return str(max(revisions)) - def write_toplevel_names(self): - pkgs = dict.fromkeys( - [k.split('.',1)[0] - for k in self.distribution.iter_distribution_names() - ] + + + + + + + + + +def write_pkg_info(cmd, basename, filename): + log.info("writing %s", filename) + if not cmd.dry_run: + metadata = cmd.distribution.metadata + metadata.version, oldver = cmd.egg_version, metadata.version + metadata.name, oldname = cmd.egg_name, metadata.name + try: + # write unescaped data to PKG-INFO, so older pkg_resources + # can still parse it + metadata.write_pkg_info(cmd.egg_info) + finally: + metadata.name, metadata.version = oldname, oldver + +def warn_depends_obsolete(cmd, basename, filename): + if os.path.exists(filename): + log.warn( + "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." ) - toplevel = os.path.join(self.egg_info, "top_level.txt") - log.info("writing list of top-level names to %s" % toplevel) - if not self.dry_run: - f = open(toplevel, 'wt') - f.write('\n'.join(pkgs)) - f.write('\n') - f.close() + + +def write_requirements(cmd, basename, filename): + dist = cmd.distribution + data = ['\n'.join(yield_lines(dist.install_requires or ()))] + for extra,reqs in (dist.extras_require or {}).items(): + data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs)))) + cmd.write_or_delete_file("requirements", filename, ''.join(data)) + +def write_toplevel_names(cmd, basename, filename): + pkgs = dict.fromkeys( + [k.split('.',1)[0] + for k in cmd.distribution.iter_distribution_names() + ] + ) + cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n') + + + + + + +def write_arg(cmd, basename, filename): + argname = os.path.splitext(basename)[0] + value = getattr(cmd.distribution, argname, None) + if value is not None: + value = '\n'.join(value)+'\n' + cmd.write_or_delete_file(argname, filename, value) + +def write_entries(cmd, basename, filename): + ep = cmd.distribution.entry_points + + if isinstance(ep,basestring) or ep is None: + data = ep + elif ep is not None: + data = [] + for section, contents in ep.items(): + if not isinstance(contents,basestring): + contents = EntryPoint.parse_list(section, contents) + contents = '\n'.join(map(str,contents.values())) + data.append('[%s]\n%s\n\n' % (section,contents)) + data = ''.join(data) + + cmd.write_or_delete_file('entry points', filename, data) + + + + + + + + + + + + - def write_or_delete_dist_arg(self, argname, filename=None): - value = getattr(self.distribution, argname, None) - filename = filename or argname+'.txt' - filename = os.path.join(self.egg_info,filename) - if value: - log.info("writing %s", filename) - if not self.dry_run: - f = open(filename, 'wt') - f.write('\n'.join(value)) - f.write('\n') - f.close() - elif os.path.exists(filename): - if value is None: - log.warn( - "%s not set in setup(), but %s exists", argname, filename - ) - return - log.info("deleting %s", filename) - if not self.dry_run: - os.unlink(filename) - def write_entry_points(self): - ep = getattr(self.distribution,'entry_points',None) - if ep is None: - return - epname = os.path.join(self.egg_info,"entry_points.txt") - log.info("writing %s", epname) - if not self.dry_run: - f = open(epname, 'wt') - if isinstance(ep,basestring): - f.write(ep) - else: - for section, contents in ep.items(): - if not isinstance(contents,basestring): - contents = EntryPoint.parse_list(section, contents) - contents = '\n'.join(map(str,contents.values())) - f.write('[%s]\n%s\n\n' % (section,contents)) - f.close() From pje at users.sourceforge.net Sat Aug 6 23:17:52 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 14:17:52 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools setup.py, 1.35, 1.36 setuptools.txt, 1.28, 1.29 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32399 Modified Files: setup.py setuptools.txt Log Message: Allow distutils extensions to define new kinds of metadata that can be written to EGG-INFO. Extensible applications and frameworks can thus make it possible for plugin projects to supply setup() metadata that can then be used by the application or framework. Index: setup.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setup.py,v retrieving revision 1.35 retrieving revision 1.36 diff -u -d -r1.35 -r1.36 --- setup.py 6 Aug 2005 18:46:27 -0000 1.35 +++ setup.py 6 Aug 2005 21:17:49 -0000 1.36 @@ -40,7 +40,6 @@ scripts = ['easy_install.py'], zip_safe = False, # We want 'python -m easy_install' to work :( - entry_points = { "distutils.commands" : [ "%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals() @@ -50,13 +49,23 @@ "eager_resources = setuptools.dist:assert_string_list", "namespace_packages = setuptools.dist:check_nsp", "extras_require = setuptools.dist:check_extras", + "install_requires = setuptools.dist:check_install_requires", "entry_points = setuptools.dist:check_entry_points", "test_suite = setuptools.dist:check_test_suite", "zip_safe = setuptools.dist:assert_bool", - ] + ], + "egg_info.writers": [ + "PKG-INFO = setuptools.command.egg_info:write_pkg_info", + "requires.txt = setuptools.command.egg_info:write_requirements", + "entry_points.txt = setuptools.command.egg_info:write_entries", + "eager_resources.txt = setuptools.command.egg_info:write_arg", + "namespace_packages.txt = setuptools.command.egg_info:write_arg", + "top_level.txt = setuptools.command.egg_info:write_toplevel_names", + "depends.txt = setuptools.command.egg_info:warn_depends_obsolete", + ], }, - - setup_requires = ['setuptools>=0.6a0'], + # uncomment for testing + # setup_requires = ['setuptools>=0.6a0'], classifiers = [f.strip() for f in """ Development Status :: 3 - Alpha @@ -68,23 +77,6 @@ Topic :: Software Development :: Libraries :: Python Modules Topic :: System :: Archiving :: Packaging Topic :: System :: Systems Administration - Topic :: Utilities - """.splitlines() if f.strip()] + Topic :: Utilities""".splitlines() if f.strip()] ) - - - - - - - - - - - - - - - - Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.28 retrieving revision 1.29 diff -u -d -r1.28 -r1.29 --- setuptools.txt 6 Aug 2005 19:29:48 -0000 1.28 +++ setuptools.txt 6 Aug 2005 21:17:49 -0000 1.29 @@ -622,6 +622,21 @@ point optional if a requirement isn't installed.) +Defining Additional Metadata +---------------------------- + +Some extensible applications and frameworks may need to define their own kinds +of metadata to include in eggs, which they can then access using the +``pkg_resources`` metadata APIs. Ordinarily, this is done by having plugin +developers include additional files in their ``ProjectName.egg-info`` +directory. However, since it can be tedious to create such files by hand, you +may want to create a distutils extension that will create the necessary files +from arguments to ``setup()``, in much the same way that ``setuptools`` does +for many of the ``setup()`` arguments it adds. See the section below on +`Creating distutils Extensions`_ for more details, especially the subsection on +`Adding new EGG-INFO Files`_. + + "Development Mode" ================== @@ -1301,6 +1316,14 @@ ``package_dir`` argument to the ``setup()`` function, if any. If there is no ``package_dir`` set, this option defaults to the current directory. +In addition to writing the core egg metadata defined by ``setuptools`` and +required by ``pkg_resources``, this command can be extended to write other +metadata files as well, by defining entry points in the ``egg_info.writers`` +group. See the section on `Adding new EGG-INFO Files`_ below for more details. +Note that using additional metadata writers may require you to include a +``setup_requires`` argument to ``setup()`` in order to ensure that the desired +writers are available on ``sys.path``. + .. _rotate: @@ -1639,6 +1662,60 @@ script lists your extension in its ``setup_requires`` argument. +Adding new EGG-INFO Files +------------------------- + +Some extensible applications or frameworks may want to allow third parties to +develop plugins with application or framework-specific metadata included in +the plugins' EGG-INFO directory, for easy access via the ``pkg_resources`` +metadata API. The easiest way to allow this is to create a distutils extension +to be used from the plugin projects' setup scripts (via ``setup_requires``) +that defines a new setup keyword, and then uses that data to write an EGG-INFO +file when the ``egg_info`` command is run. + +The ``egg_info`` command looks for extension points in an ``egg_info.writers`` +group, and calls them to write the files. Here's a simple example of a +distutils extension defining a setup argument ``foo_bar``, which is a list of +lines that will be written to ``foo_bar.txt`` in the EGG-INFO directory of any +project that uses the argument:: + + setup( + # ... + entry_points = { + "distutils.setup_keywords": [ + "foo_bar = setuptools.dist:assert_string_list", + ], + "egg_info.writers": [ + "foo_bar.txt = setuptools.command.egg_info:write_arg", + ], + }, + ) + +This simple example makes use of two utility functions defined by setuptools +for its own use: a routine to validate that a setup keyword is a sequence of +strings, and another one that looks up a setup argument and writes it to +a file. Here's what the writer utility looks like:: + + def write_arg(cmd, basename, filename): + argname = os.path.splitext(basename)[0] + value = getattr(cmd.distribution, argname, None) + if value is not None: + value = '\n'.join(value)+'\n' + cmd.write_or_delete_file(argname, filename, value) + +As you can see, ``egg_info.writers`` entry points must be a function taking +three arguments: a ``egg_info`` command instance, the basename of the file to +write (e.g. ``foo_bar.txt``), and the actual full filename that should be +written to. + +In general, writer functions should honor the command object's ``dry_run`` +setting when writing files, and use the ``distutils.log`` object to do any +console output. The easiest way to conform to this requirement is to use +the ``cmd`` object's ``write_file()``, ``delete_file()``, and +``write_or_delete_file()`` methods exclusively for your file operations. See +those methods' docstrings for more details. + + Subclassing ``Command`` ----------------------- @@ -1709,9 +1786,10 @@ ``setup_requires`` allows you to automatically find and download packages that are needed in order to *build* your project (as opposed to running it). - * ``setuptools`` now finds its commands and ``setup()`` argument validators - using entry points, so that they are extensible by third-party packages. - See `Creating distutils Extensions`_ above for more details. + * ``setuptools`` now finds its commands, ``setup()`` argument validators, and + metadata writers using entry points, so that they can be extended by + third-party packages. See `Creating distutils Extensions`_ above for more + details. * The vestigial ``depends`` command has been removed. It was never finished or documented, and never would have worked without EasyInstall - which it From bcannon at users.sourceforge.net Sun Aug 7 01:14:02 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Sat, 06 Aug 2005 16:14:02 -0700 Subject: [Python-checkins] python/nondist/peps pep-3000.txt,1.17,1.18 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16725 Modified Files: pep-3000.txt Log Message: Mention plan to remove ``raise Exception, "message"`` style of raising exceptions. Index: pep-3000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-3000.txt,v retrieving revision 1.17 retrieving revision 1.18 diff -u -d -r1.17 -r1.18 --- pep-3000.txt 28 Apr 2005 20:04:32 -0000 1.17 +++ pep-3000.txt 6 Aug 2005 23:13:59 -0000 1.18 @@ -67,6 +67,7 @@ * The ``lambda`` statement: use nested or named functions [1]_, [9]_ * String exceptions: use instances of an Exception class [2]_ +* ``raise Exception, "message"``: use ``raise Exception("message")`` [14]_ * ```x```: use ``repr(x)`` [2]_ * The ``<>`` operator: use ``!=`` instead [3]_ * Unbound methods [7]_ @@ -153,6 +154,9 @@ .. [13] python-dev email ("anonymous blocks") http://mail.python.org/pipermail/python-dev/2005-April/053060.html +.. [14] python-dev email ("PEP 8: exception style") + http://mail.python.org/pipermail/python-dev/2005-August/055190.html + Copyright ========= From pje at users.sourceforge.net Sun Aug 7 03:03:38 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 18:03:38 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools package_index.py, 1.15, 1.16 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32363/setuptools Modified Files: package_index.py Log Message: Renamed AvailableDistributions -> Environment. Add sketch of pkg_resources manual outline. Index: package_index.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/package_index.py,v retrieving revision 1.15 retrieving revision 1.16 diff -u -d -r1.15 -r1.16 --- package_index.py 24 Jul 2005 02:41:43 -0000 1.15 +++ package_index.py 7 Aug 2005 01:03:35 -0000 1.16 @@ -121,11 +121,11 @@ -class PackageIndex(AvailableDistributions): +class PackageIndex(Environment): """A distribution index that scans web pages for download URLs""" def __init__(self,index_url="http://www.python.org/pypi",*args,**kw): - AvailableDistributions.__init__(self,*args,**kw) + Environment.__init__(self,*args,**kw) self.index_url = index_url + "/"[:not index_url.endswith('/')] self.scanned_urls = {} self.fetched_urls = {} From pje at users.sourceforge.net Sun Aug 7 03:03:39 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 18:03:39 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/tests test_resources.py, 1.20, 1.21 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32363/setuptools/tests Modified Files: test_resources.py Log Message: Renamed AvailableDistributions -> Environment. Add sketch of pkg_resources manual outline. Index: test_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests/test_resources.py,v retrieving revision 1.20 retrieving revision 1.21 diff -u -d -r1.20 -r1.21 --- test_resources.py 25 Jul 2005 03:12:51 -0000 1.20 +++ test_resources.py 7 Aug 2005 01:03:36 -0000 1.21 @@ -23,7 +23,7 @@ def testCollection(self): # empty path should produce no distributions - ad = AvailableDistributions([], python=None) + ad = Environment([], python=None) self.assertEqual(list(ad), []) self.assertEqual(len(ad),0) self.assertEqual(ad.get('FooPkg'),None) @@ -122,7 +122,7 @@ def testResolve(self): - ad = AvailableDistributions([]); ws = WorkingSet([]) + ad = Environment([]); ws = WorkingSet([]) # Resolving no requirements -> nothing to install self.assertEqual( list(ws.resolve([],ad)), [] ) From pje at users.sourceforge.net Sun Aug 7 03:03:38 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 18:03:38 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command easy_install.py, 1.19, 1.20 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32363/setuptools/command Modified Files: easy_install.py Log Message: Renamed AvailableDistributions -> Environment. Add sketch of pkg_resources manual outline. Index: easy_install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/easy_install.py,v retrieving revision 1.19 retrieving revision 1.20 diff -u -d -r1.19 -r1.20 --- easy_install.py 6 Aug 2005 17:54:55 -0000 1.19 +++ easy_install.py 7 Aug 2005 01:03:36 -0000 1.20 @@ -181,7 +181,7 @@ self.package_index = self.create_index( self.index_url, search_path = self.shadow_path ) - self.local_index = AvailableDistributions(self.shadow_path) + self.local_index = Environment(self.shadow_path) if self.find_links is not None: if isinstance(self.find_links, basestring): @@ -805,7 +805,7 @@ try: args.append(dist_dir) self.run_setup(setup_script, setup_base, args) - all_eggs = AvailableDistributions([dist_dir]) + all_eggs = Environment([dist_dir]) eggs = [] for key in all_eggs: for dist in all_eggs[key]: @@ -1064,14 +1064,14 @@ -class PthDistributions(AvailableDistributions): +class PthDistributions(Environment): """A .pth file with Distribution paths in it""" dirty = False def __init__(self, filename): self.filename = filename; self._load() - AvailableDistributions.__init__( + Environment.__init__( self, list(yield_lines(self.paths)), None, None ) @@ -1109,13 +1109,13 @@ """Add `dist` to the distribution map""" if dist.location not in self.paths: self.paths.append(dist.location); self.dirty = True - AvailableDistributions.add(self,dist) + Environment.add(self,dist) def remove(self,dist): """Remove `dist` from the distribution map""" while dist.location in self.paths: self.paths.remove(dist.location); self.dirty = True - AvailableDistributions.remove(self,dist) + Environment.remove(self,dist) def main(argv, **kw): From pje at users.sourceforge.net Sun Aug 7 03:03:38 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 18:03:38 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.txt, NONE, 1.1 pkg_resources.py, 1.59, 1.60 setuptools.txt, 1.29, 1.30 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32363 Modified Files: pkg_resources.py setuptools.txt Added Files: pkg_resources.txt Log Message: Renamed AvailableDistributions -> Environment. Add sketch of pkg_resources manual outline. --- NEW FILE: pkg_resources.txt --- ============================================================= Package Discovery and Resource Access using ``pkg_resources`` ============================================================= The ``pkg_resources`` module, distributed with ``setuptools``, provides features for Python libraries to access resource files, and for extensible applications and frameworks to automatically discover plugins. It also provides runtime support for using C extensions that are inside zipfile eggs, support for merging packages that have separately-distributed modules or subpackages, and APIs for managing Python's current "working set" of active packages. .. contents:: **Table of Contents** -------- Overview -------- XXX ----------------- Developer's Guide ----------------- Accessing Resources Finding and Activating Package Distributions get_provider() require() WorkingSet iter_distributions Running Scripts Configuration Namespace Packages Extensible Applications and Frameworks Locating entry points Activation listeners Metadata access Extended Discovery and Installation Supporting Custom PEP 302 Implementations ------------- API Reference ------------- ``WorkingSet`` Objects ====================== Listeners ``Environment`` Objects ======================= XXX ``EntryPoint`` Objects ====================== XXX ``Requirement`` Objects ======================= XXX Syntax, parse_requirments, Requirement.parse, etc. ``Distribution`` Objects ======================== XXX ``ResourceManager`` Objects =========================== XXX Exceptions ========== XXX ResolutionError, VersionConflict, DistributionNotFound, UnknownExtra Utility Functions ================= Parsing Utilities ----------------- yield_lines XXX split_sections XXX parse_version XXX safe_name XXX safe_version XXX Platform Utilities ------------------ get_platform XXX compatible_platforms XXX File/Path Utilities ------------------- ensure_directory XXX normalize_path XXX Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.59 retrieving revision 1.60 diff -u -d -r1.59 -r1.60 --- pkg_resources.py 6 Aug 2005 20:54:01 -0000 1.59 +++ pkg_resources.py 7 Aug 2005 01:03:34 -0000 1.60 @@ -52,7 +52,7 @@ 'get_default_cache', # Primary implementation classes - 'AvailableDistributions', 'WorkingSet', 'ResourceManager', + 'Environment', 'WorkingSet', 'ResourceManager', 'Distribution', 'Requirement', 'EntryPoint', # Exceptions @@ -76,7 +76,7 @@ 'fixup_namespace_packages', 'get_importer', # Deprecated/backward compatibility only - 'run_main', + 'run_main', 'AvailableDistributions', ] @@ -453,7 +453,7 @@ """List all distributions needed to (recursively) meet `requirements` `requirements` must be a sequence of ``Requirement`` objects. `env`, - if supplied, should be an ``AvailableDistributions`` instance. If + if supplied, should be an ``Environment`` instance. If not supplied, it defaults to all distributions available within any entry or distribution in the working set. `installer`, if supplied, will be invoked with each requirement that cannot be met by an @@ -476,7 +476,7 @@ if dist is None: # Find the best distribution and add it to the map if env is None: - env = AvailableDistributions(self.entries) + env = Environment(self.entries) dist = best[req.key] = env.best_match(req, self, installer) if dist is None: raise DistributionNotFound(req) # XXX put more info here @@ -531,7 +531,7 @@ -class AvailableDistributions(object): +class Environment(object): """Searchable snapshot of distributions on a search path""" def __init__(self,search_path=None,platform=get_platform(),python=PY_MAJOR): @@ -645,7 +645,6 @@ return self.obtain(req, installer) # try and download/install - def obtain(self, requirement, installer=None): """Obtain a distro that matches requirement (e.g. via download)""" if installer is not None: @@ -653,6 +652,7 @@ def __len__(self): return len(self._distmap) +AvailableDistributions = Environment # XXX backward compatibility class ResourceManager: """Manage resource extraction and packages""" Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.29 retrieving revision 1.30 diff -u -d -r1.29 -r1.30 --- setuptools.txt 6 Aug 2005 21:17:49 -0000 1.29 +++ setuptools.txt 7 Aug 2005 01:03:35 -0000 1.30 @@ -51,6 +51,12 @@ * Deploy your project in "development mode", such that it's available on ``sys.path``, yet can still be edited directly from its source checkout. +* Easily extend the distutils with new commands or ``setup()`` arguments, and + distribute/reuse your extensions for multiple projects, without copying code. + +* Create extensible applications and frameworks that automatically discover + extensions, using simple "entry points" declared in a project's setup script. + .. contents:: **Table of Contents** @@ -1846,7 +1852,9 @@ that tells it to only yield distributions whose location is the passed-in path. (It defaults to False, so that the default behavior is unchanged.) - * The ``resolve()`` method of ``AvailableDistributions`` is now a method of + * ``AvailableDistributions`` is now called ``Environment`` + + * The ``resolve()`` method of ``Environment`` is now a method of ``WorkingSet`` instead, and the ``best_match()`` method now uses a working set instead of a path list as its second argument. From tim_one at users.sourceforge.net Sun Aug 7 04:47:15 2005 From: tim_one at users.sourceforge.net (tim_one@users.sourceforge.net) Date: Sat, 06 Aug 2005 19:47:15 -0700 Subject: [Python-checkins] python/dist/src/PCbuild readme.txt,1.60,1.61 Message-ID: Update of /cvsroot/python/python/dist/src/PCbuild In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12529 Modified Files: readme.txt Log Message: Update some Python version numbers. Index: readme.txt =================================================================== RCS file: /cvsroot/python/python/dist/src/PCbuild/readme.txt,v retrieving revision 1.60 retrieving revision 1.61 diff -u -d -r1.60 -r1.61 --- readme.txt 26 Jul 2005 02:29:21 -0000 1.60 +++ readme.txt 7 Aug 2005 02:47:12 -0000 1.61 @@ -12,7 +12,7 @@ The proper order to build subprojects: 1) pythoncore (this builds the main Python DLL and library files, - python21.{dll, lib} in Release mode) + python25.{dll, lib} in Release mode) NOTE: in previous releases, this subproject was named after the release number, e.g. python20. @@ -25,7 +25,7 @@ to the subsystems they implement; see SUBPROJECTS below) When using the Debug setting, the output files have a _d added to -their name: python24_d.dll, python_d.exe, parser_d.pyd, and so on. +their name: python25_d.dll, python_d.exe, parser_d.pyd, and so on. SUBPROJECTS ----------- @@ -114,7 +114,7 @@ all.tcl: Total 8420 Passed 6826 Skipped 1581 Failed 13 Sourced 91 Test Files. Files with failing tests: canvImg.test scrollbar.test textWind.test winWm.test - + Built Tix --------- Download from http://prdownloads.sourceforge.net/tix/tix-8.1.4.tar.gz From tim_one at users.sourceforge.net Sun Aug 7 04:48:01 2005 From: tim_one at users.sourceforge.net (tim_one@users.sourceforge.net) Date: Sat, 06 Aug 2005 19:48:01 -0700 Subject: [Python-checkins] python/dist/src/PCbuild readme.txt,1.61,1.62 Message-ID: Update of /cvsroot/python/python/dist/src/PCbuild In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12595 Modified Files: readme.txt Log Message: Removed XXX block about a test_bsddb3 failure that went away a long time ago. Index: readme.txt =================================================================== RCS file: /cvsroot/python/python/dist/src/PCbuild/readme.txt,v retrieving revision 1.61 retrieving revision 1.62 diff -u -d -r1.61 -r1.62 --- readme.txt 7 Aug 2005 02:47:12 -0000 1.61 +++ readme.txt 7 Aug 2005 02:47:59 -0000 1.62 @@ -238,17 +238,6 @@ XXX doesn't cause a test to fail when it happens (exceptions in XXX threads are invisible to unittest). - XXX 11-Apr-2004 tim - XXX On WinXP Pro, I got one failure from test_bsddb3: - XXX - XXX ERROR: test04_n_flag (bsddb.test.test_compat.CompatibilityTestCase) - XXX Traceback (most recent call last): - XXX File "C:\Code\python\lib\bsddb\test\test_compat.py", line 86, in test04_n_flag - XXX f = hashopen(self.filename, 'n') - XXX File "C:\Code\python\lib\bsddb\__init__.py", line 293, in hashopen - XXX d.open(file, db.DB_HASH, flags, mode) - XXX DBInvalidArgError: (22, 'Invalid argument -- DB_TRUNCATE illegal with locking specified') - _ssl Python wrapper for the secure sockets library. From tim_one at users.sourceforge.net Sun Aug 7 05:05:00 2005 From: tim_one at users.sourceforge.net (tim_one@users.sourceforge.net) Date: Sat, 06 Aug 2005 20:05:00 -0700 Subject: [Python-checkins] python/dist/src/Lib/test test_generators.py, 1.45, 1.46 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14552/Lib/test Modified Files: test_generators.py Log Message: Whitespace normalization (ran reindent.py over the whole tree). Index: test_generators.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_generators.py,v retrieving revision 1.45 retrieving revision 1.46 diff -u -d -r1.45 -r1.46 --- test_generators.py 2 Aug 2005 00:46:43 -0000 1.45 +++ test_generators.py 7 Aug 2005 03:04:58 -0000 1.46 @@ -484,7 +484,7 @@ merged A into G A->G B->G C->G D->G E->G F->G G->G H->G I->G J->G K->G L->G M->G ->>> for s in sets: s.close() # break cycles +>>> for s in sets: s.close() # break cycles """ # Emacs turd ' @@ -1467,12 +1467,12 @@ >>> g.throw(ValueError, TypeError(1)) # mismatched type, rewrapped caught ValueError (1) ->>> g.throw(ValueError(1), "foo") # bad args +>>> g.throw(ValueError(1), "foo") # bad args Traceback (most recent call last): ... TypeError: instance exception may not have a separate value ->>> g.throw(ValueError, "foo", 23) # bad args +>>> g.throw(ValueError, "foo", 23) # bad args Traceback (most recent call last): ... TypeError: throw() third argument must be a traceback object @@ -1482,13 +1482,13 @@ ... raise exc ... except: ... g.throw(*sys.exc_info()) ->>> throw(g,ValueError) # do it with traceback included +>>> throw(g,ValueError) # do it with traceback included caught ValueError () >>> g.send(1) 1 ->>> throw(g,TypeError) # terminate the generator +>>> throw(g,TypeError) # terminate the generator Traceback (most recent call last): ... TypeError @@ -1501,12 +1501,12 @@ ... StopIteration ->>> g.throw(ValueError,6) # throw on closed generator +>>> g.throw(ValueError,6) # throw on closed generator Traceback (most recent call last): ... ValueError: 6 ->>> f().throw(ValueError,7) # throw on just-opened generator +>>> f().throw(ValueError,7) # throw on just-opened generator Traceback (most recent call last): ... ValueError: 7 @@ -1527,11 +1527,11 @@ >>> f().close() # close on just-opened generator should be fine ->>> def f(): yield # an even simpler generator ->>> f().close() # close before opening +>>> def f(): yield # an even simpler generator +>>> f().close() # close before opening >>> g = f() >>> g.next() ->>> g.close() # close normally +>>> g.close() # close normally And finalization: From bcannon at users.sourceforge.net Sun Aug 7 06:14:07 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Sat, 06 Aug 2005 21:14:07 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.4,1.5 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22391 Modified Files: pep-0348.txt Log Message: Essentially version 3 of this PEP. All renamings have been removed. All new exceptions that were not superclasses have been removed. CriticalException has been renamed TerminalException. SystemError and MemoryError have been moved back under Exception, but while inheriting from the new exception VMError. ControlFlowException has been removed and its subclasses now directly inherit Exception. Also includes reformatting of the references and some editorial changes as suggested by David Goodger. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- pep-0348.txt 5 Aug 2005 05:31:44 -0000 1.4 +++ pep-0348.txt 7 Aug 2005 04:14:04 -0000 1.5 @@ -13,11 +13,11 @@ Abstract ======== -Python, as of version 2.4, has 38 exceptions (including warnings) in +Python, as 0of version 2.4, has 38 exceptions (including warnings) in the built-in namespace in a rather shallow hierarchy. This list of classes has grown over the years without a chance to learn from -mistakes and clean up the hierarchy. This PEP proposes doing a -reorganization for Python 3.0 when backwards-compatibility is not an +experience. This PEP proposes doing a reorganization of the hierarchy +for Python 3.0 when backwards-compatibility is not as much of an issue. Along with this reorganization, adding a requirement that all objects passed to a ``raise`` statement must inherit from a specific superclass is proposed. Lastly, bare ``except`` clauses will catch @@ -29,63 +29,68 @@ Exceptions are a critical part of Python. While exceptions are traditionally used to signal errors in a program, they have also grown -to be used for flow control for things such as iterators. Their -importance is great. +to be used for flow control for things such as iterators. -But the organization of the exception hierarchy is suboptimal to serve -the multiple uses of exceptions. Mostly for backwards-compatibility -reasons, the hierarchy has stayed very flat and old exceptions whose -usefulness has not been proven have been left in. Making exceptions -more hierarchical would help facilitate exception handling by making -exception catching using inheritance much more logical. This should -also help lead to fewer errors from overly broad exception catching in -``except`` clauses. +While their importance is great, there is lack of structure to them. +This stems from the fact that any object can be raised as an +exception. Because of this you have no guarantee in terms of what +kind of object will be raised, destroying any possible hierarchy +raised objects might adhere to. -A mandatory superclass for all exceptions is also being proposed -[#Summary2004-08-01]_. By requiring any object that is used in a -``raise`` statement to inherit from a specific superclass, certain -attributes (such as those laid out in PEP 344 [#PEP344]_) can be -guaranteed to exist. This also will lead to the planned removal of -string exceptions. +But exceptions do have a hierarchy, showing the severity of the +exception. The hierarchy also groups related exceptions together to +simplify catching them in ``except`` clauses. To allow peopele to +be able to rely on this hierarchy, a common superclasse that all +raise objects must inherit from is being proposed. It also allows +guarantees about the interface to raised objects to be made (see +PEP 344 [#PEP344]_). A discussion about all of this has occurred +before on python-dev [#Summary2004-08-01]_. -Lastly, bare ``except`` clauses are to catch only exceptions that -inherit from ``Exception`` [#python-dev3]_. While currently used to -catch all exceptions, that use is too far-reaching and typically not -desired. Catching only exceptions that inherit from ``Exception`` -allows other exceptions (those that should not be caught unless -explicitly desired) to continue to propagate up the execution stack. +But allowing a guarantee about the hierarchy is not the only place +where exceptions can stand improvement. Bare ``except`` clauses are +often used in an inappropriate manner. Since they catch *all* raised +objects, they can catch exceptions that should have been allowed to +propagate to the top level of the execution stack, leading to the +interpreter terminating execution. Once again, this has been +discussed on python-dev [#python-dev3]_. + +To fix this over-reaching of bare ``except`` clauses, it is being +proposed that only objects inheriting from Exception be caught by +bare ``except`` clauses. This will allow the exception hierarchy +to be organized in such a way that bare ``except`` clauses can be +more useful by allowing exceptions that should not normally be caught +to lead to the termination of the interpreter. Philosophy of Reorganization ============================ -There are several goals in this reorganization that defined the -philosophy used to guide the work. One goal was to prune out unneeded -exceptions. Extraneous exceptions should not be left in since they -just serve to clutter the built-in namespace. Unneeded exceptions -also dilute the importance of other exceptions by splitting uses -between several exceptions when all uses should have been under a -single exception. +For the reorganization of the hierarchy, there was a general +philosophy followed that developed from discussion of earlier drafts +of this PEP [#python-dev-thread1]_, [#python-dev-thread2]_, +[#python-dev-thread3]_. First and foremost was to not break anything +that works. This meant that renaming exceptions was out of the +question unless the name was deemed severely bad. This +also meant no removal of exceptions unless they were viewed as +truly misplaced. The introduction of new exceptions were only done in +situations where there might be a use for catching a superclass of a +category of exceptions. Lastly, existing exceptions would have their +inheritance tree changed only if it was felt they were truly +misplaced to begin with. -Another goal was to introduce exceptions that were deemed necessary to -fill holes in the hierarchy. Most new exceptions were added to flesh -out the inheritance hierarchy to make it easier to catch a category of -exceptions with a simpler ``except`` clause. +For all new exceptions, the proper suffix had to be chosen. For +those that signal an error, "Error" is to be used. If the exception +is a warning, then "Warning". "Exception" is to be used when none +of the other suffixes are proper to use and no specific suffix is +a better fit. -Changing inheritance to make the hierarchy more reasonable was a goal. -As stated above, having proper inheritance allows for more accurate -``except`` statements when catching exceptions based on the -inheritance tree. +After that it came down to choosing which exceptions should and +should not inherit from Exception. This was for the purpose of +making bare ``except`` clauses more useful. -Lastly, some renaming was done to make the usage of certain exceptions -more obvious. Having to look up an exception due to the name not -accurately reflecting its intended use is annoying and slows down -debugging. Having accurate names also makes debugging easier for new -programmers. But for simplicity, for the convenience of existing -users, and for the sake of transitioning to Python 3.0, only -exceptions whose names were significantly out of alignment with their -stated purpose have been renamed. All exceptions dealing with errors -will be named with an "Error" suffix. +Lastly, the entire existing hierarchy had to inherit from the new +exception meant to act as the required superclass for all exceptions +to inherit from. New Hierarchy @@ -94,76 +99,67 @@ .. Note:: Exceptions flagged with "stricter inheritance" will no longer inherit from a certain class. A "broader inheritance" flag means a class has been added to the exception's inheritance tree. + All comparisons are against the Python 2.4 exception hierarchy. .. parsed-literal:: - BaseException - +-- CriticalError (new) - +-- KeyboardInterrupt (stricter inheritance) - +-- MemoryError (stricter inheritance) - +-- SystemError (stricter inheritance) - +-- ControlFlowException (new) - +-- GeneratorExit (defined in PEP 342 [#PEP342]_) - +-- StopIteration (stricter inheritance) - +-- SystemExit (stricter inheritance) - +-- Exception - +-- StandardError - +-- ArithmeticError - +-- DivideByZeroError - +-- FloatingPointError - +-- OverflowError - +-- AssertionError - +-- AttributeError - +-- EnvironmentError - +-- IOError - +-- EOFError (broader inheritance) - +-- OSError - +-- ImportError - +-- LookupError - +-- IndexError - +-- KeyError - +-- NamespaceError (renamed from NameError) - +-- UnboundFreeError (new) - +-- UnboundGlobalError (new) - +-- UnboundLocalError - +-- NotImplementedError (stricter inheritance) - +-- SyntaxError - +-- IndentationError - +-- TabError - +-- TypeError - +-- UserError (renamed from RuntimeError) - +-- UnicodeError - +-- UnicodeDecodeError - +-- UnicodeEncodeError - +-- UnicodeTranslateError - +-- ValueError - +-- Warning - +-- AnyDeprecationWarning (new; broader inheritance for subclasses) - +-- PendingDeprecationWarning - +-- DeprecationWarning - +-- FutureWarning - +-- SyntaxWarning - +-- SemanticsWarning (renamed from RuntimeWarning) - +-- UserWarning - +-- WeakReferenceError (renamed from ReferenceError) + +-- BaseException (new; broader inheritance for subclasses) + +-- TerminalException (new; stricter inheritance for subclasses) + +-- KeyboardInterrupt + +-- SystemExit + +-- Exception + +-- GeneratorExit (defined in PEP 342 [#PEP342]_) + +-- StandardError + +-- ArithmeticError + +-- DivideByZeroError + +-- FloatingPointError + +-- OverflowError + +-- AssertionError + +-- AttributeError + +-- EnvironmentError + +-- IOError + +-- EOFError (broader inheritance) + +-- OSError + +-- ImportError + +-- LookupError + +-- IndexError + +-- KeyError + +-- NameError + +-- UnboundLocalError + +-- NotImplementedError (stricter inheritance) + +-- SyntaxError + +-- IndentationError + +-- TabError + +-- TypeError + +-- RuntimeError + +-- UnicodeError + +-- UnicodeDecodeError + +-- UnicodeEncodeError + +-- UnicodeTranslateError + +-- ValueError + +-- VMError (new; broader inheritance for subclasses) + +-- MemoryError + +-- SystemError + +-- ReferenceError + +-- StopIteration + +-- Warning + +-- AnyDeprecationWarning (new; broader inheritance for subclasses) + +-- PendingDeprecationWarning + +-- DeprecationWarning + +-- FutureWarning + +-- SyntaxWarning + +-- RuntimeWarning + +-- UserWarning Differences Compared to Python 2.4 ================================== -Changes to exceptions from Python 2.4 can take shape in three forms: -removal, renaming, or change of position in the hierarchy. There are -also new exceptions introduced in the proposed hierarchy. - -In terms of new exceptions, almost all are added to flesh out the -inheritance tree. Those that are leaf classes are added to alleviate -the overloading of another exception. - -Positional changes result in either broader or more restrictive -inheritance. The broader inheritance typically occurred to allow for -a more reasonable superclass to group related exceptions together. -Stricter inheritance happened when the pre-existing inheritance was -deemed incorrect and needed correction. +A more thorough explanation of terms is needed when discussing +inheritance changes. Inheritance changes result in either broader or +more restrictive inheritance. "Broader" is when a class has an +inheritance tree like ``cls, A`` and then becomes ``cls, B, A``. +"Stricter is the reverse. New Exceptions @@ -175,35 +171,19 @@ The superclass that all exceptions must inherit from. -CriticalError -''''''''''''' - -The superclass for severe error exceptions; typically, one would not -want to recover from such an exception. The name is meant to reflect -that these exceptions are raised asynchronously by the interpreter -when a critical event has occured. - - -ControlFlowException -'''''''''''''''''''' - -This exception exists as a superclass for all exceptions that directly -deal with control flow. Inheriting from BaseException instead of -Exception prevents them from being caught accidently when one wants to -catch errors. The name, by not mentioning "Error", does not lead to -one to confuse the subclasses as errors. - - -UnboundGlobalError -'''''''''''''''''' +TerminalException +''''''''''''''''' -Raised when a global variable was not found. +Superclass for exceptions that are meant to the termination of the +interpreter. It does not inherit from Exception so that +subclasses are not caught by bare ``except`` clauses. -UnboundFreeError -'''''''''''''''' +VMError +''''''' -Raised when a free variable is not found. +Superclass for exceptions that deal directly with the virtual +machine. AnyDeprecationWarning @@ -226,92 +206,14 @@ Too OS-specific to be kept in the built-in exception hierarchy. -Renamed Exceptions ------------------- - -RuntimeError -'''''''''''' - -Renamed to UserError. - -Meant as a generic exception for use when neither a new exception -class nor inheritance-based exception catching is desired, -RuntimeError is poorly named. Its name in Python 2.4 seems to suggest -an error that occurred at runtime, possibly an error in the VM. -Renaming the exception to UserError more clearly states the purpose of -the exception as a quick-and-dirty error exception. The name also -keeps it in line with UserWarning. - -If a user wants an non-error exception, raising BaseException directly -should be sufficient since Exception, which UserError inherits from, -is only used for errors. - - -ReferenceError -'''''''''''''' - -Renamed to WeakReferenceError. - -ReferenceError was added to the built-in exception hierarchy in Python -2.2 [#exceptions-stdlib]_. Its name comes directly from the time when -it resided in the ``weakref`` module. Unfortunately its name does not -suggest its connection to weak references and thus deserves a -renaming. - - -NameError -''''''''' - -Renamed to NamespaceError. - -While NameError suggests its common use, it is not entirely apparent. -Making it a superclass for namespace-related exceptions warrants a -renaming to make its use abundantly clear. Plus the documentation of -the exception module [#exceptions-stdlib]_ states that it was actually -meant for global names and not for just any exception. - - -RuntimeWarning -'''''''''''''' - -Renamed to SemanticsWarning. - -RuntimeWarning is supposed to represent semantic changes coming in the -future. But while saying that it affects the "runtime" is true, -flat-out stating that it is a semantic change is much clearer, -eliminating any possible association of the term "runtime" with the -virtual machine. - - Change of Position in the Exception Hierarchy --------------------------------------------- -KeyboardInterrupt, MemoryError, and SystemError -''''''''''''''''''''''''''''''''''''''''''''''' - -Inherit from CriticalError instead of from Exception. - -These three exceptions are not standard errors by any means. They are -raised asynchronously by the interpreter when something specific has -occurred. Thus they warrant not inheriting from Exception but from an -entirely separate exception that will not be caught by a bare -``except`` clause. - - -StopIteration and SystemExit -'''''''''''''''''''''''''''' - -Inherit from ControlFlowException instead of from Exception. - -By having these exceptions no longer inherit from Exception they will -not be accidentally caught by a bare ``except`` clause. - - NotImplementedError ''''''''''''''''''' -Inherits from Exception instead of from RuntimeError (renamed to -UserError). +Inherits from Exception instead of from RuntimeError. + Originally inheriting from RuntimeError, NotImplementedError does not have any direct relation to the exception meant for use in user code @@ -387,20 +289,6 @@ preserving backwards-compatibility. -Renamed Exceptions -'''''''''''''''''' - -Renamed exceptions will directly subclass the new names. When the old -exceptions are instantiated (which occurs when an exception is caught, -either by a ``try`` statement or by propagating to the top of the -execution stack), a PendingDeprecationWarning will be raised. - -This should properly preserve backwards-compatibility as old usage -won't change and the new names can also be used to catch exceptions -using the old names. The warning of the deprecation is also kept -simple. - - New Inheritance for Old Exceptions '''''''''''''''''''''''''''''''''' @@ -428,21 +316,23 @@ Required Superclass for ``raise`` --------------------------------- -A SemanticsWarning will be raised when an object is passed to +A DeprecationWarning will be raised when an object is passed to ``raise`` that does not have the proper inheritance. Removal of Bare ``except`` Clauses ---------------------------------- -A SemanticsWarning will be raised for all bare ``except`` clauses. +A RuntimeWarning will be raised for all bare ``except`` clauses that +catch an exception that does not inherit from Exception. Rejected Ideas ============== -Threads on python-dev discussing this PEP can be found at -[#python-dev-thread1]_ and [#python-dev-thread2]_ +Multiple threads on python-dev discussing this PEP have lead to +various ideas being rejected [#python-dev-thread1]_, +[#python-dev-thread2]_, [#python-dev-thread3]_. KeyboardInterrupt inheriting from ControlFlowException @@ -452,8 +342,7 @@ Some view the exception more as control flow being caused by the user. But with its asynchronous cause (the user is able to trigger the exception at any point in code) its proper place is inheriting from -CriticalException. It also keeps the name of the exception from being -"CriticalError". +CriticalError. Other Names for BaseException and Exception @@ -508,7 +397,7 @@ ---------------------------------- Proposed because a SystemError is meant to lead to a system exit, the -idea was removed since CriticalException indicates this better. +idea was removed since CriticalError indicates this better. ControlFlowException Under Exception @@ -529,18 +418,33 @@ Python have default behavior [#python-dev3]_. -Open Issues -=========== +Rename NameError to NamespaceError +---------------------------------- -Remove ControlFlowException? +NameError is considered more succinct and leaves open no possible mistyping of +the capitalization of "Namespace" [#python-dev5]_. + + +Renaming RuntimeError or Introducing SimpleError +'''''''''''''''''''''''''''''''''''''''''''''''' + +The thinking was that RuntimeError was in no way an obvious name for +an exception meant to be used when a situation did not call for the +creation of a new exception. The renaming was rejected on the basis +that the exception is already used throughout the interpreter [#python-dev6]_. +Rejection of SimpleError was founded on the thought that people +should be free to use whatever exception they choose and not have one +so blatently suggested [#python-dev7]_. + +Renaming Existing Exceptions ---------------------------- -It has been suggested that ControlFlowException is not needed. Since -the desire to catch any control flow exception will be atypical, the -suggestion is to just remove the exception and let the exceptions that -inherited from it inherit directly from BaseException. This still -preserves the seperation from Exception which is one of the driving -factors behind the introduction of ControlFlowException. +Various renamings were suggested but non garnered more than a +0 vote +(renaming ReferenceError to WeakReferenceError). The thinking was +that the existing names were fine and no one had actively complained +about them ever. To minimize backwards-compatibility issues and +causing existing Python programmers extra pain, the renamings were +removed. Acknowledgements @@ -548,51 +452,56 @@ Thanks to Robert Brewer, Josiah Carlson, Nick Coghlan, Timothy Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing, -James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull and -everyone else I missed for participating in the discussion. +James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull, +Raymond Hettinger, and everyone else I missed for participating in the +discussion. References ========== .. [#PEP342] PEP 342 (Coroutines via Enhanced Generators) - (http://www.python.org/peps/pep-0342.html) + http://www.python.org/peps/pep-0342.html .. [#PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) - (http://www.python.org/peps/pep-0344.html) + http://www.python.org/peps/pep-0344.html .. [#Summary2004-08-01] python-dev Summary (An exception is an exception, unless it doesn't inherit from Exception) - (http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception) - -.. [#Summary2004-09-01] python-dev Summary (Cleaning the Exception House) - (http://www.python.org/dev/summary/2004-09-01_2004-09-15.html#cleaning-the-exception-house) - -.. [#python-dev1] python-dev email (Exception hierarchy) - (http://mail.python.org/pipermail/python-dev/2004-August/047908.html) - -.. [#python-dev2] python-dev email (Dangerous exceptions) - (http://mail.python.org/pipermail/python-dev/2004-September/048681.html) + http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception .. [#python-dev3] python-dev email (PEP, take 2: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-August/055116.html) + http://mail.python.org/pipermail/python-dev/2005-August/055116.html .. [#exceptions-stdlib] exceptions module - (http://docs.python.org/lib/module-exceptions.html) + http://docs.python.org/lib/module-exceptions.html .. [#python-dev-thread1] python-dev thread (Pre-PEP: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-July/055020.html, - http://mail.python.org/pipermail/python-dev/2005-August/055065.html) + http://mail.python.org/pipermail/python-dev/2005-July/055020.html, + http://mail.python.org/pipermail/python-dev/2005-August/055065.html .. [#python-dev-thread2] python-dev thread (PEP, take 2: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-August/055103.html) + http://mail.python.org/pipermail/python-dev/2005-August/055103.html + +.. [#python-dev-thread3] python-dev thread (Reorg PEP checked in) + http://mail.python.org/pipermail/python-dev/2005-August/055138.html .. [#python-dev4] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0) - (http://mail.python.org/pipermail/python-dev/2005-July/055019.html) + http://mail.python.org/pipermail/python-dev/2005-July/055019.html + +.. [#python-dev5] python-dev email (PEP, take 2: Exception Reorganization for +Python 3.0) + http://mail.python.org/pipermail/python-dev/2005-August/055159.html + +.. [#python-dev6] python-dev email (Exception Reorg PEP checked in) + http://mail.python.org/pipermail/python-dev/2005-August/055149.html + +.. [#python-dev7] python-dev email (Exception Reorg PEP checked in) + http://mail.python.org/pipermail/python-dev/2005-August/055175.html Copyright From pje at users.sourceforge.net Sun Aug 7 06:50:47 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 21:50:47 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.60, 1.61 pkg_resources.txt, 1.1, 1.2 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26863 Modified Files: pkg_resources.py pkg_resources.txt Log Message: Document utility routines. Made ``split_sections()`` not lowercase its section headers any more, since e.g. entry point group names are case-sensitive. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.60 retrieving revision 1.61 diff -u -d -r1.60 -r1.61 --- pkg_resources.py 7 Aug 2005 01:03:34 -0000 1.60 +++ pkg_resources.py 7 Aug 2005 04:50:44 -0000 1.61 @@ -1557,26 +1557,29 @@ yield '*final' # ensure that alpha/beta/candidate are before final def parse_version(s): - """Convert a version string to a sortable key + """Convert a version string to a chronologically-sortable key This is a rough cross between distutils' StrictVersion and LooseVersion; if you give it versions that would work with StrictVersion, then it behaves - the same; otherwise it acts like a slightly-smarter LooseVersion. + the same; otherwise it acts like a slightly-smarter LooseVersion. It is + *possible* to create pathological version coding schemes that will fool + this parser, but they should be very rare in practice. The returned value will be a tuple of strings. Numeric portions of the version are padded to 8 digits so they will compare numerically, but without relying on how numbers compare relative to strings. Dots are dropped, but dashes are retained. Trailing zeros between alpha segments - or dashes are suppressed, so that e.g. 2.4.0 is considered the same as 2.4. - Alphanumeric parts are lower-cased. + or dashes are suppressed, so that e.g. "2.4.0" is considered the same as + "2.4". Alphanumeric parts are lower-cased. - The algorithm assumes that strings like '-' and any alpha string > "final" - represents a "patch level". So, "2.4-1" is assumed to be a branch or patch - of "2.4", and therefore "2.4.1" is considered newer than "2.4-1". + The algorithm assumes that strings like "-" and any alpha string that + alphabetically follows "final" represents a "patch level". So, "2.4-1" + is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is + considered newer than "2.4-1", whic in turn is newer than "2.4". Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that come before "final" alphabetically) are assumed to be pre-release versions, - and so the version "2.4" is considered newer than "2.4a1". + so that the version "2.4" is considered newer than "2.4a1". Finally, to handle miscellaneous cases, the strings "pre", "preview", and "rc" are treated as if they were "c", i.e. as though they were release @@ -1596,7 +1599,6 @@ - class EntryPoint(object): """Object representing an importable location""" @@ -1810,7 +1812,7 @@ dm = self.__dep_map = {None: []} for name in 'requires.txt', 'depends.txt': for extra,reqs in split_sections(self._get_metadata(name)): - dm.setdefault(extra,[]).extend(parse_requirements(reqs)) + dm.setdefault(extra.lower(),[]).extend(parse_requirements(reqs)) return dm _dep_map = property(_dep_map) @@ -2099,7 +2101,7 @@ def split_sections(s): """Split a string or iterable thereof into (section,content) pairs - Each ``section`` is a lowercase version of the section header ("[section]") + Each ``section`` is a stripped version of the section header ("[section]") and each ``content`` is a list of stripped lines excluding blank lines and comment-only lines. If there are any such lines before the first section header, they're returned in a first ``section`` of ``None``. @@ -2111,7 +2113,7 @@ if line.endswith("]"): if section or content: yield section, content - section = line[1:-1].strip().lower() + section = line[1:-1].strip() content = [] else: raise ValueError("Invalid section heading", line) Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- pkg_resources.txt 7 Aug 2005 01:03:35 -0000 1.1 +++ pkg_resources.txt 7 Aug 2005 04:50:44 -0000 1.2 @@ -2,10 +2,10 @@ Package Discovery and Resource Access using ``pkg_resources`` ============================================================= -The ``pkg_resources`` module, distributed with ``setuptools``, provides -features for Python libraries to access resource files, and for extensible +The ``pkg_resources`` module distributed with ``setuptools`` provides an API +for Python libraries to access their resource files, and for extensible applications and frameworks to automatically discover plugins. It also -provides runtime support for using C extensions that are inside zipfile +provides runtime support for using C extensions that are inside zipfile-format eggs, support for merging packages that have separately-distributed modules or subpackages, and APIs for managing Python's current "working set" of active packages. @@ -96,38 +96,128 @@ Parsing Utilities ----------------- -yield_lines - XXX +``parse_version(version)`` + Parse a project's version string, returning a value that can be used to + compare versions by chronological order. Semantically, the format is a + rough cross between distutils' ``StrictVersion`` and ``LooseVersion`` + classes; if you give it versions that would work with ``StrictVersion``, + then they will compare the same way. Otherwise, comparisons are more like + a "smarter" form of ``LooseVersion``. It is *possible* to create + pathological version coding schemes that will fool this parser, but they + should be very rare in practice. -split_sections - XXX + The returned value will be a tuple of strings. Numeric portions of the + version are padded to 8 digits so they will compare numerically, but + without relying on how numbers compare relative to strings. Dots are + dropped, but dashes are retained. Trailing zeros between alpha segments + or dashes are suppressed, so that e.g. "2.4.0" is considered the same as + "2.4". Alphanumeric parts are lower-cased. -parse_version - XXX + The algorithm assumes that strings like "-" and any alpha string that + alphabetically follows "final" represents a "patch level". So, "2.4-1" + is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is + considered newer than "2.4-1", whic in turn is newer than "2.4". -safe_name - XXX + Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that + come before "final" alphabetically) are assumed to be pre-release versions, + so that the version "2.4" is considered newer than "2.4a1". -safe_version - XXX + Finally, to handle miscellaneous cases, the strings "pre", "preview", and + "rc" are treated as if they were "c", i.e. as though they were release + candidates, and therefore are not as new as a version string that does not + contain them. + +``yield_lines(strs)`` + Yield non-empty/non-comment lines from a string/unicode or a possibly- + nested sequence thereof. If `strs` is an instance of ``basestring``, it + is split into lines, and each non-blank, non-comment line is yielded after + stripping leading and trailing whitespace. (Lines whose first non-blank + character is ``#`` are considered comment lines.) + + If `strs` is not an instance of ``basestring``, it is iterated over, and + each item is passed recursively to ``yield_lines()``, so that an arbitarily + nested sequence of strings, or sequences of sequences of strings can be + flattened out to the lines contained therein. So for example, passing + a file object or a list of strings to ``yield_lines`` will both work. + (Note that between each string in a sequence of strings there is assumed to + be an implicit line break, so lines cannot bridge two strings in a + sequence.) + + This routine is used extensively by ``pkg_resources`` to parse metadata + and file formats of various kinds, and most other ``pkg_resources`` + parsing functions that yield multiple values will use it to break up their + input. However, this routine is idempotent, so calling ``yield_lines()`` + on the output of another call to ``yield_lines()`` is completely harmless. + +``split_sections(strs)`` + Split a string (or possibly-nested iterable thereof), yielding ``(section, + content)`` pairs found using an ``.ini``-like syntax. Each ``section`` is + a whitespace-stripped version of the section name ("``[section]``") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any non-blank, non-comment lines before + the first section header, they're yielded in a first ``section`` of + ``None``. + + This routine uses ``yield_lines()`` as its front end, so you can pass in + anything that ``yield_lines()`` accepts, such as an open text file, string, + or sequence of strings. ``ValueError`` is raised if a malformed section + header is found (i.e. a line starting with ``[`` but not ending with + ``]``). + + Note that this simplistic parser assumes that any line whose first nonblank + character is ``[`` is a section heading, so it can't support .ini format + variations that allow ``[`` as the first nonblank character on other lines. + +``safe_name(name)`` + Return a "safe" form of a project's name, suitable for use in a + ``Requirement`` string, as a distribution name, or a PyPI project name. + All non-alphanumeric runs are condensed to single "-" characters, such that + a name like "The $$$ Tree" becomes "The-Tree". Note that if you are + generating a filename from this value you should replace the "-" characters + with underscores ("_") because setuptools and the distutils + +``safe_version(version)`` + Similar to ``safe_name()`` except that spaces in the input become dots, and + dots are allowed to exist in the output. As with ``safe_name()``, if you + are generating a filename from this you should replace any "-" characters + in the output with underscores. Platform Utilities ------------------ -get_platform - XXX +``get_platform()`` + Return this platform's identifier string. For Windows, the return value + is ``"win32"``, and for Mac OS X it is a string of the form + ``"macosx-10.4-ppc"``. All other platforms return the same uname-based + string that the ``distutils.util.get_platform()`` function returns. -compatible_platforms - XXX +``compatible_platforms(provided, required)`` + Return true if a distribution built on the `provided` platform may be used + on the `required` platform. If either platform value is ``None``, it is + considered a wildcard, and the platforms are therefore compatible. + Likewise, if the platform strings are equal, they're also considered + compatible, and ``True`` is returned. Currently, the only non-equal + platform strings that are considered compatible are Mac OS X platform + strings with the same hardware type (e.g. ``ppc``) and major version + (e.g. ``10``) with the `provided` platform's minor version being less than + or equal to the `required` platform's minor version. File/Path Utilities ------------------- -ensure_directory - XXX +``ensure_directory(path)`` + Ensure that the parent directory (``os.path.dirname``) of `path` actually + exists, using ``os.makedirs()`` if necessary. -normalize_path - XXX +``normalize_path(path)`` + Return a "normalized" version of `path`, such that two paths represent + the same filesystem location if they have equal ``normalized_path()`` + values. Specifically, this is a shortcut for calling ``os.path.realpath`` + and ``os.path.normcase`` on `path`. Unfortunately, on certain platforms + (notably Cygwin and Mac OS X) the ``normcase`` function does not accurately + reflect the platform's case-sensitivity, so there is always the possibility + of two apparently-different paths being equal on such platforms. + From pje at users.sourceforge.net Sun Aug 7 07:52:49 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 06 Aug 2005 22:52:49 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.61, 1.62 pkg_resources.txt, 1.2, 1.3 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv722 Modified Files: pkg_resources.py pkg_resources.txt Log Message: Add docs for exceptions, and for much of the ResourceManager API. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.61 retrieving revision 1.62 diff -u -d -r1.61 -r1.62 --- pkg_resources.py 7 Aug 2005 04:50:44 -0000 1.61 +++ pkg_resources.py 7 Aug 2005 05:52:36 -0000 1.62 @@ -656,44 +656,44 @@ class ResourceManager: """Manage resource extraction and packages""" - extraction_path = None def __init__(self): self.cached_files = {} - def resource_exists(self, package_name, resource_name): - """Does the named resource exist in the named package?""" - return get_provider(package_name).has_resource(resource_name) + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) - def resource_isdir(self, package_name, resource_name): - """Does the named resource exist in the named package?""" - return get_provider(package_name).resource_isdir(resource_name) + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) - def resource_filename(self, package_name, resource_name): + def resource_filename(self, package_or_requirement, resource_name): """Return a true filesystem path for specified resource""" - return get_provider(package_name).get_resource_filename( - self,resource_name + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name ) - def resource_stream(self, package_name, resource_name): + def resource_stream(self, package_or_requirement, resource_name): """Return a readable file-like object for specified resource""" - return get_provider(package_name).get_resource_stream( + return get_provider(package_or_requirement).get_resource_stream( self, resource_name ) - def resource_string(self, package_name, resource_name): + def resource_string(self, package_or_requirement, resource_name): """Return specified resource as a string""" - return get_provider(package_name).get_resource_string( + return get_provider(package_or_requirement).get_resource_string( self, resource_name ) - def resource_listdir(self, package_name, resource_name): - return get_provider(package_name).resource_listdir(resource_name) - - - - + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) def get_cache_path(self, archive_name, names=()): """Return absolute location in cache for `archive_name` and `names` @@ -701,7 +701,7 @@ The parent directory of the resulting path will be created if it does not already exist. `archive_name` should be the base filename of the enclosing egg (which may not be the name of the enclosing zipfile!), - including the ".egg" extension. `names`, if provided, should be a + including its ".egg" extension. `names`, if provided, should be a sequence of path name parts "under" the egg's extraction location. This method should only be called by resource providers that need to @@ -739,7 +739,12 @@ def set_extraction_path(self, path): """Set the base path where resources will be extracted to, if needed. - If not set, this defaults to ``os.expanduser("~/.python-eggs")``. + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + Resources are extracted to subdirectories of this path based upon information given by the ``IResourceProvider``. You may set this to a temporary directory, but then you must call ``cleanup_resources()`` to @@ -772,11 +777,6 @@ - - - - - def get_default_cache(): """Determine the default cache location Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- pkg_resources.txt 7 Aug 2005 04:50:44 -0000 1.2 +++ pkg_resources.txt 7 Aug 2005 05:52:37 -0000 1.3 @@ -46,6 +46,8 @@ API Reference ------------- +XXX Namespace stuff, + ``WorkingSet`` Objects ====================== @@ -74,24 +76,170 @@ ``Distribution`` Objects ======================== -XXX +Factories: get_provider, get_distribution, find_distributions; see also +WorkingSet and Environment APIs. -``ResourceManager`` Objects -=========================== +``ResourceManager`` API +======================= -XXX +The ``ResourceManager`` class provides uniform access to package resources, +whether those resources exist as files and directories or are compressed in +an archive of some kind. + +Normally, you do not need to create or explicitly manage ``ResourceManager`` +instances, as the ``pkg_resources`` module creates a global instance for you, +and makes most of its methods available as top-level names in the +``pkg_resources`` module namespace. So, for example, this code actually +calls the ``resource_string()`` method of the global ``ResourceManager``:: + + import pkg_resources + my_data = pkg_resources.resource_string(__name__, "foo.dat") + +Thus, you can use the APIs below without needing a ``ResourceManager`` +instance; just import and use them. + + +Basic Resource Access +--------------------- + +XXX explain resource paths, layout, etc. here + +``resource_exists(package_or_requirement, resource_name)`` + Does the named resource exist? + +``resource_stream(package_or_requirement, resource_name)`` + Return a readable file-like object for specified resource + +``resource_string(package_or_requirement, resource_name)`` + Return specified resource as a string + +``resource_isdir(package_or_requirement, resource_name)`` + Is the named resource an existing directory? + +``resource_listdir(package_or_requirement, resource_name)`` + List the contents of the named resource directory + + +Resource Extraction +------------------- + +``resource_filename(package_or_requirement, resource_name)`` + Sometimes, it is not sufficient to access a resource in string or stream + form, and a true filesystem filename is needed. In such cases, you can + use this method (or module-level function) to obtain a filename for a + resource. If the resource is in an archive distribution (such as a zipped + egg), it will be extracted to a cache directory, and the filename within + the cache will be returned. If the named resource is a directory, then + all resources within that directory (including subdirectories) are also + extracted. If the named resource is a C extension or "eager resource" + (see the ``setuptools`` documentation for details), then all C extensions + and eager resources are extracted at the same time. + + Archived resources are extracted to a cache location that can be managed by + the following two methods: + +``set_extraction_path(path)`` + Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which is + based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + +``cleanup_resources(force=False)`` + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + + +"Provider" Interface +-------------------- + +If you are implementing an ``IResourceProvider`` and/or ``IMetadataProvider`` +for a new distribution archive format, you may need to use the following +``ResourceManager`` methods to co-ordinate extraction of resources to the +filesystem. If you're not implementing an archive format, however, you have +no need to use these methods. Unlike the other methods listed above, they are +*not* available as top-level functions tied to the global ``ResourceManager``; +you must therefore have an explicit ``ResourceManager`` instance to use them. + +``get_cache_path(archive_name, names=())`` + Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + +``postprocess(tempname, filename)`` + Perform any platform-specific postprocessing of `tempname`. + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. Exceptions ========== -XXX ResolutionError, VersionConflict, DistributionNotFound, UnknownExtra +``pkg_resources`` provides a simple exception hierarchy for problems that may +occur when processing requests to locate and activate packages:: + + ResolutionError + DistributionNotFound + VersionConflict + UnknownExtra + +``ResolutionError`` + This class is used as a base class for the other three exceptions, so that + you can catch all of them with a single "except" clause. It is also raised + directly for miscellaneous requirement-resolution problems like trying to + run a script that doesn't exist in the distribution it was requested from. + +``DistributionNotFound`` + A distribution needed to fulfill a requirement could not be found. + +``VersionConflict`` + The requested version of a project conflicts with an already-activated + version of the same project. + +``UnknownExtra`` + One of the "extras" requested was not recognized by the distribution it + was requested from. Utility Functions ================= +In addition to its high-level APIs, ``pkg_resources`` also includes several +generally-useful utility routines. These routines are used to implement the +high-level APIs, but can also be quite useful by themselves. + Parsing Utilities ----------------- @@ -203,6 +351,14 @@ (e.g. ``10``) with the `provided` platform's minor version being less than or equal to the `required` platform's minor version. +``get_default_cache()`` + Determine the default cache location for extracting resources from zipped + eggs. This routine returns the ``PYTHON_EGG_CACHE`` environment variable, + if set. Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of + the user's "Application Data" directory. On all other systems, it returns + ``os.path.expanduser("~/.python-eggs")`` if ``PYTHON_EGG_CACHE`` is not + set. + File/Path Utilities ------------------- From rhettinger at users.sourceforge.net Sun Aug 7 15:02:55 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sun, 07 Aug 2005 06:02:55 -0700 Subject: [Python-checkins] python/dist/src/Include setobject.h,2.8,2.9 Message-ID: Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2124/Include Modified Files: setobject.h Log Message: * Bring in INIT_NONZERO_SET_SLOTS macro from dictionary code. * Bring in free list from dictionary code. * Improve several comments. * Differencing can leave many dummy entries. If more than 1/6 are dummies, then resize them away. * Factor-out common code with new macro, PyAnySet_CheckExact. Index: setobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/setobject.h,v retrieving revision 2.8 retrieving revision 2.9 diff -u -d -r2.8 -r2.9 --- setobject.h 2 Aug 2005 03:45:16 -0000 2.8 +++ setobject.h 7 Aug 2005 13:02:52 -0000 2.9 @@ -59,12 +59,16 @@ PyAPI_DATA(PyTypeObject) PySet_Type; PyAPI_DATA(PyTypeObject) PyFrozenSet_Type; -/* Invariants for frozensets only: +/* Invariants for frozensets: * data is immutable. * hash is the hash of the frozenset or -1 if not computed yet. + * Invariants for sets: + * hash is -1 */ #define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type) +#define PyAnySet_CheckExact(ob) \ + ((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type) #define PyAnySet_Check(ob) \ ((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type || \ PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \ From rhettinger at users.sourceforge.net Sun Aug 7 15:02:55 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sun, 07 Aug 2005 06:02:55 -0700 Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.44,1.45 Message-ID: Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2124/Objects Modified Files: setobject.c Log Message: * Bring in INIT_NONZERO_SET_SLOTS macro from dictionary code. * Bring in free list from dictionary code. * Improve several comments. * Differencing can leave many dummy entries. If more than 1/6 are dummies, then resize them away. * Factor-out common code with new macro, PyAnySet_CheckExact. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.44 retrieving revision 1.45 diff -u -d -r1.44 -r1.45 --- setobject.c 6 Aug 2005 18:57:13 -0000 1.44 +++ setobject.c 7 Aug 2005 13:02:53 -0000 1.45 @@ -15,13 +15,22 @@ /* Object used as dummy key to fill deleted entries */ static PyObject *dummy = NULL; /* Initialized by first call to make_new_set() */ +#define INIT_NONZERO_SET_SLOTS(so) do { \ + (so)->table = (so)->smalltable; \ + (so)->mask = PySet_MINSIZE - 1; \ + (so)->hash = -1; \ + } while(0) + #define EMPTY_TO_MINSIZE(so) do { \ memset((so)->smalltable, 0, sizeof((so)->smalltable)); \ (so)->used = (so)->fill = 0; \ - (so)->table = (so)->smalltable; \ - (so)->mask = PySet_MINSIZE - 1; \ + INIT_NONZERO_SET_SLOTS(so); \ } while(0) +/* Reuse scheme to save calls to malloc, free, and memset */ +#define MAXFREESETS 80 +static PySetObject *free_sets[MAXFREESETS]; +static int num_free_sets = 0; /* The basic lookup function used by all operations. @@ -30,13 +39,15 @@ chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent -probe indices are computed as explained earlier. +probe indices are computed as explained in Objects/dictobject.c. All arithmetic on hash should ignore overflow. -This function must never return NULL; failures are indicated by returning -a setentry* for which the value field is NULL. Exceptions are never -reported by this function, and outstanding exceptions are maintained. +The lookup function always succeeds and nevers return NULL. This simplifies +and speeds client functions which do won't have to test for and handle +errors. To meet that requirement, any errors generated by a user defined +__cmp__() function are simply cleared and ignored. +Previously outstanding exceptions are maintained. */ static setentry * @@ -187,7 +198,7 @@ freeslot = entry; } } else { - /* Simplified loop that can assume are no dummy entries */ + /* Simplified loop when there are no dummy entries. */ if (entry->hash == hash && _PyString_Eq(entry->key, key)) return entry; for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { @@ -347,7 +358,7 @@ set_insert_key(so, key, hash); if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2)) return 0; - return set_table_resize(so, so->used*(so->used>50000 ? 2 : 4)); + return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); } #define DISCARD_NOTFOUND 0 @@ -439,7 +450,6 @@ if (table_is_malloced) PyMem_DEL(table); - so->hash = -1; return 0; } @@ -710,16 +720,24 @@ } /* create PySetObject structure */ - so = (PySetObject *)type->tp_alloc(type, 0); - if (so == NULL) - return NULL; + if (num_free_sets && + (type == &PySet_Type || type == &PyFrozenSet_Type)) { + so = free_sets[--num_free_sets]; + assert (so != NULL && PyAnySet_CheckExact(so)); + so->ob_type = type; + _Py_NewReference((PyObject *)so); + EMPTY_TO_MINSIZE(so); + PyObject_GC_Track(so); + } else { + so = (PySetObject *)type->tp_alloc(type, 0); + if (so == NULL) + return NULL; + /* tp_alloc has already zeroed the structure */ + assert(so->table == NULL && so->fill == 0 && so->used == 0); + INIT_NONZERO_SET_SLOTS(so); + } - /* tp_alloc has already zeroed the structure */ - assert(so->table == NULL && so->fill == 0 && so->used == 0); - so->table = so->smalltable; - so->mask = PySet_MINSIZE - 1; so->lookup = set_lookkey_string; - so->hash = -1; so->weakreflist = NULL; if (iterable != NULL) { @@ -767,6 +785,13 @@ void PySet_Fini(void) { + PySetObject *so; + + while (num_free_sets) { + num_free_sets--; + so = free_sets[num_free_sets]; + PyObject_GC_Del(so); + } Py_XDECREF(dummy); Py_XDECREF(emptyfrozenset); } @@ -797,7 +822,10 @@ if (so->table != so->smalltable) PyMem_DEL(so->table); - so->ob_type->tp_free(so); + if (num_free_sets < MAXFREESETS && PyAnySet_CheckExact(so)) + free_sets[num_free_sets++] = so; + else + so->ob_type->tp_free(so); Py_TRASHCAN_SAFE_END(so) } @@ -1079,6 +1107,11 @@ Py_DECREF(it); if (PyErr_Occurred()) return NULL; + /* If more than 1/6 are dummies, then resize them away. */ + if ((so->fill - so->used) * 6 < so->mask) + Py_RETURN_NONE; + if (set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4) == -1) + return NULL; Py_RETURN_NONE; } From akuchling at users.sourceforge.net Sun Aug 7 15:27:56 2005 From: akuchling at users.sourceforge.net (akuchling@users.sourceforge.net) Date: Sun, 07 Aug 2005 06:27:56 -0700 Subject: [Python-checkins] python/nondist/peps pep-0008.txt,1.25,1.26 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6634 Modified Files: pep-0008.txt Log Message: Record BDFL opinion on exception-raising style Index: pep-0008.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0008.txt,v retrieving revision 1.25 retrieving revision 1.26 diff -u -d -r1.25 -r1.26 --- pep-0008.txt 6 Aug 2004 18:47:26 -0000 1.25 +++ pep-0008.txt 7 Aug 2005 13:27:54 -0000 1.26 @@ -546,6 +546,13 @@ class MessageError(Exception): """Base class for errors in the email package.""" + When raising an exception, use "raise ValueError('message')" + instead of the older form "raise ValueError, 'message'". The + paren-using form is preferred because when the exception + arguments are long or include string formatting, you don't need + to use line continuation characters thanks to the containing + parentheses. The older form will be removed in Python 3000. + - Use string methods instead of the string module unless backward-compatibility with versions earlier than Python 2.0 is important. String methods are always much faster and share the From loewis at users.sourceforge.net Sun Aug 7 16:17:45 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 07:17:45 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.3,1.4 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13976 Modified Files: pep-0347.txt Log Message: - add more rationale for using svn. - add discussion section. - change procedure to create a single repository, add conversion script. - require python-cvsroot to be preserved forever. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- pep-0347.txt 5 Aug 2005 07:26:32 -0000 1.3 +++ pep-0347.txt 7 Aug 2005 14:17:43 -0000 1.4 @@ -37,7 +37,12 @@ directories, while keeping the history of these files. - support for change sets (sets of correlated changes to multiple - files) through global revision numbers. + files) through global revision numbers. Change sets are + transactional. + +- atomic, fast tagging: a cvs tag might take many minutes; a + subversion tag (svn cp) will complete quickly, and atomically. + Likewise, branches are very efficient. - support for offline diffs, which is useful when creating patches. @@ -75,8 +80,7 @@ 3. 24 hours after the last commit, download the CVS repository. -4. Convert the CVS repository into two Subversion repositories, one - for Distutils and one for Python. +4. Convert the CVS repository into a Subversion repository. 5. Publish the repositories with write access for committers, and read-only anonymous access. @@ -93,16 +97,6 @@ initially, for the Python CVS as well, since various committers also have a username/password pair for the www SVN repository already. -Alternatives to password-based access include: - -- Subversion over SSH, using SSH key pairs. This would require - committers to be given accounts on the machine, which currently is - ruled out by the administration policy of svn.python.org. - -- Subversion over WebDAV, using SSL client certificates. This would - work, but would require to administrate a certificate authority. - - Downloading the CVS Repository ------------------------------ @@ -115,14 +109,23 @@ should be verified that the last commit, as recorded on the python-commits mailing list, is indeed included in the tarball. +After the conversion, the converted CVS tarball should be kept +forever on www.python.org/archive/python-cvsroot-.tar.bz2 + Converting the CVS Repository ----------------------------- The Python CVS repository contains two modules: distutils and python. -Keeping them together will produce quite long repository URLs, so it -is more convenient if the Python CVS and the Distutils CVS are -converted into two separate repositories. +The python module is further structured into dist and nondist, +where dist only contains src (the python code proper). nondist +contains various subdirectories. + +These should be reorganized in the Subversion repository to get +shorter URLs, following the /{trunk,tags,branches} +structure. A project will be created for each nondist directory, +plus for src (called python), plus distutils. Reorganizing the +repository is best done in the CVS tree, as shown below. Fsfs should be used as the repository format (requires Subversion 1.1). Fsfs has the advantage of being more backup-friendly, as it @@ -130,21 +133,39 @@ commands to be run. The conversion should be done using the cvs2svn utility, available -e.g. in the cvs2svn Debian package. The command for converting the -Python repository is :: - - cvs2svn -q --encoding=latin1 --force-branch=cnri-16-start \ - --force-branch=descr-branch --force-branch=release152p1-patches \ - --force-tag=r16b1 --fs-type=fsfs -s py.svn.new python/python - -The command to convert the distutils repository is :: +e.g. in the cvs2svn Debian package. As cvs2svn does not currently +support the project/trunk structure, each project needs to be +converted separately. To get each conversion result into a separate +directory in the target repository, svnadmin load must be used. +In summary, the conversion script is:: - cvs2svn -q --encoding=latin1 --fs-type=fsfs -s dist.svn.new python/distutils + #!/bin/sh + rm cvs2svn-* + rm -rf python py.new + tar xjf python-cvsroot.tar.bz2 + rm -rf python/CVSROOT + svnadmin create --fs-type fsfs py.new + mv python/python python/orig + mv python/orig/dist/src python/python + mv python/orig/nondist/* python + # nondist/nondist is empty + rmdir python/nondist + rm -rf python/orig + for a in python/* + do + b=`basename $a` + cvs2svn -q --dump-only --encoding=latin1 --force-branch=cnri-16-start \ + --force-branch=descr-branch --force-branch=release152p1-patches \ + --force-tag=r16b1 $a + svn mkdir -m"Conversion to SVN" file:///`pwd`/py.new/$b + svnadmin load -q --parent-dir $b py.new < cvs2svn-dump + rm cvs2svn-dump + done Sample results of this conversion are available at - | http://www.dcl.hpi.uni-potsdam.de/python/ - | http://www.dcl.hpi.uni-potsdam.de/distutils/ + http://www.dcl.hpi.uni-potsdam.de/pysvn/ + Publish the Repositories @@ -169,6 +190,45 @@ remains available. If desired, write access to the python and distutils modules can be disabled through a CVS commitinfo entry. +Discussion +---------- + +Several alternatives had been suggested to the procedure above. +The rejected alternatives are shortly discussed here: + +- create multiple repositories, one for python and one for + distutils. This would have allowed even shorter URLs, but + was rejected because a single repository supports moving code + across projects. + +- Nick Bastin has offered to host a Perforce repository, instead + of using subversion. Details of that offer are not clear yet. + +- Several people suggested to create the project/trunk structure + through standard cvs2svn, followed by renames. This would have + the disadvantage that old revisions use different path names + than recent revisions; the suggested approach through dump files + works without renames. + +- Several people also expressed concern about the administrative + overhead that hosting the repository on python.org would cause + to pydotorg admins. As a specific alternative, BerliOS has been + suggested. The pydotorg admins themselves haven\'t objected + to the additional workload; migrating the repository again if + they get overworked is an option. + +- People have expressed dislike of the basic auth (username/password) + authentication. Two alternatives have been suggested: + + * Subversion over SSH, using SSH key pairs for a single Unix + account, restricted to the execution of svnserve. The pydotorg + admins have ruled out creation of one account per committer; + whether a single account would be acceptable is not yet + decided. + + * Subversion over WebDAV, using SSL client certificates. This would + work, but would require to administrate a certificate authority. + Copyright --------- From loewis at users.sourceforge.net Sun Aug 7 16:23:33 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 07:23:33 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.4,1.5 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15068 Modified Files: pep-0347.txt Log Message: Fix revision tag. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- pep-0347.txt 7 Aug 2005 14:17:43 -0000 1.4 +++ pep-0347.txt 7 Aug 2005 14:23:31 -0000 1.5 @@ -1,6 +1,6 @@ PEP: 347 Title: Migrating the Python CVS to Subversion -Version: $Revision $ +Version: $Revision$ Last-Modified: $Date$ Author: Martin v. Löwis Discussions-To: From loewis at users.sourceforge.net Sun Aug 7 16:29:01 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 07:29:01 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.5,1.6 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15866 Modified Files: pep-0347.txt Log Message: Fix remaining reference to distutils svn; rename URL to /projects. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- pep-0347.txt 7 Aug 2005 14:23:31 -0000 1.5 +++ pep-0347.txt 7 Aug 2005 14:28:59 -0000 1.6 @@ -82,7 +82,7 @@ 4. Convert the CVS repository into a Subversion repository. -5. Publish the repositories with write access for committers, and +5. Publish the repository with write access for committers, and read-only anonymous access. 6. Disable CVS access on SF. @@ -168,15 +168,17 @@ -Publish the Repositories +Publish the Repository ------------------------ -The repositories should be published at https://svn.python.org/python -and https://svn.python.org/distutils. Read-write access should be -granted through basic authentication to all current SF committers; -read-only anonymous access should also be granted. As an option, -websvn (available e.g. from the Debian websvn package) could be -provided. +The repository should be published at https://svn.python.org/projects. +Read-write access should be granted through basic authentication to +all current SF committers; read-only anonymous access should also be +granted. + +As an option, websvn (available e.g. from the Debian websvn package) +could be provided. Unfortunately, in the test installation, websvn +breaks because it runs out of memory. The current SF project admins should get write access to the password file, in order to create or delete new users. From goodger at users.sourceforge.net Sun Aug 7 18:12:02 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sun, 07 Aug 2005 09:12:02 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.6,1.7 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31928 Modified Files: pep-0347.txt Log Message: added missing "us"; whitespace Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- pep-0347.txt 7 Aug 2005 14:28:59 -0000 1.6 +++ pep-0347.txt 7 Aug 2005 16:11:59 -0000 1.7 @@ -97,6 +97,7 @@ initially, for the Python CVS as well, since various committers also have a username/password pair for the www SVN repository already. + Downloading the CVS Repository ------------------------------ @@ -167,14 +168,13 @@ http://www.dcl.hpi.uni-potsdam.de/pysvn/ - Publish the Repository ------------------------ The repository should be published at https://svn.python.org/projects. Read-write access should be granted through basic authentication to all current SF committers; read-only anonymous access should also be -granted. +granted. As an option, websvn (available e.g. from the Debian websvn package) could be provided. Unfortunately, in the test installation, websvn @@ -192,6 +192,7 @@ remains available. If desired, write access to the python and distutils modules can be disabled through a CVS commitinfo entry. + Discussion ---------- @@ -229,7 +230,9 @@ decided. * Subversion over WebDAV, using SSL client certificates. This would - work, but would require to administrate a certificate authority. + work, but would require us to administrate a certificate + authority. + Copyright --------- From tp-update at paypal.com Sun Aug 7 01:09:59 2005 From: tp-update at paypal.com (tp-update@paypal.com) Date: Sat, 6 Aug 2005 19:09:59 -0400 (EDT) Subject: [Python-checkins] PayPal? Account Review Message-ID: <20050806230959.3F3826D7881@xserve.lmtinc.net> An HTML attachment was scrubbed... URL: http://mail.python.org/pipermail/python-checkins/attachments/20050806/fe670380/attachment.htm From loewis at users.sourceforge.net Sun Aug 7 22:50:39 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 13:50:39 -0700 Subject: [Python-checkins] python/dist/src/Lib/distutils msvccompiler.py, 1.64.2.3, 1.64.2.4 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/distutils In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20948/Lib/distutils Modified Files: Tag: release24-maint msvccompiler.py Log Message: Patch #827386: Support absolute source paths in msvccompiler.py. Index: msvccompiler.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/distutils/msvccompiler.py,v retrieving revision 1.64.2.3 retrieving revision 1.64.2.4 diff -u -d -r1.64.2.3 -r1.64.2.4 --- msvccompiler.py 11 Mar 2005 17:20:41 -0000 1.64.2.3 +++ msvccompiler.py 7 Aug 2005 20:50:37 -0000 1.64.2.4 @@ -270,6 +270,8 @@ obj_names = [] for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having From loewis at users.sourceforge.net Sun Aug 7 22:50:40 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 13:50:40 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.68, 1.1193.2.69 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20948/Misc Modified Files: Tag: release24-maint NEWS Log Message: Patch #827386: Support absolute source paths in msvccompiler.py. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.68 retrieving revision 1.1193.2.69 diff -u -d -r1.1193.2.68 -r1.1193.2.69 --- NEWS 2 Aug 2005 10:30:08 -0000 1.1193.2.68 +++ NEWS 7 Aug 2005 20:50:37 -0000 1.1193.2.69 @@ -53,6 +53,8 @@ Library ------- +- Patch #827386: Support absolute source paths in msvccompiler.py. + - Fix a problem in Tkinter introduced by SF patch #869468: delete bogus __hasattr__ and __delattr__ methods on class Tk that were breaking Tkdnd. From loewis at users.sourceforge.net Sun Aug 7 22:51:07 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 13:51:07 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1329,1.1330 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21005/Misc Modified Files: NEWS Log Message: Patch #827386: Support absolute source paths in msvccompiler.py. Backported to 2.4. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1329 retrieving revision 1.1330 diff -u -d -r1.1329 -r1.1330 --- NEWS 3 Aug 2005 07:30:10 -0000 1.1329 +++ NEWS 7 Aug 2005 20:51:04 -0000 1.1330 @@ -178,6 +178,8 @@ Library ------- +- Patch #827386: Support absolute source paths in msvccompiler.py. + - Patch #1105730: Apply the new implementation of commonprefix in posixpath to ntpath, macpath, os2emxpath and riscospath. From loewis at users.sourceforge.net Sun Aug 7 22:51:07 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 13:51:07 -0700 Subject: [Python-checkins] python/dist/src/Lib/distutils msvccompiler.py, 1.68, 1.69 Message-ID: Update of /cvsroot/python/python/dist/src/Lib/distutils In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21005/Lib/distutils Modified Files: msvccompiler.py Log Message: Patch #827386: Support absolute source paths in msvccompiler.py. Backported to 2.4. Index: msvccompiler.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/distutils/msvccompiler.py,v retrieving revision 1.68 retrieving revision 1.69 diff -u -d -r1.68 -r1.69 --- msvccompiler.py 12 Mar 2005 19:05:58 -0000 1.68 +++ msvccompiler.py 7 Aug 2005 20:51:04 -0000 1.69 @@ -269,6 +269,8 @@ obj_names = [] for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having From pje at users.sourceforge.net Sun Aug 7 22:54:13 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 07 Aug 2005 13:54:13 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.txt, 1.3, 1.4 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21568 Modified Files: pkg_resources.txt Log Message: Document resource and metadata access APIs. Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- pkg_resources.txt 7 Aug 2005 05:52:37 -0000 1.3 +++ pkg_resources.txt 7 Aug 2005 20:54:10 -0000 1.4 @@ -46,7 +46,11 @@ API Reference ------------- -XXX Namespace stuff, + +Namespace Package Support +========================= + +XXX ``WorkingSet`` Objects @@ -96,30 +100,64 @@ import pkg_resources my_data = pkg_resources.resource_string(__name__, "foo.dat") -Thus, you can use the APIs below without needing a ``ResourceManager`` -instance; just import and use them. +Thus, you can use the APIs below without needing an explicit +``ResourceManager`` instance; just import and use them as needed. Basic Resource Access --------------------- -XXX explain resource paths, layout, etc. here +In the following methods, the `package_or_requirement` argument may be either +a Python package/module name (e.g. ``foo.bar``) or a ``Requirement`` instance. +If it is a package or module name, the named module or package must be +importable (i.e., be in a distribution or directory on ``sys.path``), and the +`resource_name` argument is interpreted relative to the named package. (Note +that if a module name is used, then the resource name is relative to the +package immediately containing the named module.) + +If it is a ``Requirement``, then the requirement is automatically resolved +(searching the current ``Environment`` if necessary) and a matching +distribution is added to the ``WorkingSet`` and ``sys.path`` if one was not +already present. (Unless the ``Requirement`` can't be satisfied, in which +case an exception is raised.) The `resource_name` argument is then interpreted +relative to the root of the identified distribution; i.e. its first path +segment will be treated as a peer of the top-level modules or packages in the +distribution. + +Note that resource names must be ``/``-separated paths and cannot be absolute +(i.e. no leading ``/``) or contain relative names like ``..``. Do *not* use +``os.path`` routines to manipulate resource paths, as they are *not* filesystem +paths. ``resource_exists(package_or_requirement, resource_name)`` - Does the named resource exist? + Does the named resource exist? Return ``True`` or ``False`` accordingly. ``resource_stream(package_or_requirement, resource_name)`` - Return a readable file-like object for specified resource + Return a readable file-like object for the specified resource; it may be + an actual file, a ``StringIO``, or some similar object. The stream is + in "binary mode", in the sense that whatever bytes are in the resource + will be read as-is. ``resource_string(package_or_requirement, resource_name)`` - Return specified resource as a string + Return the specified resource as a string. The resource is read in + binary fashion, such that the returned string contains exactly the bytes + that are stored in the resource. ``resource_isdir(package_or_requirement, resource_name)`` - Is the named resource an existing directory? + Is the named resource a directory? Return ``True`` or ``False`` + accordingly. ``resource_listdir(package_or_requirement, resource_name)`` - List the contents of the named resource directory - + List the contents of the named resource directory, just like ``os.listdir`` + except that it works even if the resource is in a zipfile. + +Note that only ``resource_exists()`` and ``resource_isdir()`` are insensitive +as to the resource type. You cannot use ``resource_listdir()`` on a file +resource, and you can't use ``resource_string()`` or ``resource_stream()`` on +directory resources. Using an inappropriate method for the resource type may +result in an exception or undefined behavior, depending on the platform and +distribution format involved. + Resource Extraction ------------------- @@ -141,19 +179,19 @@ ``set_extraction_path(path)`` Set the base path where resources will be extracted to, if needed. - + If you do not call this routine before any extractions take place, the path defaults to the return value of ``get_default_cache()``. (Which is based on the ``PYTHON_EGG_CACHE`` environment variable, with various platform-specific fallbacks. See that routine's documentation for more details.) - + Resources are extracted to subdirectories of this path based upon information given by the ``IResourceProvider``. You may set this to a temporary directory, but then you must call ``cleanup_resources()`` to delete the extracted files when done. There is no guarantee that ``cleanup_resources()`` will be able to remove all extracted files. - + (Note: you may not change the extraction path for a given resource manager once resources have been extracted, unless you first call ``cleanup_resources()``.) @@ -182,13 +220,13 @@ ``get_cache_path(archive_name, names=())`` Return absolute location in cache for `archive_name` and `names` - + The parent directory of the resulting path will be created if it does not already exist. `archive_name` should be the base filename of the enclosing egg (which may not be the name of the enclosing zipfile!), including its ".egg" extension. `names`, if provided, should be a sequence of path name parts "under" the egg's extraction location. - + This method should only be called by resource providers that need to obtain an extraction location, and only for names they intend to extract, as it tracks the generated names for possible cleanup later. @@ -198,12 +236,71 @@ Resource providers should call this method ONLY after successfully extracting a compressed resource. They must NOT call it on resources that are already in the filesystem. - + `tempname` is the current (temporary) name of the file, and `filename` is the name it will be renamed to by the caller after this routine returns. +Metadata API +============ + +The metadata API is used to access metadata resources bundled in a pluggable +distribution. Metadata resources are virtual files or directories containing +information about the distribution, such as might be used by an extensible +application or framework to connect "plugins". Like other kinds of resources, +metadata resource names are ``/``-separated and should not contain ``..`` or +begin with a ``/``. You should not use ``os.path`` routines to manipulate +resource paths. + +The metadata API is provided by objects implementing the ``IMetadataProvider`` +interface. ``Distribution`` objects created with a ``metadata`` setting +implement this interface, as do objects returned by the ``get_provider()`` +function: + +``get_provider(package_or_requirement)`` + If a package name is supplied, return an ``IMetadataProvider`` for the + package. If a ``Requirement`` is supplied, resolve it by returning a + ``Distribution`` from the current working set (searching the current + ``Environment`` if necessary and adding the newly found ``Distribution`` + to the working set). If the named package can't be imported, or the + ``Requirement`` can't be satisfied, an exception is raised. + + Note also that if you supply a package name, and the package is not part + of a pluggable distribution (i.e., it has no metadata), then you will still + get an ``IMetadataProvider`` object, but it will just be an empty one that + answers ``False`` when asked if any given metadata resource file or + directory exists. + +The methods provided by ``IMetadataProvider`` (and ``Distribution`` objects +with a ``metadata`` attribute set) are: + +``has_metadata(name)`` + Does the named metadata resource exist? + +``metadata_isdir(name)`` + Is the named metadata resource a directory? + +``metadata_listdir(name)`` + List of metadata names in the directory (like ``os.listdir()``) + +``get_metadata(name)`` + Return the named metadata resource as a string. The data is read in binary + mode; i.e., the exact bytes of the resource file are returned. + +``get_metadata_lines(name)`` + Yield named metadata resource as list of non-blank non-comment lines. This + is short for calling ``yield_lines(provider.get_metadata(name))``. See the + section on `yield_lines()`_ below for more information on the syntax it + recognizes. + +``run_script(script_name, namespace)`` + Execute the named script in the supplied namespace dictionary. Raises + ``ResolutionError`` if there is no script by that name in the ``scripts`` + metadata directory. `namespace` should be a Python dictionary, usually + a module dictionary if the script is being run as a module. + + Exceptions ========== @@ -275,6 +372,8 @@ candidates, and therefore are not as new as a version string that does not contain them. +.. _yield_lines(): + ``yield_lines(strs)`` Yield non-empty/non-comment lines from a string/unicode or a possibly- nested sequence thereof. If `strs` is an instance of ``basestring``, it @@ -336,7 +435,7 @@ ``get_platform()`` Return this platform's identifier string. For Windows, the return value - is ``"win32"``, and for Mac OS X it is a string of the form + is ``"win32"``, and for Mac OS X it is a string of the form ``"macosx-10.4-ppc"``. All other platforms return the same uname-based string that the ``distutils.util.get_platform()`` function returns. @@ -375,5 +474,5 @@ (notably Cygwin and Mac OS X) the ``normcase`` function does not accurately reflect the platform's case-sensitivity, so there is always the possibility of two apparently-different paths being equal on such platforms. - + From loewis at users.sourceforge.net Sun Aug 7 23:09:01 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 14:09:01 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.69, 1.1193.2.70 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24031/Misc Modified Files: Tag: release24-maint NEWS Log Message: Patch #1239112: Correct LINKCC C++ test. Fixes #1189330. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.69 retrieving revision 1.1193.2.70 diff -u -d -r1.1193.2.69 -r1.1193.2.70 --- NEWS 7 Aug 2005 20:50:37 -0000 1.1193.2.69 +++ NEWS 7 Aug 2005 21:08:54 -0000 1.1193.2.70 @@ -93,6 +93,9 @@ Build ----- +- Bug #1189330: configure did not correctly determine the necessary + value of LINKCC if python was built with GCC 4.0. + - Upgrade Windows build to zlib 1.2.3 which eliminates a potential security vulnerability in zlib 1.2.1 and 1.2.2. From loewis at users.sourceforge.net Sun Aug 7 23:09:27 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 14:09:27 -0700 Subject: [Python-checkins] python/dist/src configure, 1.462.2.8, 1.462.2.9 configure.in, 1.475.2.8, 1.475.2.9 Message-ID: Update of /cvsroot/python/python/dist/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24031 Modified Files: Tag: release24-maint configure configure.in Log Message: Patch #1239112: Correct LINKCC C++ test. Fixes #1189330. Index: configure =================================================================== RCS file: /cvsroot/python/python/dist/src/configure,v retrieving revision 1.462.2.8 retrieving revision 1.462.2.9 diff -u -d -r1.462.2.8 -r1.462.2.9 --- configure 28 Mar 2005 23:23:02 -0000 1.462.2.8 +++ configure 7 Aug 2005 21:08:42 -0000 1.462.2.9 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 1.475.2.7 . +# From configure.in Revision: 1.475.2.8 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for python 2.4. # @@ -3265,9 +3265,11 @@ if test -z "$CXX"; then LINKCC="\$(PURIFY) \$(CC)" else - echo 'void foo();int main(){foo();}void foo(){}' > conftest.$ac_ext - $CXX -c conftest.$ac_ext 2>&5 - if $CC -o conftest$ac_exeext conftest.$ac_objext 2>&5 \ + echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc + $CXX -c conftest_a.cc # 2>&5 + echo 'void foo(){}' > conftest_b.$ac_ext + $CC -c conftest_b.$ac_ext # 2>&5 + if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ && test -s conftest$ac_exeext && ./conftest$ac_exeext then LINKCC="\$(PURIFY) \$(CC)" Index: configure.in =================================================================== RCS file: /cvsroot/python/python/dist/src/configure.in,v retrieving revision 1.475.2.8 retrieving revision 1.475.2.9 diff -u -d -r1.475.2.8 -r1.475.2.9 --- configure.in 28 Mar 2005 23:23:34 -0000 1.475.2.8 +++ configure.in 7 Aug 2005 21:08:53 -0000 1.475.2.9 @@ -471,9 +471,11 @@ if test -z "$CXX"; then LINKCC="\$(PURIFY) \$(CC)" else - echo 'void foo();int main(){foo();}void foo(){}' > conftest.$ac_ext - $CXX -c conftest.$ac_ext 2>&5 - if $CC -o conftest$ac_exeext conftest.$ac_objext 2>&5 \ + echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc + $CXX -c conftest_a.cc # 2>&5 + echo 'void foo(){}' > conftest_b.$ac_ext + $CC -c conftest_b.$ac_ext # 2>&5 + if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ && test -s conftest$ac_exeext && ./conftest$ac_exeext then LINKCC="\$(PURIFY) \$(CC)" From loewis at users.sourceforge.net Sun Aug 7 23:09:33 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 14:09:33 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1330,1.1331 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24327/Misc Modified Files: NEWS Log Message: Patch #1239112: Correct LINKCC C++ test. Fixes #1189330. Backported to 2.4. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1330 retrieving revision 1.1331 diff -u -d -r1.1330 -r1.1331 --- NEWS 7 Aug 2005 20:51:04 -0000 1.1330 +++ NEWS 7 Aug 2005 21:09:30 -0000 1.1331 @@ -391,6 +391,9 @@ Build ----- +- Bug #1189330: configure did not correctly determine the necessary + value of LINKCC if python was built with GCC 4.0. + - Upgrade Windows build to zlib 1.2.3 which eliminates a potential security vulnerability in zlib 1.2.1 and 1.2.2. From loewis at users.sourceforge.net Sun Aug 7 23:09:33 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 07 Aug 2005 14:09:33 -0700 Subject: [Python-checkins] python/dist/src configure, 1.472, 1.473 configure.in, 1.485, 1.486 Message-ID: Update of /cvsroot/python/python/dist/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24327 Modified Files: configure configure.in Log Message: Patch #1239112: Correct LINKCC C++ test. Fixes #1189330. Backported to 2.4. Index: configure =================================================================== RCS file: /cvsroot/python/python/dist/src/configure,v retrieving revision 1.472 retrieving revision 1.473 diff -u -d -r1.472 -r1.473 --- configure 2 Jun 2005 13:08:55 -0000 1.472 +++ configure 7 Aug 2005 21:09:30 -0000 1.473 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 1.484 . +# From configure.in Revision: 1.485 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for python 2.5. # @@ -3265,9 +3265,11 @@ if test -z "$CXX"; then LINKCC="\$(PURIFY) \$(CC)" else - echo 'void foo();int main(){foo();}void foo(){}' > conftest.$ac_ext - $CXX -c conftest.$ac_ext 2>&5 - if $CC -o conftest$ac_exeext conftest.$ac_objext 2>&5 \ + echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc + $CXX -c conftest_a.cc # 2>&5 + echo 'void foo(){}' > conftest_b.$ac_ext + $CC -c conftest_b.$ac_ext # 2>&5 + if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ && test -s conftest$ac_exeext && ./conftest$ac_exeext then LINKCC="\$(PURIFY) \$(CC)" Index: configure.in =================================================================== RCS file: /cvsroot/python/python/dist/src/configure.in,v retrieving revision 1.485 retrieving revision 1.486 diff -u -d -r1.485 -r1.486 --- configure.in 2 Jun 2005 13:09:24 -0000 1.485 +++ configure.in 7 Aug 2005 21:09:30 -0000 1.486 @@ -471,9 +471,11 @@ if test -z "$CXX"; then LINKCC="\$(PURIFY) \$(CC)" else - echo 'void foo();int main(){foo();}void foo(){}' > conftest.$ac_ext - $CXX -c conftest.$ac_ext 2>&5 - if $CC -o conftest$ac_exeext conftest.$ac_objext 2>&5 \ + echo 'extern "C" void foo();int main(){foo();}' > conftest_a.cc + $CXX -c conftest_a.cc # 2>&5 + echo 'void foo(){}' > conftest_b.$ac_ext + $CC -c conftest_b.$ac_ext # 2>&5 + if $CC -o conftest$ac_exeext conftest_a.$ac_objext conftest_b.$ac_objext 2>&5 \ && test -s conftest$ac_exeext && ./conftest$ac_exeext then LINKCC="\$(PURIFY) \$(CC)" From bwarsaw at users.sourceforge.net Mon Aug 8 06:03:43 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Sun, 07 Aug 2005 21:03:43 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.7,1.8 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21787 Modified Files: pep-0347.txt Log Message: Fixed some minor typos. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- pep-0347.txt 7 Aug 2005 16:11:59 -0000 1.7 +++ pep-0347.txt 8 Aug 2005 04:03:40 -0000 1.8 @@ -15,21 +15,21 @@ ======== The Python source code is currently managed in a CVS repository on -sourceforge.net. This PEP proposes to move it to a subversion +sourceforge.net. This PEP proposes to move it to a Subversion repository on svn.python.org. Rationale ========= -This change has two aspects: moving from CVS to subversion, and moving +This change has two aspects: moving from CVS to Subversion, and moving from SourceForge to python.org. For each, a rationale will be given. Moving to Subversion -------------------- -CVS has a number of limitations that have been elimintation by +CVS has a number of limitations that have been eliminated by Subversion. For the development of Python, the most notable improvements are: @@ -41,7 +41,7 @@ transactional. - atomic, fast tagging: a cvs tag might take many minutes; a - subversion tag (svn cp) will complete quickly, and atomically. + Subversion tag (svn cp) will complete quickly, and atomically. Likewise, branches are very efficient. - support for offline diffs, which is useful when creating patches. @@ -128,10 +128,10 @@ plus for src (called python), plus distutils. Reorganizing the repository is best done in the CVS tree, as shown below. -Fsfs should be used as the repository format (requires Subversion -1.1). Fsfs has the advantage of being more backup-friendly, as it -allows incremental repository backups, without requiring any dump -commands to be run. +The fsfs backend should be used as the repository format (which +requires Subversion 1.1). The fsfs has the advantage of being more +backup-friendly, as it allows incremental repository backups, without +requiring any dump commands to be run. The conversion should be done using the cvs2svn utility, available e.g. in the cvs2svn Debian package. As cvs2svn does not currently @@ -205,7 +205,7 @@ across projects. - Nick Bastin has offered to host a Perforce repository, instead - of using subversion. Details of that offer are not clear yet. + of using Subversion. Details of that offer are not clear yet. - Several people suggested to create the project/trunk structure through standard cvs2svn, followed by renames. This would have @@ -230,8 +230,7 @@ decided. * Subversion over WebDAV, using SSL client certificates. This would - work, but would require us to administrate a certificate - authority. + work, but would require us to administer a certificate authority. Copyright From loewis at users.sourceforge.net Mon Aug 8 09:18:20 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Mon, 08 Aug 2005 00:18:20 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.8,1.9 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14385 Modified Files: pep-0347.txt Log Message: Change authentication method to svn+ssh. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- pep-0347.txt 8 Aug 2005 04:03:40 -0000 1.8 +++ pep-0347.txt 8 Aug 2005 07:18:17 -0000 1.9 @@ -71,9 +71,8 @@ To move the Python CVS repository, the following steps need to be executed. The steps are elaborated upon in the following sections. -1. Assign passwords for all current committers for use on - svn.python.org. User names on SF and svn.python.org should be - identical, unless some committer requests a different user name. +1. Collect SSH keys for all current committers, along with usernames + to appear in commit messages. 2. At the beginning of the migration, announce that the repository on SourceForge closed. @@ -88,14 +87,28 @@ 6. Disable CVS access on SF. -Assign Passwords +Collect SSH keys ---------------- -Currently, access to Subversion on svn.python.org uses WebDAV over -https, using basic authentication. For this to work, authorized users -need to provide a password. This mechanism should be used, at least -initially, for the Python CVS as well, since various committers also -have a username/password pair for the www SVN repository already. +After some discussion, svn+ssh was selected as the best method +for write access to the repository. Developers can continue to +use their SSH keys, but they must be installed on python.org. + +In order to avoid having to create a new Unix user for each +developer, a single account should be used, with command= +attributes in the authorized_keys files. + +The lines in the authorized_keys file should read like this +(wrapped for better readability):: + + command="/usr/bin/svnserve --root=/svnroot -t + --tunnel-user=''",no-port-forwarding, + no-X11-forwarding,no-agent-forwarding,no-pty + ssh-dss + +As the usernames, the real names should be used instead of +the SF account names, so that people can be better identified +in log messages. Downloading the CVS Repository @@ -174,7 +187,8 @@ The repository should be published at https://svn.python.org/projects. Read-write access should be granted through basic authentication to all current SF committers; read-only anonymous access should also be -granted. +granted. Read-write access will go through +svn+ssh://pythondev at svn.python.org/projects. As an option, websvn (available e.g. from the Debian websvn package) could be provided. Unfortunately, in the test installation, websvn @@ -220,21 +234,21 @@ to the additional workload; migrating the repository again if they get overworked is an option. -- People have expressed dislike of the basic auth (username/password) - authentication. Two alternatives have been suggested: +- Different authentication strategies were discussed. As + alternatives to svn+ssh were suggested - * Subversion over SSH, using SSH key pairs for a single Unix - account, restricted to the execution of svnserve. The pydotorg - admins have ruled out creation of one account per committer; - whether a single account would be acceptable is not yet - decided. + * Subversion over WebDAV, using SSL and basic authentication, + with pydotorg-generated passwords mailed to the user. People + did not like that approach, since they would need to store + the password on disk (because they can't remember it); this + is a security risk. * Subversion over WebDAV, using SSL client certificates. This would work, but would require us to administer a certificate authority. Copyright ---------- +========= This document has been placed in the public domain. From goodger at users.sourceforge.net Tue Aug 9 03:17:08 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Mon, 08 Aug 2005 18:17:08 -0700 Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.9,1.10 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30980 Modified Files: pep-0347.txt Log Message: addition for readability Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- pep-0347.txt 8 Aug 2005 07:18:17 -0000 1.9 +++ pep-0347.txt 9 Aug 2005 01:17:05 -0000 1.10 @@ -142,9 +142,9 @@ repository is best done in the CVS tree, as shown below. The fsfs backend should be used as the repository format (which -requires Subversion 1.1). The fsfs has the advantage of being more -backup-friendly, as it allows incremental repository backups, without -requiring any dump commands to be run. +requires Subversion 1.1). The fsfs backend has the advantage of being +more backup-friendly, as it allows incremental repository backups, +without requiring any dump commands to be run. The conversion should be done using the cvs2svn utility, available e.g. in the cvs2svn Debian package. As cvs2svn does not currently From gvanrossum at users.sourceforge.net Tue Aug 9 04:46:15 2005 From: gvanrossum at users.sourceforge.net (gvanrossum@users.sourceforge.net) Date: Mon, 08 Aug 2005 19:46:15 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.5,1.6 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13066 Modified Files: pep-0348.txt Log Message: Fix docutils warning/2. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- pep-0348.txt 7 Aug 2005 04:14:04 -0000 1.5 +++ pep-0348.txt 9 Aug 2005 02:46:12 -0000 1.6 @@ -494,7 +494,7 @@ http://mail.python.org/pipermail/python-dev/2005-July/055019.html .. [#python-dev5] python-dev email (PEP, take 2: Exception Reorganization for -Python 3.0) + Python 3.0) http://mail.python.org/pipermail/python-dev/2005-August/055159.html .. [#python-dev6] python-dev email (Exception Reorg PEP checked in) From bcannon at users.sourceforge.net Tue Aug 9 06:26:30 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Mon, 08 Aug 2005 21:26:30 -0700 Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.6,1.7 Message-ID: Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27247 Modified Files: pep-0348.txt Log Message: Scale back proposal even more: VMError and AnyDeprecationWarning have been removed along with EOFError no longer inheriting from IOError. TerminalException was renamed TerminatingException. At this point the only changes to the exception hierarchy are the addition of BaseException and TerminatingException, and the change of inheritance for KeyboardInterrupt, SystemExit, and NotImplementedError. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- pep-0348.txt 9 Aug 2005 02:46:12 -0000 1.6 +++ pep-0348.txt 9 Aug 2005 04:26:28 -0000 1.7 @@ -7,7 +7,7 @@ Type: Standards Track Content-Type: text/x-rst Created: 28-Jul-2005 -Post-History: 03-Aug-2005 +Post-History: Abstract @@ -104,7 +104,7 @@ .. parsed-literal:: +-- BaseException (new; broader inheritance for subclasses) - +-- TerminalException (new; stricter inheritance for subclasses) + +-- TerminatingException (new; stricter inheritance for subclasses) +-- KeyboardInterrupt +-- SystemExit +-- Exception @@ -118,12 +118,13 @@ +-- AttributeError +-- EnvironmentError +-- IOError - +-- EOFError (broader inheritance) + +-- EOFError +-- OSError +-- ImportError +-- LookupError +-- IndexError +-- KeyError + +-- MemoryError +-- NameError +-- UnboundLocalError +-- NotImplementedError (stricter inheritance) @@ -137,15 +138,12 @@ +-- UnicodeEncodeError +-- UnicodeTranslateError +-- ValueError - +-- VMError (new; broader inheritance for subclasses) - +-- MemoryError - +-- SystemError +-- ReferenceError +-- StopIteration + +-- SystemError +-- Warning - +-- AnyDeprecationWarning (new; broader inheritance for subclasses) - +-- PendingDeprecationWarning - +-- DeprecationWarning + +-- PendingDeprecationWarning + +-- DeprecationWarning +-- FutureWarning +-- SyntaxWarning +-- RuntimeWarning @@ -171,30 +169,17 @@ The superclass that all exceptions must inherit from. -TerminalException -''''''''''''''''' +TerminatingException +'''''''''''''''''''' Superclass for exceptions that are meant to the termination of the interpreter. It does not inherit from Exception so that subclasses are not caught by bare ``except`` clauses. - -VMError -''''''' - -Superclass for exceptions that deal directly with the virtual -machine. - - -AnyDeprecationWarning -''''''''''''''''''''' - -A common superclass for all deprecation-related exceptions. While -having DeprecationWarning inherit from PendingDeprecationWarning was -suggested (a DeprecationWarning can be viewed as a -PendingDeprecationWarning that is happening now), the logic was not -agreed upon by a majority. But since the exceptions are related, -creating a common superclass is warranted. +Naming based on the idea that the interpreter is trying to terminate when these +exceptions are raised. An earlier proposal suggested "TerminalException" but +avoidance of any confusion with an actual terminal along with "terminal" being +more fatalistic than "terminating" led to the current name choice. Removed Exceptions @@ -221,15 +206,6 @@ Exception. -EOFError -'''''''' - -Subclasses IOError. - -Since an EOF comes from I/O it only makes sense that it be considered -an I/O error. - - Required Superclass for ``raise`` ================================= @@ -446,6 +422,30 @@ causing existing Python programmers extra pain, the renamings were removed. +Have EOFError Subclass IOError +------------------------------ + +The original thought was that sine EOFError deals directly with I/O, it should +subclass IOError. But since EOFError is used more as a signal that an event +has occurred (the exhaustion of an I/O port), it should not subclass such a +specific error exception. + + +Have MemoryError and SystemError Have a Common Superclass +--------------------------------------------------------- + +Both classes deal with the interpreter, so why not have them have a common +superclass? Because one of them means that the interpreter is in a state that +it should not recover from while the other does not. + + +Common Superclass for PendingDeprecationWarning and DeprecationWarning +---------------------------------------------------------------------- + +Grouping the deprecation warning exceptions together makes intuitive sense. +But this sensical idea does not extend well when one considers how rarely +either warning is used, let along at the same time. + Acknowledgements ================ From loewis at users.sourceforge.net Tue Aug 9 17:01:04 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:01:04 -0700 Subject: [Python-checkins] python/dist/src/Modules posixmodule.c, 2.337, 2.338 Message-ID: Update of /cvsroot/python/python/dist/src/Modules In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2103/Modules Modified Files: posixmodule.c Log Message: Patch #1180695: Implement nanosecond stat resolution on FreeBSD, add st_gen, st_birthtime. Index: posixmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v retrieving revision 2.337 retrieving revision 2.338 diff -u -d -r2.337 -r2.338 --- posixmodule.c 5 Jul 2005 15:21:58 -0000 2.337 +++ posixmodule.c 9 Aug 2005 15:00:59 -0000 2.338 @@ -706,6 +706,12 @@ #ifdef HAVE_STRUCT_STAT_ST_FLAGS {"st_flags", "user defined flags for file"}, #endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + {"st_gen", "generation number"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + {"st_birthtime", "time of creation"}, +#endif {0} }; @@ -733,6 +739,18 @@ #define ST_FLAGS_IDX ST_RDEV_IDX #endif +#ifdef HAVE_STRUCT_STAT_ST_GEN +#define ST_GEN_IDX (ST_RDEV_IDX+1) +#else +#define ST_GEN_IDX ST_RDEV_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME +#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) +#else +#define ST_BIRTHTIME_IDX ST_GEN_IDX +#endif + static PyStructSequence_Desc stat_result_desc = { "stat_result", /* name */ stat_result__doc__, /* doc */ @@ -878,8 +896,14 @@ mnsec = st.st_mtim.tv_nsec; cnsec = st.st_ctim.tv_nsec; #else +#ifdef HAVE_STAT_TV_NSEC2 + ansec = st.st_atimespec.tv_nsec; + mnsec = st.st_mtimespec.tv_nsec; + cnsec = st.st_ctimespec.tv_nsec; +#else ansec = mnsec = cnsec = 0; #endif +#endif fill_time(v, 7, st.st_atime, ansec); fill_time(v, 8, st.st_mtime, mnsec); fill_time(v, 9, st.st_ctime, cnsec); @@ -896,6 +920,29 @@ PyStructSequence_SET_ITEM(v, ST_RDEV_IDX, PyInt_FromLong((long)st.st_rdev)); #endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + PyStructSequence_SET_ITEM(v, ST_GEN_IDX, + PyInt_FromLong((long)st.st_gen)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + { + PyObject *val; + unsigned long bsec,bnsec; + bsec = (long)st.st_birthtime; +#ifdef HAVE_STAT_TV_NSEC2 + bnsec = st.st_birthtimespec.tv_nsec; +#else + bnsec = 0; +#endif + if (_stat_float_times) { + val = PyFloat_FromDouble(bsec + 1e-9*bnsec); + } else { + val = PyInt_FromLong((long)bsec); + } + PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, + val); + } +#endif #ifdef HAVE_STRUCT_STAT_ST_FLAGS PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX, PyInt_FromLong((long)st.st_flags)); From loewis at users.sourceforge.net Tue Aug 9 17:01:28 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:01:28 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libos.tex,1.164,1.165 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2103/Doc/lib Modified Files: libos.tex Log Message: Patch #1180695: Implement nanosecond stat resolution on FreeBSD, add st_gen, st_birthtime. Index: libos.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libos.tex,v retrieving revision 1.164 retrieving revision 1.165 diff -u -d -r1.164 -r1.165 --- libos.tex 18 Jul 2005 08:16:33 -0000 1.164 +++ libos.tex 9 Aug 2005 15:00:50 -0000 1.165 @@ -996,6 +996,12 @@ \member{st_rdev} (type of device if an inode device). \member{st_flags} (user defined flags for file). +On other Unix systems (such as FreeBSD), the following attributes +may be available (but may be only filled out of root tries to +use them: +\member{st_gen} (file generation number), +\member{st_birthtime} (time of file creation). + On Mac OS systems, the following attributes may also be available: \member{st_rsize}, \member{st_creator}, @@ -1037,6 +1043,7 @@ \versionchanged [Added access to values as attributes of the returned object]{2.2} +\versionchanged[Added st_gen, st_birthtime]{2.5} \end{funcdesc} \begin{funcdesc}{stat_float_times}{\optional{newvalue}} From loewis at users.sourceforge.net Tue Aug 9 17:01:29 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:01:29 -0700 Subject: [Python-checkins] python/dist/src configure, 1.473, 1.474 configure.in, 1.486, 1.487 pyconfig.h.in, 1.107, 1.108 Message-ID: Update of /cvsroot/python/python/dist/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2103 Modified Files: configure configure.in pyconfig.h.in Log Message: Patch #1180695: Implement nanosecond stat resolution on FreeBSD, add st_gen, st_birthtime. Index: configure =================================================================== RCS file: /cvsroot/python/python/dist/src/configure,v retrieving revision 1.473 retrieving revision 1.474 diff -u -d -r1.473 -r1.474 --- configure 7 Aug 2005 21:09:30 -0000 1.473 +++ configure 9 Aug 2005 14:59:51 -0000 1.474 @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 1.485 . +# From configure.in Revision: 1.486 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59 for python 2.5. # @@ -16526,6 +16526,226 @@ fi +echo "$as_me:$LINENO: checking for struct stat.st_gen" >&5 +echo $ECHO_N "checking for struct stat.st_gen... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_gen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_gen) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_gen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (sizeof ac_aggr.st_gen) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_gen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_stat_st_gen=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_gen" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_gen" >&6 +if test $ac_cv_member_struct_stat_st_gen = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_GEN 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for struct stat.st_birthtime" >&5 +echo $ECHO_N "checking for struct stat.st_birthtime... $ECHO_C" >&6 +if test "${ac_cv_member_struct_stat_st_birthtime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (ac_aggr.st_birthtime) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_birthtime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static struct stat ac_aggr; +if (sizeof ac_aggr.st_birthtime) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_stat_st_birthtime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_stat_st_birthtime=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_birthtime" >&5 +echo "${ECHO_T}$ac_cv_member_struct_stat_st_birthtime" >&6 +if test $ac_cv_member_struct_stat_st_birthtime = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +_ACEOF + + +fi + echo "$as_me:$LINENO: checking for struct stat.st_blocks" >&5 echo $ECHO_N "checking for struct stat.st_blocks... $ECHO_C" >&6 if test "${ac_cv_member_struct_stat_st_blocks+set}" = set; then @@ -20266,6 +20486,73 @@ fi +# Look for BSD style subsecond timestamps in struct stat +echo "$as_me:$LINENO: checking for tv_nsec2 in struct stat" >&5 +echo $ECHO_N "checking for tv_nsec2 in struct stat... $ECHO_C" >&6 +if test "${ac_cv_stat_tv_nsec2+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + +struct stat st; +st.st_mtimespec.tv_nsec = 1; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_stat_tv_nsec2=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_stat_tv_nsec2=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +echo "$as_me:$LINENO: result: $ac_cv_stat_tv_nsec2" >&5 +echo "${ECHO_T}$ac_cv_stat_tv_nsec2" >&6 +if test "$ac_cv_stat_tv_nsec2" = yes +then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STAT_TV_NSEC2 1 +_ACEOF + +fi + # On HP/UX 11.0, mvwdelch is a block with a return statement echo "$as_me:$LINENO: checking whether mvwdelch is an expression" >&5 echo $ECHO_N "checking whether mvwdelch is an expression... $ECHO_C" >&6 Index: configure.in =================================================================== RCS file: /cvsroot/python/python/dist/src/configure.in,v retrieving revision 1.486 retrieving revision 1.487 diff -u -d -r1.486 -r1.487 --- configure.in 7 Aug 2005 21:09:30 -0000 1.486 +++ configure.in 9 Aug 2005 15:00:36 -0000 1.487 @@ -2424,6 +2424,8 @@ AC_CHECK_MEMBERS([struct stat.st_rdev]) AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_CHECK_MEMBERS([struct stat.st_flags]) +AC_CHECK_MEMBERS([struct stat.st_gen]) +AC_CHECK_MEMBERS([struct stat.st_birthtime]) AC_STRUCT_ST_BLOCKS AC_MSG_CHECKING(for time.h that defines altzone) @@ -3042,6 +3044,23 @@ [Define if you have struct stat.st_mtim.tv_nsec]) fi +# Look for BSD style subsecond timestamps in struct stat +AC_MSG_CHECKING(for tv_nsec2 in struct stat) +AC_CACHE_VAL(ac_cv_stat_tv_nsec2, +AC_TRY_COMPILE([#include ], [ +struct stat st; +st.st_mtimespec.tv_nsec = 1; +], +ac_cv_stat_tv_nsec2=yes, +ac_cv_stat_tv_nsec2=no, +ac_cv_stat_tv_nsec2=no)) +AC_MSG_RESULT($ac_cv_stat_tv_nsec2) +if test "$ac_cv_stat_tv_nsec2" = yes +then + AC_DEFINE(HAVE_STAT_TV_NSEC2, 1, + [Define if you have struct stat.st_mtimensec]) +fi + # On HP/UX 11.0, mvwdelch is a block with a return statement AC_MSG_CHECKING(whether mvwdelch is an expression) AC_CACHE_VAL(ac_cv_mvwdelch_is_expression, Index: pyconfig.h.in =================================================================== RCS file: /cvsroot/python/python/dist/src/pyconfig.h.in,v retrieving revision 1.107 retrieving revision 1.108 diff -u -d -r1.107 -r1.108 --- pyconfig.h.in 2 Jun 2005 13:09:25 -0000 1.107 +++ pyconfig.h.in 9 Aug 2005 15:00:38 -0000 1.108 @@ -455,6 +455,9 @@ /* Define if you have struct stat.st_mtim.tv_nsec */ #undef HAVE_STAT_TV_NSEC +/* Define if you have struct stat.st_mtimensec */ +#undef HAVE_STAT_TV_NSEC2 + /* Define if your compiler supports variable length function prototypes (e.g. void fprintf(FILE *, char *, ...);) *and* */ #undef HAVE_STDARG_PROTOTYPES @@ -483,6 +486,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STROPTS_H +/* Define to 1 if `st_birthtime' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BIRTHTIME + /* Define to 1 if `st_blksize' is member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLKSIZE @@ -492,6 +498,9 @@ /* Define to 1 if `st_flags' is member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_FLAGS +/* Define to 1 if `st_gen' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_GEN + /* Define to 1 if `st_rdev' is member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_RDEV From loewis at users.sourceforge.net Tue Aug 9 17:01:31 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:01:31 -0700 Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1331,1.1332 Message-ID: Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2103/Misc Modified Files: NEWS Log Message: Patch #1180695: Implement nanosecond stat resolution on FreeBSD, add st_gen, st_birthtime. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1331 retrieving revision 1.1332 diff -u -d -r1.1331 -r1.1332 --- NEWS 7 Aug 2005 21:09:30 -0000 1.1331 +++ NEWS 9 Aug 2005 15:00:51 -0000 1.1332 @@ -121,6 +121,9 @@ Extension Modules ----------------- +- Patch #1180695: Add nanosecond stat resolution, and st_gen, + st_birthtime for FreeBSD. + - Patch #1231069: The fcntl.ioctl function now uses the 'I' code for the request code argument, which results in more C-like behaviour for large or negative values. From fdrake at users.sourceforge.net Tue Aug 9 17:24:08 2005 From: fdrake at users.sourceforge.net (fdrake@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:24:08 -0700 Subject: [Python-checkins] python/dist/src/Doc/lib libos.tex,1.165,1.166 Message-ID: Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8121/lib Modified Files: libos.tex Log Message: fix minor markup errors Index: libos.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libos.tex,v retrieving revision 1.165 retrieving revision 1.166 diff -u -d -r1.165 -r1.166 --- libos.tex 9 Aug 2005 15:00:50 -0000 1.165 +++ libos.tex 9 Aug 2005 15:24:05 -0000 1.166 @@ -796,7 +796,7 @@ directory. Availability: Macintosh, \UNIX, Windows. -\versionchanged[On Windows NT/2k/XP and Unix, if \var{path} is a Unicode +\versionchanged[On Windows NT/2k/XP and \UNIX, if \var{path} is a Unicode object, the result will be a list of Unicode objects.]{2.3} \end{funcdesc} @@ -989,14 +989,14 @@ reported if the system supports that. On Mac OS, the times are always floats. See \function{stat_float_times} for further discussion. ]{2.3} -On some Unix systems (such as Linux), the following attributes may +On some \UNIX{} systems (such as Linux), the following attributes may also be available: \member{st_blocks} (number of blocks allocated for file), \member{st_blksize} (filesystem blocksize), \member{st_rdev} (type of device if an inode device). \member{st_flags} (user defined flags for file). -On other Unix systems (such as FreeBSD), the following attributes +On other \UNIX{} systems (such as FreeBSD), the following attributes may be available (but may be only filled out of root tries to use them: \member{st_gen} (file generation number), From pje at users.sourceforge.net Tue Aug 9 17:50:41 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Tue, 09 Aug 2005 08:50:41 -0700 Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.62, 1.63 Message-ID: Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13834 Modified Files: pkg_resources.py Log Message: Fix a bug introduced by making split_sections() not lowercase section headings. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.62 retrieving revision 1.63 diff -u -d -r1.62 -r1.63 --- pkg_resources.py 7 Aug 2005 05:52:36 -0000 1.62 +++ pkg_resources.py 9 Aug 2005 15:50:39 -0000 1.63 @@ -1597,8 +1597,6 @@ - - class EntryPoint(object): """Object representing an importable location""" @@ -1812,9 +1810,9 @@ dm = self.__dep_map = {None: []} for name in 'requires.txt', 'depends.txt': for extra,reqs in split_sections(self._get_metadata(name)): - dm.setdefault(extra.lower(),[]).extend(parse_requirements(reqs)) + if extra: extra = extra.lower() + dm.setdefault(extra,[]).extend(parse_requirements(reqs)) return dm - _dep_map = property(_dep_map) def requires(self,extras=()): From pje at users.sourceforge.net Thu Aug 11 02:37:46 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Thu, 11 Aug 2005 02:37:46 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools package_index.py, 1.16, 1.17 Message-ID: <20050811003746.BE48C1E400A@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32307/setuptools Modified Files: package_index.py Log Message: Fix bugs reported by Ian Bicking, Walter Doerwald, and Vincenzo Di Massa. Index: package_index.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/package_index.py,v retrieving revision 1.16 retrieving revision 1.17 diff -u -d -r1.16 -r1.17 --- package_index.py 7 Aug 2005 01:03:35 -0000 1.16 +++ package_index.py 11 Aug 2005 00:37:37 -0000 1.17 @@ -7,6 +7,8 @@ EGG_FRAGMENT = re.compile('^egg=(\\w+(-\\w+)?)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) +# this is here to fix emacs' cruddy broken syntax highlighting: ' + URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() @@ -37,8 +39,6 @@ - - def distros_for_url(url, metadata=None): """Yield egg or source distribution objects that might be found at a URL""" @@ -371,10 +371,10 @@ def _download_to(self, url, filename): self.info("Downloading %s", url) - # Download the file fp, tfp = None, None try: + url = url.split('#', 1)[0] fp = self.open_url(url) if isinstance(fp, urllib2.HTTPError): raise DistutilsError( From pje at users.sourceforge.net Thu Aug 11 02:37:46 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Thu, 11 Aug 2005 02:37:46 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.63, 1.64 Message-ID: <20050811003746.C4ED61E400C@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32307 Modified Files: pkg_resources.py Log Message: Fix bugs reported by Ian Bicking, Walter Doerwald, and Vincenzo Di Massa. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.63 retrieving revision 1.64 diff -u -d -r1.63 -r1.64 --- pkg_resources.py 9 Aug 2005 15:50:39 -0000 1.63 +++ pkg_resources.py 11 Aug 2005 00:37:37 -0000 1.64 @@ -1794,8 +1794,8 @@ self._version = safe_version(line.split(':',1)[1].strip()) return self._version else: - raise AttributeError( - "Missing 'Version:' header in PKG-INFO", self + raise ValueError( + "Missing 'Version:' header and/or PKG-INFO file", self ) version = property(version) From pje at users.sourceforge.net Thu Aug 11 02:37:47 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Thu, 11 Aug 2005 02:37:47 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command build_py.py, 1.2, 1.3 easy_install.py, 1.20, 1.21 Message-ID: <20050811003747.07D801E400A@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32307/setuptools/command Modified Files: build_py.py easy_install.py Log Message: Fix bugs reported by Ian Bicking, Walter Doerwald, and Vincenzo Di Massa. Index: build_py.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/build_py.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- build_py.py 5 Apr 2004 20:21:53 -0000 1.2 +++ build_py.py 11 Aug 2005 00:37:37 -0000 1.3 @@ -39,7 +39,7 @@ def get_data_files(self): """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" data = [] - for package in self.packages: + for package in self.packages or (): # Locate package source directory src_dir = self.get_package_dir(package) Index: easy_install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/easy_install.py,v retrieving revision 1.20 retrieving revision 1.21 diff -u -d -r1.20 -r1.21 --- easy_install.py 7 Aug 2005 01:03:36 -0000 1.20 +++ easy_install.py 11 Aug 2005 00:37:37 -0000 1.21 @@ -269,7 +269,6 @@ "%r already exists in %s; can't do a checkout there" % (spec.key, self.build_directory) ) - @@ -281,7 +280,8 @@ - + + @@ -381,7 +381,7 @@ if dist not in requirement: return - if deps or self.always_copy: + if deps or self.always_copy: log.info("Processing dependencies for %s", requirement) else: return @@ -448,7 +448,7 @@ setup_base = dist_filename ensure_directory(dst); shutil.move(setup_base, dst) return dst - + def install_script(self, dist, script_name, script_text, dev_path=None): log.info("Installing %s script to %s", script_name,self.script_dir) target = os.path.join(self.script_dir, script_name) @@ -502,10 +502,11 @@ if os.path.isfile(dist_filename): unpack_archive(dist_filename, tmpdir, self.unpack_progress) elif os.path.isdir(dist_filename): - # note that setup_base==tmpdir here if this is a svn checkout setup_base = os.path.abspath(dist_filename) - if setup_base==tmpdir and self.build_directory and spec is not None: + if (setup_base.startswith(tmpdir) # something we downloaded + and self.build_directory and spec is not None + ): setup_base = self.maybe_move(spec, dist_filename, setup_base) # Find the setup.py file @@ -530,7 +531,6 @@ else: return self.build_and_install(setup_script, setup_base) - def egg_distribution(self, egg_path): if os.path.isdir(egg_path): metadata = PathMetadata(egg_path,os.path.join(egg_path,'EGG-INFO')) @@ -1091,7 +1091,7 @@ self.paths.pop() # skip it self.dirty = True # we cleaned up, so we're dirty now :) continue - seen[path] = 1 + seen[path] = 1 while self.paths and not self.paths[-1].strip(): self.paths.pop() From rhettinger at users.sourceforge.net Thu Aug 11 09:58:55 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Thu, 11 Aug 2005 09:58:55 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_set.py,1.19,1.20 Message-ID: <20050811075855.F0ED91E4003@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26888/Lib/test Modified Files: test_set.py Log Message: * Add short-circuit code for in-place operations with self (such as s|=s, s&=s, s-=s, or s^=s). Add related tests. * Improve names for several variables and functions. * Provide alternate table access functions (next, contains, add, and discard) that work with an entry argument instead of just a key. This improves set-vs-set operations because we already have a hash value for each key and can avoid unnecessary calls to PyObject_Hash(). Provides a 5% to 20% speed-up for quick hashing elements like strings and integers. Provides much more substantial improvements for slow hashing elements like tuples or objects defining a custom __hash__() function. * Have difference operations resize() when 1/5 of the elements are dummies. Formerly, it was 1/6. The new ratio triggers less frequently and only in cases that it can resize quicker and with greater benefit. The right answer is probably either 1/4, 1/5, or 1/6. Picked the middle value for an even trade-off between resize time and the space/time costs of dummy entries. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.19 retrieving revision 1.20 diff -u -d -r1.19 -r1.20 --- test_set.py 1 Aug 2005 21:39:28 -0000 1.19 +++ test_set.py 11 Aug 2005 07:58:45 -0000 1.20 @@ -370,6 +370,18 @@ else: self.assert_(c not in self.s) + def test_inplace_on_self(self): + t = self.s.copy() + t |= t + self.assertEqual(t, self.s) + t &= t + self.assertEqual(t, self.s) + t -= t + self.assertEqual(t, self.thetype()) + t = self.s.copy() + t ^= t + self.assertEqual(t, self.thetype()) + def test_weakref(self): s = self.thetype('gallahad') p = proxy(s) From rhettinger at users.sourceforge.net Thu Aug 11 09:58:56 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Thu, 11 Aug 2005 09:58:56 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.45,1.46 Message-ID: <20050811075856.1C4B31E4004@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26888/Objects Modified Files: setobject.c Log Message: * Add short-circuit code for in-place operations with self (such as s|=s, s&=s, s-=s, or s^=s). Add related tests. * Improve names for several variables and functions. * Provide alternate table access functions (next, contains, add, and discard) that work with an entry argument instead of just a key. This improves set-vs-set operations because we already have a hash value for each key and can avoid unnecessary calls to PyObject_Hash(). Provides a 5% to 20% speed-up for quick hashing elements like strings and integers. Provides much more substantial improvements for slow hashing elements like tuples or objects defining a custom __hash__() function. * Have difference operations resize() when 1/5 of the elements are dummies. Formerly, it was 1/6. The new ratio triggers less frequently and only in cases that it can resize quicker and with greater benefit. The right answer is probably either 1/4, 1/5, or 1/6. Picked the middle value for an even trade-off between resize time and the space/time costs of dummy entries. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.45 retrieving revision 1.46 diff -u -d -r1.45 -r1.46 --- setobject.c 7 Aug 2005 13:02:53 -0000 1.45 +++ setobject.c 11 Aug 2005 07:58:44 -0000 1.46 @@ -1,3 +1,4 @@ + /* set object implementation Written and maintained by Raymond D. Hettinger Derived from Lib/sets.py and Objects/dictobject.c. @@ -226,7 +227,6 @@ typedef setentry *(*lookupfunc)(PySetObject *, PyObject *, long); assert(so->lookup != NULL); - entry = so->lookup(so, key, hash); if (entry->key == NULL) { /* UNUSED */ @@ -336,18 +336,30 @@ return 0; } -/* CAUTION: set_add_internal() must guarantee that it won't resize the table */ +/* CAUTION: set_add_key/entry() must guarantee it won't resize the table */ + static int -set_add_internal(register PySetObject *so, PyObject *key) +set_add_entry(register PySetObject *so, setentry *entry) +{ + register int n_used; + + assert(so->fill <= so->mask); /* at least one empty slot */ + n_used = so->used; + Py_INCREF(entry->key); + set_insert_key(so, entry->key, entry->hash); + if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2)) + return 0; + return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); +} + +static int +set_add_key(register PySetObject *so, PyObject *key) { register long hash; register int n_used; - if (PyString_CheckExact(key)) { - hash = ((PyStringObject *)key)->ob_shash; - if (hash == -1) - hash = PyObject_Hash(key); - } else { + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; @@ -365,7 +377,23 @@ #define DISCARD_FOUND 1 static int -set_discard_internal(PySetObject *so, PyObject *key) +set_discard_entry(PySetObject *so, setentry *oldentry) +{ register setentry *entry; + PyObject *old_key; + + entry = (so->lookup)(so, oldentry->key, oldentry->hash); + if (entry->key == NULL || entry->key == dummy) + return DISCARD_NOTFOUND; + old_key = entry->key; + Py_INCREF(dummy); + entry->key = dummy; + so->used--; + Py_DECREF(old_key); + return DISCARD_FOUND; +} + +static int +set_discard_key(PySetObject *so, PyObject *key) { register long hash; register setentry *entry; @@ -457,39 +485,39 @@ * Iterate over a set table. Use like so: * * int pos; - * PyObject *key; + * setentry *entry; * pos = 0; # important! pos should not otherwise be changed by you - * while (set_next_internal(yourset, &pos, &key)) { - * Refer to borrowed reference in key. + * while (set_next(yourset, &pos, &entry)) { + * Refer to borrowed reference in entry->key. * } * - * CAUTION: In general, it isn't safe to use set_next_internal in a loop that + * CAUTION: In general, it isn't safe to use set_next in a loop that * mutates the table. */ static int -set_next_internal(PySetObject *so, int *pos, PyObject **key) +set_next(PySetObject *so, int *pos_ptr, setentry **entry_ptr) { register int i, mask; - register setentry *entry; + register setentry *table; assert (PyAnySet_Check(so)); - i = *pos; + i = *pos_ptr; if (i < 0) return 0; - entry = so->table; + table = so->table; mask = so->mask; - while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) + while (i <= mask && (table[i].key == NULL || table[i].key == dummy)) i++; - *pos = i+1; + *pos_ptr = i+1; if (i > mask) return 0; - if (key) - *key = entry[i].key; + if (table[i].key) + *entry_ptr = &table[i]; return 1; } static int -set_merge_internal(PySetObject *so, PyObject *otherset) +set_merge(PySetObject *so, PyObject *otherset) { PySetObject *other; register int i; @@ -525,7 +553,7 @@ } static int -set_contains_internal(PySetObject *so, PyObject *key) +set_contains_key(PySetObject *so, PyObject *key) { long hash; @@ -539,6 +567,15 @@ return key != NULL && key != dummy; } +static int +set_contains_entry(PySetObject *so, setentry *entry) +{ + PyObject *key; + + key = (so->lookup)(so, entry->key, entry->hash)->key; + return key != NULL && key != dummy; +} + /***** Set iterator type ***********************************************/ static PyTypeObject PySetIter_Type; /* Forward */ @@ -667,13 +704,13 @@ PyObject *key, *it; if (PyAnySet_Check(other)) - return set_merge_internal(so, other); + return set_merge(so, other); if (PyDict_Check(other)) { PyObject *key, *value; int pos = 0; while (PyDict_Next(other, &pos, &key, &value)) { - if (set_add_internal(so, key) == -1) + if (set_add_key(so, key) == -1) return -1; } return 0; @@ -684,7 +721,7 @@ return -1; while ((key = PyIter_Next(it)) != NULL) { - if (set_add_internal(so, key) == -1) { + if (set_add_key(so, key) == -1) { Py_DECREF(it); Py_DECREF(key); return -1; @@ -833,10 +870,10 @@ set_traverse(PySetObject *so, visitproc visit, void *arg) { int pos = 0; - PyObject *key; + setentry *entry; - while (set_next_internal(so, &pos, &key)) - Py_VISIT(key); + while (set_next(so, &pos, &entry)) + Py_VISIT(entry->key); return 0; } @@ -897,14 +934,14 @@ PyObject *tmpkey; int result; - result = set_contains_internal(so, key); + result = set_contains_key(so, key); if (result == -1 && PyAnySet_Check(key)) { PyErr_Clear(); tmpkey = make_new_set(&PyFrozenSet_Type, NULL); if (tmpkey == NULL) return -1; set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); - result = set_contains_internal(so, tmpkey); + result = set_contains_key(so, tmpkey); set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); } @@ -943,6 +980,15 @@ PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set."); static PyObject * +set_clear(PySetObject *so) +{ + set_clear_internal(so); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(clear_doc, "Remove all elements from this set."); + +static PyObject * set_union(PySetObject *so, PyObject *other) { PySetObject *result; @@ -991,6 +1037,11 @@ PySetObject *result; PyObject *key, *it, *tmp; + if ((PyObject *)so == other) { + Py_INCREF(other); + return other; + } + result = (PySetObject *)make_new_set(so->ob_type, NULL); if (result == NULL) return NULL; @@ -1001,11 +1052,12 @@ other = tmp; } - if (PyAnySet_Check(other)) { + if (PyAnySet_Check(other)) { int pos = 0; - while (set_next_internal((PySetObject *)other, &pos, &key)) { - if (set_contains_internal(so, key)) { - if (set_add_internal(result, key) == -1) { + setentry *entry; + while (set_next((PySetObject *)other, &pos, &entry)) { + if (set_contains_entry(so, entry)) { + if (set_add_entry(result, entry) == -1) { Py_DECREF(result); return NULL; } @@ -1021,8 +1073,8 @@ } while ((key = PyIter_Next(it)) != NULL) { - if (set_contains_internal(so, key)) { - if (set_add_internal(result, key) == -1) { + if (set_contains_key(so, key)) { + if (set_add_key(result, key) == -1) { Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); @@ -1087,32 +1139,48 @@ return (PyObject *)so; } -static PyObject * -set_difference_update(PySetObject *so, PyObject *other) +int +set_difference_update_internal(PySetObject *so, PyObject *other) { - PyObject *key, *it; + if ((PyObject *)so == other) + return set_clear_internal(so); - it = PyObject_GetIter(other); - if (it == NULL) - return NULL; + if (PyAnySet_Check(other)) { + setentry *entry; + int pos = 0; - while ((key = PyIter_Next(it)) != NULL) { - if (set_discard_internal(so, key) == -1) { - Py_DECREF(it); + while (set_next((PySetObject *)other, &pos, &entry)) + set_discard_entry(so, entry); + } else { + PyObject *key, *it; + it = PyObject_GetIter(other); + if (it == NULL) + return -1; + + while ((key = PyIter_Next(it)) != NULL) { + if (set_discard_key(so, key) == -1) { + Py_DECREF(it); + Py_DECREF(key); + return -1; + } Py_DECREF(key); - return NULL; } - Py_DECREF(key); + Py_DECREF(it); + if (PyErr_Occurred()) + return -1; } - Py_DECREF(it); - if (PyErr_Occurred()) - return NULL; - /* If more than 1/6 are dummies, then resize them away. */ - if ((so->fill - so->used) * 6 < so->mask) + /* If more than 1/5 are dummies, then resize them away. */ + if ((so->fill - so->used) * 5 < so->mask) + return 0; + return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); +} + +static PyObject * +set_difference_update(PySetObject *so, PyObject *other) +{ + if (set_difference_update_internal(so, other) != -1) Py_RETURN_NONE; - if (set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4) == -1) - return NULL; - Py_RETURN_NONE; + return NULL; } PyDoc_STRVAR(difference_update_doc, @@ -1121,18 +1189,16 @@ static PyObject * set_difference(PySetObject *so, PyObject *other) { - PyObject *tmp, *key, *result; + PyObject *result; + setentry *entry; int pos = 0; if (!PyAnySet_Check(other) && !PyDict_Check(other)) { result = set_copy(so); if (result == NULL) + return NULL; + if (set_difference_update_internal((PySetObject *)result, other) != -1) return result; - tmp = set_difference_update((PySetObject *)result, other); - if (tmp != NULL) { - Py_DECREF(tmp); - return result; - } Py_DECREF(result); return NULL; } @@ -1142,18 +1208,21 @@ return NULL; if (PyDict_Check(other)) { - while (set_next_internal(so, &pos, &key)) { - if (!PyDict_Contains(other, key)) { - if (set_add_internal((PySetObject *)result, key) == -1) + while (set_next(so, &pos, &entry)) { + setentry entrycopy; + entrycopy.hash = entry->hash; + entrycopy.key = entry->key; + if (!PyDict_Contains(other, entry->key)) { + if (set_add_entry((PySetObject *)result, &entrycopy) == -1) return NULL; } } return result; } - while (set_next_internal(so, &pos, &key)) { - if (!set_contains_internal((PySetObject *)other, key)) { - if (set_add_internal((PySetObject *)result, key) == -1) + while (set_next(so, &pos, &entry)) { + if (!set_contains_entry((PySetObject *)other, entry)) { + if (set_add_entry((PySetObject *)result, entry) == -1) return NULL; } } @@ -1197,16 +1266,20 @@ PySetObject *otherset; PyObject *key; int pos = 0; + setentry *entry; + + if ((PyObject *)so == other) + return set_clear(so); if (PyDict_Check(other)) { PyObject *value; int rv; while (PyDict_Next(other, &pos, &key, &value)) { - rv = set_discard_internal(so, key); + rv = set_discard_key(so, key); if (rv == -1) return NULL; if (rv == DISCARD_NOTFOUND) { - if (set_add_internal(so, key) == -1) + if (set_add_key(so, key) == -1) return NULL; } } @@ -1222,14 +1295,14 @@ return NULL; } - while (set_next_internal(otherset, &pos, &key)) { - int rv = set_discard_internal(so, key); + while (set_next(otherset, &pos, &entry)) { + int rv = set_discard_entry(so, entry); if (rv == -1) { Py_XDECREF(otherset); return NULL; } if (rv == DISCARD_NOTFOUND) { - if (set_add_internal(so, key) == -1) { + if (set_add_entry(so, entry) == -1) { Py_XDECREF(otherset); return NULL; } @@ -1312,7 +1385,7 @@ for (i=so->used ; i ; entry++, i--) { while (entry->key == NULL || entry->key==dummy) entry++; - if (!set_contains_internal((PySetObject *)other, entry->key)) + if (!set_contains_entry((PySetObject *)other, entry)) Py_RETURN_FALSE; } Py_RETURN_TRUE; @@ -1448,16 +1521,16 @@ static int set_tp_print(PySetObject *so, FILE *fp, int flags) { - PyObject *key; + setentry *entry; int pos=0; char *emit = ""; /* No separator emitted on first pass */ char *separator = ", "; fprintf(fp, "%s([", so->ob_type->tp_name); - while (set_next_internal(so, &pos, &key)) { + while (set_next(so, &pos, &entry)) { fputs(emit, fp); emit = separator; - if (PyObject_Print(key, fp, 0) != 0) + if (PyObject_Print(entry->key, fp, 0) != 0) return -1; } fputs("])", fp); @@ -1465,18 +1538,9 @@ } static PyObject * -set_clear(PySetObject *so) -{ - set_clear_internal(so); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(clear_doc, "Remove all elements from this set."); - -static PyObject * set_add(PySetObject *so, PyObject *key) { - if (set_add_internal(so, key) == -1) + if (set_add_key(so, key) == -1) return NULL; Py_RETURN_NONE; } @@ -1503,7 +1567,7 @@ return result; } - rv = set_discard_internal(so, key); + rv = set_discard_key(so, key); if (rv == -1) return NULL; else if (rv == DISCARD_NOTFOUND) { @@ -1534,7 +1598,7 @@ return result; } - if (set_discard_internal(so, key) == -1) + if (set_discard_key(so, key) == -1) return NULL; Py_RETURN_NONE; } From pje at users.sourceforge.net Thu Aug 11 16:59:05 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Thu, 11 Aug 2005 16:59:05 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command install.py, 1.3, 1.4 Message-ID: <20050811145905.44A321E4003@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13707/setuptools/command Modified Files: install.py Log Message: Fixed breakage of bdist_* commands that call the 'install' command. Index: install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/install.py,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- install.py 6 Aug 2005 19:29:49 -0000 1.3 +++ install.py 11 Aug 2005 14:58:54 -0000 1.4 @@ -1,8 +1,8 @@ -import setuptools +import setuptools, sys from distutils.command.install import install as _install class install(_install): - """Build dependencies before installation""" + """Use easy_install to install the package, w/dependencies""" def handle_extra_path(self): # We always ignore extra_path, because we always install eggs @@ -11,6 +11,16 @@ self.extra_dirs = '' def run(self): + calling_module = sys._getframe(1).f_globals.get('__name__','') + if calling_module != 'distutils.dist': + # We're not being run from the command line, so use old-style + # behavior. This is a bit kludgy, because a command might use + # dist.run_command() to run 'install', but bdist_dumb and + # bdist_wininst both call run directly at the moment. + # When this is part of the distutils, the old install behavior + # should probably be requested with a flag, or a different method. + return _install.run(self) + from setuptools.command.easy_install import easy_install cmd = easy_install( self.distribution, args="x", ignore_conflicts_at_my_risk=1 @@ -28,4 +38,3 @@ cmd.run() setuptools.bootstrap_install_from = None - From geraldocarvalho278346 at yahoo.com.br Thu Aug 11 23:31:34 2005 From: geraldocarvalho278346 at yahoo.com.br (Geraldo Carvalho) Date: Thu, 11 Aug 2005 18:31:34 -0300 Subject: [Python-checkins] mala direta, lista de emails, divulga??o de sites Message-ID: <20050811213300.180681E4009@bag.python.org> emails, e-mails por estados, e-mails cidade, cadastro e-mail, mala direta por e-mail, listas emails, e-mail regi?es, propaganda email, enviar email an?nimo, envio mala direta, estados, campanha, cidade, envio, publicidade e-mails, Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm campanhas e-mail, lista e-mail, programas e-mails, e-mails estado, publicidade emails, marketing digital, cidade, divulgar, lista email, email cidades, campanha e-mail, e-mail estado, listas email, lista emails, propaganda por e-mails, mala direta email, publicidade, cidades, marketing emails, cidade, email por regi?es, envio propaganda, listas e-mails, e-mails regi?es, divulgar e-mails, envio mala-direta, e-mail cidades, email estado, e-mails por Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm regi?o, marketing por emails, propaganda, software email em massa, propaganda digital e-mail, programas email, email, mala direta, propaganda e-mail, marketing e-mails, e-mail, mala-direta email, propaganda digital, emails por regi?o, email segmentado, estado, campanhas e-mails, e-mails cidades, e-mails segmentados, email por estado, marketing por email, emails segmentado, divulga??o, e-mails estados, cidade, campanha e-mails, software, email segmentados, regi?o, enviar e-mails an?nimo, enviar emails an?nimo, mala direta emails, marketing email, emails segmentados, programas e-mail, e-mails por cidade, lista e-mails, propaganda, mala direta por e-mails, campanha email, software spam internet, Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm emails estado, publicidade e-mail, e-mail por cidades, enviar e-mail an?nimo, software propaganda internet, emails cidade, emails, campanhas emails, mala-direta e-mail, publicidade email, mala direta e-mails, e-mail regi?o, listas, listas segmentadas, marketing, marketing digital por emails, email regi?o, divulga??o e-mail, emails por cidade, mala-direta por email, marketing digital por e-mails, listas email, lista segmentada, cidades, cadastro email, divulgue seu produto, mala-direta por e-mails, e-mail por estado, segmentos, email por cidades, propaganda por e-mail, emails cidades, publicidade por emails, envio e-mail, e-mails por estado, mala direta, mala-direta, mala-direta por emails, e-mail segmentado, marketing digital emails, cidades, divulga??o e-mails, marketing, e-mail estados, cidades, marketing por e-mail, envio emails, marketing digital email, propaganda Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm por email, envio an?nimo email, divulgue sua propaganda, propaganda digital emails, cidade, emails por cidades, e-mails segmentado, propaganda por emails, divulgar email, e-mail cidade, enviar e-mails, e-mails, cadastro emails, e-mail por cidade, envio email, cadastro, lista, envio e-mails, propaganda digital email, publicidade por e-mails, marketing digital, e-mail por regi?o, email por estados, divulga??o, emails por estados, segmentados, mala-direta emails, envio publicidade, campanhas, mala direta por emails, e-mail por estados, marketing por e-mails, emails por estado, mala-direta e-mails, marketing digital e-mail, divulgar emails, emails regi?es, publicidade, email por regi?o, e-mails por regi?es, listas e-mail, divulga??o emails, mala-direta por e-mail, enviar e-mail, enviar email, Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm divulga??o email, cidades, publicidade por e-mail, enviar, emails por regi?es, marketing digital por e-mail, email por cidade, campanhas email, marketing digital por email, marketing digital e-mails, propaganda e-mails, e-mail segmentados, envio an?nimo e-mail, software publicidade internet, segmentados, envio an?nimo e-mails, lista mala direta, programa email an?nimo, mala direta internet, publicidade email, mala direta segmentada, emails segmentados, marketing digital, mala direta email, publicidade, spam, mala direta e-mail, email regi?es, e-mails regi?o, mala direta por email, marketing e-mail, regi?es, cadastro e-mails, publicidade por email, emails regi?o, divulgar, enviar emails, campanha emails, propaganda emails, email cidade, envio an?nimo emails, email estados, divulgar e-mail, programas emails, e-mails por estados, e-mails cidade, cadastro e-mail, mala direta por e-mail, listas emails, e-mail regi?es, propaganda email, enviar email an?nimo, envio Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm mala direta, estados, campanha, cidade, envio, publicidade e-mails, campanhas e-mail, lista e-mail, programas e-mails, e-mails estado, publicidade emails, marketing digital, cidade, divulgar, lista email, emails estados, propaganda digital e-mails, e-mail por regi?es, e-mails por cidades, email cidades, campanha e-mail, e-mail estado, listas email, lista emails, propaganda por e-mails, mala direta email, publicidade, cidades, marketing emails, cidade, email por regi?es, envio propaganda, listas e-mails, e-mails regi?es, divulgar e-mails, envio mala-direta, e-mail cidades, email estado, e-mails por regi?o, marketing por emails, propaganda, software email em massa, propaganda digital e-mail, programas email, email, mala direta, propaganda e-mail, marketing e-mails, e-mail, mala-direta email, propaganda Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm digital, emails por regi?o, email segmentado, estado, campanhas e-mails, e-mails cidades, e-mails segmentados, email por estado, marketing por email, emails segmentado, divulga??o, e-mails estados, cidade, campanha e-mails, software, email segmentados, regi?o, enviar e-mails an?nimo, enviar emails an?nimo, mala direta emails, marketing email, emails segmentados, programas e-mail, e-mails por cidade, lista e-mails, propaganda, mala direta por e-mails, campanha email, software spam internet, emails Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm estado, publicidade e-mail, e-mail por cidades, enviar e-mail an?nimo, software propaganda internet, emails cidade, emails, campanhas emails, mala-direta e-mail, publicidade email, mala direta e-mails, e-mail regi?o, listas, listas segmentadas, marketing, marketing digital por emails, email regi?o, divulga??o e-mail, emails por cidade, mala-direta por email, marketing digital por e-mails, listas email, lista segmentada, cidades, cadastro email, divulgue seu produto, mala-direta por e-mails, e-mail por estado, segmentos, email por cidades, propaganda por e-mail, emails cidades, publicidade por emails, envio e-mail Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm mails por estado, mala direta, mala-direta, mala-direta por emails, e-mail segmentado, marketing digital emails, cidades, divulga??o e-mails, marketing, e-mail estados, cidades, marketing por e-mail, envio emails, marketing digital email, propaganda por email, envio an?nimo email, divulgue sua propaganda, propaganda digital emails, cidade, emails por cidades, e-mails segmentado, propaganda por emails, divulgar email, e-mail cidade, enviar e-mails, e-mails, cadastro emails, e-mail por cidade, envio email, cadastro, lista, envio e-mails, propaganda digital email, publicidade por e-mails, marketing digital, e-mail por regi?o, email por estados, divulga??o, emails por estados, segmentados, mala-direta emails, envio publicidade, campanhas, mala direta por emails, e-mail por estados, marketing por e-mail Visite agora clicando em qualquer um dos 3 links: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm mails, emails por estado, mala-direta e-mails, marketing digital e-mail, divulgar emails, emails regi?es, publicidade, email por regi?o, e-mails por regi?es, listas e-mail, divulga??o emails, mala-direta por e-mail, enviar e-mail, enviar email, divulga??o email, cidades, publicidade por e-mail, enviar, emails por regi?es, marketing digital por e-mail, email por cidade, campanhas email, marketing digital por email, marketing digital e-mails, propaganda e-mails, e-mail segmentados, envio an?nimo e-mail, software publicidade internet, segmentados, envio an?nimo e-mails, lista mala direta, programa email an?nimo, mala direta internet, publicidade email, mala direta segmentada, emails segmentados, marketing digital, mala direta email, publicidade, spam mails por estado, mala direta, mala-direta, mala-direta por emails, e-mail segmentado, marketing digital emails, cidades, divulga??o e-mails, marketing, e-mail estados, cidades, marketing por e-mail, envio emails, marketing digital email, propaganda por email. From akuchling at users.sourceforge.net Fri Aug 12 16:02:42 2005 From: akuchling at users.sourceforge.net (akuchling@users.sourceforge.net) Date: Fri, 12 Aug 2005 16:02:42 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/lib libbinascii.tex, 1.23, 1.24 Message-ID: <20050812140242.C0F131E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8122 Modified Files: libbinascii.tex Log Message: Document 'istext' parameter Index: libbinascii.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libbinascii.tex,v retrieving revision 1.23 retrieving revision 1.24 diff -u -d -r1.23 -r1.24 --- libbinascii.tex 28 Nov 2001 07:26:15 -0000 1.23 +++ libbinascii.tex 12 Aug 2005 14:02:32 -0000 1.24 @@ -51,10 +51,13 @@ Convert binary data to a line(s) of \ASCII{} characters in quoted-printable encoding. The return value is the converted line(s). If the optional argument \var{quotetabs} is present and true, all tabs -and spaces will be encoded. If the optional argument \var{header} is +and spaces will be encoded. +If the optional argument \var{istext} is present and true, +newlines are not encoded but trailing whitespace will be encoded. +If the optional argument \var{header} is present and true, spaces will be encoded as underscores per RFC1522. If the optional argument \var{header} is present and false, newline -characters will be encoded as well, otherwise linefeed conversion might +characters will be encoded as well; otherwise linefeed conversion might corrupt the binary data stream. \end{funcdesc} From akuchling at users.sourceforge.net Fri Aug 12 16:08:20 2005 From: akuchling at users.sourceforge.net (akuchling@users.sourceforge.net) Date: Fri, 12 Aug 2005 16:08:20 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/lib libbinascii.tex, 1.23, 1.23.28.1 Message-ID: <20050812140820.BC8421E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9582 Modified Files: Tag: release24-maint libbinascii.tex Log Message: Document 'istext' parameter Index: libbinascii.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libbinascii.tex,v retrieving revision 1.23 retrieving revision 1.23.28.1 diff -u -d -r1.23 -r1.23.28.1 --- libbinascii.tex 28 Nov 2001 07:26:15 -0000 1.23 +++ libbinascii.tex 12 Aug 2005 14:08:10 -0000 1.23.28.1 @@ -51,10 +51,13 @@ Convert binary data to a line(s) of \ASCII{} characters in quoted-printable encoding. The return value is the converted line(s). If the optional argument \var{quotetabs} is present and true, all tabs -and spaces will be encoded. If the optional argument \var{header} is +and spaces will be encoded. +If the optional argument \var{istext} is present and true, +newlines are not encoded but trailing whitespace will be encoded. +If the optional argument \var{header} is present and true, spaces will be encoded as underscores per RFC1522. If the optional argument \var{header} is present and false, newline -characters will be encoded as well, otherwise linefeed conversion might +characters will be encoded as well; otherwise linefeed conversion might corrupt the binary data stream. \end{funcdesc} From nascheme at users.sourceforge.net Fri Aug 12 19:35:07 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Fri, 12 Aug 2005 19:35:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_unicode.py, 1.94, 1.95 Message-ID: <20050812173507.7F0591E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19776/Lib/test Modified Files: test_unicode.py Log Message: Change the %s format specifier for str objects so that it returns a unicode instance if the argument is not an instance of basestring and calling __str__ on the argument returns a unicode instance. Index: test_unicode.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_unicode.py,v retrieving revision 1.94 retrieving revision 1.95 diff -u -d -r1.94 -r1.95 --- test_unicode.py 26 Apr 2005 03:45:26 -0000 1.94 +++ test_unicode.py 12 Aug 2005 17:34:57 -0000 1.95 @@ -388,6 +388,10 @@ self.assertEqual('%i %*.*s' % (10, 5,3,u'abc',), u'10 abc') self.assertEqual('%i%s %*.*s' % (10, 3, 5, 3, u'abc',), u'103 abc') self.assertEqual('%c' % u'a', u'a') + class Wrapper: + def __str__(self): + return u'\u1234' + self.assertEqual('%s' % Wrapper(), u'\u1234') def test_constructor(self): # unicode(obj) tests (this maps to PyObject_Unicode() at C level) From nascheme at users.sourceforge.net Fri Aug 12 19:35:07 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Fri, 12 Aug 2005 19:35:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1332,1.1333 Message-ID: <20050812173507.7E9A21E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19776/Misc Modified Files: NEWS Log Message: Change the %s format specifier for str objects so that it returns a unicode instance if the argument is not an instance of basestring and calling __str__ on the argument returns a unicode instance. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1332 retrieving revision 1.1333 diff -u -d -r1.1332 -r1.1333 --- NEWS 9 Aug 2005 15:00:51 -0000 1.1332 +++ NEWS 12 Aug 2005 17:34:57 -0000 1.1333 @@ -118,6 +118,10 @@ positions. It once again reports a syntax error if a future statement occurs after anything other than a doc string. +- Change the %s format specifier for str objects so that it returns a + unicode instance if the argument is not an instance of basestring and + calling __str__ on the argument returns a unicode instance. + Extension Modules ----------------- From nascheme at users.sourceforge.net Fri Aug 12 19:35:08 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Fri, 12 Aug 2005 19:35:08 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Include object.h,2.130,2.131 Message-ID: <20050812173508.D09E61E400C@bag.python.org> Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19776/Include Modified Files: object.h Log Message: Change the %s format specifier for str objects so that it returns a unicode instance if the argument is not an instance of basestring and calling __str__ on the argument returns a unicode instance. Index: object.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/object.h,v retrieving revision 2.130 retrieving revision 2.131 diff -u -d -r2.130 -r2.131 --- object.h 23 Sep 2004 02:39:36 -0000 2.130 +++ object.h 12 Aug 2005 17:34:58 -0000 2.131 @@ -371,6 +371,7 @@ PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); PyAPI_FUNC(void) _PyObject_Dump(PyObject *); PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *); +PyAPI_FUNC(PyObject *) _PyObject_Str(PyObject *); PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *); #ifdef Py_USING_UNICODE PyAPI_FUNC(PyObject *) PyObject_Unicode(PyObject *); From nascheme at users.sourceforge.net Fri Aug 12 19:35:08 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Fri, 12 Aug 2005 19:35:08 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects object.c, 2.226, 2.227 stringobject.c, 2.230, 2.231 Message-ID: <20050812173508.E0BA51E400F@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19776/Objects Modified Files: object.c stringobject.c Log Message: Change the %s format specifier for str objects so that it returns a unicode instance if the argument is not an instance of basestring and calling __str__ on the argument returns a unicode instance. Index: object.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/object.c,v retrieving revision 2.226 retrieving revision 2.227 diff -u -d -r2.226 -r2.227 --- object.c 26 Apr 2005 03:45:25 -0000 2.226 +++ object.c 12 Aug 2005 17:34:57 -0000 2.227 @@ -331,22 +331,48 @@ } PyObject * -PyObject_Str(PyObject *v) +_PyObject_Str(PyObject *v) { PyObject *res; - + int type_ok; if (v == NULL) return PyString_FromString(""); if (PyString_CheckExact(v)) { Py_INCREF(v); return v; } +#ifdef Py_USING_UNICODE + if (PyUnicode_CheckExact(v)) { + Py_INCREF(v); + return v; + } +#endif if (v->ob_type->tp_str == NULL) return PyObject_Repr(v); res = (*v->ob_type->tp_str)(v); if (res == NULL) return NULL; + type_ok = PyString_Check(res); +#ifdef Py_USING_UNICODE + type_ok = type_ok || PyUnicode_Check(res); +#endif + if (!type_ok) { + PyErr_Format(PyExc_TypeError, + "__str__ returned non-string (type %.200s)", + res->ob_type->tp_name); + Py_DECREF(res); + return NULL; + } + return res; +} + +PyObject * +PyObject_Str(PyObject *v) +{ + PyObject *res = _PyObject_Str(v); + if (res == NULL) + return NULL; #ifdef Py_USING_UNICODE if (PyUnicode_Check(res)) { PyObject* str; @@ -358,13 +384,7 @@ return NULL; } #endif - if (!PyString_Check(res)) { - PyErr_Format(PyExc_TypeError, - "__str__ returned non-string (type %.200s)", - res->ob_type->tp_name); - Py_DECREF(res); - return NULL; - } + assert(PyString_Check(res)); return res; } Index: stringobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/stringobject.c,v retrieving revision 2.230 retrieving revision 2.231 diff -u -d -r2.230 -r2.231 --- stringobject.c 29 Jun 2005 23:29:54 -0000 2.230 +++ stringobject.c 12 Aug 2005 17:34:57 -0000 2.231 @@ -3853,7 +3853,6 @@ return 1; } - /* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) FORMATBUFLEN is the length of the buffer in which the floats, ints, & @@ -4079,7 +4078,9 @@ break; case 's': #ifdef Py_USING_UNICODE - if (PyUnicode_Check(v)) { + temp = _PyObject_Str(v); + if (temp != NULL && PyUnicode_Check(temp)) { + Py_DECREF(temp); fmt = fmt_start; argidx = argidx_start; goto unicode; @@ -4087,16 +4088,11 @@ #endif /* Fall through */ case 'r': - if (c == 's') - temp = PyObject_Str(v); - else + if (c == 'r') temp = PyObject_Repr(v); if (temp == NULL) goto error; if (!PyString_Check(temp)) { - /* XXX Note: this should never happen, - since PyObject_Repr() and - PyObject_Str() assure this */ PyErr_SetString(PyExc_TypeError, "%s argument has non-string str()"); Py_DECREF(temp); From rhettinger at users.sourceforge.net Fri Aug 12 22:48:50 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Fri, 12 Aug 2005 22:48:50 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.46,1.47 Message-ID: <20050812204850.B36C91E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1026 Modified Files: setobject.c Log Message: * Fix SF #1257731. Make __contains__(), remove(), and discard() only do a frozenset conversion when the initial search attempt fails with a TypeError and the key is some type of set. Add a testcase. * Eliminate a duplicate if-stmt. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.46 retrieving revision 1.47 diff -u -d -r1.46 -r1.47 --- setobject.c 11 Aug 2005 07:58:44 -0000 1.46 +++ setobject.c 12 Aug 2005 20:48:39 -0000 1.47 @@ -932,20 +932,22 @@ set_contains(PySetObject *so, PyObject *key) { PyObject *tmpkey; - int result; + int rv; - result = set_contains_key(so, key); - if (result == -1 && PyAnySet_Check(key)) { + rv = set_contains_key(so, key); + if (rv == -1) { + if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return -1; PyErr_Clear(); tmpkey = make_new_set(&PyFrozenSet_Type, NULL); if (tmpkey == NULL) return -1; set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); - result = set_contains_key(so, tmpkey); + rv = set_contains(so, tmpkey); set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); } - return result; + return rv; } static PyObject * @@ -1046,15 +1048,16 @@ if (result == NULL) return NULL; - if (PyAnySet_Check(other) && set_len(other) > set_len((PyObject *)so)) { - tmp = (PyObject *)so; - so = (PySetObject *)other; - other = tmp; - } - if (PyAnySet_Check(other)) { int pos = 0; setentry *entry; + + if (set_len(other) > set_len((PyObject *)so)) { + tmp = (PyObject *)so; + so = (PySetObject *)other; + other = tmp; + } + while (set_next((PySetObject *)other, &pos, &entry)) { if (set_contains_entry(so, entry)) { if (set_add_entry(result, entry) == -1) { @@ -1556,21 +1559,20 @@ PyObject *tmpkey, *result; int rv; - if (PyType_IsSubtype(key->ob_type, &PySet_Type)) { + rv = set_discard_key(so, key); + if (rv == -1) { + if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return NULL; + PyErr_Clear(); tmpkey = make_new_set(&PyFrozenSet_Type, NULL); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)key, (PySetObject *)tmpkey); + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); result = set_remove(so, tmpkey); - set_swap_bodies((PySetObject *)key, (PySetObject *)tmpkey); + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); return result; - } - - rv = set_discard_key(so, key); - if (rv == -1) - return NULL; - else if (rv == DISCARD_NOTFOUND) { + } else if (rv == DISCARD_NOTFOUND) { PyErr_SetObject(PyExc_KeyError, key); return NULL; } @@ -1586,20 +1588,22 @@ set_discard(PySetObject *so, PyObject *key) { PyObject *tmpkey, *result; + int rv; - if (PyType_IsSubtype(key->ob_type, &PySet_Type)) { + rv = set_discard_key(so, key); + if (rv == -1) { + if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return NULL; + PyErr_Clear(); tmpkey = make_new_set(&PyFrozenSet_Type, NULL); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)key, (PySetObject *)tmpkey); + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); result = set_discard(so, tmpkey); - set_swap_bodies((PySetObject *)key, (PySetObject *)tmpkey); + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); return result; } - - if (set_discard_key(so, key) == -1) - return NULL; Py_RETURN_NONE; } From loewis at users.sourceforge.net Sat Aug 13 01:14:16 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:14:16 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.10,1.11 Message-ID: <20050812231416.638DB1E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3201 Modified Files: pep-0347.txt Log Message: Add wush.net hosting. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- pep-0347.txt 9 Aug 2005 01:17:05 -0000 1.10 +++ pep-0347.txt 12 Aug 2005 23:14:06 -0000 1.11 @@ -246,6 +246,17 @@ * Subversion over WebDAV, using SSL client certificates. This would work, but would require us to administer a certificate authority. +- Instead of hosting this on python.org, people suggested hosting + it elsewhere. One issue is whether this alternative should be + free or commercial; several people suggested it should better + be commercial, to reduce the load on the volunteers. In + particular: + + * Greg Stein suggested http://www.wush.net/subversion.php. They + offer 5 GB for $90/month, with 200 GB download/month. + The data is on a RAID drive and fully backed up. Anonymous + access and email commit notifications are supported. + Copyright ========= From loewis at users.sourceforge.net Sat Aug 13 01:15:24 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:15:24 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.11,1.12 Message-ID: <20050812231524.66FF61E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3525 Modified Files: pep-0347.txt Log Message: Remove Nick Bastin's offer; this was a misinterpretation of his message. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- pep-0347.txt 12 Aug 2005 23:14:06 -0000 1.11 +++ pep-0347.txt 12 Aug 2005 23:15:14 -0000 1.12 @@ -218,9 +218,6 @@ was rejected because a single repository supports moving code across projects. -- Nick Bastin has offered to host a Perforce repository, instead - of using Subversion. Details of that offer are not clear yet. - - Several people suggested to create the project/trunk structure through standard cvs2svn, followed by renames. This would have the disadvantage that old revisions use different path names From rhettinger at users.sourceforge.net Sat Aug 13 01:48:00 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:48:00 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_set.py, 1.16.2.1, 1.16.2.2 Message-ID: <20050812234800.0601F1E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8678/Lib/test Modified Files: Tag: release24-maint test_set.py Log Message: * SF bug #1257731: Fix logic in set.__contains__(), set.remove(), and set.discard for handling keys that both inherite from set and define their own __hash__() function. * Fixed O(n) performance issue with set.pop() which should have been an O(1) process. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.16.2.1 retrieving revision 1.16.2.2 diff -u -d -r1.16.2.1 -r1.16.2.2 --- test_set.py 19 Jun 2005 05:53:14 -0000 1.16.2.1 +++ test_set.py 12 Aug 2005 23:47:50 -0000 1.16.2.2 @@ -212,6 +212,19 @@ elem.sub = elem elem.set = set([elem]) + def test_subclass_with_custom_hash(self): + # Bug #1257731 + class H(self.thetype): + def __hash__(self): + return id(self) + s=H() + f=set() + f.add(s) + self.assert_(s in f) + f.remove(s) + f.add(s) + f.discard(s) + class TestSet(TestJointOps): thetype = set From rhettinger at users.sourceforge.net Sat Aug 13 01:48:00 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:48:00 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.70, 1.1193.2.71 Message-ID: <20050812234800.1FB3B1E4006@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8678/Misc Modified Files: Tag: release24-maint NEWS Log Message: * SF bug #1257731: Fix logic in set.__contains__(), set.remove(), and set.discard for handling keys that both inherite from set and define their own __hash__() function. * Fixed O(n) performance issue with set.pop() which should have been an O(1) process. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.70 retrieving revision 1.1193.2.71 diff -u -d -r1.1193.2.70 -r1.1193.2.71 --- NEWS 7 Aug 2005 21:08:54 -0000 1.1193.2.70 +++ NEWS 12 Aug 2005 23:47:50 -0000 1.1193.2.71 @@ -12,6 +12,11 @@ Core and builtins ----------------- +- SF bug #1257731: set.discard() and set.remove() did not correctly + handle keys that both inherited from set and defined their own + __hash__() function. Also, changed set.__contains__() to have + identical logic. + - SF bug #1238681: freed pointer is used in longobject.c:long_pow(). - SF bug #1185883: Python's small-object memory allocator took over From rhettinger at users.sourceforge.net Sat Aug 13 01:48:00 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:48:00 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c, 1.31.2.1, 1.31.2.2 Message-ID: <20050812234800.2DD771E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8678/Objects Modified Files: Tag: release24-maint setobject.c Log Message: * SF bug #1257731: Fix logic in set.__contains__(), set.remove(), and set.discard for handling keys that both inherite from set and define their own __hash__() function. * Fixed O(n) performance issue with set.pop() which should have been an O(1) process. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.31.2.1 retrieving revision 1.31.2.2 diff -u -d -r1.31.2.1 -r1.31.2.2 --- setobject.c 19 Jun 2005 05:53:15 -0000 1.31.2.1 +++ setobject.c 12 Aug 2005 23:47:50 -0000 1.31.2.2 @@ -145,18 +145,20 @@ set_contains(PySetObject *so, PyObject *key) { PyObject *tmp; - int result; + int rv; - result = PyDict_Contains(so->data, key); - if (result == -1 && PyAnySet_Check(key)) { + rv = PyDict_Contains(so->data, key); + if (rv == -1) { + if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return -1; PyErr_Clear(); tmp = frozenset_dict_wrapper(((PySetObject *)(key))->data); if (tmp == NULL) return -1; - result = PyDict_Contains(so->data, tmp); + rv = PyDict_Contains(so->data, tmp); Py_DECREF(tmp); } - return result; + return rv; } static PyObject * @@ -791,19 +793,20 @@ set_remove(PySetObject *so, PyObject *item) { PyObject *tmp, *result; + int rv; - if (PyType_IsSubtype(item->ob_type, &PySet_Type)) { - tmp = frozenset_dict_wrapper(((PySetObject *)(item))->data); - if (tmp == NULL) - return NULL; - result = set_remove(so, tmp); - Py_DECREF(tmp); - return result; - } - - if (PyDict_DelItem(so->data, item) == -1) + rv = PyDict_DelItem(so->data, item); + if (rv == 0) + Py_RETURN_NONE; + if (!PyAnySet_Check(item) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; - Py_RETURN_NONE; + PyErr_Clear(); + tmp = frozenset_dict_wrapper(((PySetObject *)(item))->data); + if (tmp == NULL) + return NULL; + result = set_remove(so, tmp); + Py_DECREF(tmp); + return result; } PyDoc_STRVAR(remove_doc, @@ -815,22 +818,24 @@ set_discard(PySetObject *so, PyObject *item) { PyObject *tmp, *result; + int rv; - if (PyType_IsSubtype(item->ob_type, &PySet_Type)) { - tmp = frozenset_dict_wrapper(((PySetObject *)(item))->data); - if (tmp == NULL) - return NULL; - result = set_discard(so, tmp); - Py_DECREF(tmp); - return result; - } - - if (PyDict_DelItem(so->data, item) == -1) { - if (!PyErr_ExceptionMatches(PyExc_KeyError)) - return NULL; + rv = PyDict_DelItem(so->data, item); + if (rv == 0) + Py_RETURN_NONE; + if (PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); + Py_RETURN_NONE; } - Py_RETURN_NONE; + if (!PyAnySet_Check(item) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return NULL; + PyErr_Clear(); + tmp = frozenset_dict_wrapper(((PySetObject *)(item))->data); + if (tmp == NULL) + return NULL; + result = set_discard(so, tmp); + Py_DECREF(tmp); + return result; } PyDoc_STRVAR(discard_doc, @@ -841,18 +846,18 @@ static PyObject * set_pop(PySetObject *so) { - PyObject *key, *value; - int pos = 0; + PyObject *item, *key; - if (!PyDict_Next(so->data, &pos, &key, &value)) { + if (set_len(so) == 0) { PyErr_SetString(PyExc_KeyError, "pop from an empty set"); return NULL; } - Py_INCREF(key); - if (PyDict_DelItem(so->data, key) == -1) { - Py_DECREF(key); + item = PyObject_CallMethod(so->data, "popitem", NULL); + if (item == NULL) return NULL; - } + key = PyTuple_GET_ITEM(item, 0); + Py_INCREF(key); + Py_DECREF(item); return key; } From rhettinger at users.sourceforge.net Sat Aug 13 01:58:32 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 01:58:32 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_set.py,1.20,1.21 Message-ID: <20050812235832.E506E1E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10343 Modified Files: test_set.py Log Message: * Fix SF #1257731. Make __contains__(), remove(), and discard() only do a frozenset conversion when the initial search attempt fails with a TypeError and the key is some type of set. Add a testcase. * Eliminate a duplicate if-stmt. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.20 retrieving revision 1.21 diff -u -d -r1.20 -r1.21 --- test_set.py 11 Aug 2005 07:58:45 -0000 1.20 +++ test_set.py 12 Aug 2005 23:58:22 -0000 1.21 @@ -213,6 +213,19 @@ elem.sub = elem elem.set = set([elem]) + def test_subclass_with_custom_hash(self): + # Bug #1257731 + class H(self.thetype): + def __hash__(self): + return id(self) + s=H() + f=set() + f.add(s) + self.assert_(s in f) + f.remove(s) + f.add(s) + f.discard(s) + class TestSet(TestJointOps): thetype = set From nascheme at users.sourceforge.net Sat Aug 13 02:28:51 2005 From: nascheme at users.sourceforge.net (nascheme@users.sourceforge.net) Date: Sat, 13 Aug 2005 02:28:51 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc gdbinit,1.9,1.10 Message-ID: <20050813002851.929E31E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17968/Misc Modified Files: gdbinit Log Message: Fix pystack command. Index: gdbinit =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/gdbinit,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- gdbinit 8 Jan 2005 21:56:43 -0000 1.9 +++ gdbinit 13 Aug 2005 00:28:41 -0000 1.10 @@ -98,7 +98,7 @@ #end define printframe - if $pc > PyEval_EvalFrame && $pc < PyEval_EvalCodeEx + if $pc > PyEval_EvalFrameEx && $pc < PyEval_EvalCodeEx pyframe else frame From goodger at users.sourceforge.net Sat Aug 13 03:37:43 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 03:37:43 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0000.txt, 1.339, 1.340 pep-0001.txt, 1.45, 1.46 Message-ID: <20050813013743.3E7C41E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29141 Modified Files: pep-0000.txt pep-0001.txt Log Message: added new Process PEP type Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.339 retrieving revision 1.340 diff -u -d -r1.339 -r1.340 --- pep-0000.txt 5 Aug 2005 02:59:00 -0000 1.339 +++ pep-0000.txt 13 Aug 2005 01:37:32 -0000 1.340 @@ -30,7 +30,7 @@ Meta-PEPs (PEPs about PEPs or Process) I 0 Index of Python Enhancement Proposals Goodger, Warsaw - I 1 PEP Guidelines Warsaw, Hylton, Goodger + P 1 PEP Guidelines Warsaw, Hylton, Goodger I 2 Procedure for Adding New Modules Faassen I 3 Guidelines for Handling Bug Reports Hylton I 4 Deprecation of Standard Modules von Loewis @@ -103,7 +103,7 @@ S 341 Unifying try-except and try-finally Birkenfeld S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones - I 347 Migrating the Python CVS to Subversion von Löwis + P 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon S 349 Generalized String Coercion Schemenauer S 754 IEEE 754 Floating Point Special Values Warnes @@ -225,7 +225,7 @@ num title owner --- ----- ----- I 0 Index of Python Enhancement Proposals Goodger, Warsaw - I 1 PEP Guidelines Warsaw, Hylton, Goodger + P 1 PEP Guidelines Warsaw, Hylton, Goodger I 2 Procedure for Adding New Modules Faassen I 3 Guidelines for Handling Bug Reports Hylton I 4 Deprecation of Standard Modules von Loewis @@ -391,7 +391,7 @@ S 344 Exception Chaining and Embedded Tracebacks Yee S 345 Metadata for Python Software Packages 1.2 Jones SR 346 User Defined ("with") Statements Coghlan - I 347 Migrating the Python CVS to Subversion von Löwis + P 347 Migrating the Python CVS to Subversion von Löwis S 348 Exception Reorganization for Python 3.0 Cannon S 349 Generalized String Coercion Schemenauer SR 666 Reject Foolish Indentation Creighton @@ -401,8 +401,10 @@ Key - I - Informational PEP S - Standards Track PEP + I - Informational PEP + P - Process PEP + A - Accepted proposal R - Rejected proposal D - Deferred proposal Index: pep-0001.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0001.txt,v retrieving revision 1.45 retrieving revision 1.46 diff -u -d -r1.45 -r1.46 --- pep-0001.txt 27 Aug 2004 21:19:48 -0000 1.45 +++ pep-0001.txt 13 Aug 2005 01:37:32 -0000 1.46 @@ -4,7 +4,7 @@ Last-Modified: $Date$ Author: Barry A. Warsaw, Jeremy Hylton, David Goodger Status: Active -Type: Informational +Type: Process Content-Type: text/x-rst Created: 13-Jun-2000 Post-History: 21-Mar-2001, 29-Jul-2002, 03-May-2003 @@ -15,8 +15,9 @@ PEP stands for Python Enhancement Proposal. A PEP is a design document providing information to the Python community, or describing -a new feature for Python. The PEP should provide a concise technical -specification of the feature and a rationale for the feature. +a new feature for Python or its processes or environment. The PEP +should provide a concise technical specification of the feature and a +rationale for the feature. We intend PEPs to be the primary mechanisms for proposing new features, for collecting community input on an issue, and for @@ -29,16 +30,31 @@ [1]_. -Kinds of PEPs -============= +PEP Types +========= -There are two kinds of PEPs. A Standards Track PEP describes a new -feature or implementation for Python. An Informational PEP describes -a Python design issue, or provides general guidelines or information -to the Python community, but does not propose a new feature. -Informational PEPs do not necessarily represent a Python community -consensus or recommendation, so users and implementors are free to -ignore Informational PEPs or follow their advice. +There are three kinds of PEP: + +1. A **Standards Track** PEP describes a new feature or implementation + for Python. + +2. An **Informational** PEP describes a Python design issue, or + provides general guidelines or information to the Python community, + but does not propose a new feature. Informational PEPs do not + necessarily represent a Python community consensus or + recommendation, so users and implementors are free to ignore + Informational PEPs or follow their advice. + +3. A **Process** PEP describes a process surrounding Python, or + proposes a change to (or an event in) a process. Process PEPs are + like Standards Track PEPs but apply to areas other than the Python + language itself. They may propose an implementation, but not to + Python's codebase; they often require community consensus; unlike + Informational PEPs, they are more than recommendations, and users + are typically not free to ignore them. Examples include release + schedules, procedures, guidelines, changes to the decision-making + process, and changes to the tools or environment used in Python + development. PEP Work Flow @@ -71,14 +87,17 @@ draft must be written in PEP style as described below. If the PEP editor approves, he will assign the PEP a number, label it -as Standards Track or Informational, give it status "Draft", and -create and check-in the initial draft of the PEP. The PEP editor will -not unreasonably deny a PEP. Reasons for denying PEP status include -duplication of effort, being technically unsound, not providing proper -motivation or addressing backwards compatibility, or not in keeping -with the Python philosophy. The BDFL (Benevolent Dictator for Life, -Guido van Rossum) can be consulted during the approval phase, and is -the final arbitrator of the draft's PEP-ability. +as Standards Track, Informational, or Process, give it status "Draft", +and create and check-in the initial draft of the PEP. The PEP editor +will not unreasonably deny a PEP. Reasons for denying PEP status +include duplication of effort, being technically unsound, not +providing proper motivation or addressing backwards compatibility, or +not in keeping with the Python philosophy. The BDFL (Benevolent +Dictator for Life, Guido van Rossum) can be consulted during the +approval phase, and is the final arbitrator of the draft's +PEP-ability. + +arbiter? If a pre-PEP is rejected, the author may elect to take the pre-PEP to the comp.lang.python newsgroup (a.k.a. python-list at python.org mailing @@ -91,22 +110,22 @@ CVS commit permissions, or can email new PEP versions to the PEP editor for committing. -Standards Track PEPs consists of two parts, a design document and a +Standards Track PEPs consist of two parts, a design document and a reference implementation. The PEP should be reviewed and accepted before a reference implementation is begun, unless a reference implementation will aid people in studying the PEP. Standards Track -PEPs must include an implementation -- in the form of code, patch, or -URL to same -- before it can be considered Final. +PEPs must include an implementation -- in the form of code, a patch, +or a URL to same -- before it can be considered Final. PEP authors are responsible for collecting community feedback on a PEP before submitting it for review. A PEP that has not been discussed on python-list at python.org and/or python-dev at python.org will not be accepted. However, wherever possible, long open-ended discussions on public mailing lists should be avoided. Strategies to keep the -discussions efficient include, setting up a separate SIG mailing list +discussions efficient include: setting up a separate SIG mailing list for the topic, having the PEP author accept private comments in the -early design phases, etc. PEP authors should use their discretion -here. +early design phases, setting up a wiki page, etc. PEP authors should +use their discretion here. Once the authors have completed a PEP, they must inform the PEP editor that it is ready for review. PEPs are reviewed by the BDFL and his @@ -144,13 +163,9 @@ obsolete. This is intended for Informational PEPs, where version 2 of an API can replace version 1. -PEP work flow is as follows:: +PEP work flow is as follows: - Draft -> Accepted -> Final -> Replaced - ^ - +----> Rejected - v - Deferred +.. image:: pep-0001-1.png Some Informational PEPs may also have a status of "Active" if they are never meant to be completed. E.g. PEP 1 (this PEP). @@ -250,7 +265,7 @@ * Discussions-To: Status: - Type: + Type: * Content-Type: * Requires: Created: @@ -286,8 +301,8 @@ on the python-list or python-dev email mailing lists. Note that email addresses in the Discussions-To header will not be obscured. -The Type header specifies the type of PEP: Informational or Standards -Track. +The Type header specifies the type of PEP: Standards Track, +Informational, or Process. The format of a PEP is specified with a Content-Type header. The acceptable values are "text/plain" for plaintext PEPs (see PEP 9 [3]_) @@ -302,7 +317,7 @@ Standards Track PEPs must have a Python-Version header which indicates the version of Python that the feature will be released with. -Informational PEPs do not need a Python-Version header. +Informational and Process PEPs do not need a Python-Version header. PEPs may have a Requires header, indicating the PEP numbers that this PEP depends on. From goodger at users.sourceforge.net Sat Aug 13 03:38:01 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 03:38:01 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0001-1.png,NONE,1.1 Message-ID: <20050813013801.3162F1E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29206 Added Files: pep-0001-1.png Log Message: PEP workflow diagram --- NEW FILE: pep-0001-1.png --- (This appears to be a binary file; contents omitted.) From goodger at users.sourceforge.net Sat Aug 13 03:42:26 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 03:42:26 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0001.txt,1.46,1.47 Message-ID: <20050813014226.B9E181E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29733 Modified Files: pep-0001.txt Log Message: fixed idiom Index: pep-0001.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0001.txt,v retrieving revision 1.46 retrieving revision 1.47 diff -u -d -r1.46 -r1.47 --- pep-0001.txt 13 Aug 2005 01:37:32 -0000 1.46 +++ pep-0001.txt 13 Aug 2005 01:42:17 -0000 1.47 @@ -94,10 +94,7 @@ providing proper motivation or addressing backwards compatibility, or not in keeping with the Python philosophy. The BDFL (Benevolent Dictator for Life, Guido van Rossum) can be consulted during the -approval phase, and is the final arbitrator of the draft's -PEP-ability. - -arbiter? +approval phase, and is the final arbiter of the draft's PEP-ability. If a pre-PEP is rejected, the author may elect to take the pre-PEP to the comp.lang.python newsgroup (a.k.a. python-list at python.org mailing From goodger at users.sourceforge.net Sat Aug 13 03:47:37 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 03:47:37 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.12,1.13 Message-ID: <20050813014737.AF33F1E4005@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30138 Modified Files: pep-0347.txt Log Message: changed PEP type Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.12 retrieving revision 1.13 diff -u -d -r1.12 -r1.13 --- pep-0347.txt 12 Aug 2005 23:15:14 -0000 1.12 +++ pep-0347.txt 13 Aug 2005 01:47:28 -0000 1.13 @@ -5,7 +5,7 @@ Author: Martin v. Löwis Discussions-To: Status: Draft -Type: Informational +Type: Process Content-Type: text/x-rst Created: 14-Jul-2004 Post-History: 14-Jul-2004 From rhettinger at users.sourceforge.net Sat Aug 13 04:29:03 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:29:03 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib sets.py,1.49,1.49.4.1 Message-ID: <20050813022903.55FE71E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3812/Lib Modified Files: Tag: release24-maint sets.py Log Message: Teach set modules to correctly compute s-=s and s^=s as the empty set. Index: sets.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/sets.py,v retrieving revision 1.49 retrieving revision 1.49.4.1 diff -u -d -r1.49 -r1.49.4.1 --- sets.py 19 Nov 2003 15:52:14 -0000 1.49 +++ sets.py 13 Aug 2005 02:28:53 -0000 1.49.4.1 @@ -480,6 +480,8 @@ value = True if not isinstance(other, BaseSet): other = Set(other) + if self is other: + self.clear() for elt in other: if elt in data: del data[elt] @@ -497,6 +499,8 @@ data = self._data if not isinstance(other, BaseSet): other = Set(other) + if self is other: + self.clear() for elt in ifilter(data.has_key, other): del data[elt] From rhettinger at users.sourceforge.net Sat Aug 13 04:29:03 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:29:03 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_set.py, 1.16.2.2, 1.16.2.3 test_sets.py, 1.31, 1.31.2.1 Message-ID: <20050813022903.583871E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3812/Lib/test Modified Files: Tag: release24-maint test_set.py test_sets.py Log Message: Teach set modules to correctly compute s-=s and s^=s as the empty set. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.16.2.2 retrieving revision 1.16.2.3 diff -u -d -r1.16.2.2 -r1.16.2.3 --- test_set.py 12 Aug 2005 23:47:50 -0000 1.16.2.2 +++ test_set.py 13 Aug 2005 02:28:53 -0000 1.16.2.3 @@ -382,6 +382,18 @@ else: self.assert_(c not in self.s) + def test_inplace_on_self(self): + t = self.s.copy() + t |= t + self.assertEqual(t, self.s) + t &= t + self.assertEqual(t, self.s) + t -= t + self.assertEqual(t, self.thetype()) + t = self.s.copy() + t ^= t + self.assertEqual(t, self.thetype()) + def test_weakref(self): s = self.thetype('gallahad') p = proxy(s) Index: test_sets.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_sets.py,v retrieving revision 1.31 retrieving revision 1.31.2.1 diff -u -d -r1.31 -r1.31.2.1 --- test_sets.py 7 Aug 2004 06:15:12 -0000 1.31 +++ test_sets.py 13 Aug 2005 02:28:53 -0000 1.31.2.1 @@ -243,6 +243,19 @@ self.assertRaises(TypeError, cmp, a, 12) self.assertRaises(TypeError, cmp, "abc", a) + def test_inplace_on_self(self): + t = self.set.copy() + t |= t + self.assertEqual(t, self.set) + t &= t + self.assertEqual(t, self.set) + t -= t + self.assertEqual(len(t), 0) + t = self.set.copy() + t ^= t + self.assertEqual(len(t), 0) + + #============================================================================== class TestUpdateOps(unittest.TestCase): From rhettinger at users.sourceforge.net Sat Aug 13 04:29:03 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:29:03 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.71, 1.1193.2.72 Message-ID: <20050813022903.9FB391E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3812/Misc Modified Files: Tag: release24-maint NEWS Log Message: Teach set modules to correctly compute s-=s and s^=s as the empty set. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.71 retrieving revision 1.1193.2.72 diff -u -d -r1.1193.2.71 -r1.1193.2.72 --- NEWS 12 Aug 2005 23:47:50 -0000 1.1193.2.71 +++ NEWS 13 Aug 2005 02:28:54 -0000 1.1193.2.72 @@ -17,6 +17,8 @@ __hash__() function. Also, changed set.__contains__() to have identical logic. +- The set() builtin can now properly compute s-=s as an empty set. + - SF bug #1238681: freed pointer is used in longobject.c:long_pow(). - SF bug #1185883: Python's small-object memory allocator took over @@ -58,6 +60,8 @@ Library ------- +- The sets module can now properly compute s-=s and s^=s as an empty set. + - Patch #827386: Support absolute source paths in msvccompiler.py. - Fix a problem in Tkinter introduced by SF patch #869468: delete bogus From rhettinger at users.sourceforge.net Sat Aug 13 04:29:03 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:29:03 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c, 1.31.2.2, 1.31.2.3 Message-ID: <20050813022903.A0B1A1E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3812/Objects Modified Files: Tag: release24-maint setobject.c Log Message: Teach set modules to correctly compute s-=s and s^=s as the empty set. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.31.2.2 retrieving revision 1.31.2.3 diff -u -d -r1.31.2.2 -r1.31.2.3 --- setobject.c 12 Aug 2005 23:47:50 -0000 1.31.2.2 +++ setobject.c 13 Aug 2005 02:28:54 -0000 1.31.2.3 @@ -193,6 +193,16 @@ PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set."); static PyObject * +set_clear(PySetObject *so) +{ + PyDict_Clear(so->data); + so->hash = -1; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(clear_doc, "Remove all elements from this set."); + +static PyObject * set_union(PySetObject *so, PyObject *other) { PySetObject *result; @@ -379,6 +389,9 @@ set_difference_update(PySetObject *so, PyObject *other) { PyObject *item, *tgtdata, *it; + + if ((PyObject *)so == other) + return set_clear(so); it = PyObject_GetIter(other); if (it == NULL) @@ -758,16 +771,6 @@ return 0; } -static PyObject * -set_clear(PySetObject *so) -{ - PyDict_Clear(so->data); - so->hash = -1; - Py_RETURN_NONE; -} - -PyDoc_STRVAR(clear_doc, "Remove all elements from this set."); - static int set_tp_clear(PySetObject *so) { From rhettinger at users.sourceforge.net Sat Aug 13 04:30:07 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:30:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_sets.py,1.31,1.32 Message-ID: <20050813023007.B29A41E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3974/Lib/test Modified Files: test_sets.py Log Message: Teach the sets module to correctly compute s-=s and s^=s as the empty set. Index: test_sets.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_sets.py,v retrieving revision 1.31 retrieving revision 1.32 diff -u -d -r1.31 -r1.32 --- test_sets.py 7 Aug 2004 06:15:12 -0000 1.31 +++ test_sets.py 13 Aug 2005 02:29:58 -0000 1.32 @@ -243,6 +243,19 @@ self.assertRaises(TypeError, cmp, a, 12) self.assertRaises(TypeError, cmp, "abc", a) + def test_inplace_on_self(self): + t = self.set.copy() + t |= t + self.assertEqual(t, self.set) + t &= t + self.assertEqual(t, self.set) + t -= t + self.assertEqual(len(t), 0) + t = self.set.copy() + t ^= t + self.assertEqual(len(t), 0) + + #============================================================================== class TestUpdateOps(unittest.TestCase): From rhettinger at users.sourceforge.net Sat Aug 13 04:30:07 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 04:30:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib sets.py,1.49,1.50 Message-ID: <20050813023007.012601E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3974/Lib Modified Files: sets.py Log Message: Teach the sets module to correctly compute s-=s and s^=s as the empty set. Index: sets.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/sets.py,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- sets.py 19 Nov 2003 15:52:14 -0000 1.49 +++ sets.py 13 Aug 2005 02:29:58 -0000 1.50 @@ -480,6 +480,8 @@ value = True if not isinstance(other, BaseSet): other = Set(other) + if self is other: + self.clear() for elt in other: if elt in data: del data[elt] @@ -497,6 +499,8 @@ data = self._data if not isinstance(other, BaseSet): other = Set(other) + if self is other: + self.clear() for elt in ifilter(data.has_key, other): del data[elt] From pje at users.sourceforge.net Sat Aug 13 05:29:09 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sat, 13 Aug 2005 05:29:09 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects genobject.c,1.5,1.6 Message-ID: <20050813032909.CF8281E4005@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12609/Objects Modified Files: genobject.c Log Message: Fix a too-aggressive assert (see SF#1257960). Previously, gen_iternext was never called during interpreter shutdown GC, so the f_back!=NULL assertion was correct. Now that generators get close()d during GC, the assertion was being triggered because the generator close() was being called as the top-level frame. However, nothing actually is broken by this; it's just that the condition was unexpected in previous Python versions. Index: genobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/genobject.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- genobject.c 2 Aug 2005 00:46:45 -0000 1.5 +++ genobject.c 13 Aug 2005 03:29:00 -0000 1.6 @@ -82,7 +82,7 @@ /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - assert(f->f_back != NULL); + assert(f->f_back == tstate->frame); Py_CLEAR(f->f_back); /* If the generator just returned (as opposed to yielding), signal From rhettinger at users.sourceforge.net Sat Aug 13 10:28:14 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 10:28:14 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.47,1.48 Message-ID: <20050813082814.0BF021E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19968 Modified Files: setobject.c Log Message: * Bring lookkey() and lookkey_string() closer to dict version. * Use set_next() for looping in issubset() and frozenset_hash(). * Re-order the presentation of cmp and hash functions. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.47 retrieving revision 1.48 diff -u -d -r1.47 -r1.48 --- setobject.c 12 Aug 2005 20:48:39 -0000 1.47 +++ setobject.c 13 Aug 2005 08:28:03 -0000 1.48 @@ -61,6 +61,7 @@ setentry *table = so->table; register setentry *entry; register int restore_error; + register int checked_error; register int cmp; PyObject *err_type, *err_value, *err_tb; PyObject *startkey; @@ -70,11 +71,13 @@ if (entry->key == NULL || entry->key == key) return entry; - restore_error = 0; + restore_error = checked_error = 0; if (entry->key == dummy) freeslot = entry; else { if (entry->hash == hash) { + /* error can't have been checked yet */ + checked_error = 1; if (_PyErr_OCCURRED()) { restore_error = 1; PyErr_Fetch(&err_type, &err_value, &err_tb); @@ -111,10 +114,13 @@ if (entry->key == key) break; if (entry->hash == hash && entry->key != dummy) { - if (_PyErr_OCCURRED()) { - restore_error = 1; - PyErr_Fetch(&err_type, &err_value, - &err_tb); + if (!checked_error) { + checked_error = 1; + if (_PyErr_OCCURRED()) { + restore_error = 1; + PyErr_Fetch(&err_type, &err_value, + &err_tb); + } } startkey = entry->key; cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); @@ -174,44 +180,28 @@ entry = &table[i]; if (entry->key == NULL || entry->key == key) return entry; - if (so->fill != so->used) { - if (entry->key == dummy) - freeslot = entry; - else { - if (entry->hash == hash && _PyString_Eq(entry->key, key)) - return entry; - freeslot = NULL; - } - - /* In the loop, key == dummy is by far (factor of 100s) the - least likely outcome, so test for that last. */ - for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { - i = (i << 2) + i + perturb + 1; - entry = &table[i & mask]; - if (entry->key == NULL) - return freeslot == NULL ? entry : freeslot; - if (entry->key == key - || (entry->hash == hash - && entry->key != dummy - && _PyString_Eq(entry->key, key))) - return entry; - if (entry->key == dummy && freeslot == NULL) - freeslot = entry; - } - } else { - /* Simplified loop when there are no dummy entries. */ + if (entry->key == dummy) + freeslot = entry; + else { if (entry->hash == hash && _PyString_Eq(entry->key, key)) return entry; - for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { - i = (i << 2) + i + perturb + 1; - entry = &table[i & mask]; - if (entry->key == NULL) - return entry; - if (entry->key == key - || (entry->hash == hash - && _PyString_Eq(entry->key, key))) - return entry; - } + freeslot = NULL; + } + + /* In the loop, key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + entry = &table[i & mask]; + if (entry->key == NULL) + return freeslot == NULL ? entry : freeslot; + if (entry->key == key + || (entry->hash == hash + && entry->key != dummy + && _PyString_Eq(entry->key, key))) + return entry; + if (entry->key == dummy && freeslot == NULL) + freeslot = entry; } } @@ -1369,11 +1359,11 @@ static PyObject * set_issubset(PySetObject *so, PyObject *other) { - PyObject *tmp, *result; - register setentry *entry; - register int i; + setentry *entry; + int pos = 0; if (!PyAnySet_Check(other)) { + PyObject *tmp, *result; tmp = make_new_set(&PySet_Type, other); if (tmp == NULL) return NULL; @@ -1384,10 +1374,7 @@ if (set_len((PyObject *)so) > set_len(other)) Py_RETURN_FALSE; - entry = &so->table[0]; - for (i=so->used ; i ; entry++, i--) { - while (entry->key == NULL || entry->key==dummy) - entry++; + while (set_next(so, &pos, &entry)) { if (!set_contains_entry((PySetObject *)other, entry)) Py_RETURN_FALSE; } @@ -1414,51 +1401,6 @@ PyDoc_STRVAR(issuperset_doc, "Report whether this set contains another set."); -static long -set_nohash(PyObject *self) -{ - PyErr_SetString(PyExc_TypeError, "set objects are unhashable"); - return -1; -} - -static int -set_nocmp(PyObject *self) -{ - PyErr_SetString(PyExc_TypeError, "cannot compare sets using cmp()"); - return -1; -} - -static long -frozenset_hash(PyObject *self) -{ - PySetObject *so = (PySetObject *)self; - long h, hash = 1927868237L; - setentry *entry; - int i; - - if (so->hash != -1) - return so->hash; - - hash *= set_len(self) + 1; - entry = &so->table[0]; - for (i=so->used ; i ; entry++, i--) { - while (entry->key == NULL || entry->key==dummy) - entry++; - /* Work to increase the bit dispersion for closely spaced hash - values. The is important because some use cases have many - combinations of a small number of elements with nearby - hashes so that many distinct combinations collapse to only - a handful of distinct hash values. */ - h = entry->hash; - hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u; - } - hash = hash * 69069L + 907133923L; - if (hash == -1) - hash = 590923713L; - so->hash = hash; - return hash; -} - static PyObject * set_richcompare(PySetObject *v, PyObject *w, int op) { @@ -1502,6 +1444,48 @@ return Py_NotImplemented; } +static int +set_nocmp(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "cannot compare sets using cmp()"); + return -1; +} + +static long +frozenset_hash(PyObject *self) +{ + PySetObject *so = (PySetObject *)self; + long h, hash = 1927868237L; + setentry *entry; + int pos = 0; + + if (so->hash != -1) + return so->hash; + + hash *= set_len(self) + 1; + while (set_next(so, &pos, &entry)) { + /* Work to increase the bit dispersion for closely spaced hash + values. The is important because some use cases have many + combinations of a small number of elements with nearby + hashes so that many distinct combinations collapse to only + a handful of distinct hash values. */ + h = entry->hash; + hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u; + } + hash = hash * 69069L + 907133923L; + if (hash == -1) + hash = 590923713L; + so->hash = hash; + return hash; +} + +static long +set_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "set objects are unhashable"); + return -1; +} + static PyObject * set_repr(PySetObject *so) { From birkenfeld at users.sourceforge.net Sat Aug 13 11:06:33 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Sat, 13 Aug 2005 11:06:33 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_doctest.py, 1.52, 1.52.2.1 Message-ID: <20050813090633.D6F371E4007@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26252/Lib/test Modified Files: Tag: release24-maint test_doctest.py Log Message: Complete backport of #1172785 fix. Index: test_doctest.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_doctest.py,v retrieving revision 1.52 retrieving revision 1.52.2.1 diff -u -d -r1.52 -r1.52.2.1 --- test_doctest.py 8 Nov 2004 22:07:36 -0000 1.52 +++ test_doctest.py 13 Aug 2005 09:06:24 -0000 1.52.2.1 @@ -1517,6 +1517,7 @@ ## 44 # # Yee ha! + >>> name = 'test.test_doctest.SampleNewStyleClass' >>> print doctest.testsource(test.test_doctest, name) @@ -1525,6 +1526,7 @@ ## 1 ## 2 ## 3 + >>> name = 'test.test_doctest.SampleClass.a_classmethod' >>> print doctest.testsource(test.test_doctest, name) @@ -1534,6 +1536,7 @@ print SampleClass(0).a_classmethod(10) # Expected: ## 12 + """ def test_debug(): r""" From birkenfeld at users.sourceforge.net Sat Aug 13 11:06:33 2005 From: birkenfeld at users.sourceforge.net (birkenfeld@users.sourceforge.net) Date: Sat, 13 Aug 2005 11:06:33 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib doctest.py, 1.120.2.2, 1.120.2.3 Message-ID: <20050813090633.E1B351E4009@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26252/Lib Modified Files: Tag: release24-maint doctest.py Log Message: Complete backport of #1172785 fix. Index: doctest.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v retrieving revision 1.120.2.2 retrieving revision 1.120.2.3 diff -u -d -r1.120.2.2 -r1.120.2.3 --- doctest.py 26 Jun 2005 22:23:44 -0000 1.120.2.2 +++ doctest.py 13 Aug 2005 09:06:24 -0000 1.120.2.3 @@ -2472,6 +2472,7 @@ blah # # Ho hum + """ output = [] for piece in DocTestParser().parse(s): From rhettinger at users.sourceforge.net Sat Aug 13 11:28:58 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Sat, 13 Aug 2005 11:28:58 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.48,1.49 Message-ID: <20050813092858.123931E4009@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29128 Modified Files: setobject.c Log Message: More function re-ordering (placing like functions together). Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.48 retrieving revision 1.49 diff -u -d -r1.48 -r1.49 --- setobject.c 13 Aug 2005 08:28:03 -0000 1.48 +++ setobject.c 13 Aug 2005 09:28:48 -0000 1.49 @@ -566,6 +566,57 @@ return key != NULL && key != dummy; } +static PyObject * +set_pop(PySetObject *so) +{ + PyObject *key; + register setentry *entry; + register int i = 0; + + assert (PyAnySet_Check(so)); + if (so->used == 0) { + PyErr_SetString(PyExc_KeyError, "pop from an empty set"); + return NULL; + } + + /* Set entry to "the first" unused or dummy set entry. We abuse + * the hash field of slot 0 to hold a search finger: + * If slot 0 has a value, use slot 0. + * Else slot 0 is being used to hold a search finger, + * and we use its hash value as the first index to look. + */ + entry = &so->table[0]; + if (entry->key == NULL || entry->key == dummy) { + i = (int)entry->hash; + /* The hash field may be a real hash value, or it may be a + * legit search finger, or it may be a once-legit search + * finger that's out of bounds now because it wrapped around + * or the table shrunk -- simply make sure it's in bounds now. + */ + if (i > so->mask || i < 1) + i = 1; /* skip slot 0 */ + while ((entry = &so->table[i])->key == NULL || entry->key==dummy) { + i++; + if (i > so->mask) + i = 1; + } + } + key = entry->key; + Py_INCREF(dummy); + entry->key = dummy; + so->used--; + so->table[0].hash = i + 1; /* next place to start */ + return key; +} + +PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element."); + +static int +set_len(PyObject *so) +{ + return ((PySetObject *)so)->used; +} + /***** Set iterator type ***********************************************/ static PyTypeObject PySetIter_Type; /* Forward */ @@ -683,12 +734,6 @@ }; static int -set_len(PyObject *so) -{ - return ((PySetObject *)so)->used; -} - -static int set_update_internal(PySetObject *so, PyObject *other) { PyObject *key, *it; @@ -918,41 +963,6 @@ } } -static int -set_contains(PySetObject *so, PyObject *key) -{ - PyObject *tmpkey; - int rv; - - rv = set_contains_key(so, key); - if (rv == -1) { - if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) - return -1; - PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); - if (tmpkey == NULL) - return -1; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); - rv = set_contains(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); - Py_DECREF(tmpkey); - } - return rv; -} - -static PyObject * -set_direct_contains(PySetObject *so, PyObject *key) -{ - long result; - - result = set_contains(so, key); - if (result == -1) - return NULL; - return PyBool_FromLong(result); -} - -PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x."); - static PyObject * set_copy(PySetObject *so) { @@ -1537,6 +1547,41 @@ \n\ This has no effect if the element is already present."); +static int +set_contains(PySetObject *so, PyObject *key) +{ + PyObject *tmpkey; + int rv; + + rv = set_contains_key(so, key); + if (rv == -1) { + if (!PyAnySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + return -1; + PyErr_Clear(); + tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + if (tmpkey == NULL) + return -1; + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); + rv = set_contains(so, tmpkey); + set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); + Py_DECREF(tmpkey); + } + return rv; +} + +static PyObject * +set_direct_contains(PySetObject *so, PyObject *key) +{ + long result; + + result = set_contains(so, key); + if (result == -1) + return NULL; + return PyBool_FromLong(result); +} + +PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x."); + static PyObject * set_remove(PySetObject *so, PyObject *key) { @@ -1597,51 +1642,6 @@ If the element is not a member, do nothing."); static PyObject * -set_pop(PySetObject *so) -{ - PyObject *key; - register setentry *entry; - register int i = 0; - - assert (PyAnySet_Check(so)); - if (so->used == 0) { - PyErr_SetString(PyExc_KeyError, "pop from an empty set"); - return NULL; - } - - /* Set entry to "the first" unused or dummy set entry. We abuse - * the hash field of slot 0 to hold a search finger: - * If slot 0 has a value, use slot 0. - * Else slot 0 is being used to hold a search finger, - * and we use its hash value as the first index to look. - */ - entry = &so->table[0]; - if (entry->key == NULL || entry->key == dummy) { - i = (int)entry->hash; - /* The hash field may be a real hash value, or it may be a - * legit search finger, or it may be a once-legit search - * finger that's out of bounds now because it wrapped around - * or the table shrunk -- simply make sure it's in bounds now. - */ - if (i > so->mask || i < 1) - i = 1; /* skip slot 0 */ - while ((entry = &so->table[i])->key == NULL || entry->key==dummy) { - i++; - if (i > so->mask) - i = 1; - } - } - key = entry->key; - Py_INCREF(dummy); - entry->key = dummy; - so->used--; - so->table[0].hash = i + 1; /* next place to start */ - return key; -} - -PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element."); - -static PyObject * set_reduce(PySetObject *so) { PyObject *keys=NULL, *args=NULL, *result=NULL, *dict=NULL; From goodger at users.sourceforge.net Sat Aug 13 14:38:03 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 14:38:03 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0006.txt, 1.11, 1.12 pep-0000.txt, 1.340, 1.341 Message-ID: <20050813123803.9BA921E4007@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25234 Modified Files: pep-0006.txt pep-0000.txt Log Message: changed PEP 6 type to Process Index: pep-0006.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0006.txt,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- pep-0006.txt 19 Aug 2004 07:06:12 -0000 1.11 +++ pep-0006.txt 13 Aug 2005 12:37:53 -0000 1.12 @@ -3,7 +3,7 @@ Version: $Revision$ Author: aahz at pobox.com (Aahz), anthony at interlink.com.au (Anthony Baxter) Status: Active -Type: Informational +Type: Process Created: 15-Mar-2001 Post-History: 15-Mar-2001 18-Apr-2001 19-Aug-2004 @@ -55,6 +55,7 @@ Breaking any of these prohibitions requires a BDFL proclamation (and a prominent warning in the release notes). + Not-Quite-Prohibitions Where possible, bug fix releases should also: @@ -69,6 +70,7 @@ the standard library should not change behavior, or worse yet, APIs. + Applicability of Prohibitions The above prohibitions and not-quite-prohibitions apply both @@ -80,6 +82,7 @@ the community happy that a bug fix release is a painless and safe upgrade. + Helping the Bug Fix Releases Happen Here's a few pointers on helping the bug fix release process along. @@ -98,6 +101,7 @@ wait until 48 hours before a bug fix release is due, and then start asking for bug fixes to be included. + Version Numbers Starting with Python 2.0, all major releases are required to have @@ -111,10 +115,11 @@ the branch is named 'release2x-maint'. For example, the branch for the 2.3 maintenance releases is release23-maint + Procedure - The process for managing bugfix releases is modeled in part on the Tcl - system [1]. + The process for managing bugfix releases is modeled in part on the + Tcl system [1]. The Patch Czar is the counterpart to the BDFL for bugfix releases. However, the BDFL and designated appointees retain veto power over @@ -151,6 +156,7 @@ bugfix releases. If, however, someone qualified wishes to continue the work to maintain an older release, they should be encouraged. + Patch Czar History Anthony Baxter is the Patch Czar for 2.3.1 through 2.3.4. @@ -190,6 +196,7 @@ Anthony Baxter then took this PEP and revised it, based on lessons from the 2.3 release cycle. + References [1] http://www.tcl.tk/cgi-bin/tct/tip/28.html Index: pep-0000.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0000.txt,v retrieving revision 1.340 retrieving revision 1.341 diff -u -d -r1.340 -r1.341 --- pep-0000.txt 13 Aug 2005 01:37:32 -0000 1.340 +++ pep-0000.txt 13 Aug 2005 12:37:53 -0000 1.341 @@ -35,7 +35,7 @@ I 3 Guidelines for Handling Bug Reports Hylton I 4 Deprecation of Standard Modules von Loewis I 5 Guidelines for Language Evolution Prescod - I 6 Bug Fix Releases Aahz + P 6 Bug Fix Releases Aahz I 7 Style Guide for C Code GvR I 8 Style Guide for Python Code GvR, Warsaw I 9 Sample Plaintext PEP Template Warsaw @@ -230,7 +230,7 @@ I 3 Guidelines for Handling Bug Reports Hylton I 4 Deprecation of Standard Modules von Loewis I 5 Guidelines for Language Evolution Prescod - I 6 Bug Fix Releases Aahz + P 6 Bug Fix Releases Aahz I 7 Style Guide for C Code GvR I 8 Style Guide for Python Code GvR, Warsaw I 9 Sample Plaintext PEP Template Warsaw From goodger at users.sourceforge.net Sat Aug 13 20:01:14 2005 From: goodger at users.sourceforge.net (goodger@users.sourceforge.net) Date: Sat, 13 Aug 2005 20:01:14 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0001.txt,1.47,1.48 Message-ID: <20050813180114.4A9BD1E4009@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23663 Modified Files: pep-0001.txt Log Message: Process PEPs may also be Active Index: pep-0001.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0001.txt,v retrieving revision 1.47 retrieving revision 1.48 diff -u -d -r1.47 -r1.48 --- pep-0001.txt 13 Aug 2005 01:42:17 -0000 1.47 +++ pep-0001.txt 13 Aug 2005 18:01:01 -0000 1.48 @@ -164,8 +164,8 @@ .. image:: pep-0001-1.png -Some Informational PEPs may also have a status of "Active" if they are -never meant to be completed. E.g. PEP 1 (this PEP). +Some Informational and Process PEPs may also have a status of "Active" +if they are never meant to be completed. E.g. PEP 1 (this PEP). What belongs in a successful PEP? From gregorykjohnson at users.sourceforge.net Sun Aug 14 00:41:45 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Sun, 14 Aug 2005 00:41:45 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/mailbox libmailbox.tex, 1.8, 1.9 mailbox.py, 1.7, 1.8 test_mailbox.py, 1.6, 1.7 Message-ID: <20050813224145.6EA9D1E4012@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10815 Modified Files: libmailbox.tex mailbox.py test_mailbox.py Log Message: * Regularize list_, get_, set_, add_, remove_, join_, and leave_ names: * Maildir and MH: keep list_folders(), change open_folder() to get_folder(), introduce add_folder, and keep remove_folder(). * Babyl: change list_labels() to get_labels(). * MaildirMessage, mboxMessage, and MMDFMessage: keep get_flags() and set_flags(), change add_flags() to add_flag(), and change remove_flags() to remove_flag(). * MHMessage: change list_sequences() to get_sequences(), introduce set_sequences(), change join_sequence() to add_sequence(), and change leave_sequence() to remove_sequence() * BabylMessage: change list_labels() to get_labels(), introduce set_labels(), and keep add_label() and remove_label() * At the suggestion of A.M. Kuchling: * Avoid os.mknod(), for MacOS compatibility. * Introduce create=True parameter for Mailbox constructors. * Prefer iteritems() to items() in Mailbox.update(). * Use fcntl=None to avoid checking globals(). * Use os.path.dirname() instead of os.path.split()[0]. * Use "':' in x" instead of "x.index(':') != -1". * Use os.path.getatime() instead of os.stat().st_atime. * Remove module-level open() function, which clutters the API. Its original motivation was to regularize Mailbox constructors while keep backward compatability, but all Mailbox classes have been renamed and regularized except Maildir, which is already extremely similar. * Introduce subclasses of Error: NoSuchMailboxError, NotEmptyError, ExternalClashError, FormatError. Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- libmailbox.tex 5 Aug 2005 16:34:45 -0000 1.8 +++ libmailbox.tex 13 Aug 2005 22:41:33 -0000 1.9 @@ -75,33 +75,7 @@ \class{Mailbox} itself is intended to define an interface and to be inherited from by format-specific subclasses but is not intended to be instantiated. -Instead, you may directly instantiate a subclass or use the module-level -\function{open()} function to do so. - -\begin{funcdesc}{open}{path\optional{, format\optional{, factory}}} -Instantiate and return an instance of the appropriate \class{Mailbox} subclass -for the mailbox at \var{path}. The mailbox is created if it does not already -exist. - -If \var{format} is specified, it should be one of "Maildir", "mbox", "MH", -"Babyl", or "MMDF" (case insensitive). If \var{format} is not specified, then -the format of the mailbox is automatically detected. Automatic detection -involves inspecting the mailbox at \var{path} if such a mailbox exists or if -no such mailbox exists inspecting \var{path} itself and assuming Maildir -format if \var{path} ends with the system's path separator (e.g., "/" or -"\textbackslash") or mbox format otherwise. - -Parameter \var{factory} is a callable object that accepts a file-like object -containing a raw message as its parameter and returns a message -representation. If \var{factory} is not specified, \class{Message} instances -are used to represent messages. - -For historical reasons, some subclasses of \class{Mailbox} take instantiation -arguments with different purposes, names, or default values. The -\function{open()} function is intended to present a convenient, uniform -interface for \class{Mailbox} instantiation while maintaining backward -compatibility. -\end{funcdesc} +Instead, directly instantiate a subclass. \class{Mailbox} instances have the following methods: @@ -256,11 +230,17 @@ \subsubsection{\class{Maildir}} \label{mailbox-maildir} -\begin{classdesc}{Maildir}{dirname\optional{, factory}} +\begin{classdesc}{Maildir}{dirname\optional{, factory=rfc822.Message\optional{, +create=True}}} A subclass of \class{Mailbox} for mailboxes in Maildir format. Parameter -\var{dirname} is the path to the mailbox. Parameter \var{factory} has the same -meaning as with the module-level \method{open()} function. For historical -reasons, \var{factory} defaults to \class{rfc822.Message}. +\var{factory} is a callable object that accepts a file-like object containing a +raw message as its parameter and returns a message representation. If +\var{factory} is \code{None}, \class{MaildirMessage} instances are used. +If \var{create} is \code{True}, the mailbox is created if it does not exist. + +It is for historical reasons that \var{factory} defaults to +\class{rfc822.Message} and that \var{dirname} is named as such rather than +\var{path}. \end{classdesc} Maildir is a directory-based mailbox format invented for the qmail MTA and now @@ -270,28 +250,35 @@ without data corruption, so file locking is unnecessary. Folders, as introduced by the Courier MTA, are supported. Each folder is -itself a Maildir mailbox and is represented as a \class{Maildir} instance. Any -subdirectory of the main Maildir directory is considered a folder if -\character{.} is the first character in its name. Folder names are represented -without the leading dot. For example, "Sent.2005.07" would be the name of a -folder implemented with a directory called ".Sent.2005.07" on the filesystem. +itself a Maildir mailbox. Any subdirectory of the main Maildir directory is +considered a folder if \character{.} is the first character in its name. Folder +names are represented without the leading dot. For example, "Sent" would be the +name of a folder implemented with a directory called ".Sent" on the filesystem. +Folders should not be nested, i.e., a Maildir mailbox that is itself a folder +should not contain other folders. Instead, logical nesting may be indicated +using \character{.} to delimit levels---for example, "Archived.2005.07". \class{Maildir} instances have all of the methods of \class{Mailbox} in addition to the following: \begin{methoddesc}{list_folders}{} -Return a list of the names of all folders. If there are no folders, the empty -list is returned. +Return a list of the names of all folders. \end{methoddesc} -\begin{methoddesc}{open_folder}{name} +\begin{methoddesc}{get_folder}{folder} Return a \class{Maildir} instance representing the folder whose name is -\var{name}. The folder will be created if it does not exist. +\var{folder}. A \exception{NoSuchMailboxError} exception is raised if the +folder does not exist. \end{methoddesc} -\begin{methoddesc}{remove_folder}{name} -Delete the folder whose name is \var{name}. If the folder contains any -messages, a \exception{mailbox.Error} exception will be raised and the folder +\begin{methoddesc}{add_folder}{folder} +Create a folder whose name is \var{folder} and return a \class{Maildir} +instance representing it. +\end{methoddesc} + +\begin{methoddesc}{remove_folder}{folder} +Delete the folder whose name is \var{folder}. If the folder contains any +messages, a \exception{NotEmptyError} exception will be raised and the folder will not be deleted. \end{methoddesc} @@ -301,13 +288,6 @@ should do this occassionally. \end{methoddesc} -\warning{Three \class{Maildir} methods---\method{add()}, -\method{__setitem__()}, and \method{update()}---generate unique file names -based upon the current process ID. When using multiple threads, undetected -clashes may occur and cause corruption of the mailbox unless threads are -coordinated to avoid using these methods to manipulate the same mailbox -simultaneously.} - Some \class{Mailbox} methods implemented by \class{Maildir} deserve special remarks: @@ -350,10 +330,12 @@ \subsubsection{\class{mbox}} \label{mailbox-mbox} -\begin{classdesc}{mbox}{path\optional{, factory}} -A subclass of \class{Mailbox} for mailboxes in mbox format. Parameters -\var{path} and \var{factory} has the same meaning as with the module-level -\method{open()} function. +\begin{classdesc}{mbox}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in mbox format. Parameter +\var{factory} is a callable object that accepts a file-like object containing a +raw message as its parameter and returns a message representation. If +\var{factory} is \code{None}, \class{mboxMessage} instances are used. If +\var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} The mbox format is the classic format for storing mail on \UNIX{} systems. All @@ -392,10 +374,12 @@ \subsubsection{\class{MH}} \label{mailbox-mh} -\begin{classdesc}{MH}{path\optional{, factory}} -A subclass of \class{Mailbox} for mailboxes in MH format. Parameters \var{path} -and \var{factory} has the same meaning as with the module-level \method{open()} -function. +\begin{classdesc}{MH}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in MH format. Parameter +\var{factory} is a callable object that accepts a file-like object containing a +raw message as its parameter and returns a message representation. If +\var{factory} is \code{None}, \class{MHMessage} instances are used. If +\var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} MH is a directory-based mailbox format invented for the MH Message Handling @@ -420,18 +404,23 @@ the following: \begin{methoddesc}{list_folders}{} -Return a list of the names of all folders. If there are no folders, the empty -list is returned. +Return a list of the names of all folders. \end{methoddesc} -\begin{methoddesc}{open_folder}{name} -Return an \class{MH} instance representing the folder whose name is \var{name}. -The folder will be created if it does not exist. +\begin{methoddesc}{get_folder}{folder} +Return an \class{MH} instance representing the folder whose name is +\var{folder}. A \exception{NoSuchMailboxError} exception is raised if the +folder does not exist. \end{methoddesc} -\begin{methoddesc}{remove_folder}{name} -Delete the folder whose name is \var{name}. If the folder contains any -messages, a \exception{mailbox.Error} exception will be raised and the folder +\begin{methoddesc}{add_folder}{folder} +Create a folder whose name is \var{folder} and return an \class{MH} instance +representing it. +\end{methoddesc} + +\begin{methoddesc}{remove_folder}{folder} +Delete the folder whose name is \var{folder}. If the folder contains any +messages, a \exception{NotEmptyError} exception will be raised and the folder will not be deleted. \end{methoddesc} @@ -474,12 +463,6 @@ \class{MH} instances do not keep any open files, so this method does nothing. \end{methoddesc} -\begin{classdesc}{MH}{path\optional{, factory}} -A subclass of \class{Mailbox} for mailboxes in MH format. Parameters \var{path} -and \var{factory} have the same meaning as with the module-level -\method{open()} function. -\end{classdesc} - \class{MH} instances have all of the methods of \class{Mailbox} in addition to the following: @@ -493,17 +476,19 @@ \seelink{http://www.nongnu.org/nmh/}{nmh - Message Handling System}{Home page of \program{nmh}, a modern version of the original \program{mh}.} \seelink{http://www.ics.uci.edu/\tilde{}mh/book/}{MH \& nmh: Email for Users \& -Programmers}{An open-source book on \program{mh} and \program{nmh}, with some +Programmers}{A GPL-licensed book on \program{mh} and \program{nmh}, with some information on the mailbox format.} \end{seealso} \subsubsection{\class{Babyl}} \label{mailbox-babyl} -\begin{classdesc}{Babyl}{path\optional{, factory}} -A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameters -\var{path} and \var{factory} have the same meaning as with the module-level -\method{open()} function. +\begin{classdesc}{Babyl}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameter +\var{factory} is a callable object that accepts a file-like object containing a +raw message as its parameter and returns a message representation. If +\var{factory} is \code{None}, \class{BabylMessage} instances are used. If +\var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} Babyl is a single-file mailbox format invented for the \program{Rmail} mail @@ -522,8 +507,8 @@ \class{Babyl} instances have all of the methods of \class{Mailbox} in addition to the following: -\begin{methoddesc}{list_labels}{} -Return a list of all user-defined labels in the mailbox. +\begin{methoddesc}{get_labels}{} +Return a list of the names of all user-defined labels used in the mailbox. \end{methoddesc} Some \class{Mailbox} methods implemented by \class{Babyl} deserve special @@ -546,10 +531,12 @@ \subsubsection{\class{MMDF}} \label{mailbox-mmdf} -\begin{classdesc}{MMDF}{path\optional{, factory}} -A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameters -\var{path} and \var{factory} have the same meaning as with the module-level -\method{open()} function. +\begin{classdesc}{MMDF}{path\optional{, factory=None\optional{, create=True}}} +A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameter +\var{factory} is a callable object that accepts a file-like object containing a +raw message as its parameter and returns a message representation. If +\var{factory} is \code{None}, \class{MMDFMessage} instances are used. If +\var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} MMDF is a single-file mailbox format invented for the Multichannel Memorandum @@ -671,30 +658,22 @@ \end{methoddesc} \begin{methoddesc}{set_flags}{flags} -Set the flags specified by \var{flags} and unset all others. Parameter -\var{flags} should be the concatenation in any order of zero or more -occurrences of each of \character{D}, \character{F}, \character{P}, -\character{R}, \character{S}, and \character{T}. The current "info" is -overwritten whether or not it contains experimental information instead of -flags. +Set the flags specified by \var{flags} and unset all others. \end{methoddesc} -\begin{methoddesc}{add_flags}{flags} -Set the flags specified by \var{flags} (if they are not set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{D}, \character{F}, -\character{P}, \character{R}, \character{S}, and \character{T}. The current -"info" is overwritten whether or not it contains experimental information -instead of flags. +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. The current "info" is overwritten whether or not it contains +experimental information rather than +flags. \end{methoddesc} -\begin{methoddesc}{remove_flags}{flags} -Unset the flags specified by \var{flags} (if they are set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{D}, \character{F}, -\character{P}, \character{R}, \character{S}, and \character{T}. If "info" -contains experimental information rather than flags, the current "info" is not -modified. +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. If "info" contains experimental information rather than flags, the +current "info" is not modified. \end{methoddesc} \begin{methoddesc}{get_info}{} @@ -804,18 +783,16 @@ \character{F}, and \character{A}. \end{methoddesc} -\begin{methoddesc}{add_flags}{flags} -Set the flags specified by \var{flags} (if they are not set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{R}, \character{O}, -\character{D}, \character{F}, and \character{A}. +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. \end{methoddesc} -\begin{methoddesc}{remove_flags}{flags} -Unset the flags specified by \var{flags} (if they are set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{R}, \character{O}, -\character{D}, \character{F}, and \character{A}. +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. \end{methoddesc} When an \class{mboxMessage} instance is created based upon a @@ -888,16 +865,20 @@ \class{MHMessage} instances offer the following methods: -\begin{methoddesc}{list_sequences}{} -Return a list of the names of sequences that include the message. +\begin{methoddesc}{get_sequences}{} +Return a list of the names of sequences that include this message. \end{methoddesc} -\begin{methoddesc}{join_sequence}{sequence} -Add \var{sequence} to the list of sequences that include the message. +\begin{methoddesc}{set_sequences}{sequences} +Set the list of sequences that include this message. \end{methoddesc} -\begin{methoddesc}{leave_sequence}{sequence} -Remove \var{sequence} from the list of sequences that include the message. +\begin{methoddesc}{add_sequence}{sequence} +Add \var{sequence} to the list of sequences that include this message. +\end{methoddesc} + +\begin{methoddesc}{remove_sequence}{sequence} +Remove \var{sequence} from the list of sequences that include this message. \end{methoddesc} When an \class{MHMessage} instance is created based upon a @@ -963,10 +944,14 @@ \class{BabylMessage} instances offer the following methods: -\begin{methoddesc}{list_labels}{} +\begin{methoddesc}{get_labels}{} Return a list of labels on the message. \end{methoddesc} +\begin{methoddesc}{set_labels}{labels} +Set the list of labels on the message to \var{labels}. +\end{methoddesc} + \begin{methoddesc}{add_label}{label} Add \var{label} to the list of labels on the message. \end{methoddesc} @@ -1088,18 +1073,16 @@ \character{F}, and \character{A}. \end{methoddesc} -\begin{methoddesc}{add_flags}{flags} -Set the flags specified by \var{flags} (if they are not set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{R}, \character{O}, -\character{D}, \character{F}, and \character{A}. +\begin{methoddesc}{add_flag}{flag} +Set the flag(s) specified by \var{flag} without changing other flags. To add +more than one flag at a time, \var{flag} may be a string of more than one +character. \end{methoddesc} -\begin{methoddesc}{remove_flags}{flags} -Unset the flags specified by \var{flags} (if they are set), and don't change -other flags. Parameter \var{flags} should be the concatenation in any order of -zero or more occurrences of each of \character{R}, \character{O}, -\character{D}, \character{F}, and \character{A}. +\begin{methoddesc}{remove_flag}{flag} +Unset the flag(s) specified by \var{flag} without changing other flags. To +remove more than one flag at a time, \var{flag} maybe a string of more than one +character. \end{methoddesc} When an \class{MMDFMessage} instance is created based upon a Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- mailbox.py 5 Aug 2005 16:34:45 -0000 1.7 +++ mailbox.py 13 Aug 2005 22:41:33 -0000 1.8 @@ -16,21 +16,17 @@ try: import fnctl except ImportError: - pass + fcntl = None -__all__ = [ 'open', 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', +__all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', 'BabylMessage', 'MMDFMessage' ] -def open(path, format=None, factory=None): - """Open a mailbox at the given path and return a Mailbox instance.""" - - class Mailbox: """A group of messages in a particular place.""" - def __init__(self, path, factory=None): + def __init__(self, path, factory=None, create=True): """Initialize a Mailbox instance.""" self._path = os.path.abspath(path) self._factory = factory @@ -154,7 +150,9 @@ def update(self, arg=None): """Change the messages that correspond to certain keys.""" - if hasattr(arg, 'items'): + if hasattr(arg, 'iteritems'): + source = arg.iteritems() + elif hasattr(arg, 'items'): source = arg.items() else: source = arg @@ -195,14 +193,17 @@ class Maildir(Mailbox): """A qmail-style Maildir mailbox.""" - def __init__(self, dirname, factory=rfc822.Message): + def __init__(self, dirname, factory=rfc822.Message, create=True): """Initialize a Maildir instance.""" - Mailbox.__init__(self, dirname, factory) + Mailbox.__init__(self, dirname, factory, create) if not os.path.exists(self._path): - os.mkdir(self._path, 0700) - os.mkdir(os.path.join(self._path, 'tmp'), 0700) - os.mkdir(os.path.join(self._path, 'new'), 0700) - os.mkdir(os.path.join(self._path, 'cur'), 0700) + if create: + os.mkdir(self._path, 0700) + os.mkdir(os.path.join(self._path, 'tmp'), 0700) + os.mkdir(os.path.join(self._path, 'new'), 0700) + os.mkdir(os.path.join(self._path, 'cur'), 0700) + else: + raise NoSuchMailboxError, self._path self._toc = {} def add(self, message): @@ -253,8 +254,8 @@ else: # temp's subdir and suffix were defaults from add(). dominant_subpath = old_subpath - subdir = os.path.split(dominant_subpath)[0] - if dominant_subpath.find(':') != -1: + subdir = os.path.dirname(dominant_subpath) + if ':' in dominant_subpath: suffix = ':' + dominant_subpath.split(':')[-1] else: suffix = '' @@ -273,7 +274,7 @@ f.close() subdir, name = os.path.split(subpath) msg.set_subdir(subdir) - if name.find(':') != -1: + if ':' in name: msg.set_info(name.split(':')[-1]) return msg @@ -327,27 +328,31 @@ result.append(entry[1:]) return result - def open_folder(self, name): - """Return a Maildir for the named folder, creating it if necessary.""" - path = os.path.join(self._path, '.' + name) - maildirfolder_path = os.path.join(path, 'maildirfolder') + def get_folder(self, folder): + """Return a Maildir instance for the named folder.""" + return Maildir(os.path.join(self._path, '.' + folder), create=False) + + def add_folder(self, folder): + """Create a folder and return a Maildir instance representing it.""" + path = os.path.join(self._path, '.' + folder) result = Maildir(path) + maildirfolder_path = os.path.join(path, 'maildirfolder') if not os.path.exists(maildirfolder_path): - os.mknod(maildirfolder_path, 0600 | stat.S_IFREG) + os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY)) return result - def remove_folder(self, name): + def remove_folder(self, folder): """Delete the named folder, which must be empty.""" - path = os.path.join(self._path, '.' + name) + path = os.path.join(self._path, '.' + folder) for entry in os.listdir(os.path.join(path, 'new')) + \ os.listdir(os.path.join(path, 'cur')): if len(entry) < 1 or entry[0] != '.': - raise Error, "Folder '%s' contains message" % name + raise NotEmptyError, "Folder '%s' contains message" % folder for entry in os.listdir(path): if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ os.path.isdir(os.path.join(path, entry)): - raise Error, "Folder '%s' contains subdirectory '%s'" % \ - (name, entry) + raise NotEmptyError, "Folder '%s' contains subdirectory '%s'" \ + % (folder, entry) for root, dirs, files in os.walk(path, topdown=False): for entry in files: os.remove(os.path.join(root, entry)) @@ -360,7 +365,7 @@ now = time.time() for entry in os.listdir(os.path.join(self._path, 'tmp')): path = os.path.join(self._path, 'tmp', entry) - if now - os.stat(path).st_atime > 129600: # 60 * 60 * 36 + if now - os.path.getatime(path) > 129600: # 60 * 60 * 36 os.remove(path) _count = 1 # This is used to generate unique file names. @@ -385,7 +390,8 @@ else: raise else: - raise Error, "Name clash prevented file creation: '%s'" % path + raise ExternalClashError, \ + "Name clash prevented file creation: '%s'" % path def _refresh(self): """Update table of contents mapping.""" @@ -412,14 +418,17 @@ class _singlefileMailbox(Mailbox): """A single-file mailbox.""" - def __init__(self, path, factory=None): + def __init__(self, path, factory=None, create=True): """Initialize a single-file mailbox.""" - Mailbox.__init__(self, path, factory) + Mailbox.__init__(self, path, factory, create) try: f = file(self._path, 'r+') except IOError, e: if e.errno == errno.ENOENT: - f = file(self._path, 'w+') + if create: + f = file(self._path, 'w+') + else: + raise NoSuchMailboxError, self._path elif e.errno == errno.EACCES: f = file(self._path, 'r') else: @@ -544,7 +553,8 @@ def _assert_mtime(self): """Raise an exception if the file has been externally modified.""" if self._mtime != os.fstat(self._file.fileno()).st_mtime: - raise Error, 'External modifications detected: use refresh()' + raise ExternalClashError, \ + 'External modifications detected: use refresh()' def _set_mtime(self): """Store the current mtime.""" @@ -625,10 +635,10 @@ class mbox(_mboxMMDF): """A classic mbox mailbox.""" - def __init__(self, path, factory=None): + def __init__(self, path, factory=None, create=True): """Initialize an mbox mailbox.""" self._message_factory = mboxMessage - _mboxMMDF.__init__(self, path, factory) + _mboxMMDF.__init__(self, path, factory, create) def _pre_message_hook(self, f): """Called before writing each message to file f.""" @@ -657,10 +667,10 @@ class MMDF(_mboxMMDF): """An MMDF mailbox.""" - def __init__(self, path, factory=None): + def __init__(self, path, factory=None, create=True): """Initialize an MMDF mailbox.""" self._message_factory = MMDFMessage - _mboxMMDF.__init__(self, path, factory) + _mboxMMDF.__init__(self, path, factory, create) def _pre_message_hook(self, f): """Called before writing each message to file f.""" @@ -701,13 +711,16 @@ class MH(Mailbox): """An MH mailbox.""" - def __init__(self, path, factory=None): + def __init__(self, path, factory=None, create=True): """Initialize an MH instance.""" - Mailbox.__init__(self, path, factory) + Mailbox.__init__(self, path, factory, create) if not os.path.exists(self._path): - os.mkdir(self._path, 0700) - os.mknod(os.path.join(self._path, '.mh_sequences'), - 0600 | stat.S_IFREG) + if create: + os.mkdir(self._path, 0700) + os.close(os.open(os.path.join(self._path, '.mh_sequences'), + os.O_CREAT | os.O_WRONLY, 0600)) + else: + raise NoSuchMailboxError, self._path def add(self, message): """Add message and return assigned key.""" @@ -779,7 +792,7 @@ f.close() for name, key_list in self.get_sequences(): if key in key_list: - msg.join_sequence(name) + msg.add_sequence(name) return msg def get_string(self, key): @@ -829,27 +842,32 @@ return def list_folders(self): - """Return a list of folders in this mailbox.""" + """Return a list of folder names.""" result = [] for entry in os.listdir(self._path): if os.path.isdir(os.path.join(self._path, entry)): result.append(entry) return result - def open_folder(self, name): - """Return an MH instance for folder name, creating it if necessary.""" - return MH(os.path.join(self._path, name)) + def get_folder(self, folder): + """Return an MH instance for the named folder.""" + return MH(os.path.join(self._path, folder), create=False) - def remove_folder(self, name): - """Delete folder name.""" - path = os.path.join(self._path, name) + def add_folder(self, folder): + """Create a folder and return an MH instance representing it.""" + return Maildir(os.path.join(self._path, '.' + folder)) + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, folder) entries = os.listdir(path) if entries == ['.mh_sequences']: os.remove(os.path.join(path, '.mh_sequences')) elif entries == []: pass else: - raise Error, "Folder '%s' is not empty" % self._path + raise NotEmptyError, "Folder '%s' is not empty" % \ + self._path os.rmdir(path) def get_sequences(self): @@ -871,8 +889,8 @@ results[name] = [key for key in sorted(keys) \ if key in all_keys] except ValueError: - raise Error, "Invalid sequence specification: '%s'" % \ - line.rstrip() + raise FormatError, "Invalid sequence specification: " % \ + "'%s'" % line.rstrip() finally: f.close() return results @@ -942,7 +960,7 @@ def _dump_sequences(self, message, key): """Inspect a new MHMessage and update sequences appropriately.""" - pending_sequences = message.list_sequences() + pending_sequences = message.get_sequences() all_sequences = self.get_sequences() for name, key_list in all_sequences.iteritems(): if name in pending_sequences: @@ -1004,7 +1022,7 @@ """Return a file-like representation or raise a KeyError.""" return StringIO.StringIO(self.get_string(key)) - def list_labels(self): + def get_labels(self): """Return a list of user-defined labels in the mailbox.""" raise NotImplementedError, 'Method not yet implemented' @@ -1172,14 +1190,14 @@ """Set the given flags and unset all others.""" self._info = '2,' + ''.join(sorted(flags)) - def add_flags(self, flags): - """Set the given flags without changing others.""" - self.set_flags(''.join(set(self.get_flags()) | set(flags))) + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) - def remove_flags(self, flags): - """Unset the given string flags (if set) without changing others.""" + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" if self.get_flags() != '': - self.set_flags(''.join(set(self.get_flags()) - set(flags))) + self.set_flags(''.join(set(self.get_flags()) - set(flag))) def get_info(self): """Get the message's "info" as a string.""" @@ -1200,23 +1218,23 @@ elif isinstance(message, _mboxMMDFMessage): flags = set(self.get_flags()) if 'S' in flags: - message.add_flags('R') + message.add_flag('R') if self.get_subdir() == 'cur': - message.add_flags('O') + message.add_flag('O') if 'T' in flags: - message.add_flags('D') + message.add_flag('D') if 'F' in flags: - message.add_flags('F') + message.add_flag('F') if 'R' in flags: - message.add_flags('A') + message.add_flag('A') elif isinstance(message, MHMessage): flags = set(self.get_flags()) if 'S' not in flags: - message.join_sequence('unseen') + message.add_sequence('unseen') if 'R' in flags: - message.join_sequence('replied') + message.add_sequence('replied') if 'F' in flags: - message.join_sequence('flagged') + message.add_sequence('flagged') elif isinstance(message, BabylMessage): flags = set(self.get_flags()) if 'S' not in flags: @@ -1287,14 +1305,14 @@ except KeyError: self.add_header('X-Status', xstatus_flags) - def add_flags(self, flags): - """Set the given flags without changing others.""" - self.set_flags(''.join(set(self.get_flags()) | set(flags))) + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) - def remove_flags(self, flags): - """Unset the given string flags (if set) without changing others.""" + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" if 'Status' in self or 'X-Status' in self: - self.set_flags(''.join(set(self.get_flags()) - set(flags))) + self.set_flags(''.join(set(self.get_flags()) - set(flag))) def _explain_to(self, message): """Copy mbox- or MMDF-specific state to message insofar as possible.""" @@ -1303,24 +1321,24 @@ if 'O' in flags: message.set_subdir('cur') if 'F' in flags: - message.add_flags('F') + message.add_flag('F') if 'A' in flags: - message.add_flags('R') + message.add_flag('R') if 'R' in flags: - message.add_flags('S') + message.add_flag('S') if 'D' in flags: - message.add_flags('T') + message.add_flag('T') elif isinstance(message, _mboxMMDFMessage): message.set_flags(self.get_flags()) message.set_from(self.get_from()) elif isinstance(message, MHMessage): flags = set(self.get_flags()) if 'R' not in flags: - message.join_sequence('unseen') + message.add_sequence('unseen') if 'A' in flags: - message.join_sequence('replied') + message.add_sequence('replied') if 'F' in flags: - message.join_sequence('flagged') + message.add_sequence('flagged') elif isinstance(message, BabylMessage): flags = set(self.get_flags()) if 'R' not in flags: @@ -1347,11 +1365,15 @@ self._sequences = [] Message.__init__(self, message) - def list_sequences(self): + def get_sequences(self): """Return a list of sequences that include the message.""" return self._sequences[:] - def join_sequence(self, sequence): + def set_sequences(self, sequences): + """Set the list of sequences that include the message.""" + self._sequences = list(sequences) + + def add_sequence(self, sequence): """Add sequence to list of sequences including the message.""" if isinstance(sequence, str): if not sequence in self._sequences: @@ -1359,7 +1381,7 @@ else: raise TypeError, "sequence must be a string" - def leave_sequence(self, sequence): + def remove_sequence(self, sequence): """Remove sequence from the list of sequences including the message.""" try: self._sequences.remove(sequence) @@ -1369,31 +1391,31 @@ def _explain_to(self, message): """Copy MH-specific state to message insofar as possible.""" if isinstance(message, MaildirMessage): - sequences = set(self.list_sequences()) + sequences = set(self.get_sequences()) if 'unseen' in sequences: message.set_subdir('cur') else: message.set_subdir('cur') - message.add_flags('S') + message.add_flag('S') if 'flagged' in sequences: - message.add_flags('F') + message.add_flag('F') if 'replied' in sequences: - message.add_flags('R') + message.add_flag('R') elif isinstance(message, _mboxMMDFMessage): - sequences = set(self.list_sequences()) + sequences = set(self.get_sequences()) if 'unseen' not in sequences: - message.add_flags('RO') + message.add_flag('RO') else: - message.add_flags('O') + message.add_flag('O') if 'flagged' in sequences: - message.add_flags('F') + message.add_flag('F') if 'replied' in sequences: - message.add_flags('A') + message.add_flag('A') elif isinstance(message, MHMessage): - for sequence in self.list_sequences(): - message.join_sequence(sequence) + for sequence in self.get_sequences(): + message.add_sequence(sequence) elif isinstance(message, BabylMessage): - sequences = set(self.list_sequences()) + sequences = set(self.get_sequences()) if 'unseen' in sequences: message.add_label('unseen') if 'replied' in sequences: @@ -1413,10 +1435,14 @@ self._visible = Message() Message.__init__(self, message) - def list_labels(self): + def get_labels(self): """Return a list of labels on the message.""" return self._labels[:] + def set_labels(self, labels): + """Set the list of labels on the message.""" + self._labels = list(labels) + def add_label(self, label): """Add label to list of labels on the message.""" if isinstance(label, str): @@ -1454,37 +1480,37 @@ def _explain_to(self, message): """Copy Babyl-specific state to message insofar as possible.""" if isinstance(message, MaildirMessage): - labels = set(self.list_labels()) + labels = set(self.get_labels()) if 'unseen' in labels: message.set_subdir('cur') else: message.set_subdir('cur') - message.add_flags('S') + message.add_flag('S') if 'forwarded' in labels or 'resent' in labels: - message.add_flags('P') + message.add_flag('P') if 'answered' in labels: - message.add_flags('R') + message.add_flag('R') if 'deleted' in labels: - message.add_flags('T') + message.add_flag('T') elif isinstance(message, _mboxMMDFMessage): - labels = set(self.list_labels()) + labels = set(self.get_labels()) if 'unseen' not in labels: - message.add_flags('RO') + message.add_flag('RO') else: - message.add_flags('O') + message.add_flag('O') if 'deleted' in labels: - message.add_flags('D') + message.add_flag('D') if 'answered' in labels: - message.add_flags('A') + message.add_flag('A') elif isinstance(message, MHMessage): - labels = set(self.list_labels()) + labels = set(self.get_labels()) if 'unseen' in labels: - message.join_sequence('unseen') + message.add_sequence('unseen') if 'answered' in labels: - message.join_sequence('replied') + message.add_sequence('replied') elif isinstance(message, BabylMessage): message.set_visible(self.get_visible()) - for label in self.list_labels(): + for label in self.get_labels(): message.add_label(label) elif isinstance(message, Message): pass @@ -1588,21 +1614,18 @@ return _ProxyFile._read(self, size, read_method) -class Error(Exception): - """Raised for module-specific errors.""" - - def _lock_file(f, path, system=True, dot=True): """Lock file f using lockf, flock, and dot locking.""" lockf_done = flock_done = dotlock_done = False try: - if system and 'fcntl' in globals(): + if system and fcntl: try: fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) lockf_done = True except IOError, e: if e.errno == errno.EAGAIN: - raise Error, "lockf: lock unavailable: %s" % path + raise ExternalClashError, \ + "lockf: lock unavailable: %s" % path else: raise try: @@ -1610,7 +1633,8 @@ flock_done = True except IOError, e: if e.errno == errno.EWOULDBLOCK: - raise Error, "flock: lock unavailable: %s" % path + raise ExternalClashError, \ + "flock: lock unavailable: %s" % path else: raise if dot: @@ -1637,7 +1661,7 @@ os.remove(tmp) except: pass - raise Error, 'dot lock unavailable: %s' % path + raise ExternalClashError, 'dot lock unavailable: %s' % path else: raise except: @@ -1651,7 +1675,7 @@ def _unlock_file(f, path, system=True, dot=True): """Unlock file f using lockf, flock, and dot locking.""" - if system and 'fcntl' in globals(): + if system and fcntl: fcntl.lockf(f, fcntl.LOCK_UN) fcntl.flock(f, fcntl.LOCK_UN) if dot and os.path.exists(path + '.lock'): @@ -1660,3 +1684,18 @@ def _create_carefully(path): """Create a file if it doesn't exist and open for reading and writing.""" return os.fdopen(os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR), 'r+') + +class Error(Exception): + """Raised for module-specific errors.""" + +class NoSuchMailboxError(Error): + """The specified mailbox does not exist and won't be created.""" + +class NotEmptyError(Error): + """The specified mailbox is not empty and deletion was requested.""" + +class ExternalClashError(Error): + """Another process caused an action to fail.""" + +class FormatError(Error): + """A file appears to have an invalid format.""" Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- test_mailbox.py 5 Aug 2005 16:34:45 -0000 1.6 +++ test_mailbox.py 13 Aug 2005 22:41:33 -0000 1.7 @@ -487,32 +487,33 @@ def test_list_folders(self): # List folders - self._box.open_folder('one') - self._box.open_folder('two') - self._box.open_folder('three') + self._box.add_folder('one') + self._box.add_folder('two') + self._box.add_folder('three') self.assert_(len(self._box.list_folders()) == 3) self.assert_(set(self._box.list_folders()) == set(('one', 'two', 'three'))) - def test_open_folder(self): + def test_get_folder(self): # Open folders - folder0 = self._box.open_folder('foo.bar') + self._box.add_folder('foo.bar') + folder0 = self._box.get_folder('foo.bar') folder0.add(self._template % 'bar') self.assert_(os.path.isdir(os.path.join(self._path, '.foo.bar'))) - folder1 = self._box.open_folder('foo.bar') + folder1 = self._box.get_folder('foo.bar') self.assert_(folder1.get_string(folder1.keys()[0]) == \ self._template % 'bar') - def test_remove_folder(self): + def test_add_and_remove_folders(self): # Delete folders - self._box.open_folder('one') - self._box.open_folder('two') + self._box.add_folder('one') + self._box.add_folder('two') self.assert_(len(self._box.list_folders()) == 2) self.assert_(set(self._box.list_folders()) == set(('one', 'two'))) self._box.remove_folder('one') self.assert_(len(self._box.list_folders()) == 1) self.assert_(set(self._box.list_folders()) == set(('two',))) - self._box.open_folder('three') + self._box.add_folder('three') self.assert_(len(self._box.list_folders()) == 2) self.assert_(set(self._box.list_folders()) == set(('two', 'three'))) self._box.remove_folder('three') @@ -775,7 +776,7 @@ self._check_sample(msg) def test_flags(self): - # Use get_flags(), set_flags(), add_flags(), remove_flags() + # Use get_flags(), set_flags(), add_flag(), remove_flag() msg = mailbox.MaildirMessage(_sample_message) self.assert_(msg.get_flags() == '') self.assert_(msg.get_subdir() == 'new') @@ -784,9 +785,9 @@ self.assert_(msg.get_flags() == 'F') msg.set_flags('SDTP') self.assert_(msg.get_flags() == 'DPST') - msg.add_flags('FT') + msg.add_flag('FT') self.assert_(msg.get_flags() == 'DFPST') - msg.remove_flags('TDRP') + msg.remove_flag('TDRP') self.assert_(msg.get_flags() == 'FS') self.assert_(msg.get_subdir() == 'new') self._check_sample(msg) @@ -810,10 +811,10 @@ msg.set_info('1,') self.assert_(msg.get_flags() == '') self.assert_(msg.get_info() == '1,') - msg.remove_flags('RPT') + msg.remove_flag('RPT') self.assert_(msg.get_flags() == '') self.assert_(msg.get_info() == '1,') - msg.add_flags('D') + msg.add_flag('D') self.assert_(msg.get_flags() == 'D') self.assert_(msg.get_info() == '2,D') self._check_sample(msg) @@ -845,16 +846,16 @@ self._check_from(msg, 'blah at temp') def test_flags(self): - # Use get_flags(), set_flags(), add_flags(), remove_flags() + # Use get_flags(), set_flags(), add_flag(), remove_flag() msg = mailbox.mboxMessage(_sample_message) self.assert_(msg.get_flags() == '') msg.set_flags('F') self.assert_(msg.get_flags() == 'F') msg.set_flags('XODR') self.assert_(msg.get_flags() == 'RODX') - msg.add_flags('FA') + msg.add_flag('FA') self.assert_(msg.get_flags() == 'RODFAX') - msg.remove_flags('FDXA') + msg.remove_flag('FDXA') self.assert_(msg.get_flags() == 'RO') self._check_sample(msg) @@ -879,21 +880,27 @@ self.assert_(msg._sequences == []) def test_sequences(self): - # List, join, and leave sequences + # Get, set, join, and leave sequences msg = mailbox.MHMessage(_sample_message) - self.assert_(msg.list_sequences() == []) - msg.join_sequence('unseen') - self.assert_(msg.list_sequences() == ['unseen']) - msg.join_sequence('flagged') - self.assert_(msg.list_sequences() == ['unseen', 'flagged']) - msg.join_sequence('flagged') - self.assert_(msg.list_sequences() == ['unseen', 'flagged']) - msg.leave_sequence('unseen') - self.assert_(msg.list_sequences() == ['flagged']) - msg.join_sequence('foobar') - self.assert_(msg.list_sequences() == ['flagged', 'foobar']) - msg.leave_sequence('replied') - self.assert_(msg.list_sequences() == ['flagged', 'foobar']) + self.assert_(msg.get_sequences() == []) + msg.set_sequences(['foobar']) + self.assert_(msg.get_sequences() == ['foobar']) + msg.set_sequences([]) + self.assert_(msg.get_sequences() == []) + msg.add_sequence('unseen') + self.assert_(msg.get_sequences() == ['unseen']) + msg.add_sequence('flagged') + self.assert_(msg.get_sequences() == ['unseen', 'flagged']) + msg.add_sequence('flagged') + self.assert_(msg.get_sequences() == ['unseen', 'flagged']) + msg.remove_sequence('unseen') + self.assert_(msg.get_sequences() == ['flagged']) + msg.add_sequence('foobar') + self.assert_(msg.get_sequences() == ['flagged', 'foobar']) + msg.remove_sequence('replied') + self.assert_(msg.get_sequences() == ['flagged', 'foobar']) + msg.set_sequences(['foobar', 'replied']) + self.assert_(msg.get_sequences() == ['foobar', 'replied']) class TestBabylMessage(TestMessage): @@ -904,21 +911,27 @@ self.assert_(msg._labels == []) def test_labels(self): - # List, add, and remove labels + # Get, set, join, and leave labels msg = mailbox.BabylMessage(_sample_message) - self.assert_(msg.list_labels() == []) + self.assert_(msg.get_labels() == []) + msg.set_labels(['foobar']) + self.assert_(msg.get_labels() == ['foobar']) + msg.set_labels([]) + self.assert_(msg.get_labels() == []) msg.add_label('filed') - self.assert_(msg.list_labels() == ['filed']) + self.assert_(msg.get_labels() == ['filed']) msg.add_label('resent') - self.assert_(msg.list_labels() == ['filed', 'resent']) + self.assert_(msg.get_labels() == ['filed', 'resent']) msg.add_label('resent') - self.assert_(msg.list_labels() == ['filed', 'resent']) + self.assert_(msg.get_labels() == ['filed', 'resent']) msg.remove_label('filed') - self.assert_(msg.list_labels() == ['resent']) + self.assert_(msg.get_labels() == ['resent']) msg.add_label('foobar') - self.assert_(msg.list_labels() == ['resent', 'foobar']) + self.assert_(msg.get_labels() == ['resent', 'foobar']) msg.remove_label('unseen') - self.assert_(msg.list_labels() == ['resent', 'foobar']) + self.assert_(msg.get_labels() == ['resent', 'foobar']) + msg.set_labels(['foobar', 'answered']) + self.assert_(msg.get_labels() == ['foobar', 'answered']) def test_visible(self): # Get, set, and update visible headers @@ -1007,7 +1020,7 @@ ('T', ['unseen']), ('DFPRST', ['replied', 'flagged'])) for setting, result in pairs: msg_maildir.set_flags(setting) - self.assert_(mailbox.MHMessage(msg_maildir).list_sequences() == \ + self.assert_(mailbox.MHMessage(msg_maildir).get_sequences() == \ result) def test_maildir_to_babyl(self): @@ -1019,7 +1032,7 @@ ('DFPRST', ['deleted', 'answered', 'forwarded'])) for setting, result in pairs: msg_maildir.set_flags(setting) - self.assert_(mailbox.BabylMessage(msg_maildir).list_labels() == \ + self.assert_(mailbox.BabylMessage(msg_maildir).get_labels() == \ result) def test_mboxmmdf_to_maildir(self): @@ -1057,7 +1070,7 @@ ('RODFA', ['replied', 'flagged'])) for setting, result in pairs: msg_mboxMMDF.set_flags(setting) - self.assert_(mailbox.MHMessage(msg_mboxMMDF).list_sequences() \ + self.assert_(mailbox.MHMessage(msg_mboxMMDF).get_sequences() \ == result) def test_mboxmmdf_to_babyl(self): @@ -1070,20 +1083,20 @@ ('RODFA', ['deleted', 'answered'])) for setting, result in pairs: msg.set_flags(setting) - self.assert_(mailbox.BabylMessage(msg).list_labels() == result) + self.assert_(mailbox.BabylMessage(msg).get_labels() == result) def test_mh_to_maildir(self): # Convert MHMessage to MaildirMessage pairs = (('unseen', ''), ('replied', 'RS'), ('flagged', 'FS')) for setting, result in pairs: msg = mailbox.MHMessage(_sample_message) - msg.join_sequence(setting) + msg.add_sequence(setting) self.assert_(mailbox.MaildirMessage(msg).get_flags() == result) self.assert_(mailbox.MaildirMessage(msg).get_subdir() == 'cur') msg = mailbox.MHMessage(_sample_message) - msg.join_sequence('unseen') - msg.join_sequence('replied') - msg.join_sequence('flagged') + msg.add_sequence('unseen') + msg.add_sequence('replied') + msg.add_sequence('flagged') self.assert_(mailbox.MaildirMessage(msg).get_flags() == 'FR') self.assert_(mailbox.MaildirMessage(msg).get_subdir() == 'cur') @@ -1092,23 +1105,23 @@ pairs = (('unseen', 'O'), ('replied', 'ROA'), ('flagged', 'ROF')) for setting, result in pairs: msg = mailbox.MHMessage(_sample_message) - msg.join_sequence(setting) + msg.add_sequence(setting) for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage): self.assert_(class_(msg).get_flags() == result) msg = mailbox.MHMessage(_sample_message) - msg.join_sequence('unseen') - msg.join_sequence('replied') - msg.join_sequence('flagged') + msg.add_sequence('unseen') + msg.add_sequence('replied') + msg.add_sequence('flagged') for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage): self.assert_(class_(msg).get_flags() == 'OFA') def test_mh_to_mh(self): # Convert MHMessage to MHMessage msg = mailbox.MHMessage(_sample_message) - msg.join_sequence('unseen') - msg.join_sequence('replied') - msg.join_sequence('flagged') - self.assert_(mailbox.MHMessage(msg).list_sequences() == \ + msg.add_sequence('unseen') + msg.add_sequence('replied') + msg.add_sequence('flagged') + self.assert_(mailbox.MHMessage(msg).get_sequences() == \ ['unseen', 'replied', 'flagged']) def test_mh_to_babyl(self): @@ -1117,13 +1130,13 @@ ('flagged', [])) for setting, result in pairs: msg = mailbox.MHMessage(_sample_message) - msg.join_sequence(setting) - self.assert_(mailbox.BabylMessage(msg).list_labels() == result) + msg.add_sequence(setting) + self.assert_(mailbox.BabylMessage(msg).get_labels() == result) msg = mailbox.MHMessage(_sample_message) - msg.join_sequence('unseen') - msg.join_sequence('replied') - msg.join_sequence('flagged') - self.assert_(mailbox.BabylMessage(msg).list_labels() == \ + msg.add_sequence('unseen') + msg.add_sequence('replied') + msg.add_sequence('flagged') + self.assert_(mailbox.BabylMessage(msg).get_labels() == \ ['unseen', 'answered']) def test_babyl_to_maildir(self): @@ -1168,12 +1181,12 @@ for setting, result in pairs: msg = mailbox.BabylMessage(_sample_message) msg.add_label(setting) - self.assert_(mailbox.MHMessage(msg).list_sequences() == result) + self.assert_(mailbox.MHMessage(msg).get_sequences() == result) msg = mailbox.BabylMessage(_sample_message) for label in ('unseen', 'deleted', 'filed', 'answered', 'forwarded', 'edited', 'resent'): msg.add_label(label) - self.assert_(mailbox.MHMessage(msg).list_sequences() == \ + self.assert_(mailbox.MHMessage(msg).get_sequences() == \ ['unseen', 'replied']) def test_babyl_to_babyl(self): @@ -1184,9 +1197,9 @@ 'edited', 'resent'): msg.add_label(label) msg2 = mailbox.BabylMessage(msg) - self.assert_(msg2.list_labels() == ['unseen', 'deleted', 'filed', - 'answered', 'forwarded', 'edited', - 'resent']) + self.assert_(msg2.get_labels() == ['unseen', 'deleted', 'filed', + 'answered', 'forwarded', 'edited', + 'resent']) self.assert_(msg.get_visible().keys() == msg2.get_visible().keys()) for key in msg.get_visible().keys(): self.assert_(msg.get_visible()[key] == msg2.get_visible()[key]) From pje at users.sourceforge.net Sun Aug 14 01:04:18 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 01:04:18 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command egg_info.py, 1.11, 1.12 Message-ID: <20050813230418.DC5101E4013@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14936/setuptools/command Modified Files: egg_info.py Log Message: Added docs for main EntryPoint APIs, and cleaned up the API itself a bit. Also fixed a few bugs. Index: egg_info.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/egg_info.py,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- egg_info.py 6 Aug 2005 21:17:50 -0000 1.11 +++ egg_info.py 13 Aug 2005 23:04:08 -0000 1.12 @@ -219,7 +219,7 @@ data = [] for section, contents in ep.items(): if not isinstance(contents,basestring): - contents = EntryPoint.parse_list(section, contents) + contents = EntryPoint.parse_group(section, contents) contents = '\n'.join(map(str,contents.values())) data.append('[%s]\n%s\n\n' % (section,contents)) data = ''.join(data) From pje at users.sourceforge.net Sun Aug 14 01:04:19 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 01:04:19 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools api_tests.txt, 1.5, 1.6 pkg_resources.py, 1.64, 1.65 pkg_resources.txt, 1.4, 1.5 Message-ID: <20050813230419.CC68B1E4014@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14936 Modified Files: api_tests.txt pkg_resources.py pkg_resources.txt Log Message: Added docs for main EntryPoint APIs, and cleaned up the API itself a bit. Also fixed a few bugs. Index: api_tests.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/api_tests.txt,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- api_tests.txt 6 Aug 2005 20:54:01 -0000 1.5 +++ api_tests.txt 13 Aug 2005 23:04:08 -0000 1.6 @@ -138,7 +138,7 @@ ['http://example.com/something'] >>> dist in ws True - >>> Distribution('foo') in ws + >>> Distribution('foo',version="") in ws False And you can iterate over its distributions:: Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.64 retrieving revision 1.65 diff -u -d -r1.64 -r1.65 --- pkg_resources.py 11 Aug 2005 00:37:37 -0000 1.64 +++ pkg_resources.py 13 Aug 2005 23:04:08 -0000 1.65 @@ -1598,7 +1598,7 @@ class EntryPoint(object): - """Object representing an importable location""" + """Object representing an advertised importable object""" def __init__(self, name, module_name, attrs=(), extras=(), dist=None): if not MODULE(module_name): @@ -1680,35 +1680,37 @@ #@classmethod - def parse_list(cls, section, contents, dist=None): - if not MODULE(section): - raise ValueError("Invalid section name", section) + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) this = {} - for line in yield_lines(contents): + for line in yield_lines(lines): ep = cls.parse(line, dist) if ep.name in this: - raise ValueError("Duplicate entry point",section,ep.name) + raise ValueError("Duplicate entry point", group, ep.name) this[ep.name]=ep return this - parse_list = classmethod(parse_list) + parse_group = classmethod(parse_group) #@classmethod def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" if isinstance(data,dict): data = data.items() else: data = split_sections(data) maps = {} - for section, contents in data: - if section is None: - if not contents: + for group, lines in data: + if group is None: + if not lines: continue - raise ValueError("Entry points must be listed in sections") - section = section.strip() - if section in maps: - raise ValueError("Duplicate section name", section) - maps[section] = cls.parse_list(section, contents, dist) + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) return maps parse_map = classmethod(parse_map) @@ -1718,8 +1720,6 @@ - - class Distribution(object): """Wrap an actual or potential sys.path entry w/metadata""" def __init__(self, @@ -1862,7 +1862,9 @@ return str(self) def __str__(self): - version = getattr(self,'version',None) or "[unknown version]" + try: version = getattr(self,'version',None) + except ValueError: version = None + version = version or "[unknown version]" return "%s %s" % (self.project_name,version) def __getattr__(self,attr): @@ -1882,8 +1884,6 @@ return Requirement.parse('%s==%s' % (self.project_name, self.version)) - - def load_entry_point(self, group, name): """Return the `name` entry point of `group` or raise ImportError""" ep = self.get_entry_info(group,name) Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- pkg_resources.txt 7 Aug 2005 20:54:10 -0000 1.4 +++ pkg_resources.txt 13 Aug 2005 23:04:08 -0000 1.5 @@ -46,11 +46,12 @@ API Reference ------------- + 'require', 'run_script', Namespace Package Support ========================= -XXX +declare_namespace, fixup_namespace_packages, register_namespace_handler ``WorkingSet`` Objects @@ -65,24 +66,188 @@ XXX -``EntryPoint`` Objects -====================== - -XXX - - ``Requirement`` Objects ======================= XXX Syntax, parse_requirments, Requirement.parse, etc. + + +Entry Points +============ + +Entry points are a simple way for distributions to "advertise" Python objects +(such as functions or classes) for use by other distributions. Extensible +applications and frameworks can search for entry points with a particular name +or group, either from a specific distribution or from all active distributions +on sys.path, and then inspect or load the advertised objects at will. + +Entry points belong to "groups" which are named with a dotted name similar to +a Python package or module name. For example, the ``setuptools`` package uses +an entry point named ``distutils.commands`` in order to find commands defined +by distutils extensions. ``setuptools`` treats the names of entry points +defined in that group as the acceptable commands for a setup script. + +In a similar way, other packages can define their own entry point groups, +either using dynamic names within the group (like ``distutils.commands``), or +possibly using predefined names within the group. For example, a blogging +framework that offers various pre- or post-publishing hooks might define an +entry point group and look for entry points named "pre_process" and +"post_process" within that group. + +To advertise an entry point, a project needs to use ``setuptools`` and provide +an ``entry_points`` argument to ``setup()`` in its setup script, so that the +entry points will be included in the distribution's metadata. For more +details, see the ``setuptools`` documentation. (XXX link here to setuptools) + +Each project distribution can advertise at most one entry point of a given +name within the same entry point group. For example, a distutils extension +could advertise two different ``distutils.commands`` entry points, as long as +they had different names. However, there is nothing that prevents *different* +projects from advertising entry points of the same name in the same group. In +some cases, this is a desirable thing, since the application or framework that +uses the entry points may be calling them as hooks, or in some other way +combining them. It is up to the application or framework to decide what to do +if multiple distributions advertise an entry point; some possibilities include +using both entry points, displaying an error message, using the first one found +in sys.path order, etc. + + +Convenience API +--------------- + +In the following functions, the `dist` argument can be a ``Distribution`` +instance, a ``Requirement`` instance, or a string specifying a requirement +(i.e. project name, version, etc.). If the argument is a string or +``Requirement``, the specified distribution is located (and added to sys.path +if not already present). An error will be raised if a matching distribution is +not available. + +The `group` argument should be a string containing a dotted identifier, +identifying an entry point group. If you are defining an entry point group, +you should include some portion of your package's name in the group name so as +to avoid collision with other packages' entry point groups. + +``load_entry_point(dist, group, name)`` + Load the named entry point from the specified distribution, or raise + ``ImportError``. + +``get_entry_info(dist, group, name)`` + Return an ``EntryPoint`` object for the given `group` and `name` from + the specified distribution. Returns ``None`` if the distribution has not + advertised a matching entry point. + +``get_entry_map(dist, group=None) + Return the distribution's entry point map for `group`, or the full entry + map for the distribution. This function always returns a dictionary, + even if the distribution advertises no entry points. If `group` is given, + the dictionary maps entry point names to the corresponding ``EntryPoint`` + object. If `group` is None, the dictionary maps group names to + dictionaries that then map entry point names to the corresponding + ``EntryPoint`` instance in that group. + +``iter_entry_points(group, name=None)`` + Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set on sys.path, otherwise only ones matching + both `group` and `name` are yielded. Entry points are yielded from + the active distributions in the order that the distributions appear on + sys.path. (Within entry points for a particular distribution, however, + there is no particular ordering.) + + +Creating and Parsing +-------------------- + +``EntryPoint(name, module_name, attrs=(), extras=(), dist=None)`` + Create an ``EntryPoint`` instance. `name` is the entry point name. The + `module_name` is the (dotted) name of the module containing the advertised + object. `attrs` is an optional tuple of names to look up from the + module to obtain the advertised object. For example, an `attrs` of + ``("foo","bar")`` and a `module_name` of ``"baz"`` would mean that the + advertised object could be obtained by the following code:: + + import baz + advertised_object = baz.foo.bar + + The `extras` are an optional tuple of "extra feature" names that the + distribution needs in order to provide this entry point. When the + entry point is loaded, these extra features are looked up in the `dist` + argument to find out what other distributions may need to be activated + on sys.path; see the ``load()`` method for more details. The `extras` + argument is only meaningful if `dist` is specified. `dist` must be + a ``Distribution`` instance. + +``EntryPoint.parse(src, dist=None)`` (classmethod) + Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1,extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional, as is the whitespace shown between + some of the items. The `dist` argument is passed through to the + ``EntryPoint()`` constructor, along with the other values parsed from + `src`. + +``EntryPoint.parse_group(group, lines, dist=None)`` (classmethod) + Parse `lines` (a string or sequence of lines) to create a dictionary + mapping entry point names to ``EntryPoint`` objects. ``ValueError`` is + raised if entry point names are duplicated, if `group` is not a valid + entry point group name, or if there are any syntax errors. (Note: the + `group` parameter is used only for validation and to create more + informative error messages.) If `dist` is provided, it will be used to + set the ``dist`` attribute of the created ``EntryPoint`` objects. + +``EntryPoint.parse_map(data, dist=None)`` (classmethod) + Parse `data` into a dictionary mapping group names to dictionaries mapping + entry point names to ``EntryPoint`` objects. If `data` is a dictionary, + then the keys are used as group names and the values are passed to + ``parse_group()`` as the `lines` argument. If `data` is a string or + sequence of lines, it is first split into .ini-style sections (using + the ``split_sections()`` utility function) and the section names are used + as group names. In either case, the `dist` argument is passed through to + ``parse_group()`` so that the entry points will be linked to the specified + distribution. + + +``EntryPoint`` Objects +---------------------- + +For simple introspection, ``EntryPoint`` objects have attributes that +correspond exactly to the constructor argument names: ``name``, +``module_name``, ``attrs``, ``extras``, and ``dist`` are all available. In +addition, the following methods are provided: + +``load(require=True, env=None, installer=None)`` + Load the entry point, returning the advertised Python object, or raise + ``ImportError`` if it cannot be obtained. If `require` is a true value, + then ``require(env, installer)`` is called before attempting the import. + +``require(env=None, installer=None)`` + Ensure that any "extras" needed by the entry point are available on + sys.path. ``UnknownExtra`` is raised if the ``EntryPoint`` has ``extras``, + but no ``dist``, or if the named extras are not defined by the + distribution. If `env` is supplied, it must be an ``Environment``, and it + will be used to search for needed distributions if they are not already + present on sys.path. If `installer` is supplied, it must be a callable + taking a ``Requirement`` instance and returning a matching importable + ``Distribution`` instance or None. + + ``Distribution`` Objects ======================== Factories: get_provider, get_distribution, find_distributions; see also WorkingSet and Environment APIs. +register_finder +register_loader_type +'load_entry_point', 'get_entry_map', 'get_entry_info' + ``ResourceManager`` API ======================= @@ -459,6 +624,21 @@ set. +PEP 302 Utilities +----------------- + +``get_importer(path_item)`` + Retrieve a PEP 302 "importer" for the given path item (which need not + actually be on ``sys.path``). This routine simulates the PEP 302 protocol + for obtaining an "importer" object. It first checks for an importer for + the path item in ``sys.path_importer_cache``, and if not found it calls + each of the ``sys.path_hooks`` and caches the result if a good importer is + found. If no importer is found, this routine returns an ``ImpWrapper`` + instance that wraps the builtin import machinery as a PEP 302-compliant + "importer" object. This ``ImpWrapper`` is *not* cached; instead a new + instance is returned each time. + + File/Path Utilities ------------------- From pje at users.sourceforge.net Sun Aug 14 01:04:19 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 01:04:19 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/tests test_resources.py, 1.21, 1.22 Message-ID: <20050813230419.C912B1E4013@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14936/setuptools/tests Modified Files: test_resources.py Log Message: Added docs for main EntryPoint APIs, and cleaned up the API itself a bit. Also fixed a few bugs. Index: test_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests/test_resources.py,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- test_resources.py 7 Aug 2005 01:03:36 -0000 1.21 +++ test_resources.py 13 Aug 2005 23:04:08 -0000 1.22 @@ -269,9 +269,9 @@ """ def testParseList(self): - self.checkSubMap(EntryPoint.parse_list("xyz", self.submap_str)) - self.assertRaises(ValueError, EntryPoint.parse_list, "x a", "foo=bar") - self.assertRaises(ValueError, EntryPoint.parse_list, "x", + self.checkSubMap(EntryPoint.parse_group("xyz", self.submap_str)) + self.assertRaises(ValueError, EntryPoint.parse_group, "x a", "foo=bar") + self.assertRaises(ValueError, EntryPoint.parse_group, "x", ["foo=baz", "foo=bar"]) def testParseMap(self): @@ -397,7 +397,7 @@ """ ) ), - [(None,["x"]), ("y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])] + [(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])] ) self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo")) From pje at users.sourceforge.net Sun Aug 14 02:37:38 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 02:37:38 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.txt, 1.5, 1.6 Message-ID: <20050814003738.E81821E4018@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv419 Modified Files: pkg_resources.txt Log Message: Document "Requirement" objects. Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- pkg_resources.txt 13 Aug 2005 23:04:08 -0000 1.5 +++ pkg_resources.txt 14 Aug 2005 00:37:28 -0000 1.6 @@ -69,9 +69,96 @@ ``Requirement`` Objects ======================= -XXX Syntax, parse_requirments, Requirement.parse, etc. +``Requirement`` objects express what versions of a project are suitable for +some purpose. These objects (or their string form) are used by various +``pkg_resources`` APIs in order to find distributions that a script or +distribution needs. + +Requirements Parsing +-------------------- + +``parse_requirements(s)`` + Yield ``Requirement`` objects for a string or list of lines. Each + requirement must start on a new line. See below for syntax. +``Requirement.parse(s)`` + Create a ``Requirement`` object from a string or list of lines. A + ``ValueError`` is raised if the string or lines do not contain a valid + requirement specifier. The syntax of a requirement specifier can be + defined in EBNF as follows:: + + requirement ::= project_name versionspec? extras? + versionspec ::= comparison version (',' comparison version)* + comparison ::= '<' | '<=' | '!=' | '==' | '>=' | '>' + extras ::= '[' extralist? ']' + extralist ::= identifier (',' identifier)* + project_name ::= identifier + identifier ::= [-A-Za-z0-9_]+ + version ::= [-A-Za-z0-9_.]+ + + Tokens can be separated by whitespace, and a requirement can be continued + over multiple lines using a backslash (``\\``). Line-end comments (using + ``#``) are also allowed. + + Some examples of valid requirement specifiers:: + + FooProject >= 1.2 + Fizzy [foo, bar] + PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1 + SomethingWhoseVersionIDontCareAbout + + The project name is the only required portion of a requirement string, and + if it's the only thing supplied, the requirement will accept any version + of that project. + + The "extras" in a requirement are used to request optional features of a + project, that may require additional project distributions in order to + function. For example, if the hypothetical "Report-O-Rama" project offered + optional PDF support, it might require an additional library in order to + provide that support. Thus, a project needing Report-O-Rama's PDF features + could use a requirement of ``Report-O-Rama[PDF]`` to request installation + or activation of both Report-O-Rama and any libraries it needs in order to + provide PDF support. For example, you could use:: + + easy_install.py Report-O-Rama[PDF] + + To install the necessary packages using the EasyInstall program, or call + ``pkg_resources.require('Report-O-Rama[PDF]')`` to add the necessary + distributions to sys.path at runtime. + + +``Requirement`` Methods and Attributes +-------------------------------------- + +``__contains__(dist_or_version)`` + Return true if `dist_or_version` fits the criteria for this requirement. + If `dist_or_version` is a ``Distribution`` object, its project name must + match the requirement's project name, and its version must meet the + requirement's version criteria. If `dist_or_version` is a string, it is + parsed using the ``parse_version()`` utility function. Otherwise, it is + assumed to be an already-parsed version. + +``__eq__(other_requirement)`` + A requirement compares equal to another requirement if they have + case-insensitively equal project names, version specifiers, and "extras". + (The order that extras and version specifiers are in is also ignored.) + Equal requirements also have equal hashes, so that requirements can be + used in sets or as dictionary keys. + +``__str__()`` + The string form of a ``Requirement`` is a string that, if passed to + ``Requirement.parse()``, would return an equal ``Requirement`` object. + +``project_name`` + The name of the required project + +``key`` + An all-lowercase version of the ``project_name``, useful for comparison + or indexing. + +``extras`` + A tuple of names of "extras" that this requirement calls for. Entry Points From pje at users.sourceforge.net Sun Aug 14 03:45:48 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 03:45:48 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools package_index.py, 1.17, 1.18 Message-ID: <20050814014548.7DFF31E4018@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9985/setuptools Modified Files: package_index.py Log Message: Document the "Environment" class, and simplify its API. Index: package_index.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/package_index.py,v retrieving revision 1.17 retrieving revision 1.18 diff -u -d -r1.17 -r1.18 --- package_index.py 11 Aug 2005 00:37:37 -0000 1.17 +++ package_index.py 14 Aug 2005 01:45:38 -0000 1.18 @@ -264,7 +264,7 @@ def obtain(self, requirement, installer=None): self.find_packages(requirement) - for dist in self.get(requirement.key, ()): + for dist in self[requirement.key]: if dist in requirement: return dist self.debug("%s does not match %s", requirement, dist) @@ -344,7 +344,7 @@ self.info("Searching for %s", requirement) def find(req): - for dist in self.get(req.key, ()): + for dist in self[req.key]: if dist in req and (dist.precedence<=SOURCE_DIST or not source): self.info("Best match: %s", dist) return self.download(dist.location, tmpdir) From pje at users.sourceforge.net Sun Aug 14 03:45:48 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 03:45:48 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/tests test_resources.py, 1.22, 1.23 Message-ID: <20050814014548.994281E4019@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9985/setuptools/tests Modified Files: test_resources.py Log Message: Document the "Environment" class, and simplify its API. Index: test_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/tests/test_resources.py,v retrieving revision 1.22 retrieving revision 1.23 diff -u -d -r1.22 -r1.23 --- test_resources.py 13 Aug 2005 23:04:08 -0000 1.22 +++ test_resources.py 14 Aug 2005 01:45:38 -0000 1.23 @@ -25,19 +25,19 @@ # empty path should produce no distributions ad = Environment([], python=None) self.assertEqual(list(ad), []) - self.assertEqual(len(ad),0) - self.assertEqual(ad.get('FooPkg'),None) - self.failIf('FooPkg' in ad) + self.assertEqual(ad['FooPkg'],[]) + ad.add(Distribution.from_filename("FooPkg-1.3_1.egg")) ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")) ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg")) # Name is in there now - self.failUnless('FooPkg' in ad) + self.failUnless(ad['FooPkg']) # But only 1 package self.assertEqual(list(ad), ['foopkg']) - self.assertEqual(len(ad),1) + + # Distributions sort by version self.assertEqual( @@ -46,7 +46,7 @@ # Removing a distribution leaves sequence alone ad.remove(ad['FooPkg'][1]) self.assertEqual( - [dist.version for dist in ad.get('FooPkg')], ['1.4','1.2'] + [dist.version for dist in ad['FooPkg']], ['1.4','1.2'] ) # And inserting adds them in order ad.add(Distribution.from_filename("FooPkg-1.9.egg")) From pje at users.sourceforge.net Sun Aug 14 03:45:49 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 03:45:49 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.65, 1.66 pkg_resources.txt, 1.6, 1.7 setuptools.txt, 1.30, 1.31 Message-ID: <20050814014549.6CF981E4018@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9985 Modified Files: pkg_resources.py pkg_resources.txt setuptools.txt Log Message: Document the "Environment" class, and simplify its API. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.65 retrieving revision 1.66 diff -u -d -r1.65 -r1.66 --- pkg_resources.py 13 Aug 2005 23:04:08 -0000 1.65 +++ pkg_resources.py 14 Aug 2005 01:45:37 -0000 1.66 @@ -234,7 +234,7 @@ def load_entry_point(dist, group, name): """Return `name` entry point of `group` for `dist` or raise ImportError""" return get_distribution(dist).load_entry_point(group, name) - + def get_entry_map(dist, group=None): """Return the entry point map for `group`, or the full entry map""" return get_distribution(dist).get_entry_map(group) @@ -537,8 +537,8 @@ def __init__(self,search_path=None,platform=get_platform(),python=PY_MAJOR): """Snapshot distributions available on a search path - Any distributions found on `search_path` are added to the distribution - map. `search_path` should be a sequence of ``sys.path`` items. If not + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not supplied, ``sys.path`` is used. `platform` is an optional string specifying the name of the platform @@ -558,31 +558,24 @@ self.scan(search_path) def can_add(self, dist): - """Is distribution `dist` acceptable for this collection?""" + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ return (self.python is None or dist.py_version is None or dist.py_version==self.python) \ and compatible_platforms(dist.platform,self.platform) - def __iter__(self): - """Iterate over distribution keys""" - return iter(self._distmap.keys()) - - def __contains__(self,name): - """Has a distribution named `name` ever been added to this map?""" - return name.lower() in self._distmap - - - def get(self,key,default=None): - """Return ``self[key]`` if `key` in self, otherwise return `default`""" - if key in self: - return self[key] - else: - return default + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) def scan(self, search_path=None): - """Scan `search_path` for distributions usable on `platform` + """Scan `search_path` for distributions usable in this environment - Any distributions found are added to the distribution map. + Any distributions found are added to the environment. `search_path` should be a sequence of ``sys.path`` items. If not supplied, ``sys.path`` is used. Only distributions conforming to the platform/python version defined at initialization are added. @@ -594,66 +587,73 @@ for dist in find_distributions(item): self.add(dist) - def __getitem__(self,key): - """Return a newest-to-oldest list of distributions for the given key - - The returned list may be modified in-place, e.g. for narrowing down - usable distributions. + def __getitem__(self,project_name): + """Return a newest-to-oldest list of distributions for `project_name` """ try: - return self._cache[key] + return self._cache[project_name] except KeyError: - key = key.lower() - if key not in self._distmap: - raise + project_name = project_name.lower() + if project_name not in self._distmap: + return [] - if key not in self._cache: - dists = self._cache[key] = self._distmap[key] + if project_name not in self._cache: + dists = self._cache[project_name] = self._distmap[project_name] _sort_dists(dists) - return self._cache[key] + return self._cache[project_name] def add(self,dist): - """Add `dist` to the distribution map, only if it's suitable""" + """Add `dist` if we ``can_add()`` it and it isn't already added""" if self.can_add(dist): - self._distmap.setdefault(dist.key,[]).append(dist) - if dist.key in self._cache: - _sort_dists(self._cache[dist.key]) + dists = self._distmap.setdefault(dist.key,[]) + if dist not in dists: + dists.append(dist) + if dist.key in self._cache: + _sort_dists(self._cache[dist.key]) - def remove(self,dist): - """Remove `dist` from the distribution map""" - self._distmap[dist.key].remove(dist) def best_match(self, req, working_set, installer=None): """Find distribution best matching `req` and usable on `working_set` - If a distribution that's already active in `working_set` is unsuitable, - a VersionConflict is raised. If one or more suitable distributions are - already active, the leftmost distribution (i.e., the one first in - the search path) is returned. Otherwise, the available distribution - with the highest version number is returned. If nothing is available, - returns ``obtain(req,installer)`` or ``None`` if no distribution can - be obtained. + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. """ dist = working_set.find(req) if dist is not None: return dist - - for dist in self.get(req.key, ()): + for dist in self[req.key]: if dist in req: return dist - return self.obtain(req, installer) # try and download/install def obtain(self, requirement, installer=None): - """Obtain a distro that matches requirement (e.g. via download)""" + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" if installer is not None: return installer(requirement) - def __len__(self): return len(self._distmap) + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: yield key AvailableDistributions = Environment # XXX backward compatibility + class ResourceManager: """Manage resource extraction and packages""" extraction_path = None @@ -744,7 +744,7 @@ is based on the ``PYTHON_EGG_CACHE`` environment variable, with various platform-specific fallbacks. See that routine's documentation for more details.) - + Resources are extracted to subdirectories of this path based upon information given by the ``IResourceProvider``. You may set this to a temporary directory, but then you must call ``cleanup_resources()`` to @@ -1369,7 +1369,7 @@ ) else: # scan for .egg and .egg-info in directory - for entry in os.listdir(path_item): + for entry in os.listdir(path_item): lower = entry.lower() if lower.endswith('.egg-info'): fullpath = os.path.join(path_item, entry) @@ -1637,7 +1637,7 @@ raise UnknownExtra("Can't require() without a distribution", self) map(working_set.add, working_set.resolve(self.dist.requires(self.extras),env,installer)) - + #@classmethod def parse(cls, src, dist=None): """Parse a single entry point from string `src` Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- pkg_resources.txt 14 Aug 2005 00:37:28 -0000 1.6 +++ pkg_resources.txt 14 Aug 2005 01:45:37 -0000 1.7 @@ -58,12 +58,91 @@ ====================== Listeners +working_set ``Environment`` Objects ======================= -XXX +An "environment" is a collection of ``Distribution`` objects, usually ones +that are present and potentially importable on the current platform. +``Environment`` objects are used by ``pkg_resources`` to index available +distributions during dependency resolution. + +``Environment(search_path=None, platform=get_platform(), python=PY_MAJOR)`` + Create an environment snapshot by scanning `search_path` for distributions + compatible with `platform` and `python`. `search_path` should be a + sequence of strings such as might be used on ``sys.path``. If a + `search_path` isn't supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'2.4'``); + it defaults to the currently-running version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to include *all* distributions, not just those compatible with the + running platform or Python version. + + Note that `search_path` is scanned immediately for distributions, and the + resulting ``Environment`` is a snapshot of the found distributions. It + is not automatically updated if the system's state changes due to e.g. + installation or removal of distributions. + +``__getitem__(project_name)`` + Returns a list of distributions for the given project name, ordered + from newest to oldest version. (And highest to lowest format precedence + for distributions that contain the same version of the project.) If there + are no distributions for the project, returns an empty list. + +``__iter__()`` + Yield the unique project names of the distributions in this environment. + The yielded names are always in lower case. + +``add(dist)`` + Add `dist` to the environment if it matches the platform and python version + specified at creation time, and only if the distribution hasn't already + been added. (i.e., adding the same distribution more than once is a no-op.) + +``remove(dist)`` + Remove `dist` from the environment. + +``can_add(dist)`` + Is distribution `dist` acceptable for this environment? If it's not + compatible with the platform and python version specified at creation of + the environment, False is returned. + +``best_match(req, working_set, installer=None)`` + Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution isn't + active, this method returns the newest distribution in the environment + that meets the ``Requirement`` in `req`. If no suitable distribution is + found, and `installer` is supplied, then the result of calling + the environment's ``obtain(req, installer)`` method will be returned. + +``obtain(requirement, installer=None)`` + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument. + +``scan(search_path=None)`` + Scan `search_path` for distributions usable on `platform` + + Any distributions found are added to the environment. `search_path` should + be a sequence of strings such as might be used on ``sys.path``. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. This + method is a shortcut for using the ``find_distributions()`` function to + find the distributions from each item in `search_path`, and then calling + ``add()`` to add each one to the environment. ``Requirement`` Objects @@ -75,18 +154,21 @@ distribution needs. -Requirements Parsing +Requirements Parsing -------------------- ``parse_requirements(s)`` - Yield ``Requirement`` objects for a string or list of lines. Each + Yield ``Requirement`` objects for a string or iterable of lines. Each requirement must start on a new line. See below for syntax. ``Requirement.parse(s)`` - Create a ``Requirement`` object from a string or list of lines. A + Create a ``Requirement`` object from a string or iterable of lines. A ``ValueError`` is raised if the string or lines do not contain a valid - requirement specifier. The syntax of a requirement specifier can be - defined in EBNF as follows:: + requirement specifier, or if they contain more than one specifier. (To + parse multiple specifiers from a string or iterable of strings, use + ``parse_requirements()`` instead.) + + The syntax of a requirement specifier can be defined in EBNF as follows:: requirement ::= project_name versionspec? extras? versionspec ::= comparison version (',' comparison version)* @@ -100,7 +182,7 @@ Tokens can be separated by whitespace, and a requirement can be continued over multiple lines using a backslash (``\\``). Line-end comments (using ``#``) are also allowed. - + Some examples of valid requirement specifiers:: FooProject >= 1.2 @@ -258,7 +340,7 @@ import baz advertised_object = baz.foo.bar - + The `extras` are an optional tuple of "extra feature" names that the distribution needs in order to provide this entry point. When the entry point is loaded, these extra features are looked up in the `dist` @@ -266,7 +348,7 @@ on sys.path; see the ``load()`` method for more details. The `extras` argument is only meaningful if `dist` is specified. `dist` must be a ``Distribution`` instance. - + ``EntryPoint.parse(src, dist=None)`` (classmethod) Parse a single entry point from string `src` @@ -324,6 +406,10 @@ taking a ``Requirement`` instance and returning a matching importable ``Distribution`` instance or None. +``__str__()`` + The string form of an ``EntryPoint`` is a string that could be passed to + ``EntryPoint.parse()`` to yield an equivalent ``EntryPoint``. + ``Distribution`` Objects ======================== Index: setuptools.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v retrieving revision 1.30 retrieving revision 1.31 diff -u -d -r1.30 -r1.31 --- setuptools.txt 7 Aug 2005 01:03:35 -0000 1.30 +++ setuptools.txt 14 Aug 2005 01:45:37 -0000 1.31 @@ -1852,7 +1852,11 @@ that tells it to only yield distributions whose location is the passed-in path. (It defaults to False, so that the default behavior is unchanged.) - * ``AvailableDistributions`` is now called ``Environment`` + * ``AvailableDistributions`` is now called ``Environment``, and the + ``get()``, ``__len__()``, and ``__contains__()`` methods were removed, + because they weren't particularly useful. ``__getitem__()`` no longer + raises ``KeyError``; it just returns an empty list if there are no + distributions for the named project. * The ``resolve()`` method of ``Environment`` is now a method of ``WorkingSet`` instead, and the ``best_match()`` method now uses a working From pje at users.sourceforge.net Sun Aug 14 08:03:32 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 08:03:32 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.66, 1.67 Message-ID: <20050814060332.66ED31E4019@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11496 Modified Files: pkg_resources.py Log Message: Make "run_script" a method of WorkingSet objects, thereby removing a global coupling. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.66 retrieving revision 1.67 diff -u -d -r1.66 -r1.67 --- pkg_resources.py 14 Aug 2005 01:45:37 -0000 1.66 +++ pkg_resources.py 14 Aug 2005 06:03:20 -0000 1.67 @@ -398,13 +398,13 @@ elif name in entries: yield entries[name] - - - - - - - + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) @@ -2147,6 +2147,8 @@ require = working_set.require iter_entry_points = working_set.iter_entry_points add_activation_listener = working_set.subscribe +run_script = working_set.run_script +run_main = run_script # backward compatibility # Activate all distributions already on sys.path, and ensure that # all distributions added to the working set in the future (e.g. by @@ -2169,5 +2171,3 @@ - - From pje at users.sourceforge.net Sun Aug 14 08:08:47 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 08:08:47 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.txt, 1.7, 1.8 Message-ID: <20050814060847.D31BA1E4019@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12011 Modified Files: pkg_resources.txt Log Message: Documentation for namespace packages, working sets, and supporting custom PEP 302 importers. Once the "Distribution" class is documented, this will be a complete API reference for pkg_resources. Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- pkg_resources.txt 14 Aug 2005 01:45:37 -0000 1.7 +++ pkg_resources.txt 14 Aug 2005 06:08:37 -0000 1.8 @@ -46,19 +46,231 @@ API Reference ------------- - 'require', 'run_script', - Namespace Package Support ========================= -declare_namespace, fixup_namespace_packages, register_namespace_handler +A namespace package is a package that only contains other packages and modules, +with no direct contents of its own. Such packages can be split across +multiple, separately-packaged distributions. Normally, you do not need to use +the namespace package APIs directly; instead you should supply the +``namespace_packages`` argument to ``setup()`` in your project's ``setup.py``. +See the `setuptools documentation on namespace packages`_ for more information. + +However, if for some reason you need to manipulate namespace packages or +directly alter ``sys.path`` at runtime, you may find these APIs useful: + +``declare_namespace(name)`` + Declare that the dotted package name `name` is a "namespace package" whose + contained packages and modules may be spread across multiple distributions. + The named package's ``__path__`` will be extended to include the + corresponding package in all distributions on ``sys.path`` that contain a + package of that name. (More precisely, if an importer's + ``find_module(name)`` returns a loader, then it will also be searched for + the package's contents.) Whenever a Distribution's ``to_install()`` method + is invoked, it checks for the presence of namespace packages and updates + their ``__path__`` contents accordingly. + +``fixup_namespace_packages(path_item)`` + Declare that `path_item` is a newly added item on ``sys.path`` that may + need to be used to update existing namespace packages. Ordinarily, this is + called for you when an egg is automatically added to ``sys.path``, but if + your application modifies ``sys.path`` to include locations that may + contain portions of a namespace package, you will need to call this + function to ensure they are added to the existing namespace packages. + +Although by default ``pkg_resources`` only supports namespace packages for +filesystem and zip importers, you can extend its support to other "importers" +compatible with PEP 302 using the ``register_namespace_handler()`` function. +See the section below on `Supporting Custom Importers`_ for details. + +.. _setuptools documentation on namespace packages: http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages ``WorkingSet`` Objects ====================== -Listeners -working_set +The ``WorkingSet`` class provides access to a collection of "active" +distributions. In general, there is only one meaningful ``WorkingSet`` +instance: the one that represents the distributions that are currently active +on ``sys.path``. This global instance is available under the name +``working_set`` in the ``pkg_resources`` module. However, specialized +tools may wish to manipulate working sets that don't correspond to +``sys.path``, and therefore may wish to create other ``WorkingSet`` instances. + +It's important to note that the global ``working_set`` object is initialized +from ``sys.path`` when ``pkg_resources`` is first imported, but is only updated +if you do all future ``sys.path`` manipulation via ``pkg_resources`` APIs. If +you manually modify ``sys.path``, you must invoke the appropriate methods on +the ``working_set`` instance to keep it in sync. Unfortunately, Python does +not provide any way to detect arbitrary changes to a list object like +``sys.path``, so ``pkg_resources`` cannot automatically update the +``working_set`` based on changes to ``sys.path``. + +``WorkingSet(entries=None)`` + Create a ``WorkingSet`` from an iterable of path entries. If `entries` + is not supplied, it defaults to the value of ``sys.path`` at the time + the constructor is called. + + Note that you will not normally construct ``WorkingSet`` instances + yourbut instead you will implicitly or explicitly use the global + ``working_set`` instance. For the most part, the ``pkg_resources`` API + is designed so that the ``working_set`` is used by default, such that you + don't have to explicitly refer to it most of the time. + + +Basic ``WorkingSet`` Methods +---------------------------- + +The following methods of ``WorkingSet`` objects are also available as module- +level functions in ``pkg_resources`` that apply to the default ``working_set`` +instance. Thus, you can use e.g. ``pkg_resources.require()`` as an +abbreviation for ``pkg_resources.working_set.require()``: + + +``require(*requirements)`` + Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + + For the syntax of requirement specifiers, see the section below on + `Requirements Parsing`_. + + Note: in general, it should not be necessary for you to call this method + directly. It's intended more for use in quick-and-dirty scripting and + interactive interpreter hacking than for production use. If you're creating + an actual library or application, it's strongly recommended that you create + a "setup.py" script using ``setuptools``, and declare all your requirements + there. That way, tools like EasyInstall can automatically detect what + requirements your package has, and deal with them accordingly. + +``run_script(requires, script_name)`` + Locate distribution specified by `requires` and run its `script_name` + script. `requires` must be a string containing a requirement specifier. + (See `Requirements Parsing`_ below for the syntax.) + + The script, if found, will be executed in *the caller's globals*. That's + because this method is intended to be called from wrapper scripts that + act as a proxy for the "real" scripts in a distribution. A wrapper script + usually doesn't need to do anything but invoke this function with the + correct arguments. + + If you need more control over the script execution environment, you + probably want to use the ``run_script()`` method of a ``Distribution`` + object's `Metadata API`_ instead. + +``iter_entry_points(group, name=None)`` + Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching both + `group` and `name` are yielded. Entry points are yielded from the active + distributions in the order that the distributions appear in the working + set. (For the global ``working_set``, this should be the same as the order + that they are listed in ``sys.path``.) Note that within the entry points + advertised by an individual distribution, there is no particular ordering. + + Please see the section below on `Entry Points`_ for more information. + + +``WorkingSet`` Methods and Attributes +------------------------------------- + +These methods are used to query or manipulate the contents of a specific +working set, so they must be explicitly invoked on a particular ``WorkingSet`` +instance: + +``add_entry(entry)`` + Add a path item to the ``entries``, finding any distributions on it. You + should use this when you add additional items to ``sys.path`` and you want + the global ``working_set`` to reflect the change. This method is also + called by the ``WorkingSet()`` constructor during initialization. + + This method uses ``find_distributions(entry,False)`` to find distributions + corresponding to the path entry, and then ``add()`` them. `entry` is + always appended to the ``entries`` attribute, even if it is already + present, however. (This is because ``sys.path`` can contain the same value + more than once, and the ``entries`` attribute should be able to reflect + this.) + +``__contains__(dist)`` + True if `dist` is active in this ``WorkingSet``. Note that only one + distribution for a given project can be active in a given ``WorkingSet``. + +``__iter__()`` + Yield distributions for non-duplicate projects in the working set. + The yield order is the order in which the items' path entries were + added to the working set. + +``find(req)`` + Find a distribution matching `req` (a ``Requirement`` instance). + If there is an active distribution for the requested project, this + returns it, as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + +``resolve(requirements, env=None, installer=None)`` + List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, an ``Environment`` is created from the working set's + ``entries``. `installer`, if supplied, will be invoked with each + requirement that cannot be met by an already-installed distribution; it + should return a ``Distribution`` or ``None``. (See the ``obtain()`` method + of `Environment Objects`_, below, for more information on the `installer` + argument.) + +``add(dist, entry=None)`` + Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to ``dist.location``. On exit from + this routine, `entry` is added to the end of the working set's ``.entries`` + (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution active in the set. If it's + successfully added, any callbacks registered with the ``subscribe()`` + method will be called. (See `Receiving Change Notifications`_, below.) + + Note: ``add()`` is automatically called for you by the ``require()`` + method, so you don't normally need to use this method directly. + +``entries`` + This attribute represents a "shadow" ``sys.path``, primarily useful for + debugging. If you are experiencing import problems, you should check + the global ``working_set`` object's ``entries`` against ``sys.path``, to + ensure that they match. If they do not, then some part of your program + is manipulating ``sys.path`` without updating the ``working_set`` + accordingly. IMPORTANT NOTE: do not directly manipulate this attribute! + Setting it equal to ``sys.path`` will not fix your problem, any more than + putting black tape over an "engine warning" light will fix your car! If + this attribute is out of sync with ``sys.path``, it's merely an *indicator* + of the problem, not the cause of it. + + +Receiving Change Notifications +------------------------------ + +Extensible applications and frameworks may need to receive notification when +a new distribution (such as a plug-in component) has been added to a working +set. This is what the ``subscribe()`` method and ``add_activation_listener()`` +function are for. + +``subscribe(callback)`` + Invoke ``callback(distribution)`` once for each active distribution that is + in the set now, or gets added later. Because the callback is invoked for + already-active distributions, you do not need to loop over the working set + yourself to deal with the existing items; just register the callback and + be prepared for the fact that it will be called immediately by this method. + +``pkg_resources.add_activation_listener()`` is an alternate spelling of +``pkg_resources.working_set.subscribe()``. ``Environment`` Objects @@ -317,7 +529,7 @@ ``EntryPoint`` instance in that group. ``iter_entry_points(group, name=None)`` - Yield entry point objects from `group` matching `name` + Yield entry point objects from `group` matching `name`. If `name` is None, yields all entry points in `group` from all distributions in the working set on sys.path, otherwise only ones matching @@ -326,6 +538,9 @@ sys.path. (Within entry points for a particular distribution, however, there is no particular ordering.) + (This API is actually a method of the global ``working_set`` object; see + the section above on `Basic WorkingSet Methods`_ for more information.) + Creating and Parsing -------------------- @@ -411,16 +626,37 @@ ``EntryPoint.parse()`` to yield an equivalent ``EntryPoint``. + ``Distribution`` Objects ======================== -Factories: get_provider, get_distribution, find_distributions; see also +Factories: see also WorkingSet and Environment APIs. register_finder register_loader_type 'load_entry_point', 'get_entry_map', 'get_entry_info' +Getting or Creating Distributions +--------------------------------- + +``find_distributions(path_item, only=False)`` + Yield distributions accessible via `path_item`. If `only` is true, yield + only distributions whose ``location`` is equal to `path_item`. In other + words, if `only` is true, this yields any distributions that would be + importable if `path_item` were on ``sys.path``. If `only` is false, this + also yields distributions that are "in" or "under" `path_item`, but would + not be importable unless their locations were also added to ``sys.path``. + +``get_distribution(dist_spec)`` + Return a ``Distribution`` object for a given ``Requirement`` or string. + If `dist_spec` is already a ``Distribution`` instance, it is returned. + If it is a ``Requirement`` object or a string that can be parsed into one, + it is used to locate and activate a matching distribution, which is then + returned. + + + ``ResourceManager`` API ======================= @@ -525,14 +761,16 @@ details.) Resources are extracted to subdirectories of this path based upon - information given by the ``IResourceProvider``. You may set this to a + information given by the resource provider. You may set this to a temporary directory, but then you must call ``cleanup_resources()`` to delete the extracted files when done. There is no guarantee that - ``cleanup_resources()`` will be able to remove all extracted files. + ``cleanup_resources()`` will be able to remove all extracted files. (On + Windows, for example, you can't unlink .pyd or .dll files that are still + in use.) - (Note: you may not change the extraction path for a given resource + Note that you may not change the extraction path for a given resource manager once resources have been extracted, unless you first call - ``cleanup_resources()``.) + ``cleanup_resources()``. ``cleanup_resources(force=False)`` Delete all extracted resource files and directories, returning a list @@ -550,7 +788,7 @@ If you are implementing an ``IResourceProvider`` and/or ``IMetadataProvider`` for a new distribution archive format, you may need to use the following -``ResourceManager`` methods to co-ordinate extraction of resources to the +``IResourceManager`` methods to co-ordinate extraction of resources to the filesystem. If you're not implementing an archive format, however, you have no need to use these methods. Unlike the other methods listed above, they are *not* available as top-level functions tied to the global ``ResourceManager``; @@ -592,12 +830,11 @@ resource paths. The metadata API is provided by objects implementing the ``IMetadataProvider`` -interface. ``Distribution`` objects created with a ``metadata`` setting -implement this interface, as do objects returned by the ``get_provider()`` -function: +or ``IResourceProvider`` interfaces. ``Distribution`` objects implement this +interface, as do objects returned by the ``get_provider()`` function: ``get_provider(package_or_requirement)`` - If a package name is supplied, return an ``IMetadataProvider`` for the + If a package name is supplied, return an ``IResourceProvider`` for the package. If a ``Requirement`` is supplied, resolve it by returning a ``Distribution`` from the current working set (searching the current ``Environment`` if necessary and adding the newly found ``Distribution`` @@ -606,12 +843,15 @@ Note also that if you supply a package name, and the package is not part of a pluggable distribution (i.e., it has no metadata), then you will still - get an ``IMetadataProvider`` object, but it will just be an empty one that - answers ``False`` when asked if any given metadata resource file or - directory exists. + get an ``IResourceProvider`` object, but it will return ``False`` when + asked if any metadata files or directories exist. -The methods provided by ``IMetadataProvider`` (and ``Distribution`` objects -with a ``metadata`` attribute set) are: + +``IMetadataProvider`` Methods +----------------------------- + +The methods provided by objects (such as ``Distribution`` instances) that +implement the ``IMetadataProvider`` or ``IResourceProvider`` interfaces are: ``has_metadata(name)`` Does the named metadata resource exist? @@ -668,6 +908,150 @@ was requested from. +Supporting Custom Importers +=========================== + +By default, ``pkg_resources`` supports normal filesystem imports, and +``zipimport`` importers. If you wish to use the ``pkg_resources`` features +with other (PEP 302-compatible) importers or module loaders, you may need to +register various handlers and support functions using these APIs: + +``register_finder(importer_type, distribution_finder)`` + Register `distribution_finder` to find distributions in ``sys.path`` items. + `importer_type` is the type or class of a PEP 302 "Importer" (``sys.path`` + item handler), and `distribution_finder` is a callable that, when passed a + path item, the importer instance, and an `only` flag, yields + ``Distribution`` instances found under that path item. (The `only` flag, + if true, means the finder should yield only ``Distribution`` objects whose + ``location`` is equal to the path item provided.) + + See the source of the ``pkg_resources.find_on_path`` function for an + example finder function. + +``register_loader_type(loader_type, provider_factory)`` + Register `provider_factory` to make ``IResourceProvider`` objects for + `loader_type`. `loader_type` is the type or class of a PEP 302 + ``module.__loader__``, and `provider_factory` is a function that, when + passed a module object, returns an `IResourceProvider`_ for that module, + allowing it to be used with the `ResourceManager API`_. + +``register_namespace_handler(importer_type, namespace_handler)`` + Register `namespace_handler` to declare namespace packages for the given + `importer_type`. `importer_type` is the type or class of a PEP 302 + "importer" (sys.path item handler), and `namespace_handler` is a callable + with a signature like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the relevant importer object has + already agreed that it can handle the relevant path item. The handler + should only return a subpath if the module ``__path__`` does not already + contain an equivalent subpath. Otherwise, it should return None. + + For an example namespace handler, see the source of the + ``pkg_resources.file_ns_handler`` function, which is used for both zipfile + importing and regular importing. + + +IResourceProvider +----------------- + +``IResourceProvider`` is an abstract class that documents what methods are +required of objects returned by a `provider_factory` registered with +``register_loader_type()``. ``IResourceProvider`` is a subclass of +``IMetadataProvider``, so objects that implement this interface must also +implement all of the `IMetadataProvider Methods`_ as well as the methods +shown here. The `manager` argument to the methods below must be an object +that supports the full `ResourceManager API`_ documented above. + +``get_resource_filename(manager, resource_name)`` + Return a true filesystem path for `resource_name`, co-ordinating the + extraction with `manager`, if the resource must be unpacked to the + filesystem. + +``get_resource_stream(manager, resource_name)`` + Return a readable file-like object for `resource_name`. + +``get_resource_string(manager, resource_name)`` + Return a string containing the contents of `resource_name`. + +``has_resource(resource_name)`` + Does the package contain the named resource? + +``resource_isdir(resource_name)`` + Is the named resource a directory? Return a false value if the resource + does not exist or is not a directory. + +``resource_listdir(resource_name)`` + Return a list of the contents of the resource directory, ala + ``os.listdir()``. Requesting the contents of a non-existent directory may + raise an exception. + +Note, by the way, that your provider classes need not (and should not) subclass +``IResourceProvider`` or ``IMetadataProvider``! These classes exist solely +for documentation purposes and do not provide any useful implementation code. +You may instead wish to subclass one of the `built-in resource providers`_. + + +Built-in Resource Providers +--------------------------- + +``pkg_resources`` includes several provider classes, whose inheritance +tree looks like this:: + + NullProvider + EggProvider + DefaultProvider + PathMetadata + ZipProvider + EggMetadata + EmptyProvider + + +``NullProvider`` + This provider class is just an abstract base that provides for common + provider behaviors (such as running scripts), given a definition for just + a few abstract methods. + +``EggProvider`` + This provider class adds in some egg-specific features that are common + to zipped and unzipped eggs. + +``DefaultProvider`` + This provider class is used for unpacked eggs and "plain old Python" + filesystem modules. + +``ZipProvider`` + This provider class is used for all zipped modules, whether they are eggs + or not. + +``EmptyProvider`` + This provider class always returns answers consistent with a provider that + has no metadata or resources. ``Distribution`` objects created without + a ``metadata`` argument use an instance of this provider class instead. + Since all ``EmptyProvider`` instances are equivalent, there is no need + to have more than one instance. ``pkg_resources`` therefore creates a + global instance of this class under the name ``empty_provider``, and you + may use it if you have need of an ``EmptyProvider`` instance. + +``PathMetadata(path, egg_info)`` + Create an ``IResourceProvider`` for a filesystem-based distribution, where + `path` is the filesystem location of the importable modules, and `egg_info` + is the filesystem location of the distribution's metadata directory. + `egg_info` should usually be the ``EGG-INFO`` subdirectory of `path` for an + "unpacked egg", and a ``ProjectName.egg-info`` subdirectory of `path` for + a "development egg". However, other uses are possible for custom purposes. + +``EggMetadata(zipimporter)`` + Create an ``IResourceProvider`` for a zipfile-based distribution. The + `zipimporter` should be a ``zipimport.zipimporter`` instance, and may + represent a "basket" (a zipfile containing multiple ".egg" subdirectories) + a specific egg *within* a basket, or a zipfile egg (where the zipfile + itself is a ".egg"). It can also be a combination, such as a zipfile egg + that also contains other eggs. + + Utility Functions ================= From pje at users.sourceforge.net Sun Aug 14 19:30:26 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 19:30:26 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.67, 1.68 pkg_resources.txt, 1.8, 1.9 Message-ID: <20050814173026.55E7B1E401F@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18452 Modified Files: pkg_resources.py pkg_resources.txt Log Message: Document "Distribution" objects. Now the API reference is complete, and I just need to write the Overview and Developer's Guide sections so that most people won't have to actually *read* the API reference. :) Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.67 retrieving revision 1.68 diff -u -d -r1.67 -r1.68 --- pkg_resources.py 14 Aug 2005 06:03:20 -0000 1.67 +++ pkg_resources.py 14 Aug 2005 17:30:15 -0000 1.68 @@ -1881,9 +1881,9 @@ from_filename = classmethod(from_filename) def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" return Requirement.parse('%s==%s' % (self.project_name, self.version)) - def load_entry_point(self, group, name): """Return the `name` entry point of `group` or raise ImportError""" ep = self.get_entry_info(group,name) Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- pkg_resources.txt 14 Aug 2005 06:08:37 -0000 1.8 +++ pkg_resources.txt 14 Aug 2005 17:30:15 -0000 1.9 @@ -626,20 +626,25 @@ ``EntryPoint.parse()`` to yield an equivalent ``EntryPoint``. - ``Distribution`` Objects ======================== -Factories: see also -WorkingSet and Environment APIs. +``Distribution`` objects represent collections of Python code that may or may +not be importable, and may or may not have metadata and resources associated +with them. Their metadata may include information such as what other projects +the distribution depends on, what entry points the distribution advertises, and +so on. -register_finder -register_loader_type -'load_entry_point', 'get_entry_map', 'get_entry_info' Getting or Creating Distributions --------------------------------- +Most commonly, you'll obtain ``Distribution`` objects from a ``WorkingSet`` or +an ``Environment``. (See the sections above on `WorkingSet Objects`_ and +`Environment Objects`_, which are containers for active distributions and +available distributions, respectively.) You can also obtain ``Distribution`` +objects from one of these high-level APIs: + ``find_distributions(path_item, only=False)`` Yield distributions accessible via `path_item`. If `only` is true, yield only distributions whose ``location`` is equal to `path_item`. In other @@ -655,7 +660,191 @@ it is used to locate and activate a matching distribution, which is then returned. +However, if you're creating specialized tools for working with distributions, +or creating a new distribution format, you may also need to create +``Distribution`` objects directly, using one of the three constructors below. + +These constructors all take an optional `metadata` argument, which is used to +access any resources or metadata associated with the distribution. `metadata` +must be an object that implements the ``IResourceProvider`` interface, or None. +If it is None, an ``EmptyProvider`` is used instead. ``Distribution`` objects +implement both the `IResourceProvider`_ and `IMetadataProvider Methods`_ by +delegating them to the `metadata` object. + +``Distribution.from_location(location, basename, metadata=None)`` (classmethod) + Create a distribution for `location`, which must be a string such as a + URL, filename, or other string that might be used on ``sys.path``. + `basename` is a string naming the distribution, like ``Foo-1.2-py2.4.egg``. + If `basename` ends with ``.egg``, then the project's name, version, python + version and platform are extracted from the filename and used to set those + properties of the created distribution. + +``Distribution.from_filename(filename, metadata=None)`` (classmethod) + Create a distribution by parsing a local filename. This is a shorter way + of saying ``Distribution.from_location(normalize_path(filename), + os.path.basename(filename), metadata)``. In other words, it creates a + distribution whose location is the normalize form of the filename, parsing + name and version information from the base portion of the filename. + +``Distribution(location,metadata,project_name,version,py_version,platform,precedence)`` + Create a distribution by setting its properties. All arguments are + optional and default to None, except for `py_version` (which defaults to + the current Python version) and `precedence` (which defaults to + ``EGG_DIST``; for more details see ``precedence`` under `Distribution + Attributes`_ below). Note that it's usually easier to use the + ``from_filename()`` or ``from_location()`` constructors than to specify + all these arguments individually. + + +``Distribution`` Attributes +--------------------------- + +location + A string indicating the distribution's location. For an importable + distribution, this is the string that would be added to ``sys.path`` to + make it actively importable. For non-importable distributions, this is + simply a filename, URL, or other way of locating the distribution. + +project_name + A string, naming the project that this distribution is for. Project names + are defined by a project's setup script, and they are used to identify + projects on PyPI. When a ``Distribution`` is constructed, the + `project_name` argument is passed through the ``safe_name()`` utility + function to filter out any unacceptable characters. + +key + ``dist.key`` is short for ``dist.project_name.lower()``. It's used for + case-insensitive comparison and indexing of distributions by project name. + +version + A string denoting what release of the project this distribution contains. + When a ``Distribution`` is constructed, the `version` argument is passed + through the ``safe_version()`` utility function to filter out any + unacceptable characters. If no `version` is specified at construction + time, then attempting to access this attribute later will cause the + ``Distribution`` to try to discover its version by reading its ``PKG-INFO`` + metadata file. If ``PKG-INFO`` is unavailable or can't be parsed, + ``ValueError`` is raised. + +parsed_version + The ``parsed_version`` is a tuple representing a "parsed" form of the + distribution's ``version``. ``dist.parsed_version`` is a shortcut for + calling ``parse_version(dist.version)``. It is used to compare or sort + distributions by version. (See the `Parsing Utilities`_ section below for + more information on the ``parse_version()`` function.) Note that accessing + ``parsed_version`` may result in a ``ValueError`` if the ``Distribution`` + was constructed without a `version` and without `metadata` capable of + supplying the missing version info. + +py_version + The major/minor Python version the distribution supports, as a string. + For example, "2.3" or "2.4". The default is the current version of Python. + +platform + A string representing the platform the distribution is intended for, or + ``None`` if the distribution is "pure Python" and therefore cross-platform. + See `Platform Utilities`_ below for more information on platform strings. + +precedence + A distribution's ``precedence`` is used to determine the relative order of + two distributions that have the same ``project_name`` and + ``parsed_version``. The default precedence is ``pkg_resources.EGG_DIST``, + which is the highest (i.e. most preferred) precedence. The full list + of predefined precedences, from most preferred to least preferred, is: + ``EGG_DIST``, ``BINARY_DIST``, ``SOURCE_DIST``, and ``CHECKOUT_DIST``. + Normally, precedences other than ``EGG_DIST`` are used only by the + ``setuptools.package_index`` module, when sorting distributions found in a + package index to determine their suitability for installation. + + +``Distribution`` Methods +------------------------ + +``activate(path=None)`` + Ensure distribution is importable on `path`. If `path` is None, + ``sys.path`` is used instead. This ensures that the distribution's + ``location`` is in the `path` list, and it also performs any necessary + namespace package fixups or declarations. (That is, if the distribution + contains namespace packages, this method ensures that they are declared, + and that the distribution's contents for those namespace packages are + merged with the contents provided by any other active distributions. See + the section above on `Namespace Package Support`_ for more information.) + + ``pkg_resources`` adds a notification callback to the global ``working_set`` + that ensures this method is called whenever a distribution is added to it. + Therefore, you should not normally need to explicitly call this method. + (Note that this means that namespace packages on ``sys.path`` are always + imported as soon as ``pkg_resources`` is, which is another reason why + namespace packages should not contain any code or import statements.) + +``as_requirement()`` + Return a ``Requirement`` instance that matches this distribution's project + name and version. + +``requires(extras=())`` + List the ``Requirement`` objects that specify this distribution's + dependencies. If `extras` is specified, it should be a sequence of names + of "extras" defined by the distribution, and the list returned will then + include any dependencies needed to support the named "extras". + +``egg_name()`` + Return what this distribution's standard filename should be, not including + the ".egg" extension. For example, a distribution for project "Foo" + version 1.2 that runs on Python 2.3 for Windows would have an ``egg_name()`` + of ``Foo-1.2-py2.3-win32``. Any dashes in the name or version are + converted to underscores. (``Distribution.from_location()`` will convert + them back when parsing a ".egg" file name.) + +``__cmp__(other)``, ``__hash__()`` + Distribution objects are hashed and compared on the basis of their parsed + version and precedence, followed by their key (lowercase project name), + location, Python version, and platform. + +The following methods are used to access ``EntryPoint`` objects advertised +by the distribution. See the section above on `Entry Points`_ for more +detailed information about these operations: + +``get_entry_info(group, name)`` + Return the ``EntryPoint`` object for `group` and `name`, or None if no + such point is advertised by this distribution. + +``get_entry_map(group=None)`` + Return the entry point map for `group`. If `group` is None, return + a dictionary mapping group names to entry point maps for all groups. + (An entry point map is a dictionary of entry point names to ``EntryPoint`` + objects.) + +``load_entry_point(group, name)`` + Short for ``get_entry_info(group, name).load()``. Returns the object + advertised by the named entry point, or raises ``ImportError`` if + the entry point isn't advertised by this distribution, or there is some + other import problem. + +In addition to the above methods, ``Distribution`` objects also implement all +of the `IResourceProvider`_ and `IMetadataProvider Methods`_ (which are +documented in later sections): + +* ``has_metadata(name)`` +* ``metadata_isdir(name)`` +* ``metadata_listdir(name)`` +* ``get_metadata(name)`` +* ``get_metadata_lines(name)`` +* ``run_script(script_name, namespace)`` +* ``get_resource_filename(manager, resource_name)`` +* ``get_resource_stream(manager, resource_name)`` +* ``get_resource_string(manager, resource_name)`` +* ``has_resource(resource_name)`` +* ``resource_isdir(resource_name)`` +* ``resource_listdir(resource_name)`` + +If the distribution was created with a `metadata` argument, these resource and +metadata access methods are all delegated to that `metadata` provider. +Otherwise, they are delegated to an ``EmptyProvider``, so that the distribution +will appear to have no resources or metadata. This delegation approach is used +so that supporting custom importers or new distribution formats can be done +simply by creating an appropriate `IResourceProvider`_ implementation; see the +section below on `Supporting Custom Importers`_ for more details. ``ResourceManager`` API @@ -1212,4 +1401,3 @@ reflect the platform's case-sensitivity, so there is always the possibility of two apparently-different paths being equal on such platforms. - From pje at users.sourceforge.net Sun Aug 14 19:48:17 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 19:48:17 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.txt, 1.9, 1.10 Message-ID: <20050814174817.23E6C1E4020@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21566 Modified Files: pkg_resources.txt Log Message: Fix some reST formatting problems and other issues discovered during a quick review. Index: pkg_resources.txt =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.txt,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- pkg_resources.txt 14 Aug 2005 17:30:15 -0000 1.9 +++ pkg_resources.txt 14 Aug 2005 17:48:07 -0000 1.10 @@ -18,13 +18,15 @@ Overview -------- -XXX +XXX TBD ----------------- Developer's Guide ----------------- +This section isn't written yet. Currently planned topics include:: + Accessing Resources Finding and Activating Package Distributions get_provider() @@ -519,7 +521,7 @@ the specified distribution. Returns ``None`` if the distribution has not advertised a matching entry point. -``get_entry_map(dist, group=None) +``get_entry_map(dist, group=None)`` Return the distribution's entry point map for `group`, or the full entry map for the distribution. This function always returns a dictionary, even if the distribution advertises no entry points. If `group` is given, @@ -876,7 +878,10 @@ importable (i.e., be in a distribution or directory on ``sys.path``), and the `resource_name` argument is interpreted relative to the named package. (Note that if a module name is used, then the resource name is relative to the -package immediately containing the named module.) +package immediately containing the named module. Also, you should not use use +a namespace package name, because a namespace package can be spread across +multiple distributions, and is therefore ambiguous as to which distribution +should be searched for the resource.) If it is a ``Requirement``, then the requirement is automatically resolved (searching the current ``Environment`` if necessary) and a matching @@ -888,7 +893,7 @@ distribution. Note that resource names must be ``/``-separated paths and cannot be absolute -(i.e. no leading ``/``) or contain relative names like ``..``. Do *not* use +(i.e. no leading ``/``) or contain relative names like ``".."``. Do *not* use ``os.path`` routines to manipulate resource paths, as they are *not* filesystem paths. @@ -1186,8 +1191,8 @@ Built-in Resource Providers --------------------------- -``pkg_resources`` includes several provider classes, whose inheritance -tree looks like this:: +``pkg_resources`` includes several provider classes that are automatically used +where appropriate. Their inheritance tree looks like this:: NullProvider EggProvider From pje at users.sourceforge.net Sun Aug 14 22:16:38 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 22:16:38 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools dist.py, 1.19, 1.20 Message-ID: <20050814201638.DAA5D1E4022@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16098/setuptools Modified Files: dist.py Log Message: Allow distributing an empty namespace package. Index: dist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v retrieving revision 1.19 retrieving revision 1.20 diff -u -d -r1.19 -r1.20 --- dist.py 6 Aug 2005 19:29:49 -0000 1.19 +++ dist.py 14 Aug 2005 20:16:28 -0000 1.20 @@ -54,7 +54,7 @@ for nsp in value: for name in dist.iter_distribution_names(): - if name.startswith(nsp+'.'): break + if name==nsp or name.startswith(nsp+'.'): break else: raise DistutilsSetupError( "Distribution contains no modules or packages for " + From pje at users.sourceforge.net Sun Aug 14 22:19:06 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 22:19:06 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools dist.py, 1.20, 1.21 Message-ID: <20050814201906.1FA451E4022@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16344/setuptools Modified Files: dist.py Log Message: On second thought, don't. :( Walter Doerwald's situation isn't really compatible with namespace packages, even if I do manage to hack up a way to make it work. Index: dist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v retrieving revision 1.20 retrieving revision 1.21 diff -u -d -r1.20 -r1.21 --- dist.py 14 Aug 2005 20:16:28 -0000 1.20 +++ dist.py 14 Aug 2005 20:18:56 -0000 1.21 @@ -54,7 +54,7 @@ for nsp in value: for name in dist.iter_distribution_names(): - if name==nsp or name.startswith(nsp+'.'): break + if name.startswith(nsp+'.'): break else: raise DistutilsSetupError( "Distribution contains no modules or packages for " + From pje at users.sourceforge.net Sun Aug 14 22:46:59 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 22:46:59 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command easy_install.py, 1.21, 1.22 Message-ID: <20050814204659.331EA1E4022@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21106/setuptools/command Modified Files: easy_install.py Log Message: Fix a bug introduced by removing the Environment.get() method. Index: easy_install.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/easy_install.py,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- easy_install.py 11 Aug 2005 00:37:37 -0000 1.21 +++ easy_install.py 14 Aug 2005 20:46:49 -0000 1.22 @@ -822,7 +822,7 @@ if self.pth_file is None: return - for d in self.pth_file.get(dist.key,()): # drop old entries + for d in self.pth_file[dist.key]: # drop old entries if self.multi_version or d.location != dist.location: log.info("Removing %s from easy-install.pth file", d) self.pth_file.remove(d) From pje at users.sourceforge.net Sun Aug 14 23:01:43 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 23:01:43 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.68, 1.69 Message-ID: <20050814210143.668261E4024@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22976 Modified Files: pkg_resources.py Log Message: Add experimental support for merging non-empty namespace packages. This lets you have one distribution containing a non-empty __init__.py for the package, as long as you call 'declare_namespace()' from that __init__.py and all other __init__.py files for the namespace package, and do *not* declare it as a namespace package in setup() (so that it won't be automatically imported if it's on sys.path, the way empty namespace packages are.) Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.68 retrieving revision 1.69 diff -u -d -r1.68 -r1.69 --- pkg_resources.py 14 Aug 2005 17:30:15 -0000 1.68 +++ pkg_resources.py 14 Aug 2005 21:01:33 -0000 1.69 @@ -13,7 +13,7 @@ method. """ -import sys, os, zipimport, time, re, imp +import sys, os, zipimport, time, re, imp, new from sets import ImmutableSet @@ -1412,7 +1412,6 @@ """ _namespace_handlers[importer_type] = namespace_handler - def _handle_ns(packageName, path_item): """Ensure that named package includes a subpath of path_item (if needed)""" importer = get_importer(path_item) @@ -1421,18 +1420,19 @@ loader = importer.find_module(packageName) if loader is None: return None - - module = sys.modules.get(packageName) or loader.load_module(packageName) - if not hasattr(module,'__path__'): + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = new.module(packageName) + module.__path__ = [] + elif not hasattr(module,'__path__'): raise TypeError("Not a package:", packageName) - handler = _find_adapter(_namespace_handlers, importer) subpath = handler(importer,path_item,packageName,module) if subpath is not None: module.__path__.append(subpath) + loader.load_module(packageName) return subpath - def declare_namespace(packageName): """Declare that package 'packageName' is a namespace package""" @@ -1451,16 +1451,16 @@ except AttributeError: raise TypeError("Not a package:", parent) - for path_item in path: - # Ensure all the parent's path items are reflected in the child, - # if they apply - _handle_ns(packageName, path_item) - # Track what packages are namespaces, so when new path items are added, # they can be updated _namespace_packages.setdefault(parent,[]).append(packageName) _namespace_packages.setdefault(packageName,[]) + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + finally: imp.release_lock() @@ -1478,9 +1478,9 @@ """Compute an ns-package subpath for a filesystem or zipfile importer""" subpath = os.path.join(path_item, packageName.split('.')[-1]) - normalized = os.path.normpath(os.path.normcase(subpath)) + normalized = normalize_path(subpath) for item in module.__path__: - if os.path.normpath(os.path.normcase(item))==normalized: + if normalize_path(item)==normalized: break else: # Only return the path if it's not already there From pje at users.sourceforge.net Sun Aug 14 23:14:56 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 23:14:56 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools dist.py, 1.21, 1.22 Message-ID: <20050814211456.D7DCD1E4271@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25594/setuptools Modified Files: dist.py Log Message: Minor refactoring of code that checks a distribution's contents. Index: dist.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- dist.py 14 Aug 2005 20:18:56 -0000 1.21 +++ dist.py 14 Aug 2005 21:14:46 -0000 1.22 @@ -53,9 +53,7 @@ assert_string_list(dist,attr,value) for nsp in value: - for name in dist.iter_distribution_names(): - if name.startswith(nsp+'.'): break - else: + if not dist.has_contents_for(nsp): raise DistutilsSetupError( "Distribution contains no modules or packages for " + "namespace package %r" % nsp @@ -80,6 +78,8 @@ "%r must be a boolean value (got %r)" % (attr,value) ) + + def check_install_requires(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: @@ -436,17 +436,17 @@ pfx = package+'.' - for p in self.packages or (): + for p in self.iter_distribution_names(): if p==package or p.startswith(pfx): return True - for p in self.py_modules or (): - if p==package or p.startswith(pfx): - return True - for p in self.ext_modules or (): - if p.name==package or p.name.startswith(pfx): - return True + + + + + + def _exclude_misc(self,name,value): From pje at users.sourceforge.net Sun Aug 14 23:17:54 2005 From: pje at users.sourceforge.net (pje@users.sourceforge.net) Date: Sun, 14 Aug 2005 23:17:54 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/setuptools/setuptools/command bdist_egg.py, 1.27, 1.28 Message-ID: <20050814211754.EF44E1E4024@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25708/setuptools/command Modified Files: bdist_egg.py Log Message: Auto-generate namespace __init__.py files for packages without them. This is a workaround for packages like 'll-color', which are distributed without 'll/__init__.py', to avoid overwriting ll-core's copy of ll/__init__.py. This allows existing packages that use this sort of kludging to be treated as a crude namespace package, as long as the "real" __init__.py also calls declare_namespace(). Index: bdist_egg.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/command/bdist_egg.py,v retrieving revision 1.27 retrieving revision 1.28 diff -u -d -r1.27 -r1.28 --- bdist_egg.py 12 Jul 2005 23:43:21 -0000 1.27 +++ bdist_egg.py 14 Aug 2005 21:17:45 -0000 1.28 @@ -26,8 +26,8 @@ ])) f.close() - - +# stub __init__.py for packages distributed without one +NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)' @@ -186,7 +186,7 @@ write_stub(os.path.basename(ext_name), pyfile) to_compile.append(pyfile) ext_outputs[p] = ext_name.replace(os.sep,'/') - + to_compile.extend(self.make_init_files()) if to_compile: cmd.byte_compile(to_compile) @@ -260,30 +260,30 @@ log.warn("zip_safe flag not set; analyzing archive contents...") return analyze_egg(self.bdist_dir, self.stubs) + def make_init_files(self): + """Create missing package __init__ files""" + init_files = [] + for base,dirs,files in walk_egg(self.bdist_dir): + if base==self.bdist_dir: + # don't put an __init__ in the root + continue + for name in files: + if name.endswith('.py'): + if '__init__.py' not in files: + pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.') + if self.distribution.has_contents_for(pkg): + log.warn("Creating missing __init__.py for %s",pkg) + filename = os.path.join(base,'__init__.py') + if not self.dry_run: + f = open(filename,'w'); f.write(NS_PKG_STUB) + f.close() + init_files.append(filename) + break + else: + # not a package, don't traverse to subdirectories + dirs[:] = [] - - - - - - - - - - - - - - - - - - - - - - - + return init_files def walk_egg(egg_dir): """Walk an unpacked egg's contents, skipping the metadata directory""" From loewis at users.sourceforge.net Sun Aug 14 23:42:44 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Sun, 14 Aug 2005 23:42:44 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Modules posixmodule.c, 2.338, 2.339 Message-ID: <20050814214244.C16CC1E4022@bag.python.org> Update of /cvsroot/python/python/dist/src/Modules In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30605/Modules Modified Files: posixmodule.c Log Message: Correct definition of ST_GEN_IDX. Index: posixmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v retrieving revision 2.338 retrieving revision 2.339 diff -u -d -r2.338 -r2.339 --- posixmodule.c 9 Aug 2005 15:00:59 -0000 2.338 +++ posixmodule.c 14 Aug 2005 21:42:34 -0000 2.339 @@ -740,9 +740,9 @@ #endif #ifdef HAVE_STRUCT_STAT_ST_GEN -#define ST_GEN_IDX (ST_RDEV_IDX+1) +#define ST_GEN_IDX (ST_FLAGS_IDX+1) #else -#define ST_GEN_IDX ST_RDEV_IDX +#define ST_GEN_IDX ST_FLAGS_IDX #endif #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME From bcannon at users.sourceforge.net Mon Aug 15 06:28:39 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Mon, 15 Aug 2005 06:28:39 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.7,1.8 Message-ID: <20050815042839.52CF11E4272@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31789 Modified Files: pep-0348.txt Log Message: Big changes: - Remove proposal of removing WindowsError - Change bare 'except' proposal to recommend their removal Minor changes: - Flesh out arguments for TerminatingException - Reorganize discussion of hierarchy difference compared to 2.4 - Strip out unneeded Rejected Idea sections based on other discussions in the PEP Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- pep-0348.txt 9 Aug 2005 04:26:28 -0000 1.7 +++ pep-0348.txt 15 Aug 2005 04:28:28 -0000 1.8 @@ -13,25 +13,34 @@ Abstract ======== -Python, as 0of version 2.4, has 38 exceptions (including warnings) in -the built-in namespace in a rather shallow hierarchy. This list of -classes has grown over the years without a chance to learn from +Python, as of version 2.4, has 38 exceptions (including warnings) in +the built-in namespace in a rather shallow hierarchy. These +classes have come about over the years without a chance to learn from experience. This PEP proposes doing a reorganization of the hierarchy for Python 3.0 when backwards-compatibility is not as much of an -issue. Along with this reorganization, adding a requirement that all +issue. + +Along with this reorganization, adding a requirement that all objects passed to a ``raise`` statement must inherit from a specific -superclass is proposed. Lastly, bare ``except`` clauses will catch -only exceptions inheriting from Exception. +superclass is proposed. This is to have guarantees about the basic +interface of exceptions and to further enhance the natural hierarchy +of exceptions. +Lastly, bare ``except`` clauses will be removed. While they had their +usefulness when exceptions could be any object, with the above +proposal +of a required superclass for exceptions bare ``except`` clauses lose +their purpose. -Rationale -========= + +Rationale For Wanting Change +============================ Exceptions are a critical part of Python. While exceptions are traditionally used to signal errors in a program, they have also grown to be used for flow control for things such as iterators. -While their importance is great, there is lack of structure to them. +While their importance is great, there is a lack of structure to them. This stems from the fact that any object can be raised as an exception. Because of this you have no guarantee in terms of what kind of object will be raised, destroying any possible hierarchy @@ -40,26 +49,23 @@ But exceptions do have a hierarchy, showing the severity of the exception. The hierarchy also groups related exceptions together to simplify catching them in ``except`` clauses. To allow peopele to -be able to rely on this hierarchy, a common superclasse that all +be able to rely on this hierarchy, a common superclass that all raise objects must inherit from is being proposed. It also allows guarantees about the interface to raised objects to be made (see PEP 344 [#PEP344]_). A discussion about all of this has occurred before on python-dev [#Summary2004-08-01]_. -But allowing a guarantee about the hierarchy is not the only place -where exceptions can stand improvement. Bare ``except`` clauses are -often used in an inappropriate manner. Since they catch *all* raised -objects, they can catch exceptions that should have been allowed to -propagate to the top level of the execution stack, leading to the -interpreter terminating execution. Once again, this has been -discussed on python-dev [#python-dev3]_. +With the requirement of a common superclass for all exceptions, bare +``except`` clauses are impacted. Currently used as a catch-all for +raised exceptions, its usefulness is terminally weakened. Now, the +same functionality is possible by catching the required superclass. +Once again, this has been discussed on python-dev [#python-dev3]_. -To fix this over-reaching of bare ``except`` clauses, it is being -proposed that only objects inheriting from Exception be caught by -bare ``except`` clauses. This will allow the exception hierarchy -to be organized in such a way that bare ``except`` clauses can be -more useful by allowing exceptions that should not normally be caught -to lead to the termination of the interpreter. +Finally, slight changes to the exception hierarchy will make it much +more reasonable in terms of structure. By minor rearranging +exceptions +that should not typically be caught can be allowed to propagate to the +top of the execution stack, terminating the interpreter as intended. Philosophy of Reorganization @@ -104,9 +110,6 @@ .. parsed-literal:: +-- BaseException (new; broader inheritance for subclasses) - +-- TerminatingException (new; stricter inheritance for subclasses) - +-- KeyboardInterrupt - +-- SystemExit +-- Exception +-- GeneratorExit (defined in PEP 342 [#PEP342]_) +-- StandardError @@ -142,12 +145,17 @@ +-- StopIteration +-- SystemError +-- Warning - +-- PendingDeprecationWarning +-- DeprecationWarning +-- FutureWarning - +-- SyntaxWarning + +-- PendingDeprecationWarning +-- RuntimeWarning + +-- SyntaxWarning +-- UserWarning + + -- WindowsError + +-- TerminatingException (new; stricter inheritance for + subclasses) + +-- KeyboardInterrupt + +-- SystemExit Differences Compared to Python 2.4 @@ -157,49 +165,111 @@ inheritance changes. Inheritance changes result in either broader or more restrictive inheritance. "Broader" is when a class has an inheritance tree like ``cls, A`` and then becomes ``cls, B, A``. -"Stricter is the reverse. - +"Stricter" is the reverse. -New Exceptions --------------- BaseException -''''''''''''' +------------- -The superclass that all exceptions must inherit from. +The superclass that all exceptions must inherit from. It's name was +chosen to reflect that it is at the base of the exception hierarchy +while being an exception itself. "Raisable" was considered as a name, +it was passed on because its name did not properly reflect the fact +that it is an exception itself. + +Direct inheritance of BaseException is not expected, and will +be discouraged for the general case. Most user-defined +exceptions should inherit from Exception instead. This allows +catching Exception to continue to work in the common case of catching +all exceptions that should be caught. Direct inheritance of +BaseException should only be done in cases where an entirely new +category of exception is desired. + +But, for cases where all +exceptions should be caught blindly, ``except BaseException`` will +work. TerminatingException -'''''''''''''''''''' +-------------------- -Superclass for exceptions that are meant to the termination of the -interpreter. It does not inherit from Exception so that -subclasses are not caught by bare ``except`` clauses. +Superclass for exceptions that are meant to symbolize the termination +of +the interpreter. It does not inherit from Exception so that the +common +``except Exception`` statement does not catch the exceptions. This +for ``try`` statements that want to catch all exceptions that are +signals of errors and handle them separately from exceptions that +signify that the interpreter should be terminated:: -Naming based on the idea that the interpreter is trying to terminate when these -exceptions are raised. An earlier proposal suggested "TerminalException" but -avoidance of any confusion with an actual terminal along with "terminal" being -more fatalistic than "terminating" led to the current name choice. + try: + ... + # Catch all exceptions that are expected to be caught + except Exception: + ... + # Catch exceptions expected to terminate the interpreter + except TerminatingException: + ... +Compare this to:: -Removed Exceptions ------------------- + try: + ... + except Exception: + ... + except (KeyboardInterrupt, SystemExit): + ... -WindowsError -'''''''''''' +While more explicit, it is not necessarily obvious why the two +exceptions are being caught directly. By providing a common +superclass with a name that explicitly states the exceptions' typical +usage the reasoning behind the catch becomes more apparent. -Too OS-specific to be kept in the built-in exception hierarchy. +Comparing it to an even more general ``except`` clause:: + try: + ... + except Exception: + ... + except BaseException: + ... + +While this will perform the same action and catch all exceptions, +guaranteed, it is once again not obvious why Exception and +BaseException are being caught separately and not just +BaseException based purely on the code. + +TerminatingException will also help with transitioning from Python +2.x to 3.0 . With TerminatingException being a new exception, when +it is used the new inheritance for KeyboardInterrupt and SystemExit +becomes more obvious. + +It has been argued that TerminatingException should not be added +because it goes against Flat Is Better Than Nested (FIBTN). +While this new exception will not make the hierarchy as flat is it +could be, the overall depth of the tree is not changed; Exception has +a much deeper inheritance tree below it. + +It has also been argued that since KeyboardInterrupt and SystemExit +are not caught together that often in 2.4, there might not be much of +a need for TerminatingException. But with their new position in the +hierarchy catching them separately from other exceptions should +become more prevalent. + +Naming is based on the idea that the interpreter is trying to +terminate when KeyboardInterrupt and SystemExit +are raised. An earlier proposal suggested +"TerminalException" but +avoidance of any confusion with an actual terminal along with +"terminal" being +more fatalistic than "terminating" led to the current name choice. -Change of Position in the Exception Hierarchy ---------------------------------------------- NotImplementedError -''''''''''''''''''' +------------------- Inherits from Exception instead of from RuntimeError. - Originally inheriting from RuntimeError, NotImplementedError does not have any direct relation to the exception meant for use in user code as a quick-and-dirty exception. Thus it now directly inherits from @@ -228,27 +298,54 @@ inheritance check applied. -Bare ``except`` Clauses Catching ``Exception`` Only -=================================================== +Removal of Bare ``except`` Clauses +================================== -While Python does have its "explicit is better than implicit" tenant, -it is not necessary if there is a reasonable default behavior. -Changing the behavior of a bare ``except`` clause makes its existance -quite reasonable. +Bare ``except`` clauses serve the purpose of catching all exceptions +in Python 2.x . This is needed thanks to the fact that in 2.x any +exception can be raised as an exception. But if this PEP is accepted +this will no longer be the case because of a required superclass for +all raised objects. +This goes against a part of the Zen of Python; +One Way To Do It (OWTDI) [#zen]_. By having bare ``except`` clauses +keep their semantic meaning, there would be two ways of doing the +same things. It also goes against Exlpicit Is Better Than Implicit +(EIBTI) by implicitly doing something that can easily be covered by +a more explicit statement. -In Python 2.4, a bare ``except`` clause will catch any and all -exceptions. Typically, though, this is not what is truly desired. -More often than not one wants to catch all error exceptions that do -not signify a "bad" interpreter state. In the new exception hierarchy -this is condition is embodied by Exception. Thus bare ``except`` -clauses will catch only exceptions inheriting from Exception. +It has also been proposed that bare ``except`` clauses be changed to +semantically be equivalent to ``except Exception``. This has been +proposed since this is what most bare ``except`` clauses are meant to +do. This would make ``except`` clauses have a more reasonable +default behavior. + +But this line of reasoning has some issues. First is that it also +goes against the Zen of Python; both OWTDI and EIBTI by providing +an implicit alternative to ``except Exception``. Secondly, +backwards-compatibility becomes more difficult. While removal will +break code, it can mechanically be changed to have the same semantic +meaning as it currently has by finding all occurances of ``except:`` +and replacing them with ``except BaseException:``. But going with +this semantic change makes compatibility more subtle. Was a bare +``except`` clause meant to catch all exceptions, or actually meant +to catch all reasonable exceptions (i.e., everything but +TerminatingException)? Every case will need to be carefully +examined, and could easily be incorrectly examined, leading to subtle +bugs. + +The shorter typing afforded by bare ``except`` statements also does +not justify its existence. "Exception", the typical exception that\ +will be caught, is only nine characters. Add in the needed space to +separate "Exception" from "except" you get only 10 more characters to +type. While this might be a nuisance in quick-and-dirty scripts, the +impact is minimal. Implementation -------------- -In the compiler, when a bare ``except`` clause is reached, the code -for ``except Exception`` will be emitted. +Changing Grammar/Grammar is all that is needed to remove bare +``except`` clauses. Transition Plan @@ -280,15 +377,6 @@ in the implementation of the bytecode. -Removed Exceptions -'''''''''''''''''' - -Exceptions scheduled for removal will be transitioned much like the -old names of renamed exceptions. Upon instantiation a -PendingDeprecationWarning will be raised stating the the exception is -due for removal in Python 3.0. - - Required Superclass for ``raise`` --------------------------------- @@ -299,36 +387,49 @@ Removal of Bare ``except`` Clauses ---------------------------------- -A RuntimeWarning will be raised for all bare ``except`` clauses that -catch an exception that does not inherit from Exception. - +A PendingDeprecationWarning will be raised when a bare ``except`` +clause is found in code. One release before they are removed the +warning will be changed to DeprecationWarning. -Rejected Ideas -============== +Doing ``from __future__ import exceptions`` will cause bare +``except`` clauses to be considered syntax errors. -Multiple threads on python-dev discussing this PEP have lead to -various ideas being rejected [#python-dev-thread1]_, -[#python-dev-thread2]_, [#python-dev-thread3]_. +Roadmap +------- -KeyboardInterrupt inheriting from ControlFlowException ------------------------------------------------------- +Python 2.x is the first version that contains changes. Python 3.0 +will be when transition will be complete. Version 3.0-1 represents +one version before 3.0 is released, 3.0-2 two versions before, etc. -KeyboardInterrupt has been a contentious point within this hierarchy. -Some view the exception more as control flow being caused by the user. -But with its asynchronous cause (the user is able to trigger the -exception at any point in code) its proper place is inheriting from -CriticalError. +* 2.x + - Add BaseException, TerminatingException + - Have KeyboardInterrupt and SystemExit inherit from both + Exception and TerminatingException + - Introduce ``from __future__ import exceptions`` + - Provide a script that mechanically changes all bare ``except`` + clauses to catch BaseException in a .py file + - PendingDeprecationWarning for bare ``except`` clauses + - PendingDeprecationWarning for all objects raised that do not + inherit from BaseException + - PendingDeprecationWarning raised when KeyboardInterrupt or + SystemExit are caught because of their inheritance of Exception +* 3.0-1 + - Turn all introduced PendingDeprecationWarnings into + DeprecationWarning +* 3.0 + - Remove DeprecationWarnings + - Have KeyboardInterrupt and SystemExit only inherit from + TerminatingException + - Remove ``exceptions`` __future__ import support -Other Names for BaseException and Exception -------------------------------------------- +Rejected Ideas +============== -Alternative names for BaseException and Exception have been -Raisable/Exception and Exception/StandardError. The former -alternatives were rejected because "Raisable" does not reflect its -exception nature well enough. The latter alternatives were rejected -because they do not reflect current use. +Multiple threads on python-dev discussing this PEP have lead to +various ideas being rejected [#python-dev-thread1]_, +[#python-dev-thread2]_, [#python-dev-thread3]_. DeprecationWarning Inheriting From PendingDeprecationWarning @@ -381,23 +482,14 @@ It has been suggested that ControlFlowException should inherit from Exception. This idea has been rejected based on the thinking that -control flow exceptions typically should not be caught by bare -``except`` clauses, whereas Exception subclasses should be. - - -Removal of Bare ``except`` Clauses ----------------------------------- - -The suggestion has been made to remove bare ``except`` clauses -altogether, in the name of "explicit is better than implicit". But -Guido has said this is too weak of an argument since other areas of -Python have default behavior [#python-dev3]_. - +control flow exceptions typically do not all need to be caught by a +single ``except`` clause. Rename NameError to NamespaceError ---------------------------------- -NameError is considered more succinct and leaves open no possible mistyping of +NameError is considered more succinct and leaves open no possible +mistyping of the capitalization of "Namespace" [#python-dev5]_. @@ -407,7 +499,8 @@ The thinking was that RuntimeError was in no way an obvious name for an exception meant to be used when a situation did not call for the creation of a new exception. The renaming was rejected on the basis -that the exception is already used throughout the interpreter [#python-dev6]_. +that the exception is already used throughout the interpreter +[#python-dev6]_. Rejection of SimpleError was founded on the thought that people should be free to use whatever exception they choose and not have one so blatently suggested [#python-dev7]_. @@ -425,26 +518,39 @@ Have EOFError Subclass IOError ------------------------------ -The original thought was that sine EOFError deals directly with I/O, it should -subclass IOError. But since EOFError is used more as a signal that an event -has occurred (the exhaustion of an I/O port), it should not subclass such a -specific error exception. +The original thought was that since EOFError deals directly with I/O, +it should +subclass IOError. But since EOFError is used more as a signal that an +event +has occurred (the exhaustion of an I/O port), it should not subclass +such a specific error exception. Have MemoryError and SystemError Have a Common Superclass --------------------------------------------------------- -Both classes deal with the interpreter, so why not have them have a common -superclass? Because one of them means that the interpreter is in a state that -it should not recover from while the other does not. +Both classes deal with the interpreter, so why not have them have a +common +superclass? Because one of them means that the interpreter is in a +state that it should not recover from while the other does not. Common Superclass for PendingDeprecationWarning and DeprecationWarning ---------------------------------------------------------------------- -Grouping the deprecation warning exceptions together makes intuitive sense. -But this sensical idea does not extend well when one considers how rarely -either warning is used, let along at the same time. +Grouping the deprecation warning exceptions together makes intuitive +sense. +But this sensical idea does not extend well when one considers how +rarely either warning is used, let along at the same time. + + +Removing WindowsError +--------------------- + +Originally proposed based on the idea that having such a +platform-specific exception should not be in the built-in namespace. +It turns out, though, enough code exists that uses the exception to +warrant it staying. Acknowledgements @@ -503,6 +609,9 @@ .. [#python-dev7] python-dev email (Exception Reorg PEP checked in) http://mail.python.org/pipermail/python-dev/2005-August/055175.html +.. [#zen] PEP 20 (The Zen of Python) + http://www.python.org/peps/pep-0020.html + Copyright ========= From emanuelkso_3w9w at yahoo.com Mon Aug 15 17:22:09 2005 From: emanuelkso_3w9w at yahoo.com (emanuelkso_3w9w@yahoo.com) Date: Mon, 15 Aug 2005 12:22:09 -0300 Subject: [Python-checkins] mala direta dividida por atividade Message-ID: <20050815152638.9F92022F5C@internet06.internett.com.br> Lista de emails - Mala Direta emails, e-mails,listas de email Lista de emails - Mala Direta emails, e-mails,listas de email Programas gratis, dicas de divulga??o pela internet: http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm e-mails e-mail free marketing Programas gratis, dicas de divulga??o pela internet mala direta dividida por atividade Lista de emails - Mala Direta emails, e-mails,listas de email Divulga??o de sites via mala direta: Visite qualquer um dos 3 links abaixo http://www.segmails.vze.com ou http://www.vendemails.cjb.net ou http://geocities.yahoo.com.br/webneggocios/listagemnova.htm E-mails cadastros cadastro de emails e-mail email lista listas de para Mala Direta Programas gratis, dicas de divulga??o pela internet Lista de emails - Mala Direta emails, e-mails,listas de email enviar email publicidade From bwarsaw at users.sourceforge.net Mon Aug 15 19:33:07 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 19:33:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_trace.py, 1.13, 1.13.4.1 Message-ID: <20050815173307.4AE561E4028@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20452/Lib/test Modified Files: Tag: release24-maint test_trace.py Log Message: Fix for SF bug # 900092, hotshot.stats.load assertion failure. This patch restores the tracing of a 'return' event for exceptions that cause a function to exit. Also, update the unit test. I will port to Python 2.5. Index: test_trace.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_trace.py,v retrieving revision 1.13 retrieving revision 1.13.4.1 diff -u -d -r1.13 -r1.13.4.1 --- test_trace.py 22 Mar 2004 19:30:39 -0000 1.13 +++ test_trace.py 15 Aug 2005 17:32:56 -0000 1.13.4.1 @@ -97,6 +97,7 @@ (-3, 'call'), (-2, 'line'), (-2, 'exception'), + (-2, 'return'), (2, 'exception'), (3, 'line'), (4, 'line'), From bwarsaw at users.sourceforge.net Mon Aug 15 19:33:07 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 19:33:07 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Python ceval.c,2.419,2.419.2.1 Message-ID: <20050815173307.5A56C1E402A@bag.python.org> Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20452/Python Modified Files: Tag: release24-maint ceval.c Log Message: Fix for SF bug # 900092, hotshot.stats.load assertion failure. This patch restores the tracing of a 'return' event for exceptions that cause a function to exit. Also, update the unit test. I will port to Python 2.5. Index: ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.419 retrieving revision 2.419.2.1 diff -u -d -r2.419 -r2.419.2.1 --- ceval.c 23 Nov 2004 18:06:08 -0000 2.419 +++ ceval.c 15 Aug 2005 17:32:56 -0000 2.419.2.1 @@ -2462,14 +2462,20 @@ fast_yield: if (tstate->use_tracing) { - if (tstate->c_tracefunc - && (why == WHY_RETURN || why == WHY_YIELD)) { - if (call_trace(tstate->c_tracefunc, - tstate->c_traceobj, f, - PyTrace_RETURN, retval)) { - Py_XDECREF(retval); - retval = NULL; - why = WHY_EXCEPTION; + if (tstate->c_tracefunc) { + if (why == WHY_RETURN || why == WHY_YIELD) { + if (call_trace(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + else if (why == WHY_EXCEPTION) { + call_trace_protected(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN); } } if (tstate->c_profilefunc) { From bwarsaw at users.sourceforge.net Mon Aug 15 19:35:53 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 19:35:53 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS, 1.1193.2.72, 1.1193.2.73 Message-ID: <20050815173553.8B9921E4028@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21152 Modified Files: Tag: release24-maint NEWS Log Message: Add news about SF bug # 900092 fix. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1193.2.72 retrieving revision 1.1193.2.73 diff -u -d -r1.1193.2.72 -r1.1193.2.73 --- NEWS 13 Aug 2005 02:28:54 -0000 1.1193.2.72 +++ NEWS 15 Aug 2005 17:35:43 -0000 1.1193.2.73 @@ -12,6 +12,9 @@ Core and builtins ----------------- +- SF bug #900092: When tracing (e.g. for hotshot), restore 'return' events for + exceptions that cause a function to exit. + - SF bug #1257731: set.discard() and set.remove() did not correctly handle keys that both inherited from set and defined their own __hash__() function. Also, changed set.__contains__() to have From bwarsaw at users.sourceforge.net Mon Aug 15 20:14:28 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 20:14:28 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_trace.py, 1.13, 1.14 Message-ID: <20050815181428.82EC01E4028@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28928/Lib/test Modified Files: test_trace.py Log Message: Port from the Python 2.4 branch, patches for SF bug # 900092, hotshot.stats.load. Index: test_trace.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_trace.py,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -r1.13 -r1.14 --- test_trace.py 22 Mar 2004 19:30:39 -0000 1.13 +++ test_trace.py 15 Aug 2005 18:14:18 -0000 1.14 @@ -97,6 +97,7 @@ (-3, 'call'), (-2, 'line'), (-2, 'exception'), + (-2, 'return'), (2, 'exception'), (3, 'line'), (4, 'line'), From bwarsaw at users.sourceforge.net Mon Aug 15 20:14:28 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 20:14:28 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1333,1.1334 Message-ID: <20050815181428.E630A1E4028@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28928/Misc Modified Files: NEWS Log Message: Port from the Python 2.4 branch, patches for SF bug # 900092, hotshot.stats.load. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1333 retrieving revision 1.1334 diff -u -d -r1.1333 -r1.1334 --- NEWS 12 Aug 2005 17:34:57 -0000 1.1333 +++ NEWS 15 Aug 2005 18:14:18 -0000 1.1334 @@ -12,6 +12,9 @@ Core and builtins ----------------- +- SF bug #900092: When tracing (e.g. for hotshot), restore 'return' events for + exceptions that cause a function to exit. + - The implementation of set() and frozenset() was revised to use its own internal data structure. Memory consumption is reduced by 1/3 and there are modest speed-ups as well. The API is unchanged. From bwarsaw at users.sourceforge.net Mon Aug 15 20:14:30 2005 From: bwarsaw at users.sourceforge.net (bwarsaw@users.sourceforge.net) Date: Mon, 15 Aug 2005 20:14:30 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Python ceval.c,2.425,2.426 Message-ID: <20050815181430.181431E402F@bag.python.org> Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28928/Python Modified Files: ceval.c Log Message: Port from the Python 2.4 branch, patches for SF bug # 900092, hotshot.stats.load. Index: ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.425 retrieving revision 2.426 diff -u -d -r2.425 -r2.426 --- ceval.c 2 Aug 2005 00:46:45 -0000 2.425 +++ ceval.c 15 Aug 2005 18:14:19 -0000 2.426 @@ -2480,14 +2480,20 @@ fast_yield: if (tstate->use_tracing) { - if (tstate->c_tracefunc - && (why == WHY_RETURN || why == WHY_YIELD)) { - if (call_trace(tstate->c_tracefunc, - tstate->c_traceobj, f, - PyTrace_RETURN, retval)) { - Py_XDECREF(retval); - retval = NULL; - why = WHY_EXCEPTION; + if (tstate->c_tracefunc) { + if (why == WHY_RETURN || why == WHY_YIELD) { + if (call_trace(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + else if (why == WHY_EXCEPTION) { + call_trace_protected(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN); } } if (tstate->c_profilefunc) { From rhettinger at users.sourceforge.net Tue Aug 16 05:48:01 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:48:01 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Misc NEWS,1.1334,1.1335 Message-ID: <20050816034801.BB0F71E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31184/Misc Modified Files: NEWS Log Message: Add a C API for sets and frozensets. Index: NEWS =================================================================== RCS file: /cvsroot/python/python/dist/src/Misc/NEWS,v retrieving revision 1.1334 retrieving revision 1.1335 diff -u -d -r1.1334 -r1.1335 --- NEWS 15 Aug 2005 18:14:18 -0000 1.1334 +++ NEWS 16 Aug 2005 03:47:51 -0000 1.1335 @@ -439,6 +439,8 @@ C API ----- +- Added a C API for set and frozenset objects. + - Removed PyRange_New(). From rhettinger at users.sourceforge.net Tue Aug 16 05:48:01 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:48:01 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/api concrete.tex,1.61,1.62 Message-ID: <20050816034801.EC6B41E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/api In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31184/Doc/api Modified Files: concrete.tex Log Message: Add a C API for sets and frozensets. Index: concrete.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/api/concrete.tex,v retrieving revision 1.61 retrieving revision 1.62 diff -u -d -r1.61 -r1.62 --- concrete.tex 18 Jun 2005 17:54:13 -0000 1.61 +++ concrete.tex 16 Aug 2005 03:47:51 -0000 1.62 @@ -2897,3 +2897,128 @@ tuple suitable for passing to \code{datetime.date.fromtimestamp()}. \versionadded{2.4} \end{cfuncdesc} + + +\subsection{Set Objects \label{setObjects}} +\sectionauthor{Raymond D. Hettinger}{python at rcn.com} + +\obindex{set} +\obindex{frozenset} +\versionadded{2.5} + +This section details the public API for \class{set} and \class{frozenset} +objects. Any functionality not listed below is best accessed using the +abstract object API (including +\cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()}, +\cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()}, +\cfunction{PyObject_IsTrue()}, \cfunction{PyObject_Print()}, and +\cfunction{PyObject_GetIter()}). + +\begin{ctypedesc}{PySetObject} + This subtype of \ctype{PyObject} is used to hold the internal data for + both \class{set} and \class{frozenset} objects. It is like a + \ctype{PyDictObject} in that it is a fixed size for small sets + (much like tuple storage) and will point to a separate, variable sized + block of memory for medium and large sized sets (much like list storage). + None of the fields of this structure should be considered public and + are subject to change. All access should be done through the + documented API. + +\end{ctypedesc} + +\begin{cvardesc}{PyTypeObject}{PySet_Type} + This is an instance of \ctype{PyTypeObject} representing the Python + \class{set} type. +\end{cvardesc} + +\begin{cvardesc}{PyTypeObject}{PyFrozenSet_Type} + This is an instance of \ctype{PyTypeObject} representing the Python + \class{frozenset} type. +\end{cvardesc} + + +The following type check macros work on pointers to any Python object. +Likewise, the constructor functions work with any iterable Python object. + +\begin{cfuncdesc}{int}{PyAnySet_Check}{PyObject *p} + Returns true if \var{p} is a \class{set} object, a \class{frozenset} + object, or an instance of a subtype. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyAnySet_CheckExact}{PyObject *p} + Returns true if \var{p} is a \class{set} object or a \class{frozenset} + object but not an instance of a subtype. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PyFrozenSet_CheckExact}{PyObject *p} + Returns true if \var{p} is a \class{frozenset} object + but not an instance of a subtype. +\end{cfuncdesc} + +\begin{cfuncdesc}{PyObject*}{PySet_New}{PyObject *iterable} + Returns a new \class{set} containing objects returned by the + \var{iterable}. The \var{iterable} may be \NULL{} to create a + new empty set. Returns the new set on success or \NULL{} on + failure. +\end{cfuncdesc} + +\begin{cfuncdesc}{PyObject*}{PyFrozenSet_New}{PyObject *iterable} + Returns a new \class{frozenset} containing objects returned by the + \var{iterable}. The \var{iterable} may be \NULL{} to create a + new empty frozenset. Returns the new set on success or \NULL{} on + failure. +\end{cfuncdesc} + + +The following functions and macros are available for instances of +\class{set} or \class{frozenset} or instances of their subtypes. + +\begin{cfuncdesc}{int}{PySet_Size}{PyObject *anyset} + Returns the length of a \class{set} or \class{frozenset} object. + Equivalent to \samp{len(\var{anyset})}. Raises a + \exception{PyExc_SystemError} if the argument is not a \class{set}, + \class{frozenset}, or an instance of a subtype. + \bifuncindex{len} +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PySet_GET_SIZE}{PyObject *anyset} + Macro form of \cfunction{PySet_Size()} without error checking. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PySet_Contains}{PyObject *anyset, PyObject *key} + Returns 1 if found, 0 if not found, and -1 if an error is + encountered. Unlike the Python \method{__contains__()} method, this + function does not automatically convert unhashable sets into temporary + frozensets. Raises a \exception{TypeError} if the key is unhashable. +\end{cfuncdesc} + +\begin{cfuncdesc}{int}{PySet_Discard}{PyObject *anyset, PyObject *key} + Returns 1 if found and removed, 0 if not found (no action taken), + and -1 if an error is encountered. Does not raise \exception{KeyError} + for missing keys. Raises a \exception{TypeError} if the key is unhashable. + Unlike the Python \method{discard()} method, this function does + not automatically convert unhashable sets into temporary frozensets. +\end{cfuncdesc} + + +The following functions are available for instances of \class{set} or +its subtypes but not for instances of \class{frozenset} or its subtypes. + +\begin{cfuncdesc}{int}{PySet_Add}{PyObject *set, PyObject *key} + Adds \var{key} to a \class{set} instance. Does not apply to + \class{frozenset} instances. Returns 0 on success or -1 on failure. + Raises a \exception{TypeError} if the key is unhashable. + Raises a \exception{MemoryError} if there is no room to grow. + Raises a \exception{SystemError} if \var{key} is an not an instance + of \class{set} or its subtype. +\end{cfuncdesc} + +\begin{cfuncdesc}{PyObject*}{PySet_Pop}{PyObject *set} + Returns a new reference to an arbitrary object in the \var{set}, + and removes the object from the \var{set}. Returns \NULL{} on + failure. Raises \exception{KeyError} if the set is empty. + Raises a \exception{SystemError} if \var{key} is an not an instance + of \class{set} or its subtype. +\end{cfuncdesc} + + From rhettinger at users.sourceforge.net Tue Aug 16 05:48:01 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:48:01 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Python marshal.c,1.87,1.88 Message-ID: <20050816034801.F26001E4008@bag.python.org> Update of /cvsroot/python/python/dist/src/Python In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31184/Python Modified Files: marshal.c Log Message: Add a C API for sets and frozensets. Index: marshal.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/marshal.c,v retrieving revision 1.87 retrieving revision 1.88 diff -u -d -r1.87 -r1.88 --- marshal.c 25 Jun 2005 08:23:41 -0000 1.87 +++ marshal.c 16 Aug 2005 03:47:52 -0000 1.88 @@ -773,11 +773,9 @@ if (v == NULL) return v; if (type == TYPE_SET) - v3 = PyObject_CallFunctionObjArgs( - (PyObject *)&PySet_Type, v, NULL); + v3 = PySet_New(v); else - v3 = PyObject_CallFunctionObjArgs( - (PyObject *)&PyFrozenSet_Type, v, NULL); + v3 = PyFrozenSet_New(v); Py_DECREF(v); return v3; From rhettinger at users.sourceforge.net Tue Aug 16 05:48:02 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:48:02 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Include setobject.h,2.9,2.10 Message-ID: <20050816034802.19A201E4029@bag.python.org> Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31184/Include Modified Files: setobject.h Log Message: Add a C API for sets and frozensets. Index: setobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/setobject.h,v retrieving revision 2.9 retrieving revision 2.10 diff -u -d -r2.9 -r2.10 --- setobject.h 7 Aug 2005 13:02:52 -0000 2.9 +++ setobject.h 16 Aug 2005 03:47:51 -0000 2.10 @@ -74,6 +74,15 @@ PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \ PyType_IsSubtype((ob)->ob_type, &PyFrozenSet_Type)) +PyAPI_FUNC(PyObject *) PySet_New(PyObject *); +PyAPI_FUNC(PyObject *) PyFrozenSet_New(PyObject *); +PyAPI_FUNC(int) PySet_Size(PyObject *anyset); +#define PySet_GET_SIZE(so) (((PySetObject *)(so))->used) +PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); +PyAPI_FUNC(int) PySet_Discard(PyObject *anyset, PyObject *key); +PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); +PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); + #ifdef __cplusplus } #endif From rhettinger at users.sourceforge.net Tue Aug 16 05:48:02 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:48:02 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.49,1.50 Message-ID: <20050816034802.303BD1E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31184/Objects Modified Files: setobject.c Log Message: Add a C API for sets and frozensets. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- setobject.c 13 Aug 2005 09:28:48 -0000 1.49 +++ setobject.c 16 Aug 2005 03:47:52 -0000 1.50 @@ -843,7 +843,7 @@ return iterable; } result = make_new_set(type, iterable); - if (result == NULL || set_len(result)) + if (result == NULL || PySet_GET_SIZE(result)) return result; Py_DECREF(result); } @@ -1052,7 +1052,7 @@ int pos = 0; setentry *entry; - if (set_len(other) > set_len((PyObject *)so)) { + if (PySet_GET_SIZE(other) > PySet_GET_SIZE(so)) { tmp = (PyObject *)so; so = (PySetObject *)other; other = tmp; @@ -1381,7 +1381,7 @@ Py_DECREF(tmp); return result; } - if (set_len((PyObject *)so) > set_len(other)) + if (PySet_GET_SIZE(so) > PySet_GET_SIZE(other)) Py_RETURN_FALSE; while (set_next(so, &pos, &entry)) { @@ -1426,11 +1426,11 @@ } switch (op) { case Py_EQ: - if (set_len((PyObject *)v) != set_len(w)) + if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w)) Py_RETURN_FALSE; return set_issubset(v, w); case Py_NE: - if (set_len((PyObject *)v) != set_len(w)) + if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w)) Py_RETURN_TRUE; r1 = set_issubset(v, w); assert (r1 != NULL); @@ -1442,11 +1442,11 @@ case Py_GE: return set_issuperset(v, w); case Py_LT: - if (set_len((PyObject *)v) >= set_len(w)) + if (PySet_GET_SIZE(v) >= PySet_GET_SIZE(w)) Py_RETURN_FALSE; return set_issubset(v, w); case Py_GT: - if (set_len((PyObject *)v) <= set_len(w)) + if (PySet_GET_SIZE(v) <= PySet_GET_SIZE(w)) Py_RETURN_FALSE; return set_issuperset(v, w); } @@ -1472,7 +1472,7 @@ if (so->hash != -1) return so->hash; - hash *= set_len(self) + 1; + hash *= PySet_GET_SIZE(self) + 1; while (set_next(so, &pos, &entry)) { /* Work to increase the bit dispersion for closely spaced hash values. The is important because some use cases have many @@ -1918,3 +1918,67 @@ frozenset_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; + + +/***** C API functions *************************************************/ + +PyObject * +PySet_New(PyObject *iterable) +{ + return make_new_set(&PySet_Type, iterable); +} + +PyObject * +PyFrozenSet_New(PyObject *iterable) +{ + PyObject *args = NULL, *result; + + if (iterable != NULL) { + args = PyTuple_Pack(1, iterable); + if (args == NULL) + return NULL; + } + result = frozenset_new(&PyFrozenSet_Type, args, NULL); + Py_DECREF(args); + return result; +} + +int +PySet_Contains(PyObject *anyset, PyObject *key) +{ + if (!PyAnySet_Check(anyset)) { + PyErr_BadInternalCall(); + return -1; + } + return set_contains_key((PySetObject *)anyset, key); +} + +int +PySet_Discard(PyObject *anyset, PyObject *key) +{ + if (!PyAnySet_Check(anyset)) { + PyErr_BadInternalCall(); + return -1; + } + return set_discard_key((PySetObject *)anyset, key); +} + +int +PySet_Add(PyObject *set, PyObject *key) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return -1; + } + return set_add_key((PySetObject *)set, key); +} + +PyObject * +PySet_Pop(PyObject *set) +{ + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { + PyErr_BadInternalCall(); + return NULL; + } + return set_pop((PySetObject *)set); +} From rhettinger at users.sourceforge.net Tue Aug 16 05:54:21 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 05:54:21 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.50,1.51 Message-ID: <20050816035421.6D3BA1E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32078 Modified Files: setobject.c Log Message: DECREF --> XDECREF Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.50 retrieving revision 1.51 diff -u -d -r1.50 -r1.51 --- setobject.c 16 Aug 2005 03:47:52 -0000 1.50 +++ setobject.c 16 Aug 2005 03:54:11 -0000 1.51 @@ -1939,7 +1939,7 @@ return NULL; } result = frozenset_new(&PyFrozenSet_Type, args, NULL); - Py_DECREF(args); + Py_XDECREF(args); return result; } From bcannon at users.sourceforge.net Tue Aug 16 08:22:03 2005 From: bcannon at users.sourceforge.net (bcannon@users.sourceforge.net) Date: Tue, 16 Aug 2005 08:22:03 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0348.txt,1.8,1.9 Message-ID: <20050816062203.052D01E4002@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23030 Modified Files: pep-0348.txt Log Message: Revised based on Guido's wishes: no more TerminatingException and bare 'except' clauses act like ``except Exception``. Transition plan completely reworked to basically not go through hoops that will negatively impact performance. Basically now BaseException is added and everything else is not changed until Python 3.0 . Also suggests docs be changed to suggest a certain practice. Index: pep-0348.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0348.txt,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- pep-0348.txt 15 Aug 2005 04:28:28 -0000 1.8 +++ pep-0348.txt 16 Aug 2005 06:21:51 -0000 1.9 @@ -9,6 +9,8 @@ Created: 28-Jul-2005 Post-History: +.. |2.x| replace:: 2.5 + Abstract ======== @@ -26,11 +28,10 @@ interface of exceptions and to further enhance the natural hierarchy of exceptions. -Lastly, bare ``except`` clauses will be removed. While they had their -usefulness when exceptions could be any object, with the above -proposal -of a required superclass for exceptions bare ``except`` clauses lose -their purpose. +Lastly, bare ``except`` clauses will be changed to be semantically +equivalent to ``except Exception``. Most people currently use bare +``except`` clause for this purpose and with the exception hierarchy +reorganization becomes a viable default. Rationale For Wanting Change @@ -48,17 +49,19 @@ But exceptions do have a hierarchy, showing the severity of the exception. The hierarchy also groups related exceptions together to -simplify catching them in ``except`` clauses. To allow peopele to +simplify catching them in ``except`` clauses. To allow people to be able to rely on this hierarchy, a common superclass that all raise objects must inherit from is being proposed. It also allows guarantees about the interface to raised objects to be made (see PEP 344 [#PEP344]_). A discussion about all of this has occurred before on python-dev [#Summary2004-08-01]_. -With the requirement of a common superclass for all exceptions, bare -``except`` clauses are impacted. Currently used as a catch-all for -raised exceptions, its usefulness is terminally weakened. Now, the -same functionality is possible by catching the required superclass. +As bare ``except`` clauses stand now, they catch *all* exceptions. +While this can be handy, it is rather overreaching for the common +case. Thanks to having a required superclass, catching all +exceptions is as easy as catching just one specific exception. +This allows bare ``except`` clauses to be used for a more useful +purpose. Once again, this has been discussed on python-dev [#python-dev3]_. Finally, slight changes to the exception hierarchy will make it much @@ -74,7 +77,9 @@ For the reorganization of the hierarchy, there was a general philosophy followed that developed from discussion of earlier drafts of this PEP [#python-dev-thread1]_, [#python-dev-thread2]_, -[#python-dev-thread3]_. First and foremost was to not break anything +[#python-dev-thread3]_, [#python-dev-thread4]_, +[#python-dev-thread5]_, [#python-dev-thread6]_. +First and foremost was to not break anything that works. This meant that renaming exceptions was out of the question unless the name was deemed severely bad. This also meant no removal of exceptions unless they were viewed as @@ -152,10 +157,8 @@ +-- SyntaxWarning +-- UserWarning + -- WindowsError - +-- TerminatingException (new; stricter inheritance for - subclasses) - +-- KeyboardInterrupt - +-- SystemExit + +-- KeyboardInterrupt (stricter inheritance) + +-- SystemExit (stricter inheritance) Differences Compared to Python 2.4 @@ -190,79 +193,14 @@ work. -TerminatingException --------------------- - -Superclass for exceptions that are meant to symbolize the termination -of -the interpreter. It does not inherit from Exception so that the -common -``except Exception`` statement does not catch the exceptions. This -for ``try`` statements that want to catch all exceptions that are -signals of errors and handle them separately from exceptions that -signify that the interpreter should be terminated:: - - try: - ... - # Catch all exceptions that are expected to be caught - except Exception: - ... - # Catch exceptions expected to terminate the interpreter - except TerminatingException: - ... - -Compare this to:: - - try: - ... - except Exception: - ... - except (KeyboardInterrupt, SystemExit): - ... - -While more explicit, it is not necessarily obvious why the two -exceptions are being caught directly. By providing a common -superclass with a name that explicitly states the exceptions' typical -usage the reasoning behind the catch becomes more apparent. - -Comparing it to an even more general ``except`` clause:: - - try: - ... - except Exception: - ... - except BaseException: - ... - -While this will perform the same action and catch all exceptions, -guaranteed, it is once again not obvious why Exception and -BaseException are being caught separately and not just -BaseException based purely on the code. - -TerminatingException will also help with transitioning from Python -2.x to 3.0 . With TerminatingException being a new exception, when -it is used the new inheritance for KeyboardInterrupt and SystemExit -becomes more obvious. - -It has been argued that TerminatingException should not be added -because it goes against Flat Is Better Than Nested (FIBTN). -While this new exception will not make the hierarchy as flat is it -could be, the overall depth of the tree is not changed; Exception has -a much deeper inheritance tree below it. - -It has also been argued that since KeyboardInterrupt and SystemExit -are not caught together that often in 2.4, there might not be much of -a need for TerminatingException. But with their new position in the -hierarchy catching them separately from other exceptions should -become more prevalent. +KeyboardInterrupt and SystemExit +-------------------------------- -Naming is based on the idea that the interpreter is trying to -terminate when KeyboardInterrupt and SystemExit -are raised. An earlier proposal suggested -"TerminalException" but -avoidance of any confusion with an actual terminal along with -"terminal" being -more fatalistic than "terminating" led to the current name choice. +Both exceptions are no longer under Exception. This is to allow bare +``except`` clauses to act as a more viable default case by catching +exceptions that inherit from Exception. With both KeyboardInterrupt +and SystemExit acting as signals that the interpreter is expected to +exit, catching them in the common case is the wrong semantics. NotImplementedError @@ -298,140 +236,69 @@ inheritance check applied. -Removal of Bare ``except`` Clauses -================================== - -Bare ``except`` clauses serve the purpose of catching all exceptions -in Python 2.x . This is needed thanks to the fact that in 2.x any -exception can be raised as an exception. But if this PEP is accepted -this will no longer be the case because of a required superclass for -all raised objects. -This goes against a part of the Zen of Python; -One Way To Do It (OWTDI) [#zen]_. By having bare ``except`` clauses -keep their semantic meaning, there would be two ways of doing the -same things. It also goes against Exlpicit Is Better Than Implicit -(EIBTI) by implicitly doing something that can easily be covered by -a more explicit statement. +Bare ``except`` Clauses Catch Exception +======================================= -It has also been proposed that bare ``except`` clauses be changed to -semantically be equivalent to ``except Exception``. This has been -proposed since this is what most bare ``except`` clauses are meant to -do. This would make ``except`` clauses have a more reasonable -default behavior. +In most existing Python 2.4 code, bare ``except`` clauses are too +broad in the exceptions they catch. Typically only exceptions that +signal an error are desired to be caught. This means that exceptions +that are used to signify that the interpreter should exit should not +be caught in the common case. -But this line of reasoning has some issues. First is that it also -goes against the Zen of Python; both OWTDI and EIBTI by providing -an implicit alternative to ``except Exception``. Secondly, -backwards-compatibility becomes more difficult. While removal will -break code, it can mechanically be changed to have the same semantic -meaning as it currently has by finding all occurances of ``except:`` -and replacing them with ``except BaseException:``. But going with -this semantic change makes compatibility more subtle. Was a bare -``except`` clause meant to catch all exceptions, or actually meant -to catch all reasonable exceptions (i.e., everything but -TerminatingException)? Every case will need to be carefully -examined, and could easily be incorrectly examined, leading to subtle -bugs. +With KeyboardInterrupt and SystemExit moved to inherit from +BaseException instead of Exception, changing bare ``except`` clauses +to act as ``except Exception`` becomes a much more reasonable +default. This change also will break very little code since these +semantics are what most people want for bare ``except`` clauses. -The shorter typing afforded by bare ``except`` statements also does -not justify its existence. "Exception", the typical exception that\ -will be caught, is only nine characters. Add in the needed space to -separate "Exception" from "except" you get only 10 more characters to -type. While this might be a nuisance in quick-and-dirty scripts, the -impact is minimal. +The complete removal of bare ``except`` clauses has been argued for. +The case has been made that they violate both Only One Way To Do It +(OOWTDI) and Explicit Is Better Than Implicit (EIBTI) as listed in the +Zen of Python [#zen]_. But Practicality Beats Purity (PBP), also in +the Zen of Python, trumps both of these in this case. The BDFL has +stated that bare ``except`` clauses will work this way +[#python-dev8]_. Implementation -------------- -Changing Grammar/Grammar is all that is needed to remove bare -``except`` clauses. +The compiler will emit the bytecode for ``except Exception`` whenever +a bare ``except`` clause is reached. Transition Plan =============== -Exception Hierarchy Changes ---------------------------- - -New Exceptions -'''''''''''''' - -New exceptions can simply be added to the built-in namespace. Any -pre-existing objects with the same name will mask the new exceptions, -preserving backwards-compatibility. - - -New Inheritance for Old Exceptions -'''''''''''''''''''''''''''''''''' - -Using multiple inheritance to our advantage, exceptions whose -inheritance is now more resrictive can be made backwards-compatible. -By inheriting from both the new superclasses as well as the original -superclasses, existing ``except`` clauses will continue to work as -before while allowing the new inheritance to be used for new code. - -A PendingDeprecationWarning will be raised based on whether the -bytecode ``COMPARE_OP(10)`` results in an exception being caught that -would not have under the new hierarchy. This will require hard-coding -in the implementation of the bytecode. - - -Required Superclass for ``raise`` ---------------------------------- - -A DeprecationWarning will be raised when an object is passed to -``raise`` that does not have the proper inheritance. - - -Removal of Bare ``except`` Clauses ----------------------------------- - -A PendingDeprecationWarning will be raised when a bare ``except`` -clause is found in code. One release before they are removed the -warning will be changed to DeprecationWarning. - -Doing ``from __future__ import exceptions`` will cause bare -``except`` clauses to be considered syntax errors. - +Because of the complexity and clutter that would be required to add +all features planned in this PEP, the transition plan is very simple. +In Python |2.x| BaseException is added. In Python 3.0, all remaining +features (required superclass, change in inheritance, bare ``except`` +clauses becoming the same as ``except Exception``) will go into +affect. In order to make all of this work in a backwards-compatible +way in Python |2.x| would require very deep hacks in the exception +machinery which could be error-prone and lead to a slowdown in +performance for little benefit. -Roadmap -------- +To help with the transition, the documentation will be changed to +reflect several programming guidelines: -Python 2.x is the first version that contains changes. Python 3.0 -will be when transition will be complete. Version 3.0-1 represents -one version before 3.0 is released, 3.0-2 two versions before, etc. +- When one wants to catch *all* exceptions, catch BaseException +- To catch all exceptions that do not represent the termination of + the interpreter, catch Exception explicitly +- Explicitly catch KeyboardInterrupt and SystemExit; don't rely on + inheritance from Exception to lead to the capture +- Always catch NotImplementedError explicitly instead of relying on + the inheritance from RuntimeError -* 2.x - - Add BaseException, TerminatingException - - Have KeyboardInterrupt and SystemExit inherit from both - Exception and TerminatingException - - Introduce ``from __future__ import exceptions`` - - Provide a script that mechanically changes all bare ``except`` - clauses to catch BaseException in a .py file - - PendingDeprecationWarning for bare ``except`` clauses - - PendingDeprecationWarning for all objects raised that do not - inherit from BaseException - - PendingDeprecationWarning raised when KeyboardInterrupt or - SystemExit are caught because of their inheritance of Exception -* 3.0-1 - - Turn all introduced PendingDeprecationWarnings into - DeprecationWarning -* 3.0 - - Remove DeprecationWarnings - - Have KeyboardInterrupt and SystemExit only inherit from - TerminatingException - - Remove ``exceptions`` __future__ import support +The documentation for the 'exceptions' module [#exceptions-stdlib]_, +tutorial [#tutorial]_, and PEP 290 [#PEP290]_ will all require +updating. Rejected Ideas ============== -Multiple threads on python-dev discussing this PEP have lead to -various ideas being rejected [#python-dev-thread1]_, -[#python-dev-thread2]_, [#python-dev-thread3]_. - - DeprecationWarning Inheriting From PendingDeprecationWarning ------------------------------------------------------------ @@ -553,6 +420,17 @@ warrant it staying. +Superclass for KeyboardInterrupt and SystemExit +----------------------------------------------- + +Proposed to make catching non-Exception inheriting exceptions easier +along with easing the transition to the new hierarchy, the idea was +rejected by the BDFL [#python-dev8]_. The argument that existing +code did not show enough instances of the pair of exceptions being +caught and thus did not justify cluttering the built-in namespace +was used. + + Acknowledgements ================ @@ -572,6 +450,9 @@ .. [#PEP344] PEP 344 (Exception Chaining and Embedded Tracebacks) http://www.python.org/peps/pep-0344.html +.. [#PEP290] PEP 290 (Code Migration and Modernization) + http://www.python.org/peps/pep-0290.html + .. [#Summary2004-08-01] python-dev Summary (An exception is an exception, unless it doesn't inherit from Exception) http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is-an-exception-unless-it-doesn-t-inherit-from-exception @@ -595,6 +476,15 @@ .. [#python-dev-thread3] python-dev thread (Reorg PEP checked in) http://mail.python.org/pipermail/python-dev/2005-August/055138.html +.. [#python-dev-thread4] python-dev thread (Major revision of PEP 348 committed) + http://mail.python.org/pipermail/python-dev/2005-August/055199.html + +.. [#python-dev-thread5] python-dev thread (Exception Reorg PEP revised yet again) + http://mail.python.org/pipermail/python-dev/2005-August/055292.html + +.. [#python-dev-thread6] python-dev thread (PEP 348 (exception reorg) revised again) + http://mail.python.org/pipermail/python-dev/2005-August/055412.html + .. [#python-dev4] python-dev email (Pre-PEP: Exception Reorganization for Python 3.0) http://mail.python.org/pipermail/python-dev/2005-July/055019.html @@ -609,9 +499,15 @@ .. [#python-dev7] python-dev email (Exception Reorg PEP checked in) http://mail.python.org/pipermail/python-dev/2005-August/055175.html +.. [#python-dev8] python-dev email (PEP 348 (exception reorg) revised again) + http://mail.python.org/pipermail/python-dev/2005-August/055423.html + .. [#zen] PEP 20 (The Zen of Python) http://www.python.org/peps/pep-0020.html +.. [#tutorial] Python Tutorial + http://docs.python.org/tut/tut.html + Copyright ========= From rhettinger at users.sourceforge.net Tue Aug 16 12:44:25 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 12:44:25 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Lib/test test_set.py,1.21,1.22 Message-ID: <20050816104425.1201D1E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Lib/test In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10493/Lib/test Modified Files: test_set.py Log Message: Numerous fix-ups to C API and docs. Added tests for C API. Index: test_set.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_set.py,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- test_set.py 12 Aug 2005 23:58:22 -0000 1.21 +++ test_set.py 16 Aug 2005 10:44:15 -0000 1.22 @@ -6,6 +6,7 @@ import pickle import os from random import randrange, shuffle +import sys class PassThru(Exception): pass @@ -402,6 +403,11 @@ s = None self.assertRaises(ReferenceError, str, p) + # C API test only available in a debug build + if hasattr(sys, "gettotalrefcount"): + def test_c_api(self): + self.assertEqual(set('abc').test_c_api(), True) + class SetSubclass(set): pass @@ -1372,7 +1378,6 @@ #============================================================================== def test_main(verbose=None): - import sys from test import test_sets test_classes = ( TestSet, From rhettinger at users.sourceforge.net Tue Aug 16 12:44:25 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 12:44:25 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Include setobject.h,2.10,2.11 Message-ID: <20050816104425.2F2441E4008@bag.python.org> Update of /cvsroot/python/python/dist/src/Include In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10493/Include Modified Files: setobject.h Log Message: Numerous fix-ups to C API and docs. Added tests for C API. Index: setobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/setobject.h,v retrieving revision 2.10 retrieving revision 2.11 diff -u -d -r2.10 -r2.11 --- setobject.h 16 Aug 2005 03:47:51 -0000 2.10 +++ setobject.h 16 Aug 2005 10:44:15 -0000 2.11 @@ -79,7 +79,7 @@ PyAPI_FUNC(int) PySet_Size(PyObject *anyset); #define PySet_GET_SIZE(so) (((PySetObject *)(so))->used) PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); -PyAPI_FUNC(int) PySet_Discard(PyObject *anyset, PyObject *key); +PyAPI_FUNC(int) PySet_Discard(PyObject *set, PyObject *key); PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); From rhettinger at users.sourceforge.net Tue Aug 16 12:44:25 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 12:44:25 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/api concrete.tex,1.62,1.63 Message-ID: <20050816104425.470121E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/api In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10493/Doc/api Modified Files: concrete.tex Log Message: Numerous fix-ups to C API and docs. Added tests for C API. Index: concrete.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/api/concrete.tex,v retrieving revision 1.62 retrieving revision 1.63 diff -u -d -r1.62 -r1.63 --- concrete.tex 16 Aug 2005 03:47:51 -0000 1.62 +++ concrete.tex 16 Aug 2005 10:44:12 -0000 1.63 @@ -2959,14 +2959,16 @@ Returns a new \class{set} containing objects returned by the \var{iterable}. The \var{iterable} may be \NULL{} to create a new empty set. Returns the new set on success or \NULL{} on - failure. + failure. Raises \exception{TypeError} if \var{iterable} is + not actually iterable. \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyFrozenSet_New}{PyObject *iterable} Returns a new \class{frozenset} containing objects returned by the \var{iterable}. The \var{iterable} may be \NULL{} to create a new empty frozenset. Returns the new set on success or \NULL{} on - failure. + failure. Raises \exception{TypeError} if \var{iterable} is + not actually iterable. \end{cfuncdesc} @@ -2976,7 +2978,7 @@ \begin{cfuncdesc}{int}{PySet_Size}{PyObject *anyset} Returns the length of a \class{set} or \class{frozenset} object. Equivalent to \samp{len(\var{anyset})}. Raises a - \exception{PyExc_SystemError} if the argument is not a \class{set}, + \exception{PyExc_SystemError} if \var{anyset} is not a \class{set}, \class{frozenset}, or an instance of a subtype. \bifuncindex{len} \end{cfuncdesc} @@ -2989,15 +2991,9 @@ Returns 1 if found, 0 if not found, and -1 if an error is encountered. Unlike the Python \method{__contains__()} method, this function does not automatically convert unhashable sets into temporary - frozensets. Raises a \exception{TypeError} if the key is unhashable. -\end{cfuncdesc} - -\begin{cfuncdesc}{int}{PySet_Discard}{PyObject *anyset, PyObject *key} - Returns 1 if found and removed, 0 if not found (no action taken), - and -1 if an error is encountered. Does not raise \exception{KeyError} - for missing keys. Raises a \exception{TypeError} if the key is unhashable. - Unlike the Python \method{discard()} method, this function does - not automatically convert unhashable sets into temporary frozensets. + frozensets. Raises a \exception{TypeError} if the \var{key} is unhashable. + Raises \exception{PyExc_SystemError} if \var{anyset} is not a \class{set}, + \class{frozenset}, or an instance of a subtype. \end{cfuncdesc} @@ -3007,17 +3003,27 @@ \begin{cfuncdesc}{int}{PySet_Add}{PyObject *set, PyObject *key} Adds \var{key} to a \class{set} instance. Does not apply to \class{frozenset} instances. Returns 0 on success or -1 on failure. - Raises a \exception{TypeError} if the key is unhashable. + Raises a \exception{TypeError} if the \var{key} is unhashable. Raises a \exception{MemoryError} if there is no room to grow. - Raises a \exception{SystemError} if \var{key} is an not an instance + Raises a \exception{SystemError} if \var{set} is an not an instance of \class{set} or its subtype. \end{cfuncdesc} +\begin{cfuncdesc}{int}{PySet_Discard}{PyObject *set, PyObject *key} + Returns 1 if found and removed, 0 if not found (no action taken), + and -1 if an error is encountered. Does not raise \exception{KeyError} + for missing keys. Raises a \exception{TypeError} if the \var{key} is + unhashable. Unlike the Python \method{discard()} method, this function + does not automatically convert unhashable sets into temporary frozensets. + Raises \exception{PyExc_SystemError} if \var{set} is an not an instance + of \class{set} or its subtype. +\end{cfuncdesc} + \begin{cfuncdesc}{PyObject*}{PySet_Pop}{PyObject *set} Returns a new reference to an arbitrary object in the \var{set}, and removes the object from the \var{set}. Returns \NULL{} on failure. Raises \exception{KeyError} if the set is empty. - Raises a \exception{SystemError} if \var{key} is an not an instance + Raises a \exception{SystemError} if \var{set} is an not an instance of \class{set} or its subtype. \end{cfuncdesc} From rhettinger at users.sourceforge.net Tue Aug 16 12:44:25 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Tue, 16 Aug 2005 12:44:25 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.51,1.52 Message-ID: <20050816104425.4E61B1E4029@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10493/Objects Modified Files: setobject.c Log Message: Numerous fix-ups to C API and docs. Added tests for C API. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.51 retrieving revision 1.52 diff -u -d -r1.51 -r1.52 --- setobject.c 16 Aug 2005 03:54:11 -0000 1.51 +++ setobject.c 16 Aug 2005 10:44:15 -0000 1.52 @@ -1697,6 +1697,13 @@ /* set object ********************************************************/ +#ifdef Py_DEBUG +static PyObject *test_c_api(PySetObject *so); + +PyDoc_STRVAR(test_c_api_doc, "Exercises C API. Returns True.\n\ +All is well if assertions don't fail."); +#endif + static PyMethodDef set_methods[] = { {"add", (PyCFunction)set_add, METH_O, add_doc}, @@ -1730,6 +1737,10 @@ symmetric_difference_doc}, {"symmetric_difference_update",(PyCFunction)set_symmetric_difference_update, METH_O, symmetric_difference_update_doc}, +#ifdef Py_DEBUG + {"test_c_api", (PyCFunction)test_c_api, METH_NOARGS, + test_c_api_doc}, +#endif {"union", (PyCFunction)set_union, METH_O, union_doc}, {"update", (PyCFunction)set_update, METH_O, @@ -1931,19 +1942,30 @@ PyObject * PyFrozenSet_New(PyObject *iterable) { - PyObject *args = NULL, *result; + PyObject *args, *result; - if (iterable != NULL) { + if (iterable == NULL) + args = PyTuple_New(0); + else args = PyTuple_Pack(1, iterable); - if (args == NULL) - return NULL; - } + if (args == NULL) + return NULL; result = frozenset_new(&PyFrozenSet_Type, args, NULL); - Py_XDECREF(args); + Py_DECREF(args); return result; } int +PySet_Size(PyObject *anyset) +{ + if (!PyAnySet_Check(anyset)) { + PyErr_BadInternalCall(); + return -1; + } + return ((PySetObject *)anyset)->used; +} + +int PySet_Contains(PyObject *anyset, PyObject *key) { if (!PyAnySet_Check(anyset)) { @@ -1954,13 +1976,13 @@ } int -PySet_Discard(PyObject *anyset, PyObject *key) +PySet_Discard(PyObject *set, PyObject *key) { - if (!PyAnySet_Check(anyset)) { + if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) { PyErr_BadInternalCall(); return -1; } - return set_discard_key((PySetObject *)anyset, key); + return set_discard_key((PySetObject *)set, key); } int @@ -1982,3 +2004,92 @@ } return set_pop((PySetObject *)set); } + + +#ifdef Py_DEBUG + +/* Test code to be called with any three element set. + Returns True and original set is restored. */ + +#define assertRaises(call_return_value, exception) \ + do { \ + assert(call_return_value); \ + assert(PyErr_ExceptionMatches(exception)); \ + PyErr_Clear(); \ + } while(0) + +static PyObject * +test_c_api(PySetObject *so) +{ + PyObject *elem, *dup, *t, *f, *ob = (PyObject *)so; + + /* Verify preconditions and exercise type/size checks */ + assert(PyAnySet_Check(ob)); + assert(PyAnySet_CheckExact(ob)); + assert(!PyFrozenSet_CheckExact(ob)); + assert(PySet_Size(ob) == 3); + assert(PySet_GET_SIZE(ob) == 3); + + /* Raise TypeError for non-iterable constructor arguments */ + assertRaises(PySet_New(Py_None) == NULL, PyExc_TypeError); + assertRaises(PyFrozenSet_New(Py_None) == NULL, PyExc_TypeError); + + /* Raise TypeError for unhashable key */ + dup = PySet_New(ob); + assertRaises(PySet_Discard(ob, dup) == -1, PyExc_TypeError); + assertRaises(PySet_Contains(ob, dup) == -1, PyExc_TypeError); + assertRaises(PySet_Add(ob, dup) == -1, PyExc_TypeError); + + /* Exercise successful pop, contains, add, and discard */ + elem = PySet_Pop(ob); + assert(PySet_Contains(ob, elem) == 0); + assert(PySet_GET_SIZE(ob) == 2); + assert(PySet_Add(ob, elem) == 0); + assert(PySet_Contains(ob, elem) == 1); + assert(PySet_GET_SIZE(ob) == 3); + assert(PySet_Discard(ob, elem) == 1); + assert(PySet_GET_SIZE(ob) == 2); + assert(PySet_Discard(ob, elem) == 0); + assert(PySet_GET_SIZE(ob) == 2); + + /* Raise SystemError when self argument is not a set or frozenset. */ + t = PyTuple_New(0); + assertRaises(PySet_Size(t) == -1, PyExc_SystemError); + assertRaises(PySet_Contains(t, elem) == -1, PyExc_SystemError); + Py_DECREF(t); + + /* Raise SystemError when self argument is not a set. */ + f = PyFrozenSet_New(dup); + assert(PySet_Size(f) == 3); + assert(PyFrozenSet_CheckExact(f)); + assertRaises(PySet_Add(f, elem) == -1, PyExc_SystemError); + assertRaises(PySet_Discard(f, elem) == -1, PyExc_SystemError); + assertRaises(PySet_Pop(f) == NULL, PyExc_SystemError); + Py_DECREF(f); + + /* Raise KeyError when popping from an empty set */ + set_clear_internal(so); + assert(PySet_GET_SIZE(ob) == 0); + assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError); + + /* Restore the set from the copy and use the abstract API */ + assert(PyObject_CallMethod(ob, "update", "O", dup) == Py_None); + Py_DECREF(Py_None); + + /* Verify constructors accept NULL arguments */ + f = PySet_New(NULL); + assert(f != NULL); + assert(PySet_GET_SIZE(f) == 0); + Py_DECREF(f); + f = PyFrozenSet_New(NULL); + assert(f != NULL); + assert(PyFrozenSet_CheckExact(f)); + assert(PySet_GET_SIZE(f) == 0); + Py_DECREF(f); + + Py_DECREF(elem); + Py_DECREF(dup); + Py_RETURN_TRUE; +} + +#endif From gregorykjohnson at users.sourceforge.net Tue Aug 16 19:12:14 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Tue, 16 Aug 2005 19:12:14 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.8, 1.9 libmailbox.tex, 1.9, 1.10 Message-ID: <20050816171214.BBD231E4002@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5623 Modified Files: mailbox.py libmailbox.tex Log Message: * Overhaul locking: * Introduce lock() and unlock() methods: * Locking granularity should be up to the user. Otherwise, either consecutive operations that should be atomic are not or locks grow stale (in as little as 30 seconds) and are stolen. * Correct miscellaneous ill-conceived sections of locking code. Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- mailbox.py 13 Aug 2005 22:41:33 -0000 1.8 +++ mailbox.py 16 Aug 2005 17:12:03 -0000 1.9 @@ -169,6 +169,14 @@ """Write any pending changes to the disk.""" raise NotImplementedError, "Method must be implemented by subclass" + def lock(self): + """Lock the mailbox.""" + raise NotImplementedError, "Method must be implemented by subclass" + + def unlock(self, f=None): + """Unlock the mailbox if it is locked.""" + raise NotImplementedError, "Method must be implemented by subclass" + def close(self): """Flush and close the mailbox.""" raise NotImplementedError, "Method must be implemented by subclass" @@ -315,6 +323,14 @@ """Write any pending changes to disk.""" return # Maildir changes are always written immediately. + def lock(self): + """Lock the mailbox.""" + return + + def unlock(self, f=None): + """Unlock the mailbox if it is locked.""" + return + def close(self): """Flush and close the mailbox.""" return @@ -437,7 +453,7 @@ self._toc = None self._next_key = 0 self._pending = False # No changes require rewriting the file. - self._mtime = os.fstat(self._file.fileno()).st_mtime + self._locked = False def add(self, message): """Add message and return assigned key.""" @@ -449,6 +465,7 @@ def remove(self, key): """Remove the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) del self._toc[key] self._pending = True @@ -474,54 +491,59 @@ self._lookup() return len(self._toc) + def lock(self): + """Lock the mailbox.""" + if not self._locked: + _lock_file(self._file) + self._locked = True + + def unlock(self, f=None): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._locked = False + def flush(self): """Write any pending changes to disk.""" if not self._pending: return self._lookup() - _lock_file(self._file, self._path) + new_file = _create_temporary(self._path) try: - os.rename(self._path, self._path + '~') - _lock_file(self._file, self._path + '~', system=False) + new_toc = {} + self._pre_mailbox_hook(new_file) + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + self._pre_message_hook(new_file) + new_start = new_file.tell() + while True: + buffer = self._file.read(min(4096, + stop - self._file.tell())) + if buffer == '': + break + new_file.write(buffer) + new_toc[key] = (new_start, new_file.tell()) + self._post_message_hook(new_file) except: - _unlock_file(self._file, self._path) + new_file.close() + os.remove(new_file.name) raise + new_file.close() + self._file.close() try: - self._assert_mtime() - f = _create_carefully(self._path) - try: - _lock_file(f, self._path, dot=False) - try: - self._pre_mailbox_hook(f) - new_toc = {} - for key in sorted(self._toc.keys()): - start, stop = self._toc[key][:2] - self._file.seek(start) - self._pre_message_hook(f) - new_start = f.tell() - while True: - buffer = self._file.read( - min(4096, stop - self._file.tell())) - if buffer == '': - break - f.write(buffer) - new_toc[key] = (new_start, f.tell()) + \ - self._toc[key][2:] # XXX: Wrong! - self._post_message_hook(f) - finally: - _unlock_file(f, self._path) - except: - f.close() + os.rename(new_file.name, self._path) + except OSError, e: + if e.errno == errno.EEXIST: + os.remove(self._path) + os.rename(new_file.name, self._path) + else: raise - finally: - _unlock_file(self._file, self._path + '~') - self._file.close() + self._file = file(self._path, 'r+') self._toc = new_toc - self._file = f - self._file.flush() - self._set_mtime() - os.remove(self._path + '~') self._pending = False + if self._locked: + _lock_file(new_file, dotlock=False) def _pre_mailbox_hook(self, f): """Called before writing the mailbox to file f.""" @@ -538,6 +560,8 @@ def close(self): """Flush and close the mailbox.""" self.flush() + if self._locked: + self.unlock() self._file.close() def _lookup(self, key=None): @@ -550,39 +574,23 @@ except KeyError: raise KeyError, "No message with key '%s'" % key - def _assert_mtime(self): - """Raise an exception if the file has been externally modified.""" - if self._mtime != os.fstat(self._file.fileno()).st_mtime: - raise ExternalClashError, \ - 'External modifications detected: use refresh()' - - def _set_mtime(self): - """Store the current mtime.""" - self._mtime = os.fstat(self._file.fileno()).st_mtime - def _append_message(self, message): """Append message to mailbox and return (start, stop, ...) offsets.""" - _lock_file(self._file, self._path) - try: - self._assert_mtime() - self._file.seek(0, 2) - self._pre_message_hook(self._file) - offsets = self._install_message(message) - self._post_message_hook(self._file) - self._file.flush() - self._set_mtime() - finally: - _unlock_file(self._file, self._path) + self._file.seek(0, 2) + self._pre_message_hook(self._file) + offsets = self._install_message(message) + self._post_message_hook(self._file) + self._file.flush() return offsets + class _mboxMMDF(_singlefileMailbox): """An mbox or MMDF mailbox.""" def get_message(self, key): """Return a Message representation or raise a KeyError.""" start, stop = self._lookup(key) - self._assert_mtime() self._file.seek(start) from_line = self._file.readline() msg = self._message_factory(self._file.read(stop - self._file.tell())) @@ -592,7 +600,6 @@ def get_string(self, key, from_=False): """Return a string representation or raise a KeyError.""" start, stop = self._lookup(key) - self._assert_mtime() self._file.seek(start) if not from_: self._file.readline() @@ -601,7 +608,6 @@ def get_file(self, key, from_=False): """Return a file-like representation or raise a KeyError.""" start, stop = self._lookup(key) - self._assert_mtime() self._file.seek(start) if not from_: self._file.readline() @@ -649,7 +655,6 @@ """Generate key-to-(start, stop) table of contents.""" starts, stops = [], [] self._file.seek(0) - self._assert_mtime() while True: line_pos = self._file.tell() line = self._file.readline() @@ -685,7 +690,6 @@ starts, stops = [], [] self._file.seek(0) next_pos = 0 - self._assert_mtime() while True: line_pos = next_pos line = self._file.readline() @@ -718,9 +722,10 @@ if create: os.mkdir(self._path, 0700) os.close(os.open(os.path.join(self._path, '.mh_sequences'), - os.O_CREAT | os.O_WRONLY, 0600)) + os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0600)) else: raise NoSuchMailboxError, self._path + self._locked = False def add(self, message): """Add message and return assigned key.""" @@ -732,62 +737,85 @@ new_path = os.path.join(self._path, str(new_key)) f = _create_carefully(new_path) try: - _lock_file(f, f.name) + if self._locked: + _lock_file(f) try: self._dump_message(message, f) if isinstance(message, MHMessage): self._dump_sequences(message, new_key) finally: - _unlock_file(f, f.name) + if self._locked: + _unlock_file(f) finally: f.close() return new_key def remove(self, key): """Remove the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) try: - os.remove(os.path.join(self._path, str(key))) - except OSError, e: + f = file(path, 'r+') + except IOError, e: if e.errno == errno.ENOENT: raise KeyError, "No message with key '%s'" % key else: raise + try: + if self._locked: + _lock_file(f) + try: + f.close() + os.remove(os.path.join(self._path, str(key))) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() def __setitem__(self, key, message): """Replace the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) try: - f_r = file(os.path.join(self._path, str(key)), 'r+') + f = file(path, 'r+') except IOError, e: if e.errno == errno.ENOENT: raise KeyError, "No message with key '%s'" % key else: raise try: - _lock_file(f_r, f_r.name) + if self._locked: + _lock_file(f) try: - f_w = file(os.path.join(self._path, str(key)), 'w') - try: - self._dump_message(message, f_w) - if isinstance(message, MHMessage): - self._dump_sequences(message, key) - finally: - f_w.close() + os.close(os.open(path, os.O_WRONLY | os.O_TRUNC)) + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, key) finally: - _unlock_file(f_r, f_r.name) + if self._locked: + _unlock_file(f) finally: - f_r.close() + f.close() def get_message(self, key): """Return a Message representation or raise a KeyError.""" try: - f = file(os.path.join(self._path, str(key)), 'r') + if self._locked: + f = file(os.path.join(self._path, str(key)), 'r+') + else: + f = file(os.path.join(self._path, str(key)), 'r') except IOError, e: if e.errno == errno.ENOENT: raise KeyError, "No message with key '%s'" % key else: raise try: - msg = MHMessage(f) + if self._locked: + _lock_file(f) + try: + msg = MHMessage(f) + finally: + if self._locked: + _unlock_file(f) finally: f.close() for name, key_list in self.get_sequences(): @@ -798,14 +826,23 @@ def get_string(self, key): """Return a string representation or raise a KeyError.""" try: - f = file(os.path.join(self._path, str(key)), 'r') + if self._locked: + f = file(os.path.join(self._path, str(key)), 'r+') + else: + f = file(os.path.join(self._path, str(key)), 'r') except IOError, e: if e.errno == errno.ENOENT: raise KeyError, "No message with key '%s'" % key else: raise try: - return f.read() + if self._locked: + _lock_file(f) + try: + return f.read() + finally: + if self._locked: + _unlock_file() finally: f.close() @@ -833,13 +870,29 @@ """Return a count of messages in the mailbox.""" return len(list(self.iterkeys())) + def lock(self): + """Lock the mailbox.""" + if not self._locked: + self._file = file(os.path.join(self._path, '.mh_sequences'), 'r+') + _lock_file(self._file) + self._locked = True + + def unlock(self, f=None): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._file.close() + del self._file + self._locked = False + def flush(self): """Write any pending changes to the disk.""" return def close(self): """Flush and close the mailbox.""" - return + if self._locked: + self.unlock() def list_folders(self): """Return a list of folder names.""" @@ -855,7 +908,7 @@ def add_folder(self, folder): """Create a folder and return an MH instance representing it.""" - return Maildir(os.path.join(self._path, '.' + folder)) + return MH(os.path.join(self._path, '.' + folder)) def remove_folder(self, folder): """Delete the named folder, which must be empty.""" @@ -899,36 +952,28 @@ """Set sequences using the given name-to-key-list dictionary.""" f = file(os.path.join(self._path, '.mh_sequences'), 'r+') try: - _lock_file(f, f.name) - try: - # .truncate() is not portable, so re-open to truncate. - f_w = file(os.path.join(self._path, '.mh_sequences'), 'w') - try: - for name, keys in sequences.iteritems(): - if len(keys) == 0: - continue - f_w.write('%s:' % name) - prev = None + os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC)) + for name, keys in sequences.iteritems(): + if len(keys) == 0: + continue + f.write('%s:' % name) + prev = None + completing = False + for key in sorted(set(keys)): + if key - 1 == prev: + if not completing: + completing = True + fw.write('-') + elif completing: completing = False - for key in sorted(set(keys)): - if key - 1 == prev: - if not completing: - completing = True - f_w.write('-') - elif completing: - completing = False - f_w.write('%s %s' % (prev, key)) - else: - f_w.write(' %s' % key) - prev = key - if completing: - f_w.write('%s%s' % (prev, os.linesep)) - else: - f_w.write(os.linesep) - finally: - f_w.close() - finally: - _unlock_file(f, f.name) + f.write('%s %s' % (prev, key)) + else: + f.write(' %s' % key) + prev = key + if completing: + f.write('%s%s' % (prev, os.linesep)) + else: + f.write(os.linesep) finally: f.close() @@ -940,13 +985,24 @@ for key in self.iterkeys(): if key - 1 != prev: changes.append((key, prev + 1)) - if hasattr(os, 'link'): - os.link(os.path.join(self._path, str(key)), - os.path.join(self._path, str(prev + 1))) - os.unlink(os.path.join(self._path, str(key))) - else: - os.rename(os.path.join(self._path, str(key)), - os.path.join(self._path, str(prev + 1))) + f = file(os.path.join(self._path, str(key)), 'r+') + try: + if self._locked: + _lock_file(f) + try: + if hasattr(os, 'link'): + os.link(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + os.unlink(os.path.join(self._path, str(key))) + else: + f.close() + os.rename(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() prev += 1 self._next_key = prev + 1 if len(changes) == 0: @@ -979,7 +1035,6 @@ def get_message(self, key): """Return a Message representation or raise a KeyError.""" start, stop = self._lookup(key) - self._assert_mtime() self._file.seek(start) self._file.readline() # XXX: parse this '1,' line for labels original_headers = StringIO.StringIO() @@ -1002,7 +1057,6 @@ def get_string(self, key): """Return a string representation or raise a KeyError.""" start, stop = self._lookup(key) - self._assert_mtime() self._file.seek(start) self._file.readline() # Skip '1,' line. original_headers = StringIO.StringIO() @@ -1031,7 +1085,6 @@ starts, stops = [], [] self._file.seek(0) next_pos = 0 - self._assert_mtime() while True: line_pos = next_pos line = self._file.readline() @@ -1614,35 +1667,30 @@ return _ProxyFile._read(self, size, read_method) -def _lock_file(f, path, system=True, dot=True): +def _lock_file(f, dotlock=True): """Lock file f using lockf, flock, and dot locking.""" - lockf_done = flock_done = dotlock_done = False try: - if system and fcntl: + if fcntl: try: fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) - lockf_done = True except IOError, e: if e.errno == errno.EAGAIN: raise ExternalClashError, \ - "lockf: lock unavailable: %s" % path + "lockf: lock unavailable: %s" % f.name else: raise try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) - flock_done = True except IOError, e: if e.errno == errno.EWOULDBLOCK: raise ExternalClashError, \ - "flock: lock unavailable: %s" % path + "flock: lock unavailable: %s" % f.name else: raise - if dot: - tmp = '%s.lock.%s.%s.%s' % (path, int(time.time()), - socket.gethostname(), os.getpid()) + if dotlock: try: - os.close(os.open(tmp, os.O_WRONLY | os.O_EXCL | os.O_CREAT, - 0600)) + pre_lock = _create_temporary(f.name + '.lock') + pre_lock.close() except IOError, e: if e.errno == errno.EACCESS: return # Without write access, just skip dotlocking. @@ -1650,40 +1698,48 @@ raise try: if hasattr(os, 'link'): - os.link(tmp, path + '.lock') - os.unlink(tmp) + os.link(pre_lock.name, f.name + '.lock') + dotlock_done = True + os.unlink(pre_lock) else: - os.rename(tmp, path + '.lock') - dotlock_done = True + os.rename(pre_lock, f.name + '.lock') + dotlock_done = True except OSError, e: if e.errno == errno.EEXIST: - try: - os.remove(tmp) - except: - pass - raise ExternalClashError, 'dot lock unavailable: %s' % path + os.remove(pre_lock) + raise ExternalClashError, 'dot lock unavailable: %s' % \ + f.name else: raise except: - if lockf_done: + if fcntl: fcntl.lockf(f, fcntl.LOCK_UN) - if flock_done: fcntl.flock(f, fcntl.LOCK_UN) if dotlock_done: - os.remove(path + '.lock') + os.remove(f.name + '.lock') raise -def _unlock_file(f, path, system=True, dot=True): +def _unlock_file(f): """Unlock file f using lockf, flock, and dot locking.""" - if system and fcntl: + if fcntl: fcntl.lockf(f, fcntl.LOCK_UN) fcntl.flock(f, fcntl.LOCK_UN) - if dot and os.path.exists(path + '.lock'): - os.remove(path + '.lock') + if os.path.exists(path + '.lock'): + os.remove(f.name + '.lock') def _create_carefully(path): """Create a file if it doesn't exist and open for reading and writing.""" - return os.fdopen(os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR), 'r+') + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR) + try: + return file(path, 'r+') + finally: + os.close(fd) + +def _create_temporary(path): + """Create a temp file based on path and open for reading and writing.""" + return _create_carefully('%s.%s.%s.%s' % (path, int(time.time()), + socket.gethostname(), + os.getpid())) class Error(Exception): """Raised for module-specific errors.""" Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- libmailbox.tex 13 Aug 2005 22:41:33 -0000 1.9 +++ libmailbox.tex 16 Aug 2005 17:12:03 -0000 1.10 @@ -221,9 +221,20 @@ subclasses, changes are written immediately and this method does nothing. \end{methoddesc} +\begin{methoddesc}{lock}{} +Acquire an exclusive advisory lock on the mailbox so that other processes know +not to modify it. An \exception{ExternalClashError} is raised if the lock is +not available. The particular locking mechanisms used depend upon the mailbox +format. +\end{methoddesc} + +\begin{methoddesc}{unlock}{} +Release the advisory lock on the mailbox, if any. +\end{methoddesc} + \begin{methoddesc}{close}{} -Flush the mailbox and close any open files. For some \class{Mailbox} -subclasses, this method does nothing. +Flush the mailbox, unlock it if necessary, and close any open files. For some +\class{Mailbox} subclasses, this method does nothing. \end{methoddesc} @@ -305,9 +316,14 @@ nothing. \end{methoddesc} +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Maildir mailboxes do not support (or require) locking, so these methods do +nothing. \end{methoddesc} + \begin{methoddesc}{close}{} -\class{Maildir} instances do not keep any open files, so this method does -nothing. +\class{Maildir} instances do not keep any open files and the underlying +mailboxes do not support locking, so this method does nothing. \end{methoddesc} \begin{methoddesc}{get_file}{key} @@ -356,6 +372,12 @@ XXX \end{methoddesc} +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + \begin{seealso} \seelink{http://www.qmail.org/man/man5/mbox.html}{mbox man page from qmail}{A specification of the format and its variations.} @@ -450,6 +472,15 @@ marking a message for deletion by prepending a comma to its name is not used. \end{methoddesc} +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. For MH mailboxes, +locking the mailbox means locking the \file{.mh_sequences} file and, only for +the duration of any operations that affect them, locking individual message +files. +\end{methoddesc} + \begin{methoddesc}{get_file}{key} XXX \end{methoddesc} @@ -460,7 +491,8 @@ \end{methoddesc} \begin{methoddesc}{close}{} -\class{MH} instances do not keep any open files, so this method does nothing. +\class{MH} instances do not keep any open files, so this method is equivelant +to \method{unlock()}. \end{methoddesc} \class{MH} instances have all of the methods of \class{Mailbox} in addition to @@ -521,6 +553,12 @@ module), which may be used like a file. \end{methoddesc} +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + \begin{seealso} \seelink{http://quimby.gnus.org/notes/BABYL}{Format of Version 5 Babyl Files}{A specification of the Babyl format.} @@ -554,6 +592,12 @@ XXX \end{methoddesc} +\begin{methoddesc}{lock}{} +\methodline{unlock}{} +Three locking mechanisms are used---dot locking and, if available, the +\cfunction{flock()} and \cfunction{lockf()} system calls. +\end{methoddesc} + \begin{seealso} \seelink{http://www.tin.org/bin/man.cgi?section=5\&topic=mmdf}{mmdf man page from tin}{A specification of MMDF format from the documentation of tin, a From loewis at users.sourceforge.net Tue Aug 16 20:17:10 2005 From: loewis at users.sourceforge.net (loewis@users.sourceforge.net) Date: Tue, 16 Aug 2005 20:17:10 +0200 (CEST) Subject: [Python-checkins] python/nondist/peps pep-0347.txt,1.13,1.14 Message-ID: <20050816181710.F03C31E4002@bag.python.org> Update of /cvsroot/python/python/nondist/peps In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24270 Modified Files: pep-0347.txt Log Message: Remove remaining mentioning of basic auth. Index: pep-0347.txt =================================================================== RCS file: /cvsroot/python/python/nondist/peps/pep-0347.txt,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -r1.13 -r1.14 --- pep-0347.txt 13 Aug 2005 01:47:28 -0000 1.13 +++ pep-0347.txt 16 Aug 2005 18:17:00 -0000 1.14 @@ -151,6 +151,12 @@ support the project/trunk structure, each project needs to be converted separately. To get each conversion result into a separate directory in the target repository, svnadmin load must be used. + +Subversion has a different view on binary-vs-text files than CVS. +To correctly carry the CVS semantics forward, svn:eol-style should +be set to native on all files that are not marked binary in the +CVS. + In summary, the conversion script is:: #!/bin/sh @@ -184,18 +190,18 @@ Publish the Repository ------------------------ -The repository should be published at https://svn.python.org/projects. -Read-write access should be granted through basic authentication to -all current SF committers; read-only anonymous access should also be -granted. Read-write access will go through -svn+ssh://pythondev at svn.python.org/projects. +The repository should be published at http://svn.python.org/projects. +Read-write access should be granted to all current SF committers +through svn+ssh://pythondev at svn.python.org/projects; +read-only anonymous access through WebDAV should also be +granted. As an option, websvn (available e.g. from the Debian websvn package) could be provided. Unfortunately, in the test installation, websvn breaks because it runs out of memory. -The current SF project admins should get write access to the password -file, in order to create or delete new users. +The current SF project admins should get write access to the +authorized_keys file of the pythondev account. Disable CVS From gregorykjohnson at users.sourceforge.net Wed Aug 17 01:38:22 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Wed, 17 Aug 2005 01:38:22 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.9, 1.10 libmailbox.tex, 1.10, 1.11 test_mailbox.py, 1.7, 1.8 Message-ID: <20050816233822.5D4761E4002@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2326 Modified Files: mailbox.py libmailbox.tex test_mailbox.py Log Message: * Add delivery date support to MaildirMessage and Maildir, and support for conversion between delivery dates in MaildirMessage, mboxMessage, and MMDFMessage instances. * Don't guess delivery dates from headers. * Add full support for labels to Babyl. * Small tweaks: * Remove outdated "start, stop, ..." mentions in comments. * Change exception raising style: use "Exception(...)" instead of "Exception, ..." in accordance with Raymond Hettinger's comment in the Python Tutorial and A.M. Kuchling's recent post on python-dev. * Use ": %s" consistently in exception messages, not " '%s'". Index: mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- mailbox.py 16 Aug 2005 17:12:03 -0000 1.9 +++ mailbox.py 16 Aug 2005 23:38:11 -0000 1.10 @@ -4,6 +4,7 @@ import os import time +import calendar import socket import errno import stat @@ -33,11 +34,11 @@ def add(self, message): """Add message and return assigned key.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def remove(self, key): """Remove the keyed message; raise KeyError if it doesn't exist.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def __delitem__(self, key): self.remove(key) @@ -51,7 +52,7 @@ def __setitem__(self, key, message): """Replace the keyed message; raise KeyError if it doesn't exist.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def get(self, key, default=None): """Return the keyed message, or default if it doesn't exist.""" @@ -69,19 +70,19 @@ def get_message(self, key): """Return a Message representation or raise a KeyError.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def get_string(self, key): """Return a string representation or raise a KeyError.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def get_file(self, key): """Return a file-like representation or raise a KeyError.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def iterkeys(self): """Return an iterator over keys.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def keys(self): """Return a list of keys.""" @@ -118,14 +119,14 @@ def has_key(self, key): """Return True if the keyed message exists, False otherwise.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def __contains__(self, key): return self.has_key(key) def __len__(self): """Return a count of messages in the mailbox.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def clear(self): """Delete all messages.""" @@ -146,7 +147,7 @@ for key in self.iterkeys(): return (key, self.pop(key)) # This is only run once. else: - raise KeyError, "No messages in mailbox" + raise KeyError('No messages in mailbox') def update(self, arg=None): """Change the messages that correspond to certain keys.""" @@ -163,23 +164,23 @@ except KeyError: bad_key = True if bad_key: - raise KeyError, "No message with key(s)" + raise KeyError('No message with key(s)') def flush(self): """Write any pending changes to the disk.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def lock(self): """Lock the mailbox.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def unlock(self, f=None): """Unlock the mailbox if it is locked.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def close(self): """Flush and close the mailbox.""" - raise NotImplementedError, "Method must be implemented by subclass" + raise NotImplementedError('Method must be implemented by subclass') def _dump_message(self, message, target): """Dump message contents to target file.""" @@ -195,7 +196,7 @@ break target.write(buffer) else: - raise TypeError, "Invalid message type" + raise TypeError('Invalid message type: %s' % type(message)) class Maildir(Mailbox): @@ -211,7 +212,7 @@ os.mkdir(os.path.join(self._path, 'new'), 0700) os.mkdir(os.path.join(self._path, 'cur'), 0700) else: - raise NoSuchMailboxError, self._path + raise NoSuchMailboxError(self._path) self._toc = {} def add(self, message): @@ -232,6 +233,8 @@ uniq = os.path.basename(tmp_file.name).split(':')[0] dest = os.path.join(self._path, subdir, uniq + suffix) os.rename(tmp_file.name, dest) + if isinstance(message, MaildirMessage): + os.utime(dest, (os.path.getatime(dest), message.get_date())) return uniq def remove(self, key): @@ -268,9 +271,11 @@ else: suffix = '' self.discard(key) - os.rename(os.path.join(self._path, temp_subpath), - os.path.join(self._path, subdir, key + suffix)) - # XXX: the mtime should be reset to keep delivery date + new_path = os.path.join(self._path, subdir, key + suffix) + os.rename(os.path.join(self._path, temp_subpath), new_path) + if isinstance(message, MaildirMessage): + os.utime(new_path, (os.path.getatime(new_path), + message.get_date())) def get_message(self, key): """Return a Message representation or raise a KeyError.""" @@ -284,6 +289,7 @@ msg.set_subdir(subdir) if ':' in name: msg.set_info(name.split(':')[-1]) + msg.set_date(os.path.getmtime(os.path.join(self._path, subpath))) return msg def get_string(self, key): @@ -363,12 +369,12 @@ for entry in os.listdir(os.path.join(path, 'new')) + \ os.listdir(os.path.join(path, 'cur')): if len(entry) < 1 or entry[0] != '.': - raise NotEmptyError, "Folder '%s' contains message" % folder + raise NotEmptyError('Folder contains message(s): %s' % folder) for entry in os.listdir(path): if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ os.path.isdir(os.path.join(path, entry)): - raise NotEmptyError, "Folder '%s' contains subdirectory '%s'" \ - % (folder, entry) + raise NotEmptyError("Folder contains subdirectory '%s': %s" % + (folder, entry)) for root, dirs, files in os.walk(path, topdown=False): for entry in files: os.remove(os.path.join(root, entry)) @@ -406,8 +412,8 @@ else: raise else: - raise ExternalClashError, \ - "Name clash prevented file creation: '%s'" % path + raise ExternalClashError('Name clash prevented file creation: %s' % + path) def _refresh(self): """Update table of contents mapping.""" @@ -428,7 +434,7 @@ try: return self._toc[key] except KeyError: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) class _singlefileMailbox(Mailbox): @@ -444,7 +450,7 @@ if create: f = file(self._path, 'w+') else: - raise NoSuchMailboxError, self._path + raise NoSuchMailboxError(self._path) elif e.errno == errno.EACCES: f = file(self._path, 'r') else: @@ -565,17 +571,17 @@ self._file.close() def _lookup(self, key=None): - """Return (start, stop), possibly with more info, or raise KeyError.""" + """Return (start, stop) or raise KeyError.""" if self._toc is None: self._generate_toc() if key is not None: try: return self._toc[key] except KeyError: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) def _append_message(self, message): - """Append message to mailbox and return (start, stop, ...) offsets.""" + """Append message to mailbox and return (start, stop) offsets.""" self._file.seek(0, 2) self._pre_message_hook(self._file) offsets = self._install_message(message) @@ -629,8 +635,7 @@ elif isinstance(message, email.Message.Message): from_line = message.get_unixfrom() # May be None. if from_line is None: - from_line = 'From MAILER-DAEMON %s' % \ - time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime()) + from_line = 'From MAILER-DAEMON %s' % time.asctime(time.gmtime()) start = self._file.tell() self._file.write('%s%s' % (from_line, os.linesep)) self._dump_message(message, self._file) @@ -724,7 +729,7 @@ os.close(os.open(os.path.join(self._path, '.mh_sequences'), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0600)) else: - raise NoSuchMailboxError, self._path + raise NoSuchMailboxError(self._path) self._locked = False def add(self, message): @@ -757,7 +762,7 @@ f = file(path, 'r+') except IOError, e: if e.errno == errno.ENOENT: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) else: raise try: @@ -779,7 +784,7 @@ f = file(path, 'r+') except IOError, e: if e.errno == errno.ENOENT: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) else: raise try: @@ -805,7 +810,7 @@ f = file(os.path.join(self._path, str(key)), 'r') except IOError, e: if e.errno == errno.ENOENT: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) else: raise try: @@ -832,7 +837,7 @@ f = file(os.path.join(self._path, str(key)), 'r') except IOError, e: if e.errno == errno.ENOENT: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) else: raise try: @@ -852,7 +857,7 @@ f = file(os.path.join(self._path, str(key)), 'r') except IOError, e: if e.errno == errno.ENOENT: - raise KeyError, "No message with key '%s'" % key + raise KeyError('No message with key: %s' % key) else: raise return _ProxyFile(f) @@ -919,8 +924,7 @@ elif entries == []: pass else: - raise NotEmptyError, "Folder '%s' is not empty" % \ - self._path + raise NotEmptyError('Folder not empty: %s' % self._path) os.rmdir(path) def get_sequences(self): @@ -942,8 +946,8 @@ results[name] = [key for key in sorted(keys) \ if key in all_keys] except ValueError: - raise FormatError, "Invalid sequence specification: " % \ - "'%s'" % line.rstrip() + raise FormatError('Invalid sequence specification: %s' % + line.rstrip()) finally: f.close() return results @@ -1032,11 +1036,38 @@ class Babyl(_singlefileMailbox): """An Rmail-style Babyl mailbox.""" + _special_labels = frozenset(('unseen', 'deleted', 'filed', 'answered', + 'forwarded', 'edited', 'resent')) + + def __init__(self, path, factory=None, create=True): + """Initialize a Babyl mailbox.""" + _singlefileMailbox.__init__(self, path, factory, create) + self._labels = {} + + def add(self, message): + """Add message and return assigned key.""" + key = _singlefileMailbox.add(self, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + return key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.remove(self, key) + if key in self._labels: + del self._labels[key] + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.__setitem__(self, key, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + def get_message(self, key): """Return a Message representation or raise a KeyError.""" start, stop = self._lookup(key) self._file.seek(start) - self._file.readline() # XXX: parse this '1,' line for labels + self._file.readline() # Skip '1,' line specifying labels. original_headers = StringIO.StringIO() while True: line = self._file.readline() @@ -1052,13 +1083,15 @@ body = self._file.read(stop - self._file.tell()) msg = BabylMessage(original_headers.getvalue() + body) msg.set_visible(visible_headers.getvalue()) + if key in self._labels: + msg.set_labels(self._labels[key]) return msg def get_string(self, key): """Return a string representation or raise a KeyError.""" start, stop = self._lookup(key) self._file.seek(start) - self._file.readline() # Skip '1,' line. + self._file.readline() # Skip '1,' line specifying labels. original_headers = StringIO.StringIO() while True: line = self._file.readline() @@ -1078,13 +1111,19 @@ def get_labels(self): """Return a list of user-defined labels in the mailbox.""" - raise NotImplementedError, 'Method not yet implemented' + self._lookup() + labels = set() + for label_list in self._labels.values(): + labels.update(label_list) + labels.difference_update(self._special_labels) + return list(labels) def _generate_toc(self): - """Generate key-to-(start, stop, eooh, body) table of contents.""" + """Generate key-to-(start, stop) table of contents.""" starts, stops = [], [] self._file.seek(0) next_pos = 0 + label_lists = [] while True: line_pos = next_pos line = self._file.readline() @@ -1093,6 +1132,10 @@ if len(stops) < len(starts): stops.append(line_pos - len(os.linesep)) starts.append(next_pos) + labels = [label.strip() for label + in self._file.readline()[1:].split(',') + if label.strip() != ''] + label_lists.append(labels) elif line == '\037' or line == '\037' + os.linesep: if len(stops) < len(starts): stops.append(line_pos - len(os.linesep)) @@ -1100,12 +1143,13 @@ stops.append(line_pos - len(os.linesep)) break self._toc = dict(enumerate(zip(starts, stops))) + self._labels = dict(enumerate(label_lists)) self._next_key = len(self._toc) def _pre_mailbox_hook(self, f): """Called before writing the mailbox to file f.""" - f.write('BABYL OPTIONS:%sVersion: 5%s\037' % (os.linesep, os.linesep)) - # XXX: write "Labels:" line too + f.write('BABYL OPTIONS:\nVersion: 5\nLabels:%s\n\037' % + ','.join(self.get_labels())) def _pre_message_hook(self, f): """Called before writing each message to file f.""" @@ -1116,9 +1160,25 @@ f.write('\n\037') def _install_message(self, message): - """Write message contents and return (start, stop, ...).""" + """Write message contents and return (start, stop).""" start = self._file.tell() - self._file.write('1,,\n') # XXX: check for labels and add them + if isinstance(message, BabylMessage): + special_labels = [] + labels = [] + for label in message.get_labels(): + if label in self._special_labels: + special_labels.append(label) + else: + labels.append(label) + self._file.write('1') + for label in special_labels: + self._file.write(', ' + label) + self._file.write(',,') + for label in labels: + self._file.write(' ' + label + ',') + self._file.write('\n') + else: + self._file.write('1,,\n') if isinstance(message, email.Message.Message): pseudofile = StringIO.StringIO() ps_generator = email.Generator.Generator(pseudofile, False, 0) @@ -1175,7 +1235,7 @@ break self._file.write(buffer) else: - raise TypeError, "Invalid message type" + raise TypeError('Invalid message type: %s' % type(message)) stop = self._file.tell() return (start, stop) @@ -1196,7 +1256,7 @@ elif message is None: email.Message.Message.__init__(self) else: - raise TypeError, "Invalid message type" + raise TypeError('Invalid message type: %s' % type(message)) def _become_message(self, message): """Assume the non-format-specific state of message.""" @@ -1209,7 +1269,7 @@ if isinstance(message, Message): return # There's nothing format-specific to explain. else: - raise TypeError, "Cannot convert to specified type" + raise TypeError('Cannot convert to specified type') class MaildirMessage(Message): @@ -1219,6 +1279,7 @@ """Initialize a MaildirMessage instance.""" self._subdir = 'new' self._info = '' + self._date = time.time() Message.__init__(self, message) def get_subdir(self): @@ -1230,7 +1291,7 @@ if subdir == 'new' or subdir == 'cur': self._subdir = subdir else: - raise ValueError, "subdir must be 'new' or 'cur'" + raise ValueError("subdir must be 'new' or 'cur': %s" % subdir) def get_flags(self): """Return as a string the flags that are set.""" @@ -1252,6 +1313,17 @@ if self.get_flags() != '': self.set_flags(''.join(set(self.get_flags()) - set(flag))) + def get_date(self): + """Return delivery date of message, in seconds since the epoch.""" + return self._date + + def set_date(self, date): + """Set delivery date of message, in seconds since the epoch.""" + try: + self._date = float(date) + except ValueError: + raise TypeError("can't convert to float: %s" % date) + def get_info(self): """Get the message's "info" as a string.""" return self._info @@ -1261,13 +1333,14 @@ if isinstance(info, str): self._info = info else: - raise TypeError, "info must be a string" + raise TypeError('info must be a string: %s' % type(info)) def _explain_to(self, message): """Copy Maildir-specific state to message insofar as possible.""" if isinstance(message, MaildirMessage): message.set_flags(self.get_flags()) message.set_subdir(self.get_subdir()) + message.set_date(self.get_date()) elif isinstance(message, _mboxMMDFMessage): flags = set(self.get_flags()) if 'S' in flags: @@ -1280,6 +1353,7 @@ message.add_flag('F') if 'R' in flags: message.add_flag('A') + message.set_from('MAILER-DAEMON', time.gmtime(self.get_date())) elif isinstance(message, MHMessage): flags = set(self.get_flags()) if 'S' not in flags: @@ -1301,7 +1375,8 @@ elif isinstance(message, Message): pass else: - raise TypeError, "Cannot convert to specified type" + raise TypeError('Cannot convert to specified type: %s' % + type(message)) class _mboxMMDFMessage(Message): @@ -1314,10 +1389,6 @@ unixfrom = message.get_unixfrom() if unixfrom is not None and unixfrom[:5] == 'From ': self.set_from(unixfrom[5:]) - elif 'Return-Path' in message: - # XXX: generate "From " line from Return-Path: and Received: - pass - Message.__init__(self, message) def get_from(self): @@ -1329,7 +1400,7 @@ if time_ is not None: if time_ is True: time_ = time.gmtime() - from_ += time.strftime(" %a %b %d %H:%M:%S %Y", time_) + from_ += ' ' + time.asctime(time_) self._from = from_ def get_flags(self): @@ -1381,6 +1452,14 @@ message.add_flag('S') if 'D' in flags: message.add_flag('T') + del message['status'] + del message['x-status'] + maybe_date = ' '.join(self.get_from().split()[-5:]) + try: + message.set_date(calendar.timegm(time.strptime(maybe_date, + '%a %b %d %H:%M:%S %Y'))) + except ValueError, OverflowError: + pass elif isinstance(message, _mboxMMDFMessage): message.set_flags(self.get_flags()) message.set_from(self.get_from()) @@ -1392,6 +1471,8 @@ message.add_sequence('replied') if 'F' in flags: message.add_sequence('flagged') + del message['status'] + del message['x-status'] elif isinstance(message, BabylMessage): flags = set(self.get_flags()) if 'R' not in flags: @@ -1400,10 +1481,13 @@ message.add_label('deleted') if 'A' in flags: message.add_label('answered') + del message['status'] + del message['x-status'] elif isinstance(message, Message): pass else: - raise TypeError, "Cannot convert to specified type" + raise TypeError('Cannot convert to specified type: %s' % + type(message)) class mboxMessage(_mboxMMDFMessage): @@ -1432,7 +1516,7 @@ if not sequence in self._sequences: self._sequences.append(sequence) else: - raise TypeError, "sequence must be a string" + raise TypeError('sequence must be a string: %s' % type(sequence)) def remove_sequence(self, sequence): """Remove sequence from the list of sequences including the message.""" @@ -1476,7 +1560,8 @@ elif isinstance(message, Message): pass else: - raise TypeError, "Cannot convert to specified type" + raise TypeError('Cannot convert to specified type: %s' % + type(message)) class BabylMessage(Message): @@ -1502,7 +1587,7 @@ if label not in self._labels: self._labels.append(label) else: - raise TypeError, "label must be a string" + raise TypeError('label must be a string: %s' % type(label)) def remove_label(self, label): """Remove label from the list of labels on the message.""" @@ -1568,7 +1653,8 @@ elif isinstance(message, Message): pass else: - raise TypeError, "Cannot convert to specified type" + raise TypeError('Cannot convert to specified type: %s' % + type(message)) class MMDFMessage(_mboxMMDFMessage): @@ -1675,16 +1761,16 @@ fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: if e.errno == errno.EAGAIN: - raise ExternalClashError, \ - "lockf: lock unavailable: %s" % f.name + raise ExternalClashError('lockf: lock unavailable: %s' % + f.name) else: raise try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: if e.errno == errno.EWOULDBLOCK: - raise ExternalClashError, \ - "flock: lock unavailable: %s" % f.name + raise ExternalClashError('flock: lock unavailable: %s' % + f.name) else: raise if dotlock: @@ -1707,8 +1793,8 @@ except OSError, e: if e.errno == errno.EEXIST: os.remove(pre_lock) - raise ExternalClashError, 'dot lock unavailable: %s' % \ - f.name + raise ExternalClashError('dot lock unavailable: %s' % + f.name) else: raise except: Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- libmailbox.tex 16 Aug 2005 17:12:03 -0000 1.10 +++ libmailbox.tex 16 Aug 2005 23:38:11 -0000 1.11 @@ -720,6 +720,16 @@ current "info" is not modified. \end{methoddesc} +\begin{methoddesc}{get_date}{} +Return the delivery date of the message as a floating-point number representing +seconds since the epoch. +\end{methoddesc} + +\begin{methoddesc}{set_date}{date} +Set the delivery date of the message to \var{date}, a floating-point number +representing seconds since the epoch. +\end{methoddesc} + \begin{methoddesc}{get_info}{} Return a string containing the "info" for a message. This is useful for accessing and modifying "info" that is experimental (i.e., not a list of @@ -807,10 +817,11 @@ \begin{methoddesc}{set_from}{from_\optional{, time_=None}} Set the "From~" line to \var{from_}, which should be specified without a -leading "From~" or trailing newline. If \var{time_} is specified, it should be -a \class{struct_time} or a tuple suitable for passing to -\method{time.strftime()}; if \var{time_} is \code{True}, the result of -\method{time.gmtime()} is used. +leading "From~" or trailing newline. For convenience, \var{time_} may be +specified and will be formatted appropriately and appended to \var{from_}. If +\var{time_} is specified, it should be a \class{struct_time}, a tuple suitable +for passing to \method{time.strftime()}, or \code{True} (to use +\method{time.gmtime()}). \end{methoddesc} \begin{methoddesc}{get_flags}{} @@ -840,7 +851,9 @@ \end{methoddesc} When an \class{mboxMessage} instance is created based upon a -\class{MaildirMessage} instance, the following conversions take place: +\class{MaildirMessage} instance, a "From~" line is generated based upon the +\class{MaildirMessage} instance's delivery date, and the following conversions +take place: \begin{tableii}{l|l}{textrm} {Resulting state}{\class{MaildirMessage} state} @@ -1097,10 +1110,11 @@ \begin{methoddesc}{set_from}{from_\optional{, time_=None}} Set the "From~" line to \var{from_}, which should be specified without a -leading "From~" or trailing newline. If \var{time_} is specified, it should be -a \class{struct_time} or a tuple suitable for passing to -\method{time.strftime()}; if \var{time_} is \code{True}, the result of -\method{time.gmtime()} is used. +leading "From~" or trailing newline. For convenience, \var{time_} may be +specified to format a time appropriately and append it to \var{from_}. If +\var{time_} is specified, it should be a \class{struct_time}, a tuple suitable +for passing to \method{time.strftime()}, or \code{True} (to use +\method{time.gmtime()}). \end{methoddesc} \begin{methoddesc}{get_flags}{} @@ -1130,7 +1144,9 @@ \end{methoddesc} When an \class{MMDFMessage} instance is created based upon a -\class{MaildirMessage} instance, the following conversions take place: +\class{MaildirMessage} instance, a "From~" line is generated based upon the +\class{MaildirMessage} instance's delivery date, and the following conversions +take place: \begin{tableii}{l|l}{textrm} {Resulting state}{\class{MaildirMessage} state} Index: test_mailbox.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v retrieving revision 1.7 retrieving revision 1.8 diff -u -d -r1.7 -r1.8 --- test_mailbox.py 13 Aug 2005 22:41:33 -0000 1.7 +++ test_mailbox.py 16 Aug 2005 23:38:11 -0000 1.8 @@ -792,6 +792,13 @@ self.assert_(msg.get_subdir() == 'new') self._check_sample(msg) + def test_date(self): + # Use get_date() and set_date() + msg = mailbox.MaildirMessage(_sample_message) + self.assert_(abs(msg.get_date() - time.time()) < 60) + msg.set_date(0.0) + self.assert_(msg.get_date() == 0.0) + def test_info(self): # Use get_info() and set_info() msg = mailbox.MaildirMessage(_sample_message) @@ -995,10 +1002,12 @@ msg_maildir = mailbox.MaildirMessage(_sample_message) msg_maildir.set_flags('DFPRST') msg_maildir.set_subdir('cur') + date = msg_maildir.get_date() msg = mailbox.MaildirMessage(msg_maildir) self._check_sample(msg) self.assert_(msg.get_flags() == 'DFPRST') self.assert_(msg.get_subdir() == 'cur') + self.assert_(msg.get_date() == date) def test_maildir_to_mboxmmdf(self): # Convert MaildirMessage to mboxmessage and MMDFMessage @@ -1006,9 +1015,13 @@ ('T', 'D'), ('DFPRST', 'RDFA')) for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage): msg_maildir = mailbox.MaildirMessage(_sample_message) + msg_maildir.set_date(0.0) for setting, result in pairs: msg_maildir.set_flags(setting) - self.assert_(class_(msg_maildir).get_flags() == result) + msg = class_(msg_maildir) + self.assert_(msg.get_flags() == result) + self.assert_(msg.get_from() == 'MAILER-DAEMON %s' % + time.asctime(time.gmtime(0.0))) msg_maildir.set_subdir('cur') self.assert_(class_(msg_maildir).get_flags() == 'RODFA') @@ -1039,12 +1052,14 @@ # Convert mboxMessage and MMDFMessage to MaildirMessage for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage): msg_mboxMMDF = class_(_sample_message) + msg_mboxMMDF.set_from('foo at bar', time.gmtime(0.0)) pairs = (('R', 'S'), ('O', ''), ('D', 'T'), ('F', 'F'), ('A', 'R'), ('RODFA', 'FRST')) for setting, result in pairs: msg_mboxMMDF.set_flags(setting) - self.assert_(mailbox.MaildirMessage(msg_mboxMMDF).get_flags() \ - == result) + msg = mailbox.MaildirMessage(msg_mboxMMDF) + self.assert_(msg.get_flags() == result) + self.assert_(msg.get_date() == 0.0, msg.get_date()) msg_mboxMMDF.set_flags('O') self.assert_(mailbox.MaildirMessage(msg_mboxMMDF).get_subdir() == \ 'cur') From rhettinger at users.sourceforge.net Wed Aug 17 02:27:53 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Wed, 17 Aug 2005 02:27:53 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.52,1.53 Message-ID: <20050817002753.88F401E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10656 Modified Files: setobject.c Log Message: Results of a line-by-line comparison back to dictobject.c. * set_merge() cannot assume that the table doesn't resize during iteration. * convert some unnecessary tests to asserts -- they were necessary in dictobject.c because PyDict_Next() is a public function. The same is not true for set_next(). * re-arrange the order of functions to more closely match the order in dictobject.c. This makes it must easier to compare the two and ought to simplify any issues of maintaining both. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.52 retrieving revision 1.53 diff -u -d -r1.52 -r1.53 --- setobject.c 16 Aug 2005 10:44:15 -0000 1.52 +++ setobject.c 17 Aug 2005 00:27:42 -0000 1.53 @@ -492,8 +492,7 @@ assert (PyAnySet_Check(so)); i = *pos_ptr; - if (i < 0) - return 0; + assert(i >= 0); table = so->table; mask = so->mask; while (i <= mask && (table[i].key == NULL || table[i].key == dummy)) @@ -501,18 +500,86 @@ *pos_ptr = i+1; if (i > mask) return 0; - if (table[i].key) - *entry_ptr = &table[i]; + assert(table[i].key != NULL); + *entry_ptr = &table[i]; return 1; } +static void +set_dealloc(PySetObject *so) +{ + register setentry *entry; + int fill = so->fill; + PyObject_GC_UnTrack(so); + Py_TRASHCAN_SAFE_BEGIN(so) + if (so->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) so); + + for (entry = so->table; fill > 0; entry++) { + if (entry->key) { + --fill; + Py_DECREF(entry->key); + } + } + if (so->table != so->smalltable) + PyMem_DEL(so->table); + if (num_free_sets < MAXFREESETS && PyAnySet_CheckExact(so)) + free_sets[num_free_sets++] = so; + else + so->ob_type->tp_free(so); + Py_TRASHCAN_SAFE_END(so) +} + +static int +set_tp_print(PySetObject *so, FILE *fp, int flags) +{ + setentry *entry; + int pos=0; + char *emit = ""; /* No separator emitted on first pass */ + char *separator = ", "; + + fprintf(fp, "%s([", so->ob_type->tp_name); + while (set_next(so, &pos, &entry)) { + fputs(emit, fp); + emit = separator; + if (PyObject_Print(entry->key, fp, 0) != 0) + return -1; + } + fputs("])", fp); + return 0; +} + +static PyObject * +set_repr(PySetObject *so) +{ + PyObject *keys, *result, *listrepr; + + keys = PySequence_List((PyObject *)so); + if (keys == NULL) + return NULL; + listrepr = PyObject_Repr(keys); + Py_DECREF(keys); + if (listrepr == NULL) + return NULL; + + result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name, + PyString_AS_STRING(listrepr)); + Py_DECREF(listrepr); + return result; +} + +static int +set_len(PyObject *so) +{ + return ((PySetObject *)so)->used; +} + static int set_merge(PySetObject *so, PyObject *otherset) { PySetObject *other; register int i; - register setentry *entry, *othertable; - register int othermask; + register setentry *entry; assert (PyAnySet_Check(so)); assert (PyAnySet_Check(otherset)); @@ -529,10 +596,8 @@ if (set_table_resize(so, (so->used + other->used)*2) != 0) return -1; } - othermask = other->mask; - othertable = other->table; - for (i = 0; i <= othermask; i++) { - entry = &othertable[i]; + for (i = 0; i <= other->mask; i++) { + entry = &other->table[i]; if (entry->key != NULL && entry->key != dummy) { Py_INCREF(entry->key); @@ -569,9 +634,9 @@ static PyObject * set_pop(PySetObject *so) { - PyObject *key; - register setentry *entry; register int i = 0; + register setentry *entry; + PyObject *key; assert (PyAnySet_Check(so)); if (so->used == 0) { @@ -612,9 +677,49 @@ PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element."); static int -set_len(PyObject *so) +set_traverse(PySetObject *so, visitproc visit, void *arg) { - return ((PySetObject *)so)->used; + int pos = 0; + setentry *entry; + + while (set_next(so, &pos, &entry)) + Py_VISIT(entry->key); + return 0; +} + +static long +frozenset_hash(PyObject *self) +{ + PySetObject *so = (PySetObject *)self; + long h, hash = 1927868237L; + setentry *entry; + int pos = 0; + + if (so->hash != -1) + return so->hash; + + hash *= PySet_GET_SIZE(self) + 1; + while (set_next(so, &pos, &entry)) { + /* Work to increase the bit dispersion for closely spaced hash + values. The is important because some use cases have many + combinations of a small number of elements with nearby + hashes so that many distinct combinations collapse to only + a handful of distinct hash values. */ + h = entry->hash; + hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u; + } + hash = hash * 69069L + 907133923L; + if (hash == -1) + hash = 590923713L; + so->hash = hash; + return hash; +} + +static long +set_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "set objects are unhashable"); + return -1; } /***** Set iterator type ***********************************************/ @@ -660,6 +765,7 @@ static PySequenceMethods setiter_as_sequence = { (inquiry)setiter_len, /* sq_length */ + 0, /* sq_concat */ }; static PyObject *setiter_iternext(setiterobject *si) @@ -681,8 +787,7 @@ } i = si->si_pos; - if (i < 0) - goto fail; + assert(i>=0); entry = so->table; mask = so->mask; while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) @@ -874,44 +979,6 @@ return make_new_set(type, NULL); } -static void -set_dealloc(PySetObject *so) -{ - register setentry *entry; - int fill = so->fill; - - PyObject_GC_UnTrack(so); - Py_TRASHCAN_SAFE_BEGIN(so) - if (so->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) so); - - for (entry = so->table; fill > 0; entry++) { - if (entry->key) { - --fill; - Py_DECREF(entry->key); - } - } - if (so->table != so->smalltable) - PyMem_DEL(so->table); - - if (num_free_sets < MAXFREESETS && PyAnySet_CheckExact(so)) - free_sets[num_free_sets++] = so; - else - so->ob_type->tp_free(so); - Py_TRASHCAN_SAFE_END(so) -} - -static int -set_traverse(PySetObject *so, visitproc visit, void *arg) -{ - int pos = 0; - setentry *entry; - - while (set_next(so, &pos, &entry)) - Py_VISIT(entry->key); - return 0; -} - /* set_swap_bodies() switches the contents of any two sets by moving their internal data pointers and, if needed, copying the internal smalltables. Semantically equivalent to: @@ -1461,79 +1528,6 @@ return -1; } -static long -frozenset_hash(PyObject *self) -{ - PySetObject *so = (PySetObject *)self; - long h, hash = 1927868237L; - setentry *entry; - int pos = 0; - - if (so->hash != -1) - return so->hash; - - hash *= PySet_GET_SIZE(self) + 1; - while (set_next(so, &pos, &entry)) { - /* Work to increase the bit dispersion for closely spaced hash - values. The is important because some use cases have many - combinations of a small number of elements with nearby - hashes so that many distinct combinations collapse to only - a handful of distinct hash values. */ - h = entry->hash; - hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u; - } - hash = hash * 69069L + 907133923L; - if (hash == -1) - hash = 590923713L; - so->hash = hash; - return hash; -} - -static long -set_nohash(PyObject *self) -{ - PyErr_SetString(PyExc_TypeError, "set objects are unhashable"); - return -1; -} - -static PyObject * -set_repr(PySetObject *so) -{ - PyObject *keys, *result, *listrepr; - - keys = PySequence_List((PyObject *)so); - if (keys == NULL) - return NULL; - listrepr = PyObject_Repr(keys); - Py_DECREF(keys); - if (listrepr == NULL) - return NULL; - - result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name, - PyString_AS_STRING(listrepr)); - Py_DECREF(listrepr); - return result; -} - -static int -set_tp_print(PySetObject *so, FILE *fp, int flags) -{ - setentry *entry; - int pos=0; - char *emit = ""; /* No separator emitted on first pass */ - char *separator = ", "; - - fprintf(fp, "%s([", so->ob_type->tp_name); - while (set_next(so, &pos, &entry)) { - fputs(emit, fp); - emit = separator; - if (PyObject_Print(entry->key, fp, 0) != 0) - return -1; - } - fputs("])", fp); - return 0; -} - static PyObject * set_add(PySetObject *so, PyObject *key) { From rhettinger at users.sourceforge.net Wed Aug 17 04:19:46 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Wed, 17 Aug 2005 04:19:46 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects dictobject.c, 2.165, 2.166 Message-ID: <20050817021946.A24F71E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32522 Modified Files: dictobject.c Log Message: Fix nits. Index: dictobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/dictobject.c,v retrieving revision 2.165 retrieving revision 2.166 diff -u -d -r2.165 -r2.166 --- dictobject.c 14 May 2005 18:08:25 -0000 2.165 +++ dictobject.c 17 Aug 2005 02:19:36 -0000 2.166 @@ -113,7 +113,7 @@ */ /* Object used as dummy key to fill deleted entries */ -static PyObject *dummy; /* Initialized by first call to newdictobject() */ +static PyObject *dummy = NULL; /* Initialized by first call to newdictobject() */ /* forward declarations */ static dictentry * @@ -567,7 +567,7 @@ */ if (!(mp->ma_used > n_used && mp->ma_fill*3 >= (mp->ma_mask+1)*2)) return 0; - return dictresize(mp, mp->ma_used*(mp->ma_used>50000 ? 2 : 4)); + return dictresize(mp, (mp->ma_used>50000 ? mp->ma_used*2 : mp->ma_used*4)); } int From rhettinger at users.sourceforge.net Wed Aug 17 12:05:35 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Wed, 17 Aug 2005 12:05:35 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/api concrete.tex,1.63,1.64 Message-ID: <20050817100535.8CBD71E40C1@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/api In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7994 Modified Files: concrete.tex Log Message: Note that the PyNumber protocol can access most set methods directly. Index: concrete.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/api/concrete.tex,v retrieving revision 1.63 retrieving revision 1.64 diff -u -d -r1.63 -r1.64 --- concrete.tex 16 Aug 2005 10:44:12 -0000 1.63 +++ concrete.tex 17 Aug 2005 10:05:22 -0000 1.64 @@ -1144,7 +1144,7 @@ If \var{consumed} is \NULL{}, behaves like \cfunction{PyUnicode_DecodeUTF16()}. If \var{consumed} is not \NULL{}, \cfunction{PyUnicode_DecodeUTF16Stateful()} will not treat trailing incomplete - UTF-16 byte sequences (i.e. an odd number of bytes or a split surrogate pair) + UTF-16 byte sequences (such as an odd number of bytes or a split surrogate pair) as an error. Those bytes will not be decoded and the number of bytes that have been decoded will be stored in \var{consumed}. \versionadded{2.4} @@ -2908,11 +2908,18 @@ This section details the public API for \class{set} and \class{frozenset} objects. Any functionality not listed below is best accessed using the -abstract object API (including +either the abstract object protocol (including \cfunction{PyObject_CallMethod()}, \cfunction{PyObject_RichCompareBool()}, \cfunction{PyObject_Hash()}, \cfunction{PyObject_Repr()}, \cfunction{PyObject_IsTrue()}, \cfunction{PyObject_Print()}, and -\cfunction{PyObject_GetIter()}). +\cfunction{PyObject_GetIter()}) +or the abstract number protocol (including +\cfunction{PyNumber_Add()}, \cfunction{PyNumber_Subtract()}, +\cfunction{PyNumber_Or()}, \cfunction{PyNumber_Xor()}, +\cfunction{PyNumber_InplaceAdd()}, \cfunction{PyNumber_InplaceSubtract()}, +\cfunction{PyNumber_InplaceOr()}, and \cfunction{PyNumber_InplaceXor()}). +Note, the latter are also useful for copying (\code{c=s+s}) and clearing +(\code{s-=s}). \begin{ctypedesc}{PySetObject} This subtype of \ctype{PyObject} is used to hold the internal data for From rhettinger at users.sourceforge.net Wed Aug 17 14:23:59 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Wed, 17 Aug 2005 14:23:59 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Doc/api concrete.tex,1.64,1.65 Message-ID: <20050817122359.732AE1E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Doc/api In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1755 Modified Files: concrete.tex Log Message: Expand the API notes. Index: concrete.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/api/concrete.tex,v retrieving revision 1.64 retrieving revision 1.65 diff -u -d -r1.64 -r1.65 --- concrete.tex 17 Aug 2005 10:05:22 -0000 1.64 +++ concrete.tex 17 Aug 2005 12:23:45 -0000 1.65 @@ -2918,8 +2918,8 @@ \cfunction{PyNumber_Or()}, \cfunction{PyNumber_Xor()}, \cfunction{PyNumber_InplaceAdd()}, \cfunction{PyNumber_InplaceSubtract()}, \cfunction{PyNumber_InplaceOr()}, and \cfunction{PyNumber_InplaceXor()}). -Note, the latter are also useful for copying (\code{c=s+s}) and clearing -(\code{s-=s}). +Note, \cfunction{PyNumber_InplaceSubtract()} is also useful clearing +clearing a set (\code{s-=s}). \begin{ctypedesc}{PySetObject} This subtype of \ctype{PyObject} is used to hold the internal data for @@ -2929,7 +2929,7 @@ block of memory for medium and large sized sets (much like list storage). None of the fields of this structure should be considered public and are subject to change. All access should be done through the - documented API. + documented API rather than by manipulating the values in the structure. \end{ctypedesc} @@ -2967,7 +2967,8 @@ \var{iterable}. The \var{iterable} may be \NULL{} to create a new empty set. Returns the new set on success or \NULL{} on failure. Raises \exception{TypeError} if \var{iterable} is - not actually iterable. + not actually iterable. The constructor is also useful for + copying a set (\code{c=set(s)}). \end{cfuncdesc} \begin{cfuncdesc}{PyObject*}{PyFrozenSet_New}{PyObject *iterable} From rhettinger at users.sourceforge.net Wed Aug 17 14:27:31 2005 From: rhettinger at users.sourceforge.net (rhettinger@users.sourceforge.net) Date: Wed, 17 Aug 2005 14:27:31 +0200 (CEST) Subject: [Python-checkins] python/dist/src/Objects setobject.c,1.53,1.54 Message-ID: <20050817122731.5EEDB1E4002@bag.python.org> Update of /cvsroot/python/python/dist/src/Objects In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2350 Modified Files: setobject.c Log Message: Add shortcuts for a|a and a&a. Index: setobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/setobject.c,v retrieving revision 1.53 retrieving revision 1.54 diff -u -d -r1.53 -r1.54 --- setobject.c 17 Aug 2005 00:27:42 -0000 1.53 +++ setobject.c 17 Aug 2005 12:27:17 -0000 1.54 @@ -1065,6 +1065,8 @@ result = (PySetObject *)set_copy(so); if (result == NULL) return NULL; + if ((PyObject *)so == other) + return (PyObject *)result; if (set_update_internal(result, other) == -1) { Py_DECREF(result); return NULL; @@ -1106,10 +1108,8 @@ PySetObject *result; PyObject *key, *it, *tmp; - if ((PyObject *)so == other) { - Py_INCREF(other); - return other; - } + if ((PyObject *)so == other) + return set_copy(so); result = (PySetObject *)make_new_set(so->ob_type, NULL); if (result == NULL) @@ -2062,13 +2062,14 @@ Py_DECREF(f); /* Raise KeyError when popping from an empty set */ - set_clear_internal(so); + assert(PyNumber_InPlaceSubtract(ob, ob) == ob); + Py_DECREF(ob); assert(PySet_GET_SIZE(ob) == 0); assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError); - /* Restore the set from the copy and use the abstract API */ - assert(PyObject_CallMethod(ob, "update", "O", dup) == Py_None); - Py_DECREF(Py_None); + /* Restore the set from the copy using the PyNumber API */ + assert(PyNumber_InPlaceOr(ob, dup) == ob); + Py_DECREF(ob); /* Verify constructors accept NULL arguments */ f = PySet_New(NULL); From gregorykjohnson at users.sourceforge.net Thu Aug 18 19:52:09 2005 From: gregorykjohnson at users.sourceforge.net (gregorykjohnson@users.sourceforge.net) Date: Thu, 18 Aug 2005 19:52:09 +0200 (CEST) Subject: [Python-checkins] python/nondist/sandbox/mailbox libmailbox.tex, 1.11, 1.12 mailbox.py, 1.10, 1.11 Message-ID: <20050818175209.1F3DE1E4004@bag.python.org> Update of /cvsroot/python/python/nondist/sandbox/mailbox In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27089 Modified Files: libmailbox.tex mailbox.py Log Message: * Overhaul existing documentation. * Minor fixes and tweaks: * Fix identifier typos in locking code. * Mangle "From " lines for mbox, as claimed. * Do tilde expansion on mailbox paths. Index: libmailbox.tex =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- libmailbox.tex 16 Aug 2005 23:38:11 -0000 1.11 +++ libmailbox.tex 17 Aug 2005 20:32:36 -0000 1.12 @@ -7,33 +7,36 @@ \modulesynopsis{Manipulate mailboxes in various formats} -The \module{mailbox} module defines objects for accessing and manipulating -Maildir, mbox, MH, Babyl, and MMDF mailboxes and the messages they contain. -(These formats are commonly used on \UNIX{} to store email messages on disk.) +This module defines two classes, \class{Mailbox} and \class{Message}, for +accessing and manipulating on-disk mailboxes and the messages they contain. +\class{Mailbox} offers a dictionary-like mapping from keys to messages. +\class{Message} extends the \module{email.Message} module's \class{Message} +class with format-specific state and behavior. Supported mailbox formats are +Maildir, mbox, MH, Babyl, and MMDF. -\class{Mailbox} and \class{Message} are the two main classes offered by the -module. \class{Mailbox} offers a dictionary-like mapping from keys to -messages. \class{Message} extends the \module{email.Message} module's -\class{Message} class with format-specific state and behavior. Both -\class{Mailbox} and \class{Message} are extended by format-specific subclasses -and are generally not instantiated directly. +An example of using the module to sort mail: -A \class{Mailbox} instance's keys are immutable objects, issued by the -\class{Mailbox} instance, that are used to select messages from it. They -remain meaningful for the life of the \class{Mailbox} instance, even if the -mailbox is modified. Each time the value corresponding to a key is requested, -a new message representation is created. Typically, messages are representated -as \class{Message} instances, but a custom factory function may be specified. -A message representation is independent of the particular \class{Mailbox} -instance that created it (if any). For message modifications to be reflected -in a mailbox, the modified message representation must be explicitly assigned -back into the \class{Mailbox} instance's mapping. +\begin{verbatim} +>>> import mailbox +>>> inbox = mailbox.Maildir('~/Maildir', None) +>>> python_box = mailbox.Maildir('~/email/python-list', None) +>>> len(inbox) # Number of messages. +13 +>>> len(python_box) +818 +>>> for key, message in inbox.iteritems(): +... if 'python-list' in message['list-id']: +... python_box.add(message) # Add the message to python_box +... del inbox[key] # and remove it from inbox. +... +>>> len(inbox) +2 +>>> len(python_box) +829 +\end{verbatim} \begin{seealso} \seemodule{email}{Represent and manipulate messages.} - \seemodule{poplib}{Access mail via POP3.} - \seemodule{imaplib}{Access mail via IMAP4.} - \seemodule{smtplib}{Transfer mail via SMTP.} \end{seealso} \subsection{\class{Mailbox} objects} @@ -49,33 +52,29 @@ instance. A key continues to identify a message even if the corresponding message is modified, such as by replacing it with another message. Messages may be added to a \class{Mailbox} instance using the set-like method -\method{add()}. Because keys are issued by a \class{Mailbox} instance rather -than being chosen, the conventional method for adding an item to a mapping -(assigning a value to a new key) cannot be used. (\strong{Implementation note:} -\class{mbox}, \class{MH}, \class{Babyl}, and \class{MMDF} instances use -integers as keys, and \class{Maildir} instances use short strings.) +\method{add()} and removed using a \code{del} statement or the set-like methods +\method{remove()} and \method{discard()}. \class{Mailbox} interface semantics differ from dictionary semantics in some -ways. Each time a message is requested, a new message representation (typically -a \class{Message} instance) is generated based upon the current state of the -underlying message. The \class{Mailbox} instance does not reuse this -representation or keep a reference to it. Similarly, when a message -representation is assigned into a \class{Mailbox} instance's mapping, the -message representation's contents are copied into the mailbox. In neither case -is a reference to the message representation kept by the \class{Mailbox} +noteworthy ways. Each time a message is requested, a new representation +(typically a \class{Message} instance) is generated, based upon the current +state of the mailbox. Similarly, when a message is added to a \class{Mailbox} +instance, the provided message representation's contents are copied. In neither +case is a reference to the message representation kept by the \class{Mailbox} instance. The default \class{Mailbox} iterator iterates over message representations, not -keys as dictionaries do. Moreover, modification of a mailbox during iteration -is safe and well-defined. Messages added to the mailbox after an iterator is -created will not be seen by the iterator. Messages removed from the mailbox -before the iterator yields them will be silently skipped, though using a key -from an iterator may result in a \exception{KeyError} exception if the -corresponding message is subsequently removed. +keys as the default dictionary iterator does. Moreover, modification of a +mailbox during iteration is safe and well-defined. Messages added to the +mailbox after an iterator is created will not be seen by the iterator. Messages +removed from the mailbox before the iterator yields them will be silently +skipped, though using a key from an iterator may result in a +\exception{KeyError} exception if the corresponding message is subsequently +removed. \class{Mailbox} itself is intended to define an interface and to be inherited from by format-specific subclasses but is not intended to be instantiated. -Instead, directly instantiate a subclass. +Instead, you should instantiate a subclass. \class{Mailbox} instances have the following methods: @@ -97,16 +96,15 @@ Delete the message corresponding to \var{key} from the mailbox. If no such message exists, a \exception{KeyError} exception is raised if the -method was called as \method{remove()} or \method{__delitem__()} and no +method was called as \method{remove()} or \method{__delitem__()} but no exception is raised if the method was called as \method{discard()}. The behavior of \method{discard()} may be preferred if the underlying mailbox format supports concurrent modification by other processes. \end{methoddesc} \begin{methoddesc}{__setitem__}{key, message} -Replace the message corresponding to \var{key} with the message represented by -\var{message}. Raise a \exception{KeyError} exception if no message already -corresponds to \var{key}. +Replace the message corresponding to \var{key} with \var{message}. Raise a +\exception{KeyError} exception if no message already corresponds to \var{key}. As with \method{add()}, parameter \var{message} may be a \class{Message} instance, an \class{email.Message.Message} instance, a string, or a file-like @@ -129,9 +127,10 @@ Return an iterator over representations of all messages if called as \method{itervalues()} or \method{__iter__()} or return a list of such representations if called as \method{values()}. The messages are represented as -\class{Message} instances unless a custom message factory was specified when -the \class{Mailbox} instance was initialized. \note{The behavior of -\method{__iter__()} is unlike that of dictionaries, which iterate over keys.} +instances of the appropriate format-specific \class{Message} subclass unless a +custom message factory was specified when the \class{Mailbox} instance was +initialized. \note{The behavior of \method{__iter__()} is unlike that of +dictionaries, which iterate over keys.} \end{methoddesc} \begin{methoddesc}{iteritems}{} @@ -139,9 +138,9 @@ Return an iterator over (\var{key}, \var{message}) pairs, where \var{key} is a key and \var{message} is a message representation, if called as \method{iteritems()} or return a list of such pairs if called as -\method{items()}. The messages are represented as \class{Message} instances -unless a custom message factory was specified when the \class{Mailbox} instance -was initialized. +\method{items()}. The messages are represented as instances of the appropriate +format-specific \class{Message} subclass unless a custom message factory was +specified when the \class{Mailbox} instance was initialized. \end{methoddesc} \begin{methoddesc}{get}{key\optional{, default=None}} @@ -149,15 +148,16 @@ Return a representation of the message corresponding to \var{key}. If no such message exists, \var{default} is returned if the method was called as \method{get()} and a \exception{KeyError} exception is raised if the method was -called as \method{__getitem__()}. The message is represented as a -\class{Message} instance unless a custom message factory was specified when the -\class{Mailbox} instance was initialized. +called as \method{__getitem__()}. The message is represented as an instance of +the appropriate format-specific \class{Message} subclass unless a custom +message factory was specified when the \class{Mailbox} instance was +initialized. \end{methoddesc} \begin{methoddesc}{get_message}{key} -Return a \class{Message} representation of the message corresponding to -\var{key}, or raise a \exception{KeyError} exception if no such message -exists. +Return a representation of the message corresponding to \var{key} as an +instance of the appropriate format-specific \class{Message} subclass, or raise +a \exception{KeyError} exception if no such message exists. \end{methoddesc} \begin{methoddesc}{get_string}{key} @@ -171,13 +171,13 @@ should be closed once it is no longer needed. \note{Unlike other representations of messages, file-like representations are -not independent of the \class{Mailbox} instance that created them or of the -underlying mailbox, although their exact behavior is mailbox-format dependent. -More specific documentation is provided by each subclass.} +not necessarily independent of the \class{Mailbox} instance that created them +or of the underlying mailbox. More specific documentation is provided by each +subclass.} \end{methoddesc} \begin{methoddesc}{has_key}{key} -\methodline{__contains__}{} +\methodline{__contains__}{key} Return \code{True} if \var{key} corresponds to a message, \code{False} otherwise. \end{methoddesc} @@ -193,32 +193,36 @@ \begin{methoddesc}{pop}{key\optional{, default}} Return a representation of the message corresponding to \var{key} and delete the message. If no such message exists, return \var{default} if it was supplied -or else raise a \exception{KeyError} exception. The message is represented as a -\class{Message} instance unless a custom message factory was specified when the -\class{Mailbox} instance was initialized. +or else raise a \exception{KeyError} exception. The message is represented as +an instance of the appropriate format-specific \class{Message} subclass unless +a custom message factory was specified when the \class{Mailbox} instance was +initialized. \end{methoddesc} \begin{methoddesc}{popitem}{} Return an arbitrary (\var{key}, \var{message}) pair, where \var{key} is a key and \var{message} is a message representation, and delete the corresponding message. If the mailbox is empty, raise a \exception{KeyError} exception. The -message is represented as a \class{Message} instance unless a custom message -factory was specified when the \class{Mailbox} instance was initialized. +message is represented as an instance of the appropriate format-specific +\class{Message} subclass unless a custom message factory was specified when the +\class{Mailbox} instance was initialized. \end{methoddesc} \begin{methoddesc}{update}{arg} Parameter \var{arg} should be a \var{key}-to-\var{message} mapping or an iterable of (\var{key}, \var{message}) pairs. Updates the mailbox so that, for each given \var{key} and \var{message}, the message corresponding to \var{key} -is set to \var{message} as if by using \method{__setitem__()}. Each \var{key} -must already correspond to a message in the mailbox or a \exception{KeyError} -exception will be raised. \note{Unlike with dictionaries, keyword arguments -are not supported.} +is set to \var{message} as if by using \method{__setitem__()}. As with +\method{__setitem__()}, each \var{key} must already correspond to a message in +the mailbox or else a \exception{KeyError} exception will be raised, so in +general it is incorrect for \var{arg} to be a \class{Mailbox} instance. +\note{Unlike with dictionaries, keyword arguments are not supported.} \end{methoddesc} \begin{methoddesc}{flush}{} Write any pending changes to the filesystem. For some \class{Mailbox} -subclasses, changes are written immediately and this method does nothing. +subclasses, changes are always written immediately and this method does +nothing. \end{methoddesc} \begin{methoddesc}{lock}{} @@ -229,7 +233,7 @@ \end{methoddesc} \begin{methoddesc}{unlock}{} -Release the advisory lock on the mailbox, if any. +Release the lock on the mailbox, if any. \end{methoddesc} \begin{methoddesc}{close}{} @@ -244,30 +248,39 @@ \begin{classdesc}{Maildir}{dirname\optional{, factory=rfc822.Message\optional{, create=True}}} A subclass of \class{Mailbox} for mailboxes in Maildir format. Parameter -\var{factory} is a callable object that accepts a file-like object containing a -raw message as its parameter and returns a message representation. If -\var{factory} is \code{None}, \class{MaildirMessage} instances are used. -If \var{create} is \code{True}, the mailbox is created if it does not exist. +\var{factory} is a callable object that accepts a file-like message +representation and returns a custom representation. If \var{factory} is +\code{None}, \class{MaildirMessage} is used as the default message +representation. If \var{create} is \code{True}, the mailbox is created if it +does not exist. It is for historical reasons that \var{factory} defaults to \class{rfc822.Message} and that \var{dirname} is named as such rather than -\var{path}. +\var{path}. For a \class{Maildir} instance that behaves like instances of other +\class{Mailbox} subclasses, set \var{factory} to \code{None}. \end{classdesc} -Maildir is a directory-based mailbox format invented for the qmail MTA and now -widely supported by other programs. Messages in a Maildir mailbox are stored -in separate files within a shared directory structure. This structure allows -Maildir mailboxes to be accessed and modified by multiple unrelated programs -without data corruption, so file locking is unnecessary. +Maildir is a directory-based mailbox format invented for the qmail mail +transfer agent and now widely supported by other programs. Messages in a +Maildir mailbox are stored in separate files within a common directory +structure. This design allows Maildir mailboxes to be accessed and modified by +multiple unrelated programs without data corruption, so file locking is +unnecessary. -Folders, as introduced by the Courier MTA, are supported. Each folder is -itself a Maildir mailbox. Any subdirectory of the main Maildir directory is -considered a folder if \character{.} is the first character in its name. Folder -names are represented without the leading dot. For example, "Sent" would be the -name of a folder implemented with a directory called ".Sent" on the filesystem. -Folders should not be nested, i.e., a Maildir mailbox that is itself a folder -should not contain other folders. Instead, logical nesting may be indicated -using \character{.} to delimit levels---for example, "Archived.2005.07". +Maildir mailboxes contain three subdirectories, namely: \file{tmp}, \file{new}, +and \file{cur}. Messages are created momentarily in the \file{tmp} subdirectory +and then moved to the \file{new} subdirectory to finalize delivery. A mail user +agent may subsequently move the message to the \file{cur} subdirectory and +store information about the state of the message in a special "info" section +appended to its file name. + +Folders of the style introduced by the Courier mail transfer agent are also +supported. Any subdirectory of the main mailbox is considered a folder if +\character{.} is the first character in its name. Folder names are represented +by \class{Maildir} without the leading \character{.}. Each folder is itself a +Maildir mailbox but should not contain other folders. Instead, a logical +nesting is indicated using \character{.} to delimit levels, e.g., +"Archived.2005.07". \class{Maildir} instances have all of the methods of \class{Mailbox} in addition to the following: @@ -327,16 +340,15 @@ \end{methoddesc} \begin{methoddesc}{get_file}{key} -Depending upon the host platform, it may not be possible to use a -\class{Maildir} instance to modify or remove the underlying message while the -returned file remains open. +Depending upon the host platform, it may not be possible to modify or remove +the underlying message while the returned file remains open. \end{methoddesc} \begin{seealso} \seelink{http://www.qmail.org/man/man5/maildir.html}{maildir man page from qmail}{The original specification of the format.} \seelink{http://cr.yp.to/proto/maildir.html}{Using maildir format}{Notes - on Maildir by it's inventor. Includes an updated name-creation scheme and + on Maildir by its inventor. Includes an updated name-creation scheme and details on "info" semantics.} \seelink{http://www.courier-mta.org/?maildir.html}{maildir man page from Courier}{Another specification of the format. Describes a common extension @@ -348,15 +360,16 @@ \begin{classdesc}{mbox}{path\optional{, factory=None\optional{, create=True}}} A subclass of \class{Mailbox} for mailboxes in mbox format. Parameter -\var{factory} is a callable object that accepts a file-like object containing a -raw message as its parameter and returns a message representation. If -\var{factory} is \code{None}, \class{mboxMessage} instances are used. If -\var{create} is \code{True}, the mailbox is created if it does not exist. +\var{factory} is a callable object that accepts a file-like message +representation and returns a custom representation. If \var{factory} is +\code{None}, \class{mboxMessage} is used as the default message representation. +If \var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} The mbox format is the classic format for storing mail on \UNIX{} systems. All messages in an mbox mailbox are stored in a single file with the beginning of each message indicated by a line whose first five characters are "From~". + Several variations of the mbox format exist to address perceived shortcomings. In the interest of compatibility, \class{mbox} implements the original format, which is sometimes referred to as \dfn{mboxo}. This means that the @@ -369,7 +382,8 @@ remarks: \begin{methoddesc}{get_file}{key} -XXX +Using the file after calling \method{flush()} or \method{close()} on the +\class{mbox} instance may yield unpredictable results or raise an exception. \end{methoddesc} \begin{methoddesc}{lock}{} @@ -398,29 +412,24 @@ \begin{classdesc}{MH}{path\optional{, factory=None\optional{, create=True}}} A subclass of \class{Mailbox} for mailboxes in MH format. Parameter -\var{factory} is a callable object that accepts a file-like object containing a -raw message as its parameter and returns a message representation. If -\var{factory} is \code{None}, \class{MHMessage} instances are used. If -\var{create} is \code{True}, the mailbox is created if it does not exist. +\var{factory} is a callable object that accepts a file-like message +representation and returns a custom representation. If \var{factory} is +\code{None}, \class{MHMessage} is used as the default message representation. +If \var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} MH is a directory-based mailbox format invented for the MH Message Handling -System, a mail reading application. Each message in an MH mailbox resides in -its own file. An MH mailbox may contain other MH mailboxes (called -\dfn{folders}) in addition to messages. Folders may be nested indefinitely. - -MH mailboxes support \dfn{sequences}, which are named lists used to logically -group messages without moving them to sub-folders. Sequences are defined in a -file called \file{.mh_sequences} in each folder. Some mail reading programs -(although not the standard \program{mh} and \program{nmh} implementations) use -sequences to the same end as flags are used in other formats: unread messages -are added to the "unseen" sequence, replied-to messages are added to the -"replied" sequence, and important messages are added upon request to the -"flagged" sequence. +System, a mail user agent. Each message in an MH mailbox resides in its own +file. An MH mailbox may contain other MH mailboxes (called \dfn{folders}) in +addition to messages. Folders may be nested indefinitely. MH mailboxes also +support \dfn{sequences}, which are named lists used to logically group messages +without moving them to sub-folders. Sequences are defined in a file called +\file{.mh_sequences} in each folder. -\class{MH} manipulates MH mailboxes, but it does not attempt to emulate -\program{mh}. In particular, it does not access or modify \file{context} or -\file{.mh_profile} files. +The \class{MH} class manipulates MH mailboxes, but it does not attempt to +emulate all of \program{mh}'s behaviors. In particular, it does not access or +modify the \file{context} or \file{.mh_profile} files that are used by +\program{mh} to store its state and configuration. \class{MH} instances have all of the methods of \class{Mailbox} in addition to the following: @@ -453,14 +462,14 @@ \begin{methoddesc}{set_sequences}{sequences} Re-define the sequences that exist in the mailbox based upon \var{sequences}, a -dictionary of names mapped to key lists like returned by +dictionary of names mapped to key lists, like returned by \method{get_sequences()}. \end{methoddesc} \begin{methoddesc}{pack}{} -Renames messages in the mailbox as necessary to eliminate gaps in numbering. -Entries in the sequences list are updated correspondingly. Already-issued keys -are invalidated by this operation. +Rename messages in the mailbox as necessary to eliminate gaps in numbering. +Entries in the sequences list are updated correspondingly. \note{Already-issued +keys are invalidated by this operation and should not be subsequently used.} \end{methoddesc} Some \class{Mailbox} methods implemented by \class{MH} deserve special remarks: @@ -468,8 +477,8 @@ \begin{methoddesc}{remove}{key} \methodline{__delitem__}{key} \methodline{discard}{key} -These methods immediately delete the message. The \program{mh} convention of -marking a message for deletion by prepending a comma to its name is not used. +These methods immediately delete the message. The MH convention of marking a +message for deletion by prepending a comma to its name is not used. \end{methoddesc} \begin{methoddesc}{lock}{} @@ -482,7 +491,8 @@ \end{methoddesc} \begin{methoddesc}{get_file}{key} -XXX +Depending upon the host platform, it may not be possible to remove the +underlying message while the returned file remains open. \end{methoddesc} \begin{methoddesc}{flush}{} @@ -495,15 +505,6 @@ to \method{unlock()}. \end{methoddesc} -\class{MH} instances have all of the methods of \class{Mailbox} in addition to -the following: - -Some \class{Mailbox} methods implemented by \class{MH} deserve special remarks: - -\begin{methoddesc}{get_file}{key} -XXX -\end{methoddesc} - \begin{seealso} \seelink{http://www.nongnu.org/nmh/}{nmh - Message Handling System}{Home page of \program{nmh}, a modern version of the original \program{mh}.} @@ -517,30 +518,37 @@ \begin{classdesc}{Babyl}{path\optional{, factory=None\optional{, create=True}}} A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameter -\var{factory} is a callable object that accepts a file-like object containing a -raw message as its parameter and returns a message representation. If -\var{factory} is \code{None}, \class{BabylMessage} instances are used. If -\var{create} is \code{True}, the mailbox is created if it does not exist. +\var{factory} is a callable object that accepts a file-like message +representation and returns a custom representation. If \var{factory} is +\code{None}, \class{BabylMessage} is used as the default message +representation. If \var{create} is \code{True}, the mailbox is created if it +does not exist. \end{classdesc} -Babyl is a single-file mailbox format invented for the \program{Rmail} mail -reading application included with Emacs. A Babyl mailbox begins with an options -section that indicates the format of the mailbox and contains a list of -user-defined labels that appear in the mailbox. Messages follow the options -section. The beginning of a message is indicated by a line containing exactly -two control characters, namely Control-Underscore -(\character{\textbackslash037}) followed by Control-L -(\character{\textbackslash014}). The end of a message is indicated by the start -of the next message or, in the case of the last message, a line containing only -a Control-Underscore (\character{\textbackslash037}) character. Each message in -a Babyl mailbox has an accompanying list of \dfn{labels}, or short strings that -record extra information about the message. +Babyl is a single-file mailbox format invented for the Rmail mail user agent +included with Emacs. The beginning of a message is indicated by a line +containing exactly the two characters Control-Underscore +(\character{\textbackslash037}) and Control-L (\character{\textbackslash014}). +The end of a message is indicated by the start of the next message or, in the +case of the last message, a line containing only a Control-Underscore +(\character{\textbackslash037}) character. + +Messages in a Babyl mailbox have two sets of headers, original headers and +so-called visible headers. Visible headers are typically a subset of the +original headers that have been reformatted or abridged to be more attractive. +Each message in a Babyl mailbox also has an accompanying list of \dfn{labels}, +or short strings that record extra information about the message, and a list of +all user-defined labels found in the mailbox is kept in the Babyl options +section. \class{Babyl} instances have all of the methods of \class{Mailbox} in addition to the following: \begin{methoddesc}{get_labels}{} Return a list of the names of all user-defined labels used in the mailbox. +\note{The actual messages are inspected to determine which labels exist in the +mailbox rather than consulting the list of labels in the Babyl options section, +but the Babyl section is updated whenever the mailbox is modified.} \end{methoddesc} Some \class{Mailbox} methods implemented by \class{Babyl} deserve special @@ -548,9 +556,11 @@ \begin{methoddesc}{get_file}{key} In Babyl mailboxes, the headers of a message are not stored contiguously with -the body of the message. To generate a file-like representation, they are -copied together into a \class{StringIO} instance (from the \module{StringIO} -module), which may be used like a file. +the body of the message. To generate a file-like representation, the headers +and body are copied together into a \class{StringIO} instance (from the +\module{StringIO} module), which has an API identical to that of a file. As a +result, the file-like object is truly independent of the underlying mailbox but +does not save memory compared to a string representation. \end{methoddesc} \begin{methoddesc}{lock}{} @@ -571,25 +581,28 @@ \begin{classdesc}{MMDF}{path\optional{, factory=None\optional{, create=True}}} A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameter -\var{factory} is a callable object that accepts a file-like object containing a -raw message as its parameter and returns a message representation. If -\var{factory} is \code{None}, \class{MMDFMessage} instances are used. If -\var{create} is \code{True}, the mailbox is created if it does not exist. +\var{factory} is a callable object that accepts a file-like message +representation and returns a custom representation. If \var{factory} is +\code{None}, \class{MMDFMessage} is used as the default message representation. +If \var{create} is \code{True}, the mailbox is created if it does not exist. \end{classdesc} MMDF is a single-file mailbox format invented for the Multichannel Memorandum Distribution Facility, a mail transfer agent. Each message is in the same form as an mbox message but is bracketed before and after by lines containing four -Control-A characters. As with the mbox format, the beginning of each message -indicated by a line whose first five characters are "From~", but because of the -additional message separators it is unnecessary to transform "From~" to -">From~" when storing messages. +Control-A (\character{\textbackslash001}) characters. As with the mbox format, +the beginning of each message is indicated by a line whose first five +characters are "From~", but additional occurrences of "From~" are not +transformed to ">From~" when storing messages because the additional message +separator lines prevent mistaking such occurrences for the starts of subsequent +messages. Some \class{Mailbox} methods implemented by \class{MMDF} deserve special remarks: \begin{methoddesc}{get_file}{key} -XXX +Using the file after calling \method{flush()} or \method{close()} on the +\class{MMDF} instance may yield unpredictable results or raise an exception. \end{methoddesc} \begin{methoddesc}{lock}{} @@ -609,42 +622,33 @@ \subsection{\class{Message} objects} \label{mailbox-message-objects} -The \class{Message} class is an extension of a class of the same name from the -\module{email.Message} module. In addition, subclasses of \class{Message} -support mailbox-format-specific state and behavior. - \begin{classdesc}{Message}{\optional{message}} -A message with mailbox-format-specific properties. +A subclass of the \module{email.Message} module's \class{Message}. Subclasses +of \class{mailbox.Message} add mailbox-format-specific state and behavior. If \var{message} is omitted, the new instance is created in a default, empty state. If \var{message} is an \class{email.Message.Message} instance, its -contents are copied, converting any format-specific information insofar as -possible if \var{message} is a \class{Message} instance. If \var{message} is a -string or a file, it should contain an \rfc{2822}-compliant message, which is -read and parsed. +contents are copied; furthermore, any format-specific information is converted +insofar as possible if \var{message} is a \class{Message} instance. If +\var{message} is a string or a file, it should contain an \rfc{2822}-compliant +message, which is read and parsed. \end{classdesc} The format-specific state and behaviors offered by subclasses vary, but in -general it is only the properties that are not specific to a particular -mailbox that are supported (although presumably the properties are specific to -a particular mailbox format). For example, file offsets for single-file mailbox +general it is only the properties that are not specific to a particular mailbox +that are supported (although presumably the properties are specific to a +particular mailbox format). For example, file offsets for single-file mailbox formats and file names for directory-based mailbox formats are not retained, -but state such as whether a message has been read or marked as important by the -user is. - -In some situations, the time and memory overhead involved in generating -\class{Message} representations might not not justified. For such situations, -\class{Mailbox} instances also offer string and file-like representations, and -a custom message factory may be specified when a \class{Mailbox} instance is -initialized. There is no requirement to use the \class{Message} class to -represent messages from a mailbox. - -All of the \class{email.Message.Message} class's methods and members are -supported by \class{Message}, and subclasses of \class{Message} provide many -additional format-specific methods. Some functionality supported by all -\class{Message} subclasses is accessible via the following methods: +because they are only applicable to the original mailbox. But state such as +whether a message has been read by the user or marked as important is retained, +because it applies to the message itself. -XXX +There is no requirement that \class{Message} instances be used to represent +messages retrieved using \class{Mailbox} instances. In some situations, the +time and memory required to generate \class{Message} representations might not +not acceptable. For such situations, \class{Mailbox} instances also offer +string and file-like representations, and a custom message factory may be +specified when a \class{Mailbox} instance is initialized. \subsubsection{\class{MaildirMessage}} \label{mailbox-maildirmessage} @@ -654,26 +658,22 @@ has the same meaning as with the \class{Message} constructor. \end{classdesc} -Maildir messages are stored in individual files, in either the \file{new} or -the \file{cur} subdirectory of the Maildir. Messages are delivered to the -\file{new} subdirectory. Typically, a mail reading application moves messages -to the \file{cur} subdirectory after the user opens and closes the mailbox, -thereby recording that the messages are old whether or not they've actually -been read. - -Each message in \file{cur} has an "info" section added to its file name to -store information about its state. (Some mail readers may also add an "info" -section to messages in \file{new}.) The "info" section may take one of two -forms: it may contain "2," followed by a list of standardized flags (e.g., -"2,FR") or it may contain "1," followed by so-called experimental information. -Standard flags for Maildir messages are as follows: +Typically, a mail user agent application moves all of the messages in the +\file{new} subdirectory to the \file{cur} subdirectory after the first time the +user opens and closes the mailbox, recording that the messages are old whether +or not they've actually been read. Each message in \file{cur} has an "info" +section added to its file name to store information about its state. (Some mail +readers may also add an "info" section to messages in \file{new}.) The "info" +section may take one of two forms: it may contain "2," followed by a list of +standardized flags (e.g., "2,FR") or it may contain "1," followed by so-called +experimental information. Standard flags for Maildir messages are as follows: \begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation} \lineiii{D}{Draft}{Under composition} -\lineiii{F}{Flagged}{Marked by the user as important} -\lineiii{P}{Passed}{Forwarded, resent, or bounced by the user} -\lineiii{R}{Replied}{Responded to} -\lineiii{S}{Seen}{Read by the user} +\lineiii{F}{Flagged}{Marked as important} +\lineiii{P}{Passed}{Forwarded, resent, or bounced} +\lineiii{R}{Replied}{Replied to} +\lineiii{S}{Seen}{Read} \lineiii{T}{Trashed}{Marked for subsequent deletion} \end{tableiii} @@ -684,7 +684,7 @@ subdirectory) or "cur" (if the message should be stored in the \file{cur} subdirectory). \note{A message is typically moved from \file{new} to \file{cur} after its mailbox has been accessed, whether or not the message is has been -read. A message has been read if \code{"S" not in get_flags()}.} +read. A message has been read if \code{"S" not in get_flags()} is \code{True}.} \end{methoddesc} \begin{methoddesc}{set_subdir}{subdir} @@ -796,11 +796,11 @@ Conventional flags for mbox messages are as follows: \begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation} -\lineiii{R}{Read}{Read by the user} -\lineiii{O}{Old}{Previously detected by mail reader} +\lineiii{R}{Read}{Read} +\lineiii{O}{Old}{Previously detected by MUA} \lineiii{D}{Deleted}{Marked for subsequent deletion} -\lineiii{F}{Flagged}{Marked by the user as important} -\lineiii{A}{Answered}{Responded to} +\lineiii{F}{Flagged}{Marked as important} +\lineiii{A}{Answered}{Replied to} \end{tableiii} The "R" and "O" flags are stored in the \mailheader{Status} header, and the @@ -819,8 +819,8 @@ Set the "From~" line to \var{from_}, which should be specified without a leading "From~" or trailing newline. For convenience, \var{time_} may be specified and will be formatted appropriately and appended to \var{from_}. If -\var{time_} is specified, it should be a \class{struct_time}, a tuple suitable -for passing to \method{time.strftime()}, or \code{True} (to use +\var{time_} is specified, it should be a \class{struct_time} instance, a tuple +suitable for passing to \method{time.strftime()}, or \code{True} (to use \method{time.gmtime()}). \end{methoddesc} @@ -907,17 +907,14 @@ \end{classdesc} MH messages do not support marks or flags in the traditional sense, but they do -support sequenc