diff --git a/doc/source/whatsnew/v3.1.0.rst b/doc/source/whatsnew/v3.1.0.rst index ba68d142e1eda..d46933f459b3b 100644 --- a/doc/source/whatsnew/v3.1.0.rst +++ b/doc/source/whatsnew/v3.1.0.rst @@ -303,6 +303,7 @@ Indexing - Bug in :meth:`Index.take` where ``fill_value`` was silently ignored and integer-dtype indexes raised ``ValueError`` instead of filling with the provided value. Passing ``fill_value=None`` now fills ``-1`` entries with the Index's NA value (matching the :class:`~pandas.api.extensions.ExtensionArray` convention); omit ``fill_value`` to retain the previous behavior where negative indices wrap (:issue:`65210`) - Bug in :meth:`Index.where` and :meth:`Index.putmask` preserving ``numpy.datetime64`` / ``numpy.timedelta64`` ``NaT`` scalars in the object-dtype result for mismatched-dtype inputs, instead of normalizing to :attr:`pandas.NaT` as :meth:`Series.where` does (:issue:`55174`) - Bug in :meth:`MultiIndex.get_loc` returning a slice instead of an integer for a unique key when the :class:`MultiIndex` contained duplicates elsewhere, causing ``.loc`` to return a :class:`Series` instead of a scalar (:issue:`42102`) +- Bug in :meth:`RangeIndex.memory_usage` and :attr:`RangeIndex.nbytes` raising ``TypeError`` on PyPy (:issue:`46176`) - Bug in :meth:`Series.where` and :meth:`Series.mask` raising ``ValueError`` when ``other`` is a tuple on object-dtype :class:`Series` (:issue:`37681`) - Fixed bug in :meth:`DataFrame.loc` where assigning with duplicate column names and new columns corrupted unrelated columns (:issue:`58317`) - Fixed segfault in :meth:`DataFrame.loc` when repeatedly adding new rows to an object-dtype-indexed :class:`DataFrame` (:issue:`21968`) diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 0a67d80aa6611..c0a479de4f6cb 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -417,8 +417,12 @@ def nbytes(self) -> int: Return the number of bytes in the underlying data. """ rng = self._range - return getsizeof(rng) + sum( - getsizeof(getattr(rng, attr_name)) + # passing a default to getsizeof avoids a TypeError on PyPy, where + # sys.getsizeof always raises TypeError unless a default is provided + # (GH#46176) + objsize = 24 + return getsizeof(rng, objsize) + sum( + getsizeof(getattr(rng, attr_name), objsize) for attr_name in ["start", "stop", "step"] ) diff --git a/pandas/tests/indexes/ranges/test_range.py b/pandas/tests/indexes/ranges/test_range.py index 325393a320c2a..91a3d5cdc0986 100644 --- a/pandas/tests/indexes/ranges/test_range.py +++ b/pandas/tests/indexes/ranges/test_range.py @@ -9,6 +9,7 @@ RangeIndex, ) import pandas._testing as tm +import pandas.core.indexes.range as range_module from pandas.core.indexes.range import min_fitting_element @@ -371,6 +372,19 @@ def test_nbytes(self): i2 = RangeIndex(0, 10) assert idx.nbytes == i2.nbytes + def test_nbytes_pypy_compat(self, monkeypatch): + # GH#46176 sys.getsizeof always raises TypeError on PyPy unless + # a default is provided; nbytes must keep working there. + def pypy_getsizeof(obj, default=None): + if default is None: + raise TypeError("PyPy") + return default + + monkeypatch.setattr(range_module, "getsizeof", pypy_getsizeof) + idx = RangeIndex(0, 1000) + assert isinstance(idx.nbytes, int) + assert idx.memory_usage() == idx.nbytes + @pytest.mark.parametrize( "start,stop,step", [