1""" basic inference routines """
2
3from collections import abc
4from numbers import Number
5import re
6from typing import Pattern
7
8import numpy as np
9
10from pandas._libs import lib
11
12is_bool = lib.is_bool
13
14is_integer = lib.is_integer
15
16is_float = lib.is_float
17
18is_complex = lib.is_complex
19
20is_scalar = lib.is_scalar
21
22is_decimal = lib.is_decimal
23
24is_interval = lib.is_interval
25
26is_list_like = lib.is_list_like
27
28is_iterator = lib.is_iterator
29
30
31def is_number(obj) -> bool:
32    """
33    Check if the object is a number.
34
35    Returns True when the object is a number, and False if is not.
36
37    Parameters
38    ----------
39    obj : any type
40        The object to check if is a number.
41
42    Returns
43    -------
44    is_number : bool
45        Whether `obj` is a number or not.
46
47    See Also
48    --------
49    api.types.is_integer: Checks a subgroup of numbers.
50
51    Examples
52    --------
53    >>> pd.api.types.is_number(1)
54    True
55    >>> pd.api.types.is_number(7.15)
56    True
57
58    Booleans are valid because they are int subclass.
59
60    >>> pd.api.types.is_number(False)
61    True
62
63    >>> pd.api.types.is_number("foo")
64    False
65    >>> pd.api.types.is_number("5")
66    False
67    """
68    return isinstance(obj, (Number, np.number))
69
70
71def iterable_not_string(obj) -> bool:
72    """
73    Check if the object is an iterable but not a string.
74
75    Parameters
76    ----------
77    obj : The object to check.
78
79    Returns
80    -------
81    is_iter_not_string : bool
82        Whether `obj` is a non-string iterable.
83
84    Examples
85    --------
86    >>> iterable_not_string([1, 2, 3])
87    True
88    >>> iterable_not_string("foo")
89    False
90    >>> iterable_not_string(1)
91    False
92    """
93    return isinstance(obj, abc.Iterable) and not isinstance(obj, str)
94
95
96def is_file_like(obj) -> bool:
97    """
98    Check if the object is a file-like object.
99
100    For objects to be considered file-like, they must
101    be an iterator AND have either a `read` and/or `write`
102    method as an attribute.
103
104    Note: file-like objects must be iterable, but
105    iterable objects need not be file-like.
106
107    Parameters
108    ----------
109    obj : The object to check
110
111    Returns
112    -------
113    is_file_like : bool
114        Whether `obj` has file-like properties.
115
116    Examples
117    --------
118    >>> import io
119    >>> buffer = io.StringIO("data")
120    >>> is_file_like(buffer)
121    True
122    >>> is_file_like([1, 2, 3])
123    False
124    """
125    if not (hasattr(obj, "read") or hasattr(obj, "write")):
126        return False
127
128    if not hasattr(obj, "__iter__"):
129        return False
130
131    return True
132
133
134def is_re(obj) -> bool:
135    """
136    Check if the object is a regex pattern instance.
137
138    Parameters
139    ----------
140    obj : The object to check
141
142    Returns
143    -------
144    is_regex : bool
145        Whether `obj` is a regex pattern.
146
147    Examples
148    --------
149    >>> is_re(re.compile(".*"))
150    True
151    >>> is_re("foo")
152    False
153    """
154    return isinstance(obj, Pattern)
155
156
157def is_re_compilable(obj) -> bool:
158    """
159    Check if the object can be compiled into a regex pattern instance.
160
161    Parameters
162    ----------
163    obj : The object to check
164
165    Returns
166    -------
167    is_regex_compilable : bool
168        Whether `obj` can be compiled as a regex pattern.
169
170    Examples
171    --------
172    >>> is_re_compilable(".*")
173    True
174    >>> is_re_compilable(1)
175    False
176    """
177    try:
178        re.compile(obj)
179    except TypeError:
180        return False
181    else:
182        return True
183
184
185def is_array_like(obj) -> bool:
186    """
187    Check if the object is array-like.
188
189    For an object to be considered array-like, it must be list-like and
190    have a `dtype` attribute.
191
192    Parameters
193    ----------
194    obj : The object to check
195
196    Returns
197    -------
198    is_array_like : bool
199        Whether `obj` has array-like properties.
200
201    Examples
202    --------
203    >>> is_array_like(np.array([1, 2, 3]))
204    True
205    >>> is_array_like(pd.Series(["a", "b"]))
206    True
207    >>> is_array_like(pd.Index(["2016-01-01"]))
208    True
209    >>> is_array_like([1, 2, 3])
210    False
211    >>> is_array_like(("a", "b"))
212    False
213    """
214    return is_list_like(obj) and hasattr(obj, "dtype")
215
216
217def is_nested_list_like(obj) -> bool:
218    """
219    Check if the object is list-like, and that all of its elements
220    are also list-like.
221
222    Parameters
223    ----------
224    obj : The object to check
225
226    Returns
227    -------
228    is_list_like : bool
229        Whether `obj` has list-like properties.
230
231    Examples
232    --------
233    >>> is_nested_list_like([[1, 2, 3]])
234    True
235    >>> is_nested_list_like([{1, 2, 3}, {1, 2, 3}])
236    True
237    >>> is_nested_list_like(["foo"])
238    False
239    >>> is_nested_list_like([])
240    False
241    >>> is_nested_list_like([[1, 2, 3], 1])
242    False
243
244    Notes
245    -----
246    This won't reliably detect whether a consumable iterator (e. g.
247    a generator) is a nested-list-like without consuming the iterator.
248    To avoid consuming it, we always return False if the outer container
249    doesn't define `__len__`.
250
251    See Also
252    --------
253    is_list_like
254    """
255    return (
256        is_list_like(obj)
257        and hasattr(obj, "__len__")
258        and len(obj) > 0
259        and all(is_list_like(item) for item in obj)
260    )
261
262
263def is_dict_like(obj) -> bool:
264    """
265    Check if the object is dict-like.
266
267    Parameters
268    ----------
269    obj : The object to check
270
271    Returns
272    -------
273    is_dict_like : bool
274        Whether `obj` has dict-like properties.
275
276    Examples
277    --------
278    >>> is_dict_like({1: 2})
279    True
280    >>> is_dict_like([1, 2, 3])
281    False
282    >>> is_dict_like(dict)
283    False
284    >>> is_dict_like(dict())
285    True
286    """
287    dict_like_attrs = ("__getitem__", "keys", "__contains__")
288    return (
289        all(hasattr(obj, attr) for attr in dict_like_attrs)
290        # [GH 25196] exclude classes
291        and not isinstance(obj, type)
292    )
293
294
295def is_named_tuple(obj) -> bool:
296    """
297    Check if the object is a named tuple.
298
299    Parameters
300    ----------
301    obj : The object to check
302
303    Returns
304    -------
305    is_named_tuple : bool
306        Whether `obj` is a named tuple.
307
308    Examples
309    --------
310    >>> from collections import namedtuple
311    >>> Point = namedtuple("Point", ["x", "y"])
312    >>> p = Point(1, 2)
313    >>>
314    >>> is_named_tuple(p)
315    True
316    >>> is_named_tuple((1, 2))
317    False
318    """
319    return isinstance(obj, tuple) and hasattr(obj, "_fields")
320
321
322def is_hashable(obj) -> bool:
323    """
324    Return True if hash(obj) will succeed, False otherwise.
325
326    Some types will pass a test against collections.abc.Hashable but fail when
327    they are actually hashed with hash().
328
329    Distinguish between these and other types by trying the call to hash() and
330    seeing if they raise TypeError.
331
332    Returns
333    -------
334    bool
335
336    Examples
337    --------
338    >>> import collections
339    >>> a = ([],)
340    >>> isinstance(a, collections.abc.Hashable)
341    True
342    >>> is_hashable(a)
343    False
344    """
345    # Unfortunately, we can't use isinstance(obj, collections.abc.Hashable),
346    # which can be faster than calling hash. That is because numpy scalars
347    # fail this test.
348
349    # Reconsider this decision once this numpy bug is fixed:
350    # https://github.com/numpy/numpy/issues/5562
351
352    try:
353        hash(obj)
354    except TypeError:
355        return False
356    else:
357        return True
358
359
360def is_sequence(obj) -> bool:
361    """
362    Check if the object is a sequence of objects.
363    String types are not included as sequences here.
364
365    Parameters
366    ----------
367    obj : The object to check
368
369    Returns
370    -------
371    is_sequence : bool
372        Whether `obj` is a sequence of objects.
373
374    Examples
375    --------
376    >>> l = [1, 2, 3]
377    >>>
378    >>> is_sequence(l)
379    True
380    >>> is_sequence(iter(l))
381    False
382    """
383    try:
384        iter(obj)  # Can iterate over it.
385        len(obj)  # Has a length associated with it.
386        return not isinstance(obj, (str, bytes))
387    except (TypeError, AttributeError):
388        return False
389
390
391def is_dataclass(item):
392    """
393    Checks if the object is a data-class instance
394
395    Parameters
396    ----------
397    item : object
398
399    Returns
400    --------
401    is_dataclass : bool
402        True if the item is an instance of a data-class,
403        will return false if you pass the data class itself
404
405    Examples
406    --------
407    >>> from dataclasses import dataclass
408    >>> @dataclass
409    ... class Point:
410    ...     x: int
411    ...     y: int
412
413    >>> is_dataclass(Point)
414    False
415    >>> is_dataclass(Point(0,2))
416    True
417
418    """
419    try:
420        from dataclasses import is_dataclass
421
422        return is_dataclass(item) and not isinstance(item, type)
423    except ImportError:
424        return False
425