Skip to content

CLN: Remove collections.abc classes from pandas.compat #25957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions pandas/compat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import struct
import inspect
from collections import namedtuple
import collections

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] >= 3
Expand Down Expand Up @@ -106,16 +105,6 @@ def lmap(*args, **kwargs):

def lfilter(*args, **kwargs):
return list(filter(*args, **kwargs))

Hashable = collections.abc.Hashable
Iterable = collections.abc.Iterable
Iterator = collections.abc.Iterator
Mapping = collections.abc.Mapping
MutableMapping = collections.abc.MutableMapping
Sequence = collections.abc.Sequence
Sized = collections.abc.Sized
Set = collections.abc.Set

else:
# Python 2
_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
Expand All @@ -138,15 +127,6 @@ def signature(f):
lmap = builtins.map
lfilter = builtins.filter

Hashable = collections.Hashable
Iterable = collections.Iterable
Iterator = collections.Iterator
Mapping = collections.Mapping
MutableMapping = collections.MutableMapping
Sequence = collections.Sequence
Sized = collections.Sized
Set = collections.Set

if PY2:
def iteritems(obj, **kw):
return obj.iteritems(**kw)
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/arrays/sparse.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
SparseArray data structure
"""
from collections.abc import Mapping
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it'd be preferred, I could instead follow the pattern below:

from collections import abc

...

if isinstance(mapper, abc.Mapping):
    ...

Copy link
Member

@gfyoung gfyoung Apr 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we (as maintainers) have much of a preference on this.

My personal MO is that if you find yourself using it multiple times in the codebase, best to import directly into the namespace (fewer characters).

Copy link
Member

@gfyoung gfyoung Apr 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From #25957 (review):

we get to the point where we are doing from typing import Iterable

@WillAyd : Hmm...are we really sure we want to do that? IMO, the word Iterable is not immediately obvious as a "type" versus the actual Iterable object itself (e.g. from collections.abc)

IMO, if it's possible, I would prefer that we namespace those.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea the convention for imports from typing are called out in MyPy documentation:

https://mypy.readthedocs.io/en/latest/getting_started.html#the-typing-module

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal MO is that if you find yourself using it multiple times in the codebase, best to import directly into the namespace (fewer characters).

Agreed, that's my MO too. The only reason I brought it up here is because these names can be a bit ambiguous, so it might be more readable and immediately obvious to see the abc prefix. Aside from the typing conflicts there's also a conflict where we've named a custom class Iterator:

class Iterator(object):

Will leave the PR as-is for now until more people weigh in. I don't have a strong preference as long as we're consistent throughout the codebase. The first thing I'd think upon seeing Iterable would be the collections.abc version but I'm likely biased from not having done much of anything with typing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see...sigh...it's rather unfortunate that they decided have Iterable both refer to the object as well as the type.

Fair enough, then let's go with what Python says then. However, I'm relatively certainly that we will be writing abc.Iterable many more times than we will be writing Iterable as a type hint (it's going to be more characters, I suspect 🙂)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, didn't refresh and see the link about the conventions before my last comment; abc prefix it is then.

import numbers
import operator
import re
Expand Down Expand Up @@ -1377,7 +1378,7 @@ def map(self, mapper):
if isinstance(mapper, ABCSeries):
mapper = mapper.to_dict()

if isinstance(mapper, compat.Mapping):
if isinstance(mapper, Mapping):
fill_value = mapper.get(self.fill_value, self.fill_value)
sp_values = [mapper.get(x, None) for x in self.sp_values]
else:
Expand Down
12 changes: 6 additions & 6 deletions pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import collections
from collections import OrderedDict
from collections.abc import Mapping
from datetime import datetime, timedelta
from functools import partial
import inspect
Expand All @@ -14,7 +15,6 @@
import numpy as np

from pandas._libs import lib, tslibs
import pandas.compat as compat
from pandas.compat import PY36, iteritems

from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike
Expand Down Expand Up @@ -374,13 +374,13 @@ def standardize_mapping(into):

Parameters
----------
into : instance or subclass of collections.Mapping
into : instance or subclass of collections.abc.Mapping
Must be a class, an initialized collections.defaultdict,
or an instance of a collections.Mapping subclass.
or an instance of a collections.abc.Mapping subclass.

Returns
-------
mapping : a collections.Mapping subclass or other constructor
mapping : a collections.abc.Mapping subclass or other constructor
a callable object that can accept an iterator to create
the desired Mapping.

Expand All @@ -394,7 +394,7 @@ def standardize_mapping(into):
return partial(
collections.defaultdict, into.default_factory)
into = type(into)
if not issubclass(into, compat.Mapping):
if not issubclass(into, Mapping):
raise TypeError('unsupported type: {into}'.format(into=into))
elif into == collections.defaultdict:
raise TypeError(
Expand Down Expand Up @@ -471,7 +471,7 @@ def _get_rename_function(mapper):
Returns a function that will map names/labels, dependent if mapper
is a dict, Series or just a function.
"""
if isinstance(mapper, (compat.Mapping, ABCSeries)):
if isinstance(mapper, (Mapping, ABCSeries)):

def f(x):
if x in mapper:
Expand Down
21 changes: 10 additions & 11 deletions pandas/core/dtypes/inference.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
""" basic inference routines """

from collections.abc import Iterable, Set
from numbers import Number
import re

import numpy as np

from pandas._libs import lib
from pandas.compat import PY2, Set, re_type

from pandas import compat
from pandas.compat import PY2, re_type

is_bool = lib.is_bool

Expand Down Expand Up @@ -113,7 +112,7 @@ def _iterable_not_string(obj):
False
"""

return isinstance(obj, compat.Iterable) and not isinstance(obj, str)
return isinstance(obj, Iterable) and not isinstance(obj, str)


def is_iterator(obj):
Expand Down Expand Up @@ -288,7 +287,7 @@ def is_list_like(obj, allow_sets=True):
False
"""

return (isinstance(obj, compat.Iterable) and
return (isinstance(obj, Iterable) and
# we do not count strings/unicode/bytes as list-like
not isinstance(obj, (str, bytes)) and

Expand Down Expand Up @@ -436,23 +435,23 @@ def is_named_tuple(obj):
def is_hashable(obj):
"""Return True if hash(obj) will succeed, False otherwise.

Some types will pass a test against collections.Hashable but fail when they
are actually hashed with hash().
Some types will pass a test against collections.abc.Hashable but fail when
they are actually hashed with hash().

Distinguish between these and other types by trying the call to hash() and
seeing if they raise TypeError.

Examples
--------
>>> a = ([],)
>>> isinstance(a, collections.Hashable)
>>> isinstance(a, collections.abc.Hashable)
True
>>> is_hashable(a)
False
"""
# Unfortunately, we can't use isinstance(obj, collections.Hashable), which
# can be faster than calling hash. That is because numpy scalars on Python
# 3 fail this test.
# Unfortunately, we can't use isinstance(obj, collections.abc.Hashable),
# which can be faster than calling hash. That is because numpy scalars
# fail this test.

# Reconsider this decision once this numpy bug is fixed:
# https://github.com/numpy/numpy/issues/5562
Expand Down
16 changes: 8 additions & 8 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""
import collections
from collections import OrderedDict
from collections.abc import Iterable, Iterator, Sequence
import functools
import itertools
import sys
Expand All @@ -33,8 +34,7 @@
validate_axis_style_args)

from pandas import compat
from pandas.compat import (
PY36, Iterator, StringIO, lmap, lzip, raise_with_traceback)
from pandas.compat import PY36, StringIO, lmap, lzip, raise_with_traceback
from pandas.compat.numpy import function as nv
from pandas.core.dtypes.cast import (
maybe_upcast,
Expand Down Expand Up @@ -426,9 +426,9 @@ def __init__(self, data=None, index=None, columns=None, dtype=None,
copy=copy)

# For data is list-like, or Iterable (will consume into list)
elif (isinstance(data, compat.Iterable) and
elif (isinstance(data, Iterable) and
not isinstance(data, (str, bytes))):
if not isinstance(data, compat.Sequence):
if not isinstance(data, Sequence):
data = list(data)
if len(data) > 0:
if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1:
Expand Down Expand Up @@ -1203,7 +1203,7 @@ def to_dict(self, orient='dict', into=dict):
indicates `split`.

into : class, default dict
The collections.Mapping subclass used for all Mappings
The collections.abc.Mapping subclass used for all Mappings
in the return value. Can be the actual class or an empty
instance of the mapping type you want. If you want a
collections.defaultdict, you must pass it initialized.
Expand All @@ -1212,8 +1212,8 @@ def to_dict(self, orient='dict', into=dict):

Returns
-------
dict, list or collections.Mapping
Return a collections.Mapping object representing the DataFrame.
dict, list or collections.abc.Mapping
Return a collections.abc.Mapping object representing the DataFrame.
The resulting transformation depends on the `orient` parameter.

See Also
Expand Down Expand Up @@ -4080,7 +4080,7 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
the same length as the calling DataFrame, or a list containing an
arbitrary combination of column keys and arrays. Here, "array"
encompasses :class:`Series`, :class:`Index`, ``np.ndarray``, and
instances of :class:`abc.Iterator`.
instances of :class:`~collections.abc.Iterator`.
drop : bool, default True
Delete columns to be used as the new index.
append : bool, default False
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/groupby/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

import collections
from collections.abc import Iterable
import copy
from functools import partial
from textwrap import dedent
Expand Down Expand Up @@ -770,7 +771,7 @@ def aggregate(self, func_or_funcs, *args, **kwargs):
if isinstance(func_or_funcs, str):
return getattr(self, func_or_funcs)(*args, **kwargs)

if isinstance(func_or_funcs, compat.Iterable):
if isinstance(func_or_funcs, Iterable):
# Catch instances of lists / tuples
# but not the class list / tuple itself.
ret = self._aggregate_multiple_funcs(func_or_funcs,
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/internals/construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
constructors before passing them to a BlockManager.
"""
from collections import OrderedDict
from collections.abc import Mapping

import numpy as np
import numpy.ma as ma

from pandas._libs import lib
from pandas._libs.tslibs import IncompatibleFrequency
import pandas.compat as compat
from pandas.compat import lmap, lrange, raise_with_traceback

from pandas.core.dtypes.cast import (
Expand Down Expand Up @@ -395,7 +395,7 @@ def to_arrays(data, columns, coerce_float=False, dtype=None):
if isinstance(data[0], (list, tuple)):
return _list_to_arrays(data, columns, coerce_float=coerce_float,
dtype=dtype)
elif isinstance(data[0], compat.Mapping):
elif isinstance(data[0], Mapping):
return _list_of_dict_to_arrays(data, columns,
coerce_float=coerce_float, dtype=dtype)
elif isinstance(data[0], ABCSeries):
Expand Down
8 changes: 4 additions & 4 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Data structure for 1-dimensional cross-sectional and time series data
"""
from collections import OrderedDict
from collections.abc import Iterable, Sized
from shutil import get_terminal_size
from textwrap import dedent
import warnings
Expand Down Expand Up @@ -224,8 +225,7 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
raise TypeError("{0!r} type is unordered"
"".format(data.__class__.__name__))
# If data is Iterable but not list-like, consume into list.
elif (isinstance(data, compat.Iterable)
and not isinstance(data, compat.Sized)):
elif (isinstance(data, Iterable) and not isinstance(data, Sized)):
data = list(data)
else:

Expand Down Expand Up @@ -1496,7 +1496,7 @@ def to_dict(self, into=dict):
Parameters
----------
into : class, default dict
The collections.Mapping subclass to use as the return
The collections.abc.Mapping subclass to use as the return
object. Can be the actual class or an empty
instance of the mapping type you want. If you want a
collections.defaultdict, you must pass it initialized.
Expand All @@ -1505,7 +1505,7 @@ def to_dict(self, into=dict):

Returns
-------
collections.Mapping
collections.abc.Mapping
Key-value representation of Series.

Examples
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/sparse/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

# pylint: disable=E1101,E1103,W0231

from collections.abc import Mapping
import warnings

import numpy as np

import pandas._libs.index as libindex
import pandas._libs.sparse as splib
from pandas._libs.sparse import BlockIndex, IntIndex
import pandas.compat as compat
from pandas.compat.numpy import function as nv
from pandas.util._decorators import Appender, Substitution

Expand Down Expand Up @@ -82,7 +82,7 @@ def __init__(self, data=None, index=None, sparse_index=None, kind='block',
if index is not None:
data = data.reindex(index)

elif isinstance(data, compat.Mapping):
elif isinstance(data, Mapping):
data, index = Series()._init_dict(data, index=index)

elif is_scalar(data) and index is not None:
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/tools/datetimes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections.abc import MutableMapping
from datetime import datetime, time
from functools import partial

Expand All @@ -17,7 +18,6 @@
from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries
from pandas.core.dtypes.missing import notna

from pandas import compat
from pandas.core import algorithms


Expand Down Expand Up @@ -599,7 +599,7 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
else:
values = convert_listlike(arg._values, True, format)
result = arg._constructor(values, index=arg.index, name=arg.name)
elif isinstance(arg, (ABCDataFrame, compat.MutableMapping)):
elif isinstance(arg, (ABCDataFrame, MutableMapping)):
result = _assemble_from_unit_mappings(arg, errors, box, tz)
elif isinstance(arg, ABCIndexClass):
cache_array = _maybe_cache(arg, format, cache, convert_listlike)
Expand Down
4 changes: 2 additions & 2 deletions pandas/io/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

"""

from collections.abc import Iterable
from distutils.version import LooseVersion
import numbers
import os
import re

import pandas.compat as compat
from pandas.compat import iteritems, lmap, lrange, raise_with_traceback
from pandas.errors import AbstractMethodError, EmptyDataError

Expand Down Expand Up @@ -857,7 +857,7 @@ def _validate_flavor(flavor):
flavor = 'lxml', 'bs4'
elif isinstance(flavor, str):
flavor = flavor,
elif isinstance(flavor, compat.Iterable):
elif isinstance(flavor, Iterable):
if not all(isinstance(flav, str) for flav in flavor):
raise TypeError('Object of type {typ!r} is not an iterable of '
'strings'
Expand Down
3 changes: 1 addition & 2 deletions pandas/tests/arithmetic/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
# Arithmetc tests for DataFrame/Series/Index/Array classes that should
# behave identically.
# Specifically for numeric dtypes
from collections.abc import Iterable
from decimal import Decimal
from itertools import combinations
import operator

import numpy as np
import pytest

from pandas.compat import Iterable

import pandas as pd
from pandas import Index, Series, Timedelta, TimedeltaIndex
from pandas.core import ops
Expand Down
Loading