Issue 1294232: Error in metaclass search order (original) (raw)

Created on 2005-09-18 01:07 by pwerneck, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (31)

msg26304 - (view)

Author: Pedro Werneck (pwerneck)

Date: 2005-09-18 01:07

In a simple class hierarchy, I have class A with metaclass M_A and class B, subclass of A, with metaclass M_B, subclass of M_A, as required.

A new class C, subclass of B, must have M_B or a subclass of it as subclass, or a TypeError, metaclass conflict exception is raised. The exception is raised in a multiple class hierarchy (diamond, trees, etc) or in a single class hierarchy when using a metaclass with no relation to M_A and M_B.

If M_A or type are used as C metaclass, the interpreter is ignoring dict['metaclass'], which has priority over B.class and using M_B, when it was supposed to raise TypeError, with the "metaclass conflict" error message.

More details in attached file.

msg26305 - (view)

Author: Rodrigo Dias Arruda Senra (rodsenra)

Date: 2005-09-18 23:04

Logged In: YES user_id=9057

I have discussed this at length with Pedro Werneck by email. I personally believe the best path to follow is to document that the entity specified in metaclass inside C class body, can be automagically replaced by the most specialized metaclass among the metaclasses associated to C ancestors. I think that will suffice for the meta-adventurous.

msg26306 - (view)

Author: Pedro Werneck (pwerneck)

Date: 2005-09-19 00:42

Logged In: YES user_id=696687

Yes. I think this confusion was caused because of the lack of documentation on this topic, especially on the case described here, which seems to break some rules.

Since the "Unifying types and classes" essay seems to be the most used Python document about this topic and, I suggest the first rule on determining a metaclass be changed from:

"If dict['metaclass'] exists, it is used."

To something like:

"If dict['metaclass'] exists and is equal to, or a subclass of, each of the metaclasses of the bases, it is used; if it exists and is a base class of any metaclass of the bases, the most specialized metaclass in the hierarchy is used; if it exists and doesn't satisfies any of these constraints, TypeError is raised."

msg113209 - (view)

Author: Terry J. Reedy (terry.reedy) * (Python committer)

Date: 2010-08-07 21:24

Is the 3.1/2 doc still lacking in this area?

msg113239 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2010-08-08 07:56

I think the situation is a bit more complicated. Here is the example described by Pedro Werneck (this is py3k, but its essentially the same in 2.x):

class M_A(type): ... def new(mcls, name, bases, ns): ... print('M_A.new') ... return super().new(mcls, name, bases, ns) ... class A(metaclass=M_A): pass ... M_A.new

class M_B(M_A): ... def new(mcls, name, bases, ns): ... print('M_B.new') ... return super().new(mcls, name, bases, ns) ... class B(A, metaclass=M_B): pass ... M_B.new M_A.new

class C(B, metaclass=M_A): pass ... M_A.new M_B.new M_A.new

As it is clear from the last three lines, the given metaclass (M_A) to C is not ignored. It is actually called (and produces the 'M_A.new' output line). Then M_A.new calls type.new (with super()). type.new then searches the bases of C, find the metaclass of B, M_B, and calls its new. M_B.new then prints the line 'M_B.new', then calls M_A.new again (with super()). This produces the last line, 'M_A.new'. So the trick is in type.new.

msg116316 - (view)

Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer)

Date: 2010-09-13 15:35

What also worries me is the difference between the "class" statement and the type() function.

class M_A(type): def new(mcls, name, bases, ns): print('M_A.new', mcls, name, bases) return super().new(mcls, name, bases, ns)

class M_B(M_A): def new(mcls, name, bases, ns): print('M_B.new', mcls, name, bases) return super().new(mcls, name, bases, ns)

class A(metaclass=M_A): pass class B(metaclass=M_B): pass

class C(A, B): pass D = type('D', (A, B), {})

The construction of C and D won't print the same messages.

msg123420 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2010-12-05 12:15

What also worries me is the difference between the "class" statement and the type() function.

I think the reason of this is that the class statement uses the build_class builtin function. This function determines the metaclass to use (by getting the metaclass of the first base class), and calls it. When one directly calls type, one doesn't call the metaclass (though type.new will later call the "real" metaclass).

An example:

class M_A(type): ... def new(mcls, name, bases, ns): ... print('M_A.new', mcls, name, bases) ... return super().new(mcls, name, bases, ns) ... class M_B(M_A): ... def new(mcls, name, bases, ns): ... print('M_B.new', mcls, name, bases) ... return super().new(mcls, name, bases, ns) ... class A(metaclass=M_A): pass ... M_A.new <class '__main__.M_A'> A ()

class B(metaclass=M_B): pass ... M_B.new <class '__main__.M_B'> B () M_A.new <class '__main__.M_B'> B ()

class C(A, B): pass ... M_A.new <class '__main__.M_A'> C (<class '__main__.A'>, <class '__main__.B'>) M_B.new <class '__main__.M_B'> C (<class '__main__.A'>, <class '__main__.B'>) M_A.new <class '__main__.M_B'> C (<class '__main__.A'>, <class '__main__.B'>)

Above build_class calls M_A (because that is the metaclass of the first base class, A). Then M_A calls type.new with super(), then type.new searches the "real" metaclass, M_B, and calls its new. Then M_B.new calls again M_A.new.

D = type('D', (A, B), {}) M_B.new <class '__main__.M_B'> D (<class '__main__.A'>, <class '__main__.B'>) M_A.new <class '__main__.M_B'> D (<class '__main__.A'>, <class '__main__.B'>)

Above type.call directly calls type.new, which determines the "real" metaclass, M_B, and calls it (which then class M_A):

class C2(B, A): pass ... M_B.new <class '__main__.M_B'> C2 (<class '__main__.B'>, <class '__main__.A'>) M_A.new <class '__main__.M_B'> C2 (<class '__main__.B'>, <class '__main__.A'>)

If we reverse the order of the base classes of C (as above for C2), build_class will use M_B as the metaclass.

D2 = M_B('D', (A, B), {}) M_B.new <class '__main__.M_B'> D (<class '__main__.A'>, <class '__main__.B'>) M_A.new <class '__main__.M_B'> D (<class '__main__.A'>, <class '__main__.B'>)

And of course, if we call directly the "real" metaclass, M_B (as above), we get the same result.

I used the expression "real metaclass" with the meaning "the class of the class we are currently creating":

C.class <class '__main__.M_B'> C2.class <class '__main__.M_B'> D.class <class '__main__.M_B'> D2.class <class '__main__.M_B'>

Summary: the problem seems to be, that build_class doesn't call the "real" metaclass, but the metaclass of the first base. (Note: I think this is approximately consistent with the documentation: "Otherwise, if there is at least one base class, its metaclass is used." But I don't know, if this is the desired behaviour.)

This behaviour of build_class can result in problems. For example, if the two metaclasses define prepare. In some cases build_class won't call the "real" metaclass' prepare, but the other's:

class M_A(type): ... def new(mcls, name, bases, ns): ... print('M_A.new', mcls, name, bases) ... return super().new(mcls, name, bases, ns) ... @classmethod ... def prepare(mcls, name, bases): ... print('M_A.prepare', mcls, name, bases) ... return {} ... class M_B(M_A): ... def new(mcls, name, bases, ns): ... print('M_B.new', mcls, name, bases, ns) ... return super().new(mcls, name, bases, ns) ... @classmethod ... def prepare(mcls, name, bases): ... print('M_B.prepare', mcls, name, bases) ... return {'M_B_was_here': True} ...

The prepare method of the two metaclass differs, M_B leaves a 'M_B_was_here' name in the namespace.

class A(metaclass=M_A): pass ... M_A.prepare <class '__main__.M_A'> A () M_A.new <class '__main__.M_A'> A ()

class B(metaclass=M_B): pass ... M_B.prepare <class '__main__.M_B'> B () M_B.new <class '__main__.M_B'> B () {'M_B_was_here': True, 'module': 'main'} M_A.new <class '__main__.M_B'> B ()

class C(A, B): pass ... M_A.prepare <class '__main__.M_A'> C (<class '__main__.A'>, <class '__main__.B'>) M_A.new <class '__main__.M_A'> C (<class '__main__.A'>, <class '__main__.B'>) M_B.new <class '__main__.M_B'> C (<class '__main__.A'>, <class '__main__.B'>) {'module': 'main'} M_A.new <class '__main__.M_B'> C (<class '__main__.A'>, <class '__main__.B'>)

'M_B_was_here' in C.dict False

build_class calls M_A.prepare, so the new class won't have a 'M_B_was_here' attribute (though its class is M_B).

class C2(B, A): pass ... M_B.prepare <class '__main__.M_B'> C2 (<class '__main__.B'>, <class '__main__.A'>) M_B.new <class '__main__.M_B'> C2 (<class '__main__.B'>, <class '__main__.A'>) {'M_B_was_here': True, 'module': 'main'} M_A.new <class '__main__.M_B'> C2 (<class '__main__.B'>, <class '__main__.A'>)

'M_B_was_here' in C2.dict True

I we reverse the order of the bases, M_B.prepare is called.

C.class <class '__main__.M_B'> C2.class <class '__main__.M_B'>

But the "real" metaclass of both classes is M_B.

(Sorry for the long post.)

msg127315 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-01-28 16:30

It seems, that this possible problem already came up when build_class got implemented, see . The second and third version of the patch at this issue ( and ) contains a comment: "XXX Should we do the "winner" calculation here?". But the next version, which contains the C implementation of build_class still uses simply the first base. The "winner calculation" probably refers to the algorithm determining the proper metaclass in lines 1950-1976 of typeobject.c (in type_new).

msg132876 - (view)

Author: Terry J. Reedy (terry.reedy) * (Python committer)

Date: 2011-04-03 19:58

I would make the same guess about 'winner calculation'. I am surprised that the class statement does not result in the same calculation (why else would type_new do it). Perhaps build_class (which I have not read) should either call type_new or a new function with the winner calculation factored out of type_new.

I suggest you repost your simplified example from the list here and use it as the basis of a test. Guido agreed that it shows a bug that might be backported to 3.2 (after discussion). If not also backported to 2.7, a separate 2.7 doc patch might be needed.

msg132880 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-03 20:44

The attached test case currently fails. I'll try to make a patch soon.

Perhaps build_class (which I have not read) should either call type_new or a new function with the winner calculation factored out of type_new.

Yeah, that's exactly what I'm planning to do (the factoring out).

msg132926 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-04 09:50

The attached patch seems to correct this issue. It contains the test attached yesterday, and it passes now.

I factored out the winner calculation from type_new to a new _PyType_CalculateWinner function, and type_new calls this. I've put the declaration of this function into object.h, so build_class can also call it, instead of using the metaclass of the first base. (Am I correct in thinking that the underscore prefix keeps it out of the public API?)

A slight problem may be, that in some cases this function will be called twice. But it is quite simple, so I don't think it matters much:

Without patch: $ ./python -m timeit -- "class A(type): pass class B: pass class C(metaclass=A): pass class D(B, C): pass " 10000 loops, best of 3: 371 usec per loop

With patch: $ ./python -m timeit -- "class A(type): pass class B: pass class C(metaclass=A): pass class D(B, C): pass " 10000 loops, best of 3: 381 usec per loop

(Note, that I generated the patch with hg extdiff, because the output of hg qdiff was much more unreadable than simple diff. I can provide an equivalent patch generated by hg if needed.)

msg134052 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-19 13:26

Yep, the leading underscore and the fact you added it to a block of code that is skipped when Py_LIMITED_API is defined makes it OK to include the prototype in object.h.

However, I would suggest _PyType_CalculateMetaclass as the name - CalculateWinner is a bit vague without the specific context of calculating the metaclass.

On a broader point, I think there is an issue that needs to be brought up on python-dev: in light of PEP 3115, "type(name, bases, ns)" is no longer an entirely safe way to create dynamic types, as it bypasses prepare methods. There should be an official way to access the full class building process, including correct invocation of prepare methods (likely by making build_class an official part of the language spec rather than a CPython implementation detail).

msg134054 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-19 13:37

Removed 2.7 from the affected versions - with neither build_class nor prepare in 2.x, there's no alternate metaclass calculation to go wrong.

This should definitely be fixed for the final 3.1 release and for 3.2 though. An updated patch against current 3.1 would be most convenient (hg should take care of the forward porting from that point).

msg134055 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-19 14:03

Sorry, my last review wasn't right and the patch is incorrect as it stands. After digging a little deeper, there is still a case that isn't covered correctly in build_class.

Specifically, the case where the most derived class has a declared metatype that is the parent of the metaclass that should be used.

This can be seen in type_new, where it does the "if (winner != metatype)" check. There is no equivalent check in build_class - instead, the explicitly declared metaclass always wins.

What needs to happen is that the call to _PyType_CalculateMetaclass in build_class should be moved out so that it is unconditional. The passed in metatype will either by the explicitly declared one (if it exists) or else PyType_Type. An additional test to pick up this case is also needed.

msg134097 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-19 20:43

Thanks for the review!

I've updated my patch:

However I noticed another problem: the declared metaclass (the object passed with the metaclass keyword in the class definition) according to PEP 3115 can be any callable object, not only a PyTypeObject. Problems:

  1. In this case, PyType_IsSubtype will be called on something that is not a PyTypeObject (I don't know if that's a big problem, currently it seems to work).

  2. The bigger problem: a simple construct, like:

class X(object, metaclass=func): pass

(where func is for example a function) won't work, because in _PyType_CalculateMetaclass it will detect, that func isn't a super- or subtype of object.class, and will raise an exception: "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".

My first idea to solve this problem is to ignore this case in build_class (check for a returned NULL, and call PyErr_Clear), and use the declared metaclass. (I don't know, if this can cause other problems, I haven't thought much about it yet.)

msg134116 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-20 01:07

This last point sounds like an error in PEP 3115 rather than an error in the implementation - as the exception says, the metaclass of a derived class must be the same as or a subclass of the metaclasses of all of its bases. Since that rule applies recursively, and all classes in 3.x implicitly inherit from object, it follows that all metaclasses must directly or indirectly inherit from type.

msg134173 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-20 19:26

That may be, but with my latest patch, this works (func is a function):

class X(metaclass=func): pass

But this blows up with a TypeError:

class X(object, metaclass=func): pass

Is this the desired behaviour? Or should we disallow non-class metaclasses in every case? (And what about backwards-compatibility?)

msg134178 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2011-04-20 20:22

class X(metaclass=func) should definitely continue to work; that is a long-standing (if relatively unknown, and very advanced) feature. In fact, metaclasses have their origins in this, via the "Don Beaudry hook" -- see http://python-history.blogspot.com/2009/04/metaclasses-and-extension-classes-aka.html .

IMO we should also keep class X(object, metaclass=func) working; it should just construct a tuple of bases (object,) and pass it to func.

I realize there is now a circular dependency: you need the metaclass computation in order to find the metaclass and you need the metaclass in order to find the prepare hook, but you need to call the prepare hook before you call the metaclass and hence before the metaclass computation is carried out. I'm not sure how to reconcile that but I think we should look harder rather than give up.

As to how PEP 3115 seems to imply that all classes derive from object, that only applies as long as their metaclass is (derived from) type. And that is in a sense a tautology since (dynamically) we only call something a class if its metaclass is (derived from) type. However the class statement does not necessarily create a class object! It creates whatever the metaclass creates, with suitable defaults: if there's no explicit metaclass, look at the class of the first given base; if no bases are given either, use object. But an explicit metaclass should win.

Maybe the metaclass computation (which should come up with the "most derived" metaclass if there are multiple bases or bases and a metaclass) is something that we can give a name as another attribute on the "initial" metaclass, and a suitable default? Let's say compute_metaclass(tuple_of_bases). The default could be the initial metaclass, and type could override it to do the correct computation (which computes the most derived metaclass, and raises an error if any of the bases has a metaclass that is not in the answer's ancestor). Then this computation could run before we get prepare (from the answer). When metaclass=func is given one could have prepare as a function attribute and even the compute_metacass could be overridden as a function attribute, so everything is still fully general.

[Sorry for blathering on. Not feeling great.]

msg134212 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-21 14:11

I think PEP 3115 is OK - the error about all metaclasses inheriting from type was a mistake on my part (I thought the ability to create non-type metaclasses went away along with old-style classes, but I was simply wrong on that point).

That got me curious as to how the explicit inheritance from object + explicit non-type metaclass case was working in 2.7, and it turns out it does share the same initial metaclass determination error as 3.x - it is just that build_class() is embedded in ceval.c rather than being published as a builtin.

So I have two conclusions:

msg134225 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2011-04-21 15:48

Thanks for diving deep! How much of this can we claim as a bug and how much as a feature?

msg134227 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-21 16:36

As near as I can tell, the only visible behavioural change with Daniel's patch (updated as per my last comment) will be that the two error cases noted above (i.e. when an explicit metaclass or the first parent type's metaclass is not the most derived metaclass) will now correctly invoke the real metaclass immediately, instead of first traversing up the chain to type(), which then jumps all the way back down to the most derived metaclass.

The "new" special case is actually just a matter of preserving the current behaviour in the one situation where that is the right thing to do.

msg134228 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-04-21 16:46

Commenting on the latest patch here, since the Rietveld integration isn't coping with the "hg extdiff" output (I would guess that the revision numbers in the pathnames are confusing the script that attempts to determine the base revision).

Aside from the note above about needing to restore the special case handling for non-type metaclasses, the only other recommendation I have is for the new metaclass invocation tests to add a second metaclass to the hierarchy and explicitly check the order of the new calls, as in the original examples above that invoked the wrong M_A/M_B/M_A sequence. Those tests will then also be applicable to Python 2.7 (which doesn't have prepare_).

msg134254 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-21 22:00

I'm attaching the updated patch. Changes:

The special case isn't just checking if the object is a class, but instead checking if "isinstance(obj, type) and issubclass(obj, type)", because I think only these are the objects we can do the metaclass calculation for. All other objects (including classes which are not "real" metaclasses (they do not derive from type) and functions) are used "as is", without any metaclass calculation. (It's late here, so it is quite possible that I've overlooked someting in this part.)

(I'm using "hg extdiff" because the normal hg diff results in a longer and harder to read patch, but if it's needed I will send a normal diff.)

msg134256 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-21 22:35

I've just realized, that my patch still breaks a case, that previously worked: when the bases are not classes.

This works in 3.2, but not with my patch:

class Foo: # not a subclass of type! ... def new(mcls, name='foo', bases=(), namespace={}): ... self = super().new(mcls) ... self.name = name ... return self ... foo1 = Foo('foo1') foo1.name 'foo1'

foo2 = Foo('foo2') foo2.name 'foo2'

class foo3(foo1, foo2):pass ... foo3 <__main__.Foo object at 0xb74aa96c> foo3.name 'foo3'

This raises a TypeError: "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases". In this case the type of all of its bases is the same (Foo), but that type is not a metaclass, but a regular class.

Right now I don't know if this is a real problem, or how to solve it.

msg134263 - (view)

Author: Daniel Urban (daniel.urban) * (Python triager)

Date: 2011-04-22 05:41

Okay, probably the check in my previous patch was too strict (sorry for the noise). I'm attaching an updated patch again. Now the algorithm in build_class is this:

  1. If an object is explicitly given with the metaclass keyword, that is the "starting metaclass".

  2. Else if there are bases, the type of the first base is the starting metaclass (note, that this possibly will be replaced later, but before the prepare call).

  3. Else the starting metaclass is type.

  4. If the starting metaclass is a class, do the metaclass calculation, so the starting metaclass may be replaced with its most derived descendant.

  5. Else we cannot do the metaclass calculation, so use the starting metaclass as is.

There are also tests for these cases, and the example in now works too.

msg144242 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-09-18 12:27

Looking at Daniel's updated patch is still on my to-do list, but I won't object if anyone else wants to take this forward (it will be at least a few weeks before I get to it).

msg146230 - (view)

Author: Roundup Robot (python-dev) (Python triager)

Date: 2011-10-23 12:37

New changeset c2a89b509be4 by Nick Coghlan in branch '3.2': Issue 1294232: Fix errors in metaclass calculation affecting some cases of metaclass inheritance. Patch by Daniel Urban. http://hg.python.org/cpython/rev/c2a89b509be4

New changeset c72063032a7a by Nick Coghlan in branch 'default': Merge issue 1294232 patch from 3.2 http://hg.python.org/cpython/rev/c72063032a7a

msg146232 - (view)

Author: Alyssa Coghlan (ncoghlan) * (Python committer)

Date: 2011-10-23 12:41

Fix has been applied to 3.x and hence will be in 3.3 and the next 3.2 release.

I have adjusted the issue metadata to reflect the fact 2,7 still exhibits the problem, but the patch requires significant work to account for the 3.x vs 2.x changes in class creation before it can be backported.

msg146547 - (view)

Author: Florent Xicluna (flox) * (Python committer)

Date: 2011-10-28 10:00

After changeset c72063032a7a I get this complain:

Python/bltinmodule.c: In function ‘builtin___build_class__’: Python/bltinmodule.c:43: warning: unused variable ‘nbases’

msg146558 - (view)

Author: Roundup Robot (python-dev) (Python triager)

Date: 2011-10-28 13:07

New changeset b9bb9340eb0c by Florent Xicluna in branch 'default': Merge 3.2 (linked to issue #1294232) http://hg.python.org/cpython/rev/b9bb9340eb0c

msg370429 - (view)

Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer)

Date: 2020-05-31 12:39

Python 2.7 is no longer supported.

History

Date

User

Action

Args

2022-04-11 14:56:13

admin

set

github: 42380

2020-05-31 12:39:15

serhiy.storchaka

set

status: open -> closed

nosy: + serhiy.storchaka
messages: +

resolution: fixed
stage: needs patch -> resolved

2012-12-06 02:07:04

bruno.dupuis

set

nosy: + bruno.dupuis

2012-05-07 12:05:37

ncoghlan

set

assignee: ncoghlan ->

2011-12-17 02:00:20

barry

set

nosy: + barry

2011-10-28 13:07:07

python-dev

set

messages: +

2011-10-28 10:00:10

flox

set

nosy: + flox
messages: +

2011-10-23 12:41:32

ncoghlan

set

stage: patch review -> needs patch
messages: +
components: - Documentation
versions: - Python 3.1, Python 3.2, Python 3.3

2011-10-23 12:37:17

python-dev

set

nosy: + python-dev
messages: +

2011-09-18 22:49:14

meador.inge

set

nosy: + meador.inge

stage: test needed -> patch review

2011-09-18 12:27:17

ncoghlan

set

messages: +

2011-07-09 20:51:58

eric.snow

set

nosy: + eric.snow

2011-06-10 07:04:35

ncoghlan

set

assignee: ncoghlan

2011-04-22 05:43:21

daniel.urban

set

files: + issue_1294232_4.patch

2011-04-22 05:42:57

daniel.urban

set

files: - issue11256_4.patch

2011-04-22 05:41:24

daniel.urban

set

files: + issue11256_4.patch

messages: +

2011-04-21 22:35:34

daniel.urban

set

messages: +

2011-04-21 22:00:36

daniel.urban

set

files: + issue_1294232_3.patch

messages: +

2011-04-21 16:46:51

ncoghlan

set

messages: +

2011-04-21 16:36:46

ncoghlan

set

messages: +

2011-04-21 15:48:21

gvanrossum

set

messages: +

2011-04-21 14:11:30

ncoghlan

set

messages: +
versions: + Python 2.7

2011-04-20 20:22:32

gvanrossum

set

messages: +

2011-04-20 19:26:38

daniel.urban

set

messages: +

2011-04-20 01:07:31

ncoghlan

set

messages: +

2011-04-19 20:43:30

daniel.urban

set

files: + issue_1294232_2.patch

messages: +

2011-04-19 14:03:33

ncoghlan

set

messages: +

2011-04-19 13:37:07

ncoghlan

set

messages: +
versions: - Python 2.7

2011-04-19 13:26:48

ncoghlan

set

assignee: docs@python -> (no value)

messages: +
nosy: + ncoghlan

2011-04-04 09:50:46

daniel.urban

set

files: + issue_1294232.patch

messages: +

2011-04-03 20:44:03

daniel.urban

set

files: + test_issue1294232.patch

type: enhancement -> behavior
components: + Interpreter Core

keywords: + patch
nosy: + gvanrossum, benjamin.peterson
messages: +

2011-04-03 20:07:25

terry.reedy

set

versions: + Python 3.3

2011-04-03 19:58:13

terry.reedy

set

messages: +

2011-01-28 16:30:52

daniel.urban

set

nosy:georg.brandl, terry.reedy, amaury.forgeotdarc, rodsenra, pwerneck, daniel.urban
messages: +

2010-12-05 12:15:56

daniel.urban

set

messages: +

2010-10-29 10:07:21

admin

set

assignee: georg.brandl -> docs@python

2010-09-13 15:35:11

amaury.forgeotdarc

set

nosy: + amaury.forgeotdarc
messages: +

2010-08-08 07:56:35

daniel.urban

set

nosy: + daniel.urban
messages: +

2010-08-07 21:24:34

terry.reedy

set

nosy: + terry.reedy

messages: +
versions: + Python 3.1, Python 2.7, Python 3.2, - Python 2.6

2009-10-22 16:08:38

georg.brandl

link

issue6927 superseder

2009-09-16 23:24:00

terry.reedy

set

assignee: georg.brandl

nosy: + georg.brandl

2009-03-20 22:30:47

ajaksu2

set

priority: normal -> low
stage: test needed
type: enhancement
versions: + Python 2.6, - Python 2.4

2005-09-18 01:07:12

pwerneck

create