Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
bdbad71b
Commit
bdbad71b
authored
Jun 02, 2019
by
Serhiy Storchaka
Committed by
GitHub
Jun 02, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-20092. Use __index__ in constructors of int, float and complex. (GH-13108)
parent
1a4d9ffa
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
181 additions
and
23 deletions
+181
-23
Doc/c-api/complex.rst
Doc/c-api/complex.rst
+7
-1
Doc/c-api/float.rst
Doc/c-api/float.rst
+4
-0
Doc/library/functions.rst
Doc/library/functions.rst
+19
-2
Doc/reference/datamodel.rst
Doc/reference/datamodel.rst
+3
-5
Doc/whatsnew/3.8.rst
Doc/whatsnew/3.8.rst
+10
-1
Lib/test/test_cmath.py
Lib/test/test_cmath.py
+3
-4
Lib/test/test_complex.py
Lib/test/test_complex.py
+18
-0
Lib/test/test_float.py
Lib/test/test_float.py
+15
-0
Lib/test/test_getargs2.py
Lib/test/test_getargs2.py
+6
-0
Lib/test/test_index.py
Lib/test/test_index.py
+1
-1
Lib/test/test_int.py
Lib/test/test_int.py
+60
-6
Misc/NEWS.d/next/Core and Builtins/2019-05-31-11-55-49.bpo-20092.KIMjBW.rst
...ore and Builtins/2019-05-31-11-55-49.bpo-20092.KIMjBW.rst
+4
-0
Objects/abstract.c
Objects/abstract.c
+19
-0
Objects/complexobject.c
Objects/complexobject.c
+3
-3
Objects/floatobject.c
Objects/floatobject.c
+9
-0
No files found.
Doc/c-api/complex.rst
View file @
bdbad71b
...
...
@@ -129,4 +129,10 @@ Complex Numbers as Python Objects
If *op* is not a Python complex number object but has a :meth:`__complex__`
method, this method will first be called to convert *op* to a Python complex
number object. Upon failure, this method returns ``-1.0`` as a real value.
number object. If ``__complex__()`` is not defined then it falls back to
:meth:`__float__`. If ``__float__()`` is not defined then it falls back
to :meth:`__index__`. Upon failure, this method returns ``-1.0`` as a real
value.
.. versionchanged:: 3.8
Use :meth:`__index__` if available.
Doc/c-api/float.rst
View file @
bdbad71b
...
...
@@ -47,9 +47,13 @@ Floating Point Objects
Return a C :c:type:`double` representation of the contents of *pyfloat*. If
*pyfloat* is not a Python floating point object but has a :meth:`__float__`
method, this method will first be called to convert *pyfloat* into a float.
If ``__float__()`` is not defined then it falls back to :meth:`__index__`.
This method returns ``-1.0`` upon failure, so one should call
:c:func:`PyErr_Occurred` to check for errors.
.. versionchanged:: 3.8
Use :meth:`__index__` if available.
.. c:function:: double PyFloat_AS_DOUBLE(PyObject *pyfloat)
...
...
Doc/library/functions.rst
View file @
bdbad71b
...
...
@@ -318,6 +318,11 @@ are always available. They are listed here in alphabetical order.
:class:`int` and :class:`float`. If both arguments are omitted, returns
``0j``.
For a general Python object ``x``, ``complex(x)`` delegates to
``x.__complex__()``. If ``__complex__()`` is not defined then it falls back
to :meth:`__float__`. If ``__float__()`` is not defined then it falls back
to :meth:`__index__`.
.. note::
When converting from a string, the string must not contain whitespace
...
...
@@ -330,6 +335,10 @@ are always available. They are listed here in alphabetical order.
.. versionchanged:: 3.6
Grouping digits with underscores as in code literals is allowed.
.. versionchanged:: 3.8
Falls back to :meth:`__index__` if :meth:`__complex__` and
:meth:`__float__` are not defined.
.. function:: delattr(object, name)
...
...
@@ -584,7 +593,8 @@ are always available. They are listed here in alphabetical order.
float, an :exc:`OverflowError` will be raised.
For a general Python object ``x``, ``float(x)`` delegates to
``x.__float__()``.
``x.__float__()``. If ``__float__()`` is not defined then it falls back
to :meth:`__index__`.
If no argument is given, ``0.0`` is returned.
...
...
@@ -609,6 +619,9 @@ are always available. They are listed here in alphabetical order.
.. versionchanged:: 3.7
*x* is now a positional-only parameter.
.. versionchanged:: 3.8
Falls back to :meth:`__index__` if :meth:`__float__` is not defined.
.. index::
single: __format__
...
...
@@ -780,7 +793,8 @@ are always available. They are listed here in alphabetical order.
Return an integer object constructed from a number or string *x*, or return
``0`` if no arguments are given. If *x* defines :meth:`__int__`,
``int(x)`` returns ``x.__int__()``. If *x* defines :meth:`__trunc__`,
``int(x)`` returns ``x.__int__()``. If *x* defines :meth:`__index__`,
it returns ``x.__index__()``. If *x* defines :meth:`__trunc__`,
it returns ``x.__trunc__()``.
For floating point numbers, this truncates towards zero.
...
...
@@ -812,6 +826,9 @@ are always available. They are listed here in alphabetical order.
.. versionchanged:: 3.7
*x* is now a positional-only parameter.
.. versionchanged:: 3.8
Falls back to :meth:`__index__` if :meth:`__int__` is not defined.
.. function:: isinstance(object, classinfo)
...
...
Doc/reference/datamodel.rst
View file @
bdbad71b
...
...
@@ -2394,11 +2394,9 @@ left undefined.
functions). Presence of this method indicates that the numeric object is
an integer type. Must return an integer.
.. note::
In order to have a coherent integer type class, when :meth:`__index__` is
defined :meth:`__int__` should also be defined, and both should return
the same value.
If :meth:`__int__`, :meth:`__float__` and :meth:`__complex__` are not
defined then corresponding built-in functions :func:`int`, :func:`float`
and :func:`complex` fall back to :meth:`__index__`.
.. method:: object.__round__(self, [,ndigits])
...
...
Doc/whatsnew/3.8.rst
View file @
bdbad71b
...
...
@@ -250,6 +250,12 @@ Other Language Changes
compatible with the existing :meth:`float.as_integer_ratio` method.
(Contributed by Lisa Roach in :issue:`33073`.)
* Constructors of :class:`int`, :class:`float` and :class:`complex` will now
use the :meth:`~object.__index__` special method, if available and the
corresponding method :meth:`~object.__int__`, :meth:`~object.__float__`
or :meth:`~object.__complex__` is not available.
(Contributed by Serhiy Storchaka in :issue:`20092`.)
* Added support of ``\N{name}`` escapes in :mod:`regular expressions <re>`.
(Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.)
...
...
@@ -868,7 +874,10 @@ Build and C API Changes
``__index__()`` method (like :class:`~decimal.Decimal` and
:class:`~fractions.Fraction`). :c:func:`PyNumber_Check` will now return
``1`` for objects implementing ``__index__()``.
(Contributed by Serhiy Storchaka in :issue:`36048`.)
:c:func:`PyNumber_Long`, :c:func:`PyNumber_Float` and
:c:func:`PyFloat_AsDouble` also now use the ``__index__()`` method if
available.
(Contributed by Serhiy Storchaka in :issue:`36048` and :issue:`20092`.)
* Heap-allocated type objects will now increase their reference count
in :c:func:`PyObject_Init` (and its parallel macro ``PyObject_INIT``)
...
...
Lib/test/test_cmath.py
View file @
bdbad71b
...
...
@@ -220,12 +220,11 @@ class CMathTests(unittest.TestCase):
pass
class
NeitherComplexNorFloatOS
:
pass
class
MyInt
(
object
)
:
class
Index
:
def
__int__
(
self
):
return
2
def
__index__
(
self
):
return
2
class
MyInt
OS
:
class
MyInt
:
def
__int__
(
self
):
return
2
def
__index__
(
self
):
return
2
# other possible combinations of __float__ and __complex__
# that should work
...
...
@@ -255,6 +254,7 @@ class CMathTests(unittest.TestCase):
self
.
assertEqual
(
f
(
FloatAndComplexOS
()),
f
(
cx_arg
))
self
.
assertEqual
(
f
(
JustFloat
()),
f
(
flt_arg
))
self
.
assertEqual
(
f
(
JustFloatOS
()),
f
(
flt_arg
))
self
.
assertEqual
(
f
(
Index
()),
f
(
int
(
Index
())))
# TypeError should be raised for classes not providing
# either __complex__ or __float__, even if they provide
# __int__ or __index__. An old-style class
...
...
@@ -263,7 +263,6 @@ class CMathTests(unittest.TestCase):
self
.
assertRaises
(
TypeError
,
f
,
NeitherComplexNorFloat
())
self
.
assertRaises
(
TypeError
,
f
,
MyInt
())
self
.
assertRaises
(
Exception
,
f
,
NeitherComplexNorFloatOS
())
self
.
assertRaises
(
Exception
,
f
,
MyIntOS
())
# non-complex return value from __complex__ -> TypeError
for
bad_complex
in
non_complexes
:
self
.
assertRaises
(
TypeError
,
f
,
MyComplex
(
bad_complex
))
...
...
Lib/test/test_complex.py
View file @
bdbad71b
...
...
@@ -368,6 +368,24 @@ class ComplexTest(unittest.TestCase):
self
.
assertAlmostEqual
(
complex
(
real
=
float2
(
17.
),
imag
=
float2
(
23.
)),
17
+
23j
)
self
.
assertRaises
(
TypeError
,
complex
,
float2
(
None
))
class
MyIndex
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__index__
(
self
):
return
self
.
value
self
.
assertAlmostEqual
(
complex
(
MyIndex
(
42
)),
42.0
+
0.0j
)
self
.
assertAlmostEqual
(
complex
(
123
,
MyIndex
(
42
)),
123.0
+
42.0j
)
self
.
assertRaises
(
OverflowError
,
complex
,
MyIndex
(
2
**
2000
))
self
.
assertRaises
(
OverflowError
,
complex
,
123
,
MyIndex
(
2
**
2000
))
class
MyInt
:
def
__int__
(
self
):
return
42
self
.
assertRaises
(
TypeError
,
complex
,
MyInt
())
self
.
assertRaises
(
TypeError
,
complex
,
123
,
MyInt
())
class
complex0
(
complex
):
"""Test usage of __complex__() when inheriting from 'complex'"""
def
__complex__
(
self
):
...
...
Lib/test/test_float.py
View file @
bdbad71b
...
...
@@ -223,6 +223,21 @@ class GeneralFloatCases(unittest.TestCase):
with
self
.
assertWarns
(
DeprecationWarning
):
self
.
assertIs
(
type
(
FloatSubclass
(
F
())),
FloatSubclass
)
class
MyIndex
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__index__
(
self
):
return
self
.
value
self
.
assertEqual
(
float
(
MyIndex
(
42
)),
42.0
)
self
.
assertRaises
(
OverflowError
,
float
,
MyIndex
(
2
**
2000
))
class
MyInt
:
def
__int__
(
self
):
return
42
self
.
assertRaises
(
TypeError
,
float
,
MyInt
())
def
test_keyword_args
(
self
):
with
self
.
assertRaisesRegex
(
TypeError
,
'keyword argument'
):
float
(
x
=
'3.14'
)
...
...
Lib/test/test_getargs2.py
View file @
bdbad71b
...
...
@@ -457,6 +457,8 @@ class Float_TestCase(unittest.TestCase):
with
self
.
assertWarns
(
DeprecationWarning
):
self
.
assertEqual
(
getargs_f
(
BadFloat2
()),
4.25
)
self
.
assertEqual
(
getargs_f
(
BadFloat3
(
7.5
)),
7.5
)
self
.
assertEqual
(
getargs_f
(
Index
()),
99.0
)
self
.
assertRaises
(
TypeError
,
getargs_f
,
Int
())
for
x
in
(
FLT_MIN
,
-
FLT_MIN
,
FLT_MAX
,
-
FLT_MAX
,
INF
,
-
INF
):
self
.
assertEqual
(
getargs_f
(
x
),
x
)
...
...
@@ -489,6 +491,8 @@ class Float_TestCase(unittest.TestCase):
with
self
.
assertWarns
(
DeprecationWarning
):
self
.
assertEqual
(
getargs_d
(
BadFloat2
()),
4.25
)
self
.
assertEqual
(
getargs_d
(
BadFloat3
(
7.5
)),
7.5
)
self
.
assertEqual
(
getargs_d
(
Index
()),
99.0
)
self
.
assertRaises
(
TypeError
,
getargs_d
,
Int
())
for
x
in
(
DBL_MIN
,
-
DBL_MIN
,
DBL_MAX
,
-
DBL_MAX
,
INF
,
-
INF
):
self
.
assertEqual
(
getargs_d
(
x
),
x
)
...
...
@@ -511,6 +515,8 @@ class Float_TestCase(unittest.TestCase):
with
self
.
assertWarns
(
DeprecationWarning
):
self
.
assertEqual
(
getargs_D
(
BadComplex2
()),
4.25
+
0.5j
)
self
.
assertEqual
(
getargs_D
(
BadComplex3
(
7.5
+
0.25j
)),
7.5
+
0.25j
)
self
.
assertEqual
(
getargs_D
(
Index
()),
99.0
+
0j
)
self
.
assertRaises
(
TypeError
,
getargs_D
,
Int
())
for
x
in
(
DBL_MIN
,
-
DBL_MIN
,
DBL_MAX
,
-
DBL_MAX
,
INF
,
-
INF
):
c
=
complex
(
x
,
1.0
)
...
...
Lib/test/test_index.py
View file @
bdbad71b
...
...
@@ -60,7 +60,7 @@ class BaseTestCase(unittest.TestCase):
# subclasses. See issue #17576.
class
MyInt
(
int
):
def
__index__
(
self
):
return
int
(
s
elf
)
+
1
return
int
(
s
tr
(
self
)
)
+
1
my_int
=
MyInt
(
7
)
direct_index
=
my_int
.
__index__
()
...
...
Lib/test/test_int.py
View file @
bdbad71b
...
...
@@ -378,15 +378,23 @@ class IntTestCases(unittest.TestCase):
int
(
ExceptionalTrunc
())
for
trunc_result_base
in
(
object
,
Classic
):
class
In
tegral
(
trunc_result_base
):
def
__in
t
__
(
self
):
class
In
dex
(
trunc_result_base
):
def
__in
dex
__
(
self
):
return
42
class
TruncReturnsNonInt
(
base
):
def
__trunc__
(
self
):
return
Integral
()
with
self
.
assertWarns
(
DeprecationWarning
):
self
.
assertEqual
(
int
(
TruncReturnsNonInt
()),
42
)
return
Index
()
self
.
assertEqual
(
int
(
TruncReturnsNonInt
()),
42
)
class
Intable
(
trunc_result_base
):
def
__int__
(
self
):
return
42
class
TruncReturnsNonIndex
(
base
):
def
__trunc__
(
self
):
return
Intable
()
self
.
assertEqual
(
int
(
TruncReturnsNonInt
()),
42
)
class
NonIntegral
(
trunc_result_base
):
def
__trunc__
(
self
):
...
...
@@ -418,6 +426,21 @@ class IntTestCases(unittest.TestCase):
with
self
.
assertRaises
(
TypeError
):
int
(
TruncReturnsBadInt
())
def
test_int_subclass_with_index
(
self
):
class
MyIndex
(
int
):
def
__index__
(
self
):
return
42
class
BadIndex
(
int
):
def
__index__
(
self
):
return
42.0
my_int
=
MyIndex
(
7
)
self
.
assertEqual
(
my_int
,
7
)
self
.
assertEqual
(
int
(
my_int
),
7
)
self
.
assertEqual
(
int
(
BadIndex
()),
0
)
def
test_int_subclass_with_int
(
self
):
class
MyInt
(
int
):
def
__int__
(
self
):
...
...
@@ -431,9 +454,19 @@ class IntTestCases(unittest.TestCase):
self
.
assertEqual
(
my_int
,
7
)
self
.
assertEqual
(
int
(
my_int
),
42
)
self
.
assertRaises
(
TypeError
,
int
,
BadInt
())
my_int
=
BadInt
(
7
)
self
.
assertEqual
(
my_int
,
7
)
self
.
assertRaises
(
TypeError
,
int
,
my_int
)
def
test_int_returns_int_subclass
(
self
):
class
BadIndex
:
def
__index__
(
self
):
return
True
class
BadIndex2
(
int
):
def
__index__
(
self
):
return
True
class
BadInt
:
def
__int__
(
self
):
return
True
...
...
@@ -442,6 +475,10 @@ class IntTestCases(unittest.TestCase):
def
__int__
(
self
):
return
True
class
TruncReturnsBadIndex
:
def
__trunc__
(
self
):
return
BadIndex
()
class
TruncReturnsBadInt
:
def
__trunc__
(
self
):
return
BadInt
()
...
...
@@ -450,6 +487,17 @@ class IntTestCases(unittest.TestCase):
def
__trunc__
(
self
):
return
True
bad_int
=
BadIndex
()
with
self
.
assertWarns
(
DeprecationWarning
):
n
=
int
(
bad_int
)
self
.
assertEqual
(
n
,
1
)
self
.
assertIs
(
type
(
n
),
int
)
bad_int
=
BadIndex2
()
n
=
int
(
bad_int
)
self
.
assertEqual
(
n
,
0
)
self
.
assertIs
(
type
(
n
),
int
)
bad_int
=
BadInt
()
with
self
.
assertWarns
(
DeprecationWarning
):
n
=
int
(
bad_int
)
...
...
@@ -462,6 +510,12 @@ class IntTestCases(unittest.TestCase):
self
.
assertEqual
(
n
,
1
)
self
.
assertIs
(
type
(
n
),
int
)
bad_int
=
TruncReturnsBadIndex
()
with
self
.
assertWarns
(
DeprecationWarning
):
n
=
int
(
bad_int
)
self
.
assertEqual
(
n
,
1
)
self
.
assertIs
(
type
(
n
),
int
)
bad_int
=
TruncReturnsBadInt
()
with
self
.
assertWarns
(
DeprecationWarning
):
n
=
int
(
bad_int
)
...
...
Misc/NEWS.d/next/Core and Builtins/2019-05-31-11-55-49.bpo-20092.KIMjBW.rst
0 → 100644
View file @
bdbad71b
Constructors of :class:`int`, :class:`float` and :class:`complex` will now
use the :meth:`~object.__index__` special method, if available and the
corresponding method :meth:`~object.__int__`, :meth:`~object.__float__`
or :meth:`~object.__complex__` is not available.
Objects/abstract.c
View file @
bdbad71b
...
...
@@ -1373,6 +1373,13 @@ PyNumber_Long(PyObject *o)
}
return
result
;
}
if
(
m
&&
m
->
nb_index
)
{
result
=
_PyLong_FromNbIndexOrNbInt
(
o
);
if
(
result
!=
NULL
&&
!
PyLong_CheckExact
(
result
))
{
Py_SETREF
(
result
,
_PyLong_Copy
((
PyLongObject
*
)
result
));
}
return
result
;
}
trunc_func
=
_PyObject_LookupSpecial
(
o
,
&
PyId___trunc__
);
if
(
trunc_func
)
{
result
=
_PyObject_CallNoArg
(
trunc_func
);
...
...
@@ -1480,6 +1487,18 @@ PyNumber_Float(PyObject *o)
Py_DECREF
(
res
);
return
PyFloat_FromDouble
(
val
);
}
if
(
m
&&
m
->
nb_index
)
{
PyObject
*
res
=
PyNumber_Index
(
o
);
if
(
!
res
)
{
return
NULL
;
}
double
val
=
PyLong_AsDouble
(
res
);
Py_DECREF
(
res
);
if
(
val
==
-
1
.
0
&&
PyErr_Occurred
())
{
return
NULL
;
}
return
PyFloat_FromDouble
(
val
);
}
if
(
PyFloat_Check
(
o
))
{
/* A float subclass with nb_float == NULL */
return
PyFloat_FromDouble
(
PyFloat_AS_DOUBLE
(
o
));
}
...
...
Objects/complexobject.c
View file @
bdbad71b
...
...
@@ -984,7 +984,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
}
nbr
=
r
->
ob_type
->
tp_as_number
;
if
(
nbr
==
NULL
||
nbr
->
nb_float
==
NULL
)
{
if
(
nbr
==
NULL
||
(
nbr
->
nb_float
==
NULL
&&
nbr
->
nb_index
==
NULL
)
)
{
PyErr_Format
(
PyExc_TypeError
,
"complex() first argument must be a string or a number, "
"not '%.200s'"
,
...
...
@@ -996,7 +996,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
}
if
(
i
!=
NULL
)
{
nbi
=
i
->
ob_type
->
tp_as_number
;
if
(
nbi
==
NULL
||
nbi
->
nb_float
==
NULL
)
{
if
(
nbi
==
NULL
||
(
nbi
->
nb_float
==
NULL
&&
nbi
->
nb_index
==
NULL
)
)
{
PyErr_Format
(
PyExc_TypeError
,
"complex() second argument must be a number, "
"not '%.200s'"
,
...
...
@@ -1052,7 +1052,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
/* The "imag" part really is entirely imaginary, and
contributes nothing in the real direction.
Just treat it as a double. */
tmp
=
(
*
nbi
->
nb_float
)
(
i
);
tmp
=
PyNumber_Float
(
i
);
if
(
tmp
==
NULL
)
return
NULL
;
ci
.
real
=
PyFloat_AsDouble
(
tmp
);
...
...
Objects/floatobject.c
View file @
bdbad71b
...
...
@@ -246,6 +246,15 @@ PyFloat_AsDouble(PyObject *op)
nb
=
Py_TYPE
(
op
)
->
tp_as_number
;
if
(
nb
==
NULL
||
nb
->
nb_float
==
NULL
)
{
if
(
nb
&&
nb
->
nb_index
)
{
PyObject
*
res
=
PyNumber_Index
(
op
);
if
(
!
res
)
{
return
-
1
;
}
double
val
=
PyLong_AsDouble
(
res
);
Py_DECREF
(
res
);
return
val
;
}
PyErr_Format
(
PyExc_TypeError
,
"must be real number, not %.50s"
,
op
->
ob_type
->
tp_name
);
return
-
1
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment