Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c3647ac

Browse files
committed
Make subclasses of int, long, complex, float, and unicode perform type
conversion using the proper magic slot (e.g., __int__()). Also move conversion code out of PyNumber_*() functions in the C API into the nb_* function. Applied patch #1109424. Thanks Walter Doewald.
1 parent d7c795e commit c3647ac

10 files changed

Lines changed: 326 additions & 76 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,37 @@ def test_float(self):
545545
self.assertEqual(float(unicode(" 3.14 ")), 3.14)
546546
self.assertEqual(float(unicode(" \u0663.\u0661\u0664 ",'raw-unicode-escape')), 3.14)
547547

548+
def test_floatconversion(self):
549+
# Make sure that calls to __float__() work properly
550+
class Foo0:
551+
def __float__(self):
552+
return 42.
553+
554+
class Foo1(object):
555+
def __float__(self):
556+
return 42.
557+
558+
class Foo2(float):
559+
def __float__(self):
560+
return 42.
561+
562+
class Foo3(float):
563+
def __new__(cls, value=0.):
564+
return float.__new__(cls, 2*value)
565+
566+
def __float__(self):
567+
return self
568+
569+
class Foo4(float):
570+
def __float__(self):
571+
return 42
572+
573+
self.assertAlmostEqual(float(Foo0()), 42.)
574+
self.assertAlmostEqual(float(Foo1()), 42.)
575+
self.assertAlmostEqual(float(Foo2()), 42.)
576+
self.assertAlmostEqual(float(Foo3(21)), 42.)
577+
self.assertRaises(TypeError, float, Foo4(42))
578+
548579
def test_getattr(self):
549580
import sys
550581
self.assert_(getattr(sys, 'stdout') is sys.stdout)
@@ -650,6 +681,39 @@ def test_int(self):
650681

651682
self.assertEqual(int('0123', 0), 83)
652683

684+
def test_intconversion(self):
685+
# Test __int__()
686+
class Foo0:
687+
def __int__(self):
688+
return 42
689+
690+
class Foo1(object):
691+
def __int__(self):
692+
return 42
693+
694+
class Foo2(int):
695+
def __int__(self):
696+
return 42
697+
698+
class Foo3(int):
699+
def __int__(self):
700+
return self
701+
702+
class Foo4(int):
703+
def __int__(self):
704+
return 42L
705+
706+
class Foo5(int):
707+
def __int__(self):
708+
return 42.
709+
710+
self.assertEqual(int(Foo0()), 42)
711+
self.assertEqual(int(Foo1()), 42)
712+
self.assertEqual(int(Foo2()), 42)
713+
self.assertEqual(int(Foo3()), 0)
714+
self.assertEqual(int(Foo4()), 42L)
715+
self.assertRaises(TypeError, int, Foo5())
716+
653717
def test_intern(self):
654718
self.assertRaises(TypeError, intern)
655719
s = "never interned before"
@@ -810,6 +874,39 @@ def test_long(self):
810874
self.assertRaises(ValueError, long, '53', 40)
811875
self.assertRaises(TypeError, long, 1, 12)
812876

877+
def test_longconversion(self):
878+
# Test __long__()
879+
class Foo0:
880+
def __long__(self):
881+
return 42L
882+
883+
class Foo1(object):
884+
def __long__(self):
885+
return 42L
886+
887+
class Foo2(long):
888+
def __long__(self):
889+
return 42L
890+
891+
class Foo3(long):
892+
def __long__(self):
893+
return self
894+
895+
class Foo4(long):
896+
def __long__(self):
897+
return 42
898+
899+
class Foo5(long):
900+
def __long__(self):
901+
return 42.
902+
903+
self.assertEqual(long(Foo0()), 42L)
904+
self.assertEqual(long(Foo1()), 42L)
905+
self.assertEqual(long(Foo2()), 42L)
906+
self.assertEqual(long(Foo3()), 0)
907+
self.assertEqual(long(Foo4()), 42)
908+
self.assertRaises(TypeError, long, Foo5())
909+
813910
def test_map(self):
814911
self.assertEqual(
815912
map(None, 'hello world'),

Lib/test/test_complex.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,28 @@ def __float__(self):
273273
self.assertAlmostEqual(complex(real=float2(17.), imag=float2(23.)), 17+23j)
274274
self.assertRaises(TypeError, complex, float2(None))
275275

276+
class complex0(complex):
277+
"""Test usage of __complex__() when inheriting from 'complex'"""
278+
def __complex__(self):
279+
return 42j
280+
281+
class complex1(complex):
282+
"""Test usage of __complex__() with a __new__() method"""
283+
def __new__(self, value=0j):
284+
return complex.__new__(self, 2*value)
285+
def __complex__(self):
286+
return self
287+
288+
class complex2(complex):
289+
"""Make sure that __complex__() calls fail if anything other than a
290+
complex is returned"""
291+
def __complex__(self):
292+
return None
293+
294+
self.assertAlmostEqual(complex(complex0(1j)), 42j)
295+
self.assertAlmostEqual(complex(complex1(1j)), 2j)
296+
self.assertRaises(TypeError, complex, complex2(1j))
297+
276298
def test_hash(self):
277299
for x in xrange(-30, 30):
278300
self.assertEqual(hash(x), hash(complex(x, 0)))

Lib/test/test_str.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,69 @@ def test_formatting(self):
1919
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
2020
self.assertRaises(OverflowError, '%c'.__mod__, 0x1234)
2121

22+
def test_conversion(self):
23+
# Make sure __str__() behaves properly
24+
class Foo0:
25+
def __unicode__(self):
26+
return u"foo"
27+
28+
class Foo1:
29+
def __str__(self):
30+
return "foo"
31+
32+
class Foo2(object):
33+
def __str__(self):
34+
return "foo"
35+
36+
class Foo3(object):
37+
def __str__(self):
38+
return u"foo"
39+
40+
class Foo4(unicode):
41+
def __str__(self):
42+
return u"foo"
43+
44+
class Foo5(str):
45+
def __str__(self):
46+
return u"foo"
47+
48+
class Foo6(str):
49+
def __str__(self):
50+
return "foos"
51+
52+
def __unicode__(self):
53+
return u"foou"
54+
55+
class Foo7(unicode):
56+
def __str__(self):
57+
return "foos"
58+
def __unicode__(self):
59+
return u"foou"
60+
61+
class Foo8(str):
62+
def __new__(cls, content=""):
63+
return str.__new__(cls, 2*content)
64+
def __str__(self):
65+
return self
66+
67+
class Foo9(str):
68+
def __str__(self):
69+
return "string"
70+
def __unicode__(self):
71+
return "not unicode"
72+
73+
self.assert_(str(Foo0()).startswith("<")) # this is different from __unicode__
74+
self.assertEqual(str(Foo1()), "foo")
75+
self.assertEqual(str(Foo2()), "foo")
76+
self.assertEqual(str(Foo3()), "foo")
77+
self.assertEqual(str(Foo4("bar")), "foo")
78+
self.assertEqual(str(Foo5("bar")), "foo")
79+
self.assertEqual(str(Foo6("bar")), "foos")
80+
self.assertEqual(str(Foo7("bar")), "foos")
81+
self.assertEqual(str(Foo8("foo")), "foofoo")
82+
self.assertEqual(str(Foo9("foo")), "string")
83+
self.assertEqual(unicode(Foo9("foo")), u"not unicode")
84+
2285
def test_main():
2386
test_support.run_unittest(StrTest)
2487

Lib/test/test_unicode.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ def test_formatting(self):
389389
self.assertEqual('%i%s %*.*s' % (10, 3, 5, 3, u'abc',), u'103 abc')
390390
self.assertEqual('%c' % u'a', u'a')
391391

392-
393392
def test_constructor(self):
394393
# unicode(obj) tests (this maps to PyObject_Unicode() at C level)
395394

@@ -725,6 +724,69 @@ def test_ucs4(self):
725724
y = x.encode("raw-unicode-escape").decode("raw-unicode-escape")
726725
self.assertEqual(x, y)
727726

727+
def test_conversion(self):
728+
# Make sure __unicode__() works properly
729+
class Foo0:
730+
def __str__(self):
731+
return "foo"
732+
733+
class Foo1:
734+
def __unicode__(self):
735+
return u"foo"
736+
737+
class Foo2(object):
738+
def __unicode__(self):
739+
return u"foo"
740+
741+
class Foo3(object):
742+
def __unicode__(self):
743+
return "foo"
744+
745+
class Foo4(str):
746+
def __unicode__(self):
747+
return "foo"
748+
749+
class Foo5(unicode):
750+
def __unicode__(self):
751+
return "foo"
752+
753+
class Foo6(str):
754+
def __str__(self):
755+
return "foos"
756+
757+
def __unicode__(self):
758+
return u"foou"
759+
760+
class Foo7(unicode):
761+
def __str__(self):
762+
return "foos"
763+
def __unicode__(self):
764+
return u"foou"
765+
766+
class Foo8(unicode):
767+
def __new__(cls, content=""):
768+
return unicode.__new__(cls, 2*content)
769+
def __unicode__(self):
770+
return self
771+
772+
class Foo9(unicode):
773+
def __str__(self):
774+
return "string"
775+
def __unicode__(self):
776+
return "not unicode"
777+
778+
self.assertEqual(unicode(Foo0()), u"foo")
779+
self.assertEqual(unicode(Foo1()), u"foo")
780+
self.assertEqual(unicode(Foo2()), u"foo")
781+
self.assertEqual(unicode(Foo3()), u"foo")
782+
self.assertEqual(unicode(Foo4("bar")), u"foo")
783+
self.assertEqual(unicode(Foo5("bar")), u"foo")
784+
self.assertEqual(unicode(Foo6("bar")), u"foou")
785+
self.assertEqual(unicode(Foo7("bar")), u"foou")
786+
self.assertEqual(unicode(Foo8("foo")), u"foofoo")
787+
self.assertEqual(str(Foo9("foo")), "string")
788+
self.assertEqual(unicode(Foo9("foo")), u"not unicode")
789+
728790
def test_main():
729791
test_support.run_unittest(UnicodeTest)
730792

Misc/NEWS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ What's New in Python 2.5 alpha 1?
1212
Core and builtins
1313
-----------------
1414

15+
- patch #1109424: int, long, float, complex, and unicode now check for the
16+
proper magic slot for type conversions when subclassed. Previously the
17+
magic slot was ignored during conversion. Semantics now match the way
18+
subclasses of str always behaved. int/long/float, conversion of an instance
19+
to the base class has been moved the prroper nb_* magic slot and out of
20+
PyNumber_*().
21+
Thanks Walter D�rwald.
22+
1523
- Descriptors defined in C with a PyGetSetDef structure, where the setter is
1624
NULL, now raise an AttributeError when attempting to set or delete the
1725
attribute. Previously a TypeError was raised, but this was inconsistent

0 commit comments

Comments
 (0)