Thanks for the bugs! This is a great list of incompatibilities, I've gone ahead and opened a bug to track these.
I don't know that we'll get all of these fixed for the next release, but we'll certainly look into fixing what we can, and getting the rest fixed shortly after that. If some of these are more important to you than others let us know and we'll try to prioritize appropriately. Do you want to help develop Dynamic languages on CLR? (http://members.microsoft.com/careers/search/details.aspx?JobID=6D4754DE-11F0-45DF-8B78-DC1B43134038) -----Original Message----- From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of Žiga Seilnacht Sent: Monday, May 08, 2006 3:36 PM To: users@lists.ironpython.com Subject: [IronPython] Some incompatibilities with CPython Hello, while playing with the last version of IronPython (1.0.60420), I noticed the following bugs (the errors are given in comments, while the expected output is from a Python 2.4.3 and should be usable as doctest): - Classes (types) are missing a __class__ attribute: >>> class C(object): pass ... >>> C.__class__ # Raises AttributeError <type 'type'> - The metaclass is determined incorrectly: >>> class M_A(type): ... def __new__(meta, name, bases, dict): ... print 'metaclass:', meta.__name__, 'class:', name ... return type.__new__(meta, name, bases, dict) ... >>> class M_B(M_A): ... pass ... >>> class A: ... __metaclass__ = M_A ... metaclass: M_A class: A >>> class B: ... __metaclass__ = M_B ... metaclass: M_B class: B >>> class C(A, B): # prints only metaclass: M_A class: C ... pass ... metaclass: M_A class: C metaclass: M_B class: C >>> type(C).__name__ # this prints 'M_A' instead 'M_B' CPython uses the following rules to determine the right metaclass for C: - Since C does not have a __metaclass__ attribute, its type is determined from its bases. - A is the first base, therfore its type (M_A) is called; unfortunately this is not the way metaclasses are supposed to work; the most derived metaclass should be selected. - M_A's __new__ method calls type.__new__. - In type.__new__, it is determined that M_A is not the best type for class C; it should be actually M_B. - Since type.__new__ was called with wrong metaclass as the first argument, it calls the correct metaclass. - This calls M_B.__new__, which again calls type.__new__, but this time with M_B as the first argument, which is correct. type's __new__ method should start with the following: class Classic: pass ClassType = type(Classic) class Type(type): """ Metaclass that follows CPython's behaviour in "metaclass resolution". Code is taken from CPython's Objects/typeobject.c file. """ def __new__(meta, name, bases, dict): winner = meta for cls in bases: candidate = type(cls) if candidate is ClassType: continue if issubclass(winner, candidate): continue if issubclass(candidate, winner): winner = candidate continue raise TypeError("metaclass conflict: ...") if winner is not meta and winner.__new__ != Type.__new__: return winner.__new__(winner, name, bases, dict) return type.__new__(winner, name, bases, dict) - Coercion rules are incorrect: Reference manual http://docs.python.org/dev/ref/coercion-rules.html says: Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base's __rop__() method, the right operand's __rop__() method is tried before the left operand's __op__() method. >>> class A(object): ... def __add__(self, other): ... print self.__class__.__name__ ... __radd__ = __add__ ... >>> class B(A): ... def __add__(self, other): ... print self.__class__.__name__ ... __radd__ = __add__ ... >>> class C(A): pass ... >>> a = A() >>> b = B() >>> c = C() >>> a + b # prints A B >>> a + c A - Method's member 'im_inst' has the wrong name; it should be called 'im_self': >>> class C(object): ... def meth(self): pass ... >>> print C.meth.im_self # raises AttributeError None - instancemethod's constructor requires the third argument: >>> MethodType = type(C.meth) >>> MethodType(C.meth.im_func, 1) # raises TypeError <bound method ?.meth of 1> - __get__ methods require the second argument: >>> def f(self='spam'): ... print self ... >>> cm = classmethod(f) >>> sm = staticmethod(f) >>> prop = property(f) >>> f.__get__(1)() # raises TypeError 1 >>> cm.__get__(1)() # raises TypeError <type 'int'> >>> sm.__get__(1)() # raises TypeError spam >>> prop.__get__(1) # raises TypeError 1 - classmethods don't pass the class' metaclass to method constructor: >>> class D(object): ... @classmethod ... def classmeth(cls): pass ... >>> D.classmeth.im_class # prints <class '__main__.D'> <type 'type'> - builtin object super has a lot of limitations: - it is missing the __self_class__ member, and doesn't expose any of its members as attributes: >>> class A(object): ... def __init__(self, name): ... self.__name__ = name ... def meth(self): ... print self.__name__ ... classmeth = classmethod(meth) ... >>> class B(A): ... pass ... >>> b = B('b') >>> sup = super(B, b) >>> sup.__thisclass__.__name__ # raises AttributeError 'B' >>> sup.__self__.__name__ # raises AttributeError 'b' >>> sup.__self_class__.__name__ # raises AttributeError 'B' - it works only for a limited amount of descriptors, since it passes itself as the second argument to the __get__ methods, where it should pass __self_class__: >>> sup.classmeth() # raises AttributeError B - it doesn't work when both arguments are classes, since it allways stores the second argument in its __self__ member, even when it should store it in its __self_class__ member. As a consequence it passes the second class as the first argument to the __get__ methods: >>> sup = super(B, B) >>> sup.__thisclass__.__name__ # raises AttributeError 'B' >>> sup.__self__.__name__ # raises AttributeError 'B' >>> sup.__self_class__.__name__ # raises AttributeError 'B' >>> sup.meth() # raises AttributeError Traceback (most recent call last): ... TypeError: unbound method meth() must be called with B instance as first argument (got nothing instead) >>> sup.classmeth() # raises AttributeError B - unbound super objects don't act as descriptors: >>> class A(object): ... def meth(self): ... print "A.meth called" ... >>> class B(A): ... def meth(self): ... print "B.meth called" ... return self.__super.meth() ... >>> B._B__super = super(B) >>> b = B() >>> b.meth() # raises TypeError B.meth called A.meth called I hope this report helps, Ziga _______________________________________________ users mailing list users@lists.ironpython.com http://lists.ironpython.com/listinfo.cgi/users-ironpython.com _______________________________________________ users mailing list users@lists.ironpython.com http://lists.ironpython.com/listinfo.cgi/users-ironpython.com