Repository: pandas-dev/pandas

Issue:
API: scope of the public types module
Currently, the public `pandas.api.types` modules holds the following functions:

```
In [11]: [f for f in dir(pd.api.types) if not f.startswith('_')]
Out[11]: 
['infer_dtype',
 'is_any_int_dtype',
 'is_bool',
 'is_bool_dtype',
 'is_categorical',
 'is_categorical_dtype',
 'is_complex',
 'is_complex_dtype',
 'is_datetime64_any_dtype',
 'is_datetime64_dtype',
 'is_datetime64_ns_dtype',
 'is_datetime64tz_dtype',
 'is_datetimetz',
 'is_dict_like',
 'is_dtype_equal',
 'is_extension_type',
 'is_file_like',
 'is_float',
 'is_float_dtype',
 'is_floating_dtype',
 'is_hashable',
 'is_int64_dtype',
 'is_integer',
 'is_integer_dtype',
 'is_interval',
 'is_interval_dtype',
 'is_iterator',
 'is_list_like',
 'is_named_tuple',
 'is_number',
 'is_numeric_dtype',
 'is_object_dtype',
 'is_period',
 'is_period_dtype',
 'is_re',
 'is_re_compilable',
 'is_scalar',
 'is_sequence',
 'is_signed_integer_dtype',
 'is_sparse',
 'is_string_dtype',
 'is_timedelta64_dtype',
 'is_timedelta64_ns_dtype',
 'is_unsigned_integer_dtype',
 'pandas_dtype',
 'union_categoricals']
```

Two questions I would like to discuss a bit:

- **Do we need to expose all of them?**
  Putting the functions here, means keeping them stable. So we could also limit it to the more pandas specific ones. For example, `is_re`, `is_dict_like`, `is_iterator`, etc are general utility functions not actually related to pandas specifics. Of course it can be handy for other projects that they can use them instead of implementing it themselves, but it increases the number of functions we have to keep stable.


~~- **Do we need to expose more of the pandas extension types API?** (xref #16099)~~

  From comment of @wesm (https://github.com/pandas-dev/pandas/pull/15541#issuecomment-286008496):

  > I've been running into these internal API / external API issues lately (e.g. needing to use `DatetimeTZDtype` in an external library), so it might be worth documenting the pandas < 0.19 way to get some of these APIs

  It is how the dtypes will be implemented in pandas 1.x, so shouldn't we just expose this officially and put it in `types`? `pyarrow` uses the datetime tz one, and in https://github.com/pandas-dev/pandas/pull/16015 we will probably also like to add `CategoricalDtype` 
  Not sure if the others (interval, period) are also needed.
  


Discussion:
I think exposing ``DatetimeTZDtype`` and ``CategoricalDtype`` are fine here as well.
I don't think its a bit deal to expose *all* (or almost all) of our introspection functions.
@jorisvandenbossche 

so we should finish documenting these ``is_*`` functions before adding them to api.rst.

I am not against leaving all of these. sure some are trivial but it does offer 1-stop shopping.
> so we should finish documenting these is_* functions before adding them to api.rst.

yes, the fact that they were not yet documented was the reason I didn't add them yet in the other PR.

> I am not against leaving all of these. sure some are trivial but it does offer 1-stop shopping.

yes, in general OK as well. 
I quickly went through them, and noted:

- `is_any_int_dtype` is IMO a bit confusing name (it is also only used at two places in the whole pandas codebase, so not sure it is worth exposing here)
- `is_float_dtype` / `is_floating_dtype` distinction is also not very clear. And did a search and apparently we do not use `is_floating_dtype` internally, so would remove that
- the distinction between `is_*` and `is_*dtype` is not for all of them consistent. For some, the `is_*` version only accepts scalars, for others it also return True for a array/series of such elements.
ok I think ok then to remove (i'll depecate) some of these.

Resolving patch:
diff --git a/pandas/api/types/__init__.py b/pandas/api/types/__init__.py
--- a/pandas/api/types/__init__.py
+++ b/pandas/api/types/__init__.py
@@ -7,4 +7,3 @@
                                        IntervalDtype)
 from pandas.core.dtypes.concat import union_categoricals  # noqa
 from pandas._libs.lib import infer_dtype  # noqa
-del np  # noqa
diff --git a/pandas/core/dtypes/api.py b/pandas/core/dtypes/api.py
--- a/pandas/core/dtypes/api.py
+++ b/pandas/core/dtypes/api.py
@@ -1,6 +1,6 @@
 # flake8: noqa
 
-import numpy as np
+import sys
 
 from .common import (pandas_dtype,
                      is_dtype_equal,
@@ -40,12 +40,10 @@
                      is_float,
                      is_complex,
                      is_number,
-                     is_any_int_dtype,
                      is_integer_dtype,
                      is_int64_dtype,
                      is_numeric_dtype,
                      is_float_dtype,
-                     is_floating_dtype,
                      is_bool_dtype,
                      is_complex_dtype,
                      is_signed_integer_dtype,
@@ -61,3 +59,24 @@
                      is_hashable,
                      is_named_tuple,
                      is_sequence)
+
+
+# deprecated
+m = sys.modules['pandas.core.dtypes.api']
+
+for t in ['is_any_int_dtype', 'is_floating_dtype']:
+
+    def outer(t=t):
+
+        def wrapper(arr_or_dtype):
+            import warnings
+            import pandas
+            warnings.warn("{t} is deprecated and will be "
+                          "removed in a future version".format(t=t),
+                          FutureWarning, stacklevel=3)
+            return getattr(pandas.core.dtypes.common, t)(arr_or_dtype)
+        return wrapper
+
+    setattr(m, t, outer(t))
+
+del sys, m, t, outer


Test patch:
diff --git a/pandas/tests/api/test_types.py b/pandas/tests/api/test_types.py
--- a/pandas/tests/api/test_types.py
+++ b/pandas/tests/api/test_types.py
@@ -15,13 +15,13 @@
 
 class TestTypes(Base, tm.TestCase):
 
-    allowed = ['is_any_int_dtype', 'is_bool', 'is_bool_dtype',
+    allowed = ['is_bool', 'is_bool_dtype',
                'is_categorical', 'is_categorical_dtype', 'is_complex',
                'is_complex_dtype', 'is_datetime64_any_dtype',
                'is_datetime64_dtype', 'is_datetime64_ns_dtype',
                'is_datetime64tz_dtype', 'is_datetimetz', 'is_dtype_equal',
                'is_extension_type', 'is_float', 'is_float_dtype',
-               'is_floating_dtype', 'is_int64_dtype', 'is_integer',
+               'is_int64_dtype', 'is_integer',
                'is_integer_dtype', 'is_number', 'is_numeric_dtype',
                'is_object_dtype', 'is_scalar', 'is_sparse',
                'is_string_dtype', 'is_signed_integer_dtype',
@@ -33,12 +33,13 @@ class TestTypes(Base, tm.TestCase):
                'is_list_like', 'is_hashable',
                'is_named_tuple', 'is_sequence',
                'pandas_dtype', 'union_categoricals', 'infer_dtype']
+    deprecated = ['is_any_int_dtype', 'is_floating_dtype']
     dtypes = ['CategoricalDtype', 'DatetimeTZDtype',
               'PeriodDtype', 'IntervalDtype']
 
     def test_types(self):
 
-        self.check(types, self.allowed + self.dtypes)
+        self.check(types, self.allowed + self.dtypes + self.deprecated)
 
     def check_deprecation(self, fold, fnew):
         with tm.assert_produces_warning(DeprecationWarning):
@@ -87,6 +88,13 @@ def test_removed_from_core_common(self):
                   'ensure_float']:
             pytest.raises(AttributeError, lambda: getattr(com, t))
 
+    def test_deprecated_from_api_types(self):
+
+        for t in ['is_any_int_dtype', 'is_floating_dtype']:
+            with tm.assert_produces_warning(FutureWarning,
+                                            check_stacklevel=False):
+                getattr(types, t)(1)
+
 
 def test_moved_infer_dtype():