diff --git a/pandas/_config/config.py b/pandas/_config/config.py index 35949d7683abc..041596cf002a3 100644 --- a/pandas/_config/config.py +++ b/pandas/_config/config.py @@ -944,3 +944,11 @@ def is_callable(obj: object) -> bool: if not callable(obj): raise ValueError("Value must be a callable") return True + + +# import set_module here would cause circular import +get_option.__module__ = "pandas" +set_option.__module__ = "pandas" +describe_option.__module__ = "pandas" +reset_option.__module__ = "pandas" +option_context.__module__ = "pandas" diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index ae0bbeb7afa4c..8f124eed35a9c 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -41,6 +41,7 @@ from cython cimport ( from pandas._config import using_string_dtype from pandas._libs.missing import check_na_tuples_nonequal +from pandas.util._decorators import set_module import_datetime() @@ -154,6 +155,7 @@ def memory_usage_of_objects(arr: object[:]) -> int64_t: # ---------------------------------------------------------------------- +@set_module("pandas.api.types") def is_scalar(val: object) -> bool: """ Return True if given object is scalar. @@ -255,6 +257,7 @@ cdef int64_t get_itemsize(object val): return -1 +@set_module("pandas.api.types") def is_iterator(obj: object) -> bool: """ Check if the object is an iterator. @@ -1095,6 +1098,7 @@ def indices_fast(ndarray[intp_t, ndim=1] index, const int64_t[:] labels, list ke # core.common import for fast inference checks +@set_module("pandas.api.types") def is_float(obj: object) -> bool: """ Return True if given object is float. @@ -1128,6 +1132,7 @@ def is_float(obj: object) -> bool: return util.is_float_object(obj) +@set_module("pandas.api.types") def is_integer(obj: object) -> bool: """ Return True if given object is integer. @@ -1172,6 +1177,7 @@ def is_int_or_none(obj) -> bool: return obj is None or util.is_integer_object(obj) +@set_module("pandas.api.types") def is_bool(obj: object) -> bool: """ Return True if given object is boolean. @@ -1202,6 +1208,7 @@ def is_bool(obj: object) -> bool: return util.is_bool_object(obj) +@set_module("pandas.api.types") def is_complex(obj: object) -> bool: """ Return True if given object is complex. @@ -1237,6 +1244,7 @@ cpdef bint is_decimal(object obj): return isinstance(obj, Decimal) +@set_module("pandas.api.types") def is_list_like(obj: object, allow_sets: bool = True) -> bool: """ Check if the object is list-like. @@ -1520,6 +1528,7 @@ cdef object _try_infer_map(object dtype): return None +@set_module("pandas.api.types") def infer_dtype(value: object, skipna: bool = True) -> str: """ Return a string label of the type of the elements in a list-like input. diff --git a/pandas/_testing/asserters.py b/pandas/_testing/asserters.py index fc2cae5fadaa7..3f95997bb84f4 100644 --- a/pandas/_testing/asserters.py +++ b/pandas/_testing/asserters.py @@ -17,7 +17,10 @@ import pandas._libs.testing as _testing from pandas._libs.tslibs.np_datetime import compare_mismatched_resolutions from pandas.errors import Pandas4Warning -from pandas.util._decorators import deprecate_kwarg +from pandas.util._decorators import ( + deprecate_kwarg, + set_module, +) from pandas.core.dtypes.common import ( is_bool, @@ -181,6 +184,7 @@ def assert_dict_equal(left, right, compare_keys: bool = True) -> None: _testing.assert_dict_equal(left, right, compare_keys=compare_keys) +@set_module("pandas.testing") def assert_index_equal( left: Index, right: Index, @@ -695,6 +699,7 @@ def _raise(left, right, err_msg) -> NoReturn: assert_attr_equal("dtype", left, right, obj=obj) +@set_module("pandas.testing") def assert_extension_array_equal( left, right, @@ -850,6 +855,7 @@ def assert_extension_array_equal( # This could be refactored to use the NDFrame.equals method +@set_module("pandas.testing") @deprecate_kwarg(Pandas4Warning, "check_datetimelike_compat", new_arg_name=None) def assert_series_equal( left, @@ -1143,6 +1149,7 @@ def assert_series_equal( # This could be refactored to use the NDFrame.equals method +@set_module("pandas.testing") @deprecate_kwarg(Pandas4Warning, "check_datetimelike_compat", new_arg_name=None) def assert_frame_equal( left, diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py index 47cf0452bc32c..fe2e8f34807ea 100644 --- a/pandas/core/accessor.py +++ b/pandas/core/accessor.py @@ -14,7 +14,10 @@ ) import warnings -from pandas.util._decorators import doc +from pandas.util._decorators import ( + doc, + set_module, +) from pandas.util._exceptions import find_stack_level if TYPE_CHECKING: @@ -323,6 +326,7 @@ def decorator(accessor: TypeT) -> TypeT: dtype: int64""" +@set_module("pandas.api.extensions") @doc(_register_accessor, klass="DataFrame", examples=_register_df_examples) def register_dataframe_accessor(name: str) -> Callable[[TypeT], TypeT]: from pandas import DataFrame @@ -354,6 +358,7 @@ def register_dataframe_accessor(name: str) -> Callable[[TypeT], TypeT]: np.int64(6)""" +@set_module("pandas.api.extensions") @doc(_register_accessor, klass="Series", examples=_register_series_examples) def register_series_accessor(name: str) -> Callable[[TypeT], TypeT]: from pandas import Series @@ -388,6 +393,7 @@ def register_series_accessor(name: str) -> Callable[[TypeT], TypeT]: [2, 8]""" +@set_module("pandas.api.extensions") @doc(_register_accessor, klass="Index", examples=_register_index_examples) def register_index_accessor(name: str) -> Callable[[TypeT], TypeT]: from pandas import Index diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index bbca78459ca75..03127e58a9f41 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -35,7 +35,10 @@ TakeIndexer, npt, ) -from pandas.util._decorators import doc +from pandas.util._decorators import ( + doc, + set_module, +) from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.cast import ( @@ -325,6 +328,7 @@ def unique(values: T) -> T: ... def unique(values: np.ndarray | Series) -> np.ndarray: ... +@set_module("pandas") def unique(values): """ Return unique values based on a hash table. @@ -649,6 +653,7 @@ def factorize_array( return codes, uniques +@set_module("pandas") @doc( values=dedent( """\ @@ -1111,6 +1116,7 @@ def rank( # ---- # +@set_module("pandas.api.extensions") def take( arr, indices: TakeIndexer, diff --git a/pandas/core/col.py b/pandas/core/col.py index 0182188dd0317..416a34aaa6c4a 100644 --- a/pandas/core/col.py +++ b/pandas/core/col.py @@ -9,6 +9,8 @@ Any, ) +from pandas.util._decorators import set_module + from pandas.core.series import Series if TYPE_CHECKING: @@ -222,6 +224,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Expression: return wrapper +@set_module("pandas") def col(col_name: Hashable) -> Expression: """ Generate deferred object representing a column of a DataFrame. diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py index f8e3200ef2ba0..a15d3dc86c841 100644 --- a/pandas/core/computation/eval.py +++ b/pandas/core/computation/eval.py @@ -11,6 +11,7 @@ ) import warnings +from pandas.util._decorators import set_module from pandas.util._exceptions import find_stack_level from pandas.util._validators import validate_bool_kwarg @@ -174,6 +175,7 @@ def _check_for_locals(expr: str, stack_level: int, parser: str) -> None: raise SyntaxError(msg) +@set_module("pandas") def eval( expr: str | BinOp, # we leave BinOp out of the docstr bc it isn't for users parser: str = "pandas", diff --git a/pandas/core/construction.py b/pandas/core/construction.py index eda9f4492219f..5868bdaa1225b 100644 --- a/pandas/core/construction.py +++ b/pandas/core/construction.py @@ -23,6 +23,7 @@ get_supported_dtype, is_supported_dtype, ) +from pandas.util._decorators import set_module from pandas.core.dtypes.base import ExtensionDtype from pandas.core.dtypes.cast import ( @@ -72,6 +73,7 @@ ) +@set_module("pandas") def array( data: Sequence[object] | AnyArrayLike, dtype: Dtype | None = None, diff --git a/pandas/core/dtypes/base.py b/pandas/core/dtypes/base.py index 709f96125da39..ee48cf45c6c9f 100644 --- a/pandas/core/dtypes/base.py +++ b/pandas/core/dtypes/base.py @@ -19,6 +19,7 @@ from pandas._libs.hashtable import object_hash from pandas._libs.properties import cache_readonly from pandas.errors import AbstractMethodError +from pandas.util._decorators import set_module from pandas.core.dtypes.generic import ( ABCDataFrame, @@ -480,6 +481,7 @@ def na_value(self) -> libmissing.NAType: return libmissing.NA +@set_module("pandas.api.extensions") def register_extension_dtype(cls: type_t[ExtensionDtypeT]) -> type_t[ExtensionDtypeT]: """ Register an ExtensionType with pandas as class decorator. diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index fc7cc59ecfb6a..dd63445266134 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -22,6 +22,7 @@ ) from pandas._libs.tslibs import conversion from pandas.errors import Pandas4Warning +from pandas.util._decorators import set_module from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.base import _registry as registry @@ -138,6 +139,7 @@ def _classes_and_not_datetimelike(*klasses) -> Callable: ) +@set_module("pandas.api.types") def is_object_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the object dtype. @@ -183,6 +185,7 @@ def is_object_dtype(arr_or_dtype) -> bool: return _is_dtype_type(arr_or_dtype, classes(np.object_)) +@set_module("pandas.api.types") def is_sparse(arr) -> bool: """ Check whether an array-like is a 1-D pandas sparse array. @@ -282,6 +285,7 @@ def is_scipy_sparse(arr) -> bool: return _is_scipy_sparse(arr) +@set_module("pandas.api.types") def is_datetime64_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the datetime64 dtype. @@ -323,6 +327,7 @@ def is_datetime64_dtype(arr_or_dtype) -> bool: return _is_dtype_type(arr_or_dtype, classes(np.datetime64)) +@set_module("pandas.api.types") def is_datetime64tz_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of a DatetimeTZDtype dtype. @@ -384,6 +389,7 @@ def is_datetime64tz_dtype(arr_or_dtype) -> bool: return DatetimeTZDtype.is_dtype(arr_or_dtype) +@set_module("pandas.api.types") def is_timedelta64_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the timedelta64 dtype. @@ -426,6 +432,7 @@ def is_timedelta64_dtype(arr_or_dtype) -> bool: return _is_dtype_type(arr_or_dtype, classes(np.timedelta64)) +@set_module("pandas.api.types") def is_period_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the Period dtype. @@ -479,6 +486,7 @@ def is_period_dtype(arr_or_dtype) -> bool: return PeriodDtype.is_dtype(arr_or_dtype) +@set_module("pandas.api.types") def is_interval_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the Interval dtype. @@ -537,6 +545,7 @@ def is_interval_dtype(arr_or_dtype) -> bool: return IntervalDtype.is_dtype(arr_or_dtype) +@set_module("pandas.api.types") def is_categorical_dtype(arr_or_dtype) -> bool: """ Check whether an array-like or dtype is of the Categorical dtype. @@ -598,6 +607,7 @@ def is_string_or_object_np_dtype(dtype: np.dtype) -> bool: return dtype == object or dtype.kind in "SU" +@set_module("pandas.api.types") def is_string_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of the string dtype. @@ -650,6 +660,7 @@ def condition(dtype) -> bool: return _is_dtype(arr_or_dtype, condition) +@set_module("pandas.api.types") def is_dtype_equal(source, target) -> bool: """ Check if two dtypes are equal. @@ -714,6 +725,7 @@ def is_dtype_equal(source, target) -> bool: return False +@set_module("pandas.api.types") def is_integer_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of an integer dtype. @@ -780,6 +792,7 @@ def is_integer_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_signed_integer_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a signed integer dtype. @@ -848,6 +861,7 @@ def is_signed_integer_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_unsigned_integer_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of an unsigned integer dtype. @@ -907,6 +921,7 @@ def is_unsigned_integer_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_int64_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of the int64 dtype. @@ -980,6 +995,7 @@ def is_int64_dtype(arr_or_dtype) -> bool: return _is_dtype_type(arr_or_dtype, classes(np.int64)) +@set_module("pandas.api.types") def is_datetime64_any_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of the datetime64 dtype. @@ -1042,6 +1058,7 @@ def is_datetime64_any_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_datetime64_ns_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of the datetime64[ns] dtype. @@ -1097,6 +1114,7 @@ def is_datetime64_ns_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_timedelta64_ns_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of the timedelta64[ns] dtype. @@ -1224,6 +1242,7 @@ def needs_i8_conversion(dtype: DtypeObj | None) -> bool: return isinstance(dtype, (PeriodDtype, DatetimeTZDtype)) +@set_module("pandas.api.types") def is_numeric_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a numeric dtype. @@ -1278,6 +1297,7 @@ def is_numeric_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_any_real_numeric_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a real number dtype. @@ -1321,6 +1341,7 @@ def is_any_real_numeric_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_float_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a float dtype. @@ -1368,6 +1389,7 @@ def is_float_dtype(arr_or_dtype) -> bool: ) +@set_module("pandas.api.types") def is_bool_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a boolean dtype. @@ -1455,6 +1477,7 @@ def is_1d_only_ea_dtype(dtype: DtypeObj | None) -> bool: return isinstance(dtype, ExtensionDtype) and not dtype._supports_2d +@set_module("pandas.api.types") def is_extension_array_dtype(arr_or_dtype) -> bool: """ Check if an object is a pandas extension array type. @@ -1532,6 +1555,7 @@ def is_ea_or_datetimelike_dtype(dtype: DtypeObj | None) -> bool: return isinstance(dtype, ExtensionDtype) or (lib.is_np_dtype(dtype, "mM")) +@set_module("pandas.api.types") def is_complex_dtype(arr_or_dtype) -> bool: """ Check whether the provided array or dtype is of a complex dtype. @@ -1794,6 +1818,7 @@ def validate_all_hashable(*args, error_name: str | None = None) -> None: raise TypeError("All elements must be hashable") +@set_module("pandas.api.types") def pandas_dtype(dtype) -> DtypeObj: """ Convert input into a pandas only dtype object or a numpy dtype object. diff --git a/pandas/core/dtypes/concat.py b/pandas/core/dtypes/concat.py index 78833568025a3..51c5a4afe14c5 100644 --- a/pandas/core/dtypes/concat.py +++ b/pandas/core/dtypes/concat.py @@ -12,6 +12,7 @@ import numpy as np from pandas._libs import lib +from pandas.util._decorators import set_module from pandas.core.dtypes.astype import astype_array from pandas.core.dtypes.cast import ( @@ -168,6 +169,7 @@ def _get_result_dtype( return any_ea, kinds, target_dtype +@set_module("pandas.api.types") def union_categoricals( to_union, sort_categories: bool = False, ignore_order: bool = False ) -> Categorical: diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 928e44e51b3cf..5e6f6d2124734 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -14,6 +14,7 @@ import numpy as np from pandas._libs import lib +from pandas.util._decorators import set_module if TYPE_CHECKING: from collections.abc import Hashable @@ -35,6 +36,7 @@ is_iterator = lib.is_iterator +@set_module("pandas.api.types") def is_number(obj: object) -> TypeGuard[Number | np.number]: """ Check if the object is a number. @@ -101,6 +103,7 @@ def iterable_not_string(obj: object) -> bool: return isinstance(obj, abc.Iterable) and not isinstance(obj, str) +@set_module("pandas.api.types") def is_file_like(obj: object) -> bool: """ Check if the object is a file-like object. @@ -148,6 +151,7 @@ def is_file_like(obj: object) -> bool: return bool(hasattr(obj, "__iter__")) +@set_module("pandas.api.types") def is_re(obj: object) -> TypeGuard[Pattern]: """ Check if the object is a regex pattern instance. @@ -184,6 +188,7 @@ def is_re(obj: object) -> TypeGuard[Pattern]: return isinstance(obj, Pattern) +@set_module("pandas.api.types") def is_re_compilable(obj: object) -> bool: """ Check if the object can be compiled into a regex pattern instance. @@ -218,6 +223,7 @@ def is_re_compilable(obj: object) -> bool: return True +@set_module("pandas.api.types") def is_array_like(obj: object) -> bool: """ Check if the object is array-like. @@ -297,6 +303,7 @@ def is_nested_list_like(obj: object) -> bool: ) +@set_module("pandas.api.types") def is_dict_like(obj: object) -> bool: """ Check if the object is dict-like. @@ -339,6 +346,7 @@ def is_dict_like(obj: object) -> bool: ) +@set_module("pandas.api.types") def is_named_tuple(obj: object) -> bool: """ Check if the object is a named tuple. @@ -376,6 +384,7 @@ def is_named_tuple(obj: object) -> bool: return isinstance(obj, abc.Sequence) and hasattr(obj, "_fields") +@set_module("pandas.api.types") def is_hashable(obj: object) -> TypeGuard[Hashable]: """ Return True if hash(obj) will succeed, False otherwise. diff --git a/pandas/core/indexers/utils.py b/pandas/core/indexers/utils.py index cfc49b8083267..85c298d8c3a48 100644 --- a/pandas/core/indexers/utils.py +++ b/pandas/core/indexers/utils.py @@ -12,6 +12,7 @@ import numpy as np from pandas._libs import lib +from pandas.util._decorators import set_module from pandas.core.dtypes.common import ( is_array_like, @@ -417,6 +418,7 @@ def unpack_tuple_and_ellipses(item: tuple): # Public indexer validation +@set_module("pandas.api.indexers") def check_array_indexer(array: AnyArrayLike, indexer: Any) -> Any: """ Check if `indexer` is a valid array indexer for `array`. diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 41925864abbbf..063d375f0595f 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1108,6 +1108,7 @@ def _is_type_compatible(a, b) -> bool: ) +@set_module("pandas") def interval_range( start=None, end=None, diff --git a/pandas/core/interchange/from_dataframe.py b/pandas/core/interchange/from_dataframe.py index daef8287e3263..bcbeb546f845c 100644 --- a/pandas/core/interchange/from_dataframe.py +++ b/pandas/core/interchange/from_dataframe.py @@ -12,6 +12,7 @@ from pandas._config import using_string_dtype from pandas.compat._optional import import_optional_dependency +from pandas.util._decorators import set_module import pandas as pd from pandas.core.interchange.dataframe_protocol import ( @@ -34,6 +35,7 @@ } +@set_module("pandas.api.interchange") def from_dataframe(df, allow_copy: bool = True) -> pd.DataFrame: """ Build a ``pd.DataFrame`` from any DataFrame supporting the interchange protocol. diff --git a/pandas/core/reshape/encoding.py b/pandas/core/reshape/encoding.py index 775dd9ca38ca3..5c5fed272b925 100644 --- a/pandas/core/reshape/encoding.py +++ b/pandas/core/reshape/encoding.py @@ -12,6 +12,7 @@ from pandas._libs import missing as libmissing from pandas._libs.sparse import IntIndex +from pandas.util._decorators import set_module from pandas.core.dtypes.common import ( is_integer_dtype, @@ -38,6 +39,7 @@ from pandas._typing import NpDtype +@set_module("pandas") def get_dummies( data, prefix=None, @@ -364,6 +366,7 @@ def get_empty_frame(data) -> DataFrame: return DataFrame(dummy_mat, index=index, columns=dummy_cols, dtype=_dtype) +@set_module("pandas") def from_dummies( data: DataFrame, sep: None | str = None, diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 2084088d6a823..c9dd179fac6e0 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -8,6 +8,8 @@ from pandas._config import get_option +from pandas.util._decorators import set_module + from pandas.core.dtypes.common import ( is_integer, is_list_like, @@ -44,6 +46,7 @@ def holds_integer(column: Index) -> bool: return column.dtype.kind in "iu" +@set_module("pandas.plotting") def hist_series( self: Series, by=None, @@ -142,6 +145,7 @@ def hist_series( ) +@set_module("pandas.plotting") def hist_frame( data: DataFrame, column: IndexLabel | None = None, @@ -271,6 +275,7 @@ def hist_frame( ) +@set_module("pandas.plotting") def boxplot( data: DataFrame, column: str | list[str] | None = None, @@ -458,6 +463,7 @@ def boxplot( ) +@set_module("pandas.plotting") def boxplot_frame( self: DataFrame, column=None, @@ -615,6 +621,9 @@ def boxplot_frame( returned by `boxplot`. When ``return_type='axes'`` is selected, the matplotlib axes on which the boxplot is drawn are returned: + .. plot:: + :context: close-figs + >>> boxplot = df.boxplot(column=["Col1", "Col2"], return_type="axes") >>> type(boxplot) @@ -622,6 +631,9 @@ def boxplot_frame( When grouping with ``by``, a Series mapping columns to ``return_type`` is returned: + .. plot:: + :context: close-figs + >>> boxplot = df.boxplot(column=["Col1", "Col2"], by="X", return_type="axes") >>> type(boxplot) @@ -629,6 +641,9 @@ def boxplot_frame( If ``return_type`` is `None`, a NumPy array of axes with the same shape as ``layout`` is returned: + .. plot:: + :context: close-figs + >>> boxplot = df.boxplot(column=["Col1", "Col2"], by="X", return_type=None) >>> type(boxplot) @@ -650,6 +665,7 @@ def boxplot_frame( ) +@set_module("pandas.plotting") def boxplot_frame_groupby( grouped: DataFrameGroupBy, subplots: bool = True, diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index f5f62b168450d..42b02ce99cb3c 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -6,6 +6,8 @@ Any, ) +from pandas.util._decorators import set_module + from pandas.plotting._core import _get_plot_backend if TYPE_CHECKING: @@ -26,6 +28,7 @@ ) +@set_module("pandas.plotting") def table(ax: Axes, data: DataFrame | Series, **kwargs) -> Table: """ Helper function to convert DataFrame and Series to matplotlib.table. @@ -79,6 +82,7 @@ def table(ax: Axes, data: DataFrame | Series, **kwargs) -> Table: ) +@set_module("pandas.plotting") def register() -> None: """ Register pandas formatters and converters with matplotlib. @@ -125,6 +129,7 @@ def register() -> None: plot_backend.register() +@set_module("pandas.plotting") def deregister() -> None: """ Remove pandas formatters and converters. @@ -169,6 +174,7 @@ def deregister() -> None: plot_backend.deregister() +@set_module("pandas.plotting") def scatter_matrix( frame: DataFrame, alpha: float = 0.5, @@ -264,6 +270,7 @@ def scatter_matrix( ) +@set_module("pandas.plotting") def radviz( frame: DataFrame, class_column: str, @@ -352,6 +359,7 @@ def radviz( ) +@set_module("pandas.plotting") def andrews_curves( frame: DataFrame, class_column: str, @@ -427,6 +435,7 @@ def andrews_curves( ) +@set_module("pandas.plotting") def bootstrap_plot( series: Series, fig: Figure | None = None, @@ -487,6 +496,7 @@ def bootstrap_plot( ) +@set_module("pandas.plotting") def parallel_coordinates( frame: DataFrame, class_column: str, @@ -573,6 +583,7 @@ def parallel_coordinates( ) +@set_module("pandas.plotting") def lag_plot(series: Series, lag: int = 1, ax: Axes | None = None, **kwds) -> Axes: """ Lag plot for time series. @@ -622,13 +633,13 @@ def lag_plot(series: Series, lag: int = 1, ax: Axes | None = None, **kwds) -> Ax .. plot:: :context: close-figs - >>> pd.plotting.lag_plot(s, lag=1) - + >>> _ = pd.plotting.lag_plot(s, lag=1) """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.lag_plot(series=series, lag=lag, ax=ax, **kwds) +@set_module("pandas.plotting") def autocorrelation_plot(series: Series, ax: Axes | None = None, **kwargs) -> Axes: """ Autocorrelation plot for time series. diff --git a/pandas/tests/api/test_api.py b/pandas/tests/api/test_api.py index 6cf182b65cdb9..c699bc92c6212 100644 --- a/pandas/tests/api/test_api.py +++ b/pandas/tests/api/test_api.py @@ -486,91 +486,6 @@ def test_util_in_top_level(self): pd.util.foo -def test_set_module(): - assert pd.DataFrame.__module__ == "pandas" - assert pd.CategoricalDtype.__module__ == "pandas" - assert pd.DatetimeTZDtype.__module__ == "pandas" - assert pd.PeriodDtype.__module__ == "pandas" - assert pd.IntervalDtype.__module__ == "pandas" - assert pd.SparseDtype.__module__ == "pandas" - assert pd.ArrowDtype.__module__ == "pandas" - assert pd.StringDtype.__module__ == "pandas" - assert pd.BooleanDtype.__module__ == "pandas" - assert pd.Int8Dtype.__module__ == "pandas" - assert pd.Int16Dtype.__module__ == "pandas" - assert pd.Int32Dtype.__module__ == "pandas" - assert pd.Int64Dtype.__module__ == "pandas" - assert pd.UInt8Dtype.__module__ == "pandas" - assert pd.UInt16Dtype.__module__ == "pandas" - assert pd.UInt32Dtype.__module__ == "pandas" - assert pd.UInt64Dtype.__module__ == "pandas" - assert pd.Float32Dtype.__module__ == "pandas" - assert pd.Float64Dtype.__module__ == "pandas" - assert pd.Index.__module__ == "pandas" - assert pd.CategoricalIndex.__module__ == "pandas" - assert pd.DatetimeIndex.__module__ == "pandas" - assert pd.IntervalIndex.__module__ == "pandas" - assert pd.MultiIndex.__module__ == "pandas" - assert pd.PeriodIndex.__module__ == "pandas" - assert pd.RangeIndex.__module__ == "pandas" - assert pd.TimedeltaIndex.__module__ == "pandas" - assert pd.Period.__module__ == "pandas" - assert pd.Timestamp.__module__ == "pandas" - assert pd.Timedelta.__module__ == "pandas" - assert pd.concat.__module__ == "pandas" - assert pd.isna.__module__ == "pandas" - assert pd.notna.__module__ == "pandas" - assert pd.merge.__module__ == "pandas" - assert pd.merge_ordered.__module__ == "pandas" - assert pd.merge_asof.__module__ == "pandas" - assert pd.read_csv.__module__ == "pandas" - assert pd.read_table.__module__ == "pandas" - assert pd.read_fwf.__module__ == "pandas" - assert pd.Series.__module__ == "pandas" - assert pd.date_range.__module__ == "pandas" - assert pd.bdate_range.__module__ == "pandas" - assert pd.period_range.__module__ == "pandas" - assert pd.timedelta_range.__module__ == "pandas" - assert pd.to_datetime.__module__ == "pandas" - assert pd.to_timedelta.__module__ == "pandas" - assert pd.to_numeric.__module__ == "pandas" - assert pd.NamedAgg.__module__ == "pandas" - assert pd.IndexSlice.__module__ == "pandas" - assert pd.lreshape.__module__ == "pandas" - assert pd.melt.__module__ == "pandas" - assert pd.wide_to_long.__module__ == "pandas" - assert pd.crosstab.__module__ == "pandas" - assert pd.pivot_table.__module__ == "pandas" - assert pd.pivot.__module__ == "pandas" - assert pd.cut.__module__ == "pandas" - assert pd.qcut.__module__ == "pandas" - assert pd.read_clipboard.__module__ == "pandas" - assert pd.ExcelFile.__module__ == "pandas" - assert pd.ExcelWriter.__module__ == "pandas" - assert pd.read_excel.__module__ == "pandas" - assert pd.read_feather.__module__ == "pandas" - assert pd.set_eng_float_format.__module__ == "pandas" - assert pd.read_html.__module__ == "pandas" - assert pd.read_iceberg.__module__ == "pandas" - assert pd.read_json.__module__ == "pandas" - assert pd.json_normalize.__module__ == "pandas" - assert pd.read_orc.__module__ == "pandas" - assert pd.read_parquet.__module__ == "pandas" - assert pd.read_pickle.__module__ == "pandas" - assert pd.to_pickle.__module__ == "pandas" - assert pd.HDFStore.__module__ == "pandas" - assert pd.read_hdf.__module__ == "pandas" - assert pd.read_sas.__module__ == "pandas" - assert pd.read_spss.__module__ == "pandas" - assert pd.read_sql.__module__ == "pandas" - assert pd.read_sql_query.__module__ == "pandas" - assert pd.read_sql_table.__module__ == "pandas" - assert pd.read_stata.__module__ == "pandas" - assert pd.read_xml.__module__ == "pandas" - assert api.typing.SeriesGroupBy.__module__ == "pandas.api.typing" - assert api.typing.DataFrameGroupBy.__module__ == "pandas.api.typing" - - def get_pandas_objects( module_name: str, recurse: bool ) -> list[tuple[str, str, object]]: @@ -595,10 +510,6 @@ def get_pandas_objects( objs = [] for name, obj in inspect.getmembers(module): - if inspect.isfunction(obj) or type(obj).__name__ == "cython_function_or_method": - # We have not set __module__ on public functions; may do - # so in the future. - continue module_dunder = getattr(obj, "__module__", None) if isinstance(module_dunder, str) and module_dunder.startswith("pandas"): objs.append((module_name, name, obj)) @@ -633,6 +544,9 @@ def get_pandas_objects( ], ) def test_attributes_module(module_name): + """ + Ensures that all public objects have their __module__ set to the public import path. + """ recurse = module_name not in ["pandas", "pandas.testing"] objs = get_pandas_objects(module_name, recurse=recurse) failures = [ @@ -645,4 +559,14 @@ def test_attributes_module(module_name): or (name == "Categorical" and obj.__module__ == "pandas") ) ] - assert len(failures) == 0, failures + assert len(failures) == 0, "\n".join(str(e) for e in failures) + + # Check that all objects can indeed be imported from their __module__ + failures = [] + for module_name, name, obj in objs: + module = importlib.import_module(obj.__module__) + try: + getattr(module, name) + except Exception: + failures.append((module_name, name, type(obj), obj.__module__)) + assert len(failures) == 0, "\n".join(str(e) for e in failures) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 88ea1bfa3c6ed..4003221a06f6a 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -30,7 +30,10 @@ to_offset, ) from pandas._libs.tslibs.parsing import get_rule_month -from pandas.util._decorators import cache_readonly +from pandas.util._decorators import ( + cache_readonly, + set_module, +) from pandas.core.dtypes.common import is_numeric_dtype from pandas.core.dtypes.dtypes import ( @@ -83,6 +86,7 @@ def get_period_alias(offset_str: str) -> str | None: # Period codes +@set_module("pandas") def infer_freq( index: DatetimeIndex | TimedeltaIndex | Series | DatetimeLikeArrayMixin, ) -> str | None: diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index 00724c76b9ba8..8a97c70080228 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -8,6 +8,8 @@ import sys from typing import TYPE_CHECKING +from pandas.util._decorators import set_module + if TYPE_CHECKING: from pandas._typing import JSONSerializable @@ -90,6 +92,7 @@ def _get_dependency_info() -> dict[str, JSONSerializable]: return result +@set_module("pandas") def show_versions(as_json: str | bool = False) -> None: """ Provide useful information, important for bug reports. diff --git a/pandas/util/_tester.py b/pandas/util/_tester.py index f455e06dde8bb..e69ec8e123b08 100644 --- a/pandas/util/_tester.py +++ b/pandas/util/_tester.py @@ -8,10 +8,12 @@ import sys from pandas.compat._optional import import_optional_dependency +from pandas.util._decorators import set_module PKG = os.path.dirname(os.path.dirname(__file__)) +@set_module("pandas") def test(extra_args: list[str] | None = None, run_doctests: bool = False) -> None: # noqa: PT028 """ Run the pandas test suite using pytest.