Skip to content

TYP: use __all__ to signal public API to type checkers #43695

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 10 commits into from
Sep 28, 2021
124 changes: 120 additions & 4 deletions pandas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
qcut,
)

import pandas.api
from pandas import api, arrays, errors, io, plotting, testing, tseries
from pandas.util._print_versions import show_versions

from pandas.io.api import (
Expand Down Expand Up @@ -170,8 +170,6 @@
from pandas.io.json import _json_normalize as json_normalize

from pandas.util._tester import test
import pandas.testing
import pandas.arrays

# use the closest tagged version if possible
from pandas._version import get_versions
Expand All @@ -181,7 +179,6 @@
__git_version__ = v.get("full-revisionid")
del get_versions, v


# GH 27101
__deprecated_num_index_names = ["Float64Index", "Int64Index", "UInt64Index"]

Expand Down Expand Up @@ -303,3 +300,122 @@ def __getattr__(name):
- Time series-specific functionality: date range generation and frequency
conversion, moving window statistics, date shifting and lagging.
"""

# Use __all__ to let type checkers know what is part of the public API.
# Pandas is not (yet) a py.typed library: the public API is determined
# based on the documentation.
__all__ = [
"BooleanDtype",
"Categorical",
"CategoricalDtype",
"CategoricalIndex",
"DataFrame",
"DateOffset",
"DatetimeIndex",
"DatetimeTZDtype",
"ExcelFile",
"ExcelWriter",
"Flags",
"Float32Dtype",
"Float64Dtype",
"Grouper",
"HDFStore",
"Index",
"IndexSlice",
"Int16Dtype",
"Int32Dtype",
"Int64Dtype",
"Int8Dtype",
"Interval",
"IntervalDtype",
"IntervalIndex",
"MultiIndex",
"NA",
"NaT",
"NamedAgg",
"NumericIndex",
"Period",
"PeriodDtype",
"PeriodIndex",
"RangeIndex",
"Series",
"SparseDtype",
"StringDtype",
"Timedelta",
"TimedeltaIndex",
"Timestamp",
"UInt16Dtype",
"UInt32Dtype",
"UInt64Dtype",
"UInt8Dtype",
"api",
"array",
"arrays",
"bdate_range",
"concat",
"crosstab",
"cut",
"date_range",
"describe_option",
"errors",
"eval",
"factorize",
"get_dummies",
"get_option",
"infer_freq",
"interval_range",
"io",
"isna",
"isnull",
"json_normalize",
"lreshape",
"melt",
"merge",
"merge_asof",
"merge_ordered",
"notna",
"notnull",
"offsets",
"option_context",
"options",
"period_range",
"pivot",
"pivot_table",
"plotting",
"qcut",
"read_clipboard",
"read_csv",
"read_excel",
"read_feather",
"read_fwf",
"read_gbq",
"read_hdf",
"read_html",
"read_json",
"read_orc",
"read_parquet",
"read_pickle",
"read_sas",
"read_spss",
"read_sql",
"read_sql_query",
"read_sql_table",
"read_stata",
"read_table",
"read_xml",
"reset_option",
"set_eng_float_format",
"set_option",
"show_versions",
"test",
"testing",
"timedelta_range",
"to_datetime",
"to_numeric",
"to_pickle",
"to_timedelta",
"tseries",
"unique",
"value_counts",
"wide_to_long",
]
12 changes: 12 additions & 0 deletions pandas/io/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
# import modules that have public classes/functions
from pandas.io import (
formats,
json,
stata,
)

# and mark only those modules as public
__all__ = ["formats", "json", "stata"]
8 changes: 8 additions & 0 deletions pandas/io/formats/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
# import modules that have public classes/functions
from pandas.io.formats import style

# and mark only those modules as public
__all__ = ["style"]
38 changes: 28 additions & 10 deletions pandas/tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,18 @@ class TestPDApi(Base):
ignored = ["tests", "locale", "conftest"]

# top-level sub-packages
lib = [
public_lib = [
"api",
"arrays",
"compat",
"core",
"errors",
"pandas",
"plotting",
"options",
"test",
"testing",
"tseries",
"util",
"options",
"errors",
"plotting",
"io",
"tseries",
]
private_lib = ["compat", "core", "pandas", "util"]

# these are already deprecated; awaiting removal
deprecated_modules: list[str] = ["np", "datetime"]
Expand Down Expand Up @@ -204,7 +201,8 @@ class TestPDApi(Base):
def test_api(self):

checkthese = (
self.lib
self.public_lib
+ self.private_lib
+ self.misc
+ self.modules
+ self.classes
Expand All @@ -217,6 +215,26 @@ def test_api(self):
)
self.check(namespace=pd, expected=checkthese, ignored=self.ignored)

def test_api_all(self):
expected = set(
self.public_lib
+ self.misc
+ self.modules
+ self.classes
+ self.funcs
+ self.funcs_option
+ self.funcs_read
+ self.funcs_json
+ self.funcs_to
) - set(self.deprecated_classes)
actual = set(pd.__all__)

extraneous = actual - expected
assert not extraneous

missing = expected - actual
assert not missing
Copy link
Member Author

Choose a reason for hiding this comment

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

assert actual == expected would work, but it doesn't create a nice log in pytest.


def test_depr(self):
deprecated_list = (
self.deprecated_modules
Expand Down
11 changes: 11 additions & 0 deletions pandas/tseries/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
# import modules that have public classes/functions:
from pandas.tseries import (
frequencies,
offsets,
)

# and mark only those modules as public
__all__ = ["frequencies", "offsets"]