diff --git a/docs/tests.md b/docs/tests.md index f81d2d050..a42b4dcd0 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -11,5 +11,6 @@ Here are the most important options. Fore more details, please use `poe --help`. - Run only pytest: `poe pytest` - Run only pre-commit: `poe style` - Run tests against the installed stubs (this will install and uninstall the stubs): `poe test_dist` +- Optional: Run stubtest to compare the installed pandas-stubs against pandas (this will fail): `poe stubtest`. If you have created an allowlist to ignore certain errors: `poe stubtest path_to_the_allow_list` These tests originally came from https://github.com/VirtusLab/pandas-stubs. diff --git a/pandas-stubs/core/arrays/_arrow_utils.pyi b/pandas-stubs/core/arrays/_arrow_utils.pyi deleted file mode 100644 index e69de29bb..000000000 diff --git a/pandas-stubs/core/arrays/categorical.pyi b/pandas-stubs/core/arrays/categorical.pyi index 31674f6f5..c9621716d 100644 --- a/pandas-stubs/core/arrays/categorical.pyi +++ b/pandas-stubs/core/arrays/categorical.pyi @@ -67,15 +67,15 @@ class Categorical(ExtensionArray, PandasObject): @overload def set_ordered(self, value, inplace: Literal[True]) -> None: ... @overload - def set_ordered(self, value, inplace: Literal[False]) -> Categorical: ... + def set_ordered(self, value, inplace: Literal[False] = ...) -> Categorical: ... @overload - def set_ordered(self, value, inplace: bool) -> Categorical | None: ... + def set_ordered(self, value, inplace: bool = ...) -> Categorical | None: ... @overload def as_ordered(self, inplace: Literal[True]) -> None: ... @overload - def as_ordered(self, inplace: Literal[False]) -> Categorical: ... + def as_ordered(self, inplace: Literal[False] = ...) -> Categorical: ... @overload - def as_ordered(self, inplace: bool) -> Categorical | None: ... + def as_ordered(self, inplace: bool = ...) -> Categorical | None: ... @overload def as_unordered(self, inplace: Literal[True]) -> None: ... @overload @@ -155,7 +155,7 @@ class Categorical(ExtensionArray, PandasObject): def __ge__(self, other) -> bool: ... @property def shape(self): ... - def shift(self, periods, fill_value=...): ... + def shift(self, periods=..., fill_value=...): ... def __array__(self, dtype=...) -> np.ndarray: ... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... @property diff --git a/pandas-stubs/core/computation/expressions.pyi b/pandas-stubs/core/computation/expressions.pyi index 90d1243a0..63481a24b 100644 --- a/pandas-stubs/core/computation/expressions.pyi +++ b/pandas-stubs/core/computation/expressions.pyi @@ -1,6 +1,6 @@ def set_use_numexpr(v: bool = ...) -> None: ... def set_numexpr_threads(n=...) -> None: ... -def evaluate(op, op_str, a, b, use_numexpr: bool = ...): ... +def evaluate(op, a, b, use_numexpr: bool = ...): ... def where(cond, a, b, use_numexpr: bool = ...): ... def set_test_mode(v: bool = ...) -> None: ... def get_test_result(): ... diff --git a/pandas-stubs/core/computation/ops.pyi b/pandas-stubs/core/computation/ops.pyi index 67c147362..70ce7351d 100644 --- a/pandas-stubs/core/computation/ops.pyi +++ b/pandas-stubs/core/computation/ops.pyi @@ -1,7 +1,7 @@ import numpy as np class UndefinedVariableError(NameError): - def __init__(self, name, is_local: bool) -> None: ... + def __init__(self, name, is_local: bool = ...) -> None: ... class Term: def __new__(cls, name, env, side=..., encoding=...): ... diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index a081d5c09..62b6ad33a 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -1291,7 +1291,7 @@ class DataFrame(NDFrame, OpsMixin): fill_value: float | None = ..., ) -> DataFrame: ... def droplevel( - self, level: Level | list[Level] = ..., axis: AxisType = ... + self, level: Level | list[Level], axis: AxisType = ... ) -> DataFrame: ... def eq( self, other, axis: AxisType = ..., level: Level | None = ... diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 02f64b849..6f9b6deda 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -191,7 +191,7 @@ class Index(IndexOpsMixin, PandasObject): def map(self, mapper, na_action=...) -> Index: ... def isin(self, values, level=...) -> np_ndarray_bool: ... def slice_indexer(self, start=..., end=..., step=..., kind=...): ... - def get_slice_bound(self, label, side, kind): ... + def get_slice_bound(self, label, side, kind=...): ... def slice_locs(self, start=..., end=..., step=..., kind=...): ... def delete(self, loc): ... def insert(self, loc, item): ... diff --git a/pandas-stubs/core/indexes/category.pyi b/pandas-stubs/core/indexes/category.pyi index fbcab8576..1a4d4dfbf 100644 --- a/pandas-stubs/core/indexes/category.pyi +++ b/pandas-stubs/core/indexes/category.pyi @@ -25,7 +25,7 @@ class CategoricalIndex(ExtensionIndex, accessor.PandasDelegate): def __contains__(self, key) -> bool: ... def __array__(self, dtype=...) -> np.ndarray: ... def astype(self, dtype: DtypeArg, copy: bool = ...) -> Index: ... - def fillna(self, value, downcast=...): ... + def fillna(self, value=..., downcast=...): ... def is_unique(self) -> bool: ... @property def is_monotonic_increasing(self) -> bool: ... diff --git a/pandas-stubs/core/indexes/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index d70509921..b7b797917 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -117,7 +117,7 @@ class MultiIndex(Index): def get_indexer_non_unique(self, target): ... def reindex(self, target, method=..., level=..., limit=..., tolerance=...): ... def get_slice_bound( - self, label: Hashable | Sequence[Hashable], side: str, kind: str + self, label: Hashable | Sequence[Hashable], side: str, kind: str = ... ) -> int: ... def slice_locs(self, start=..., end=..., step=..., kind=...): ... def get_loc(self, key, method=...): ... diff --git a/pandas-stubs/core/ops/roperator.pyi b/pandas-stubs/core/ops/roperator.pyi deleted file mode 100644 index ec889046d..000000000 --- a/pandas-stubs/core/ops/roperator.pyi +++ /dev/null @@ -1,12 +0,0 @@ -def radd(left, right): ... -def rsub(left, right): ... -def rmul(left, right): ... -def rdiv(left, right): ... -def rtruediv(left, right): ... -def rfloordiv(left, right): ... -def rmod(left, right): ... -def rdivmod(left, right): ... -def rpow(left, right): ... -def rand_(left, right): ... -def ror_(left, right): ... -def rxor(left, right): ... diff --git a/pandas-stubs/core/resample.pyi b/pandas-stubs/core/resample.pyi index 7701bdd31..4d6d307dd 100644 --- a/pandas-stubs/core/resample.pyi +++ b/pandas-stubs/core/resample.pyi @@ -6,9 +6,7 @@ from pandas.core.groupby.grouper import Grouper as Grouper from pandas._typing import FrameOrSeriesUnion class Resampler(BaseGroupBy, ShallowMixin): - def __init__( - self, obj, groupby=..., axis: int = ..., kind=..., **kwargs - ) -> None: ... + def __init__(self, obj, groupby, axis: int = ..., kind=..., **kwargs) -> None: ... def __getattr__(self, attr: str): ... def __iter__(self): ... @property @@ -16,7 +14,7 @@ class Resampler(BaseGroupBy, ShallowMixin): @property def ax(self): ... def pipe(self, func, *args, **kwargs): ... - def aggregate(self, func, *args, **kwargs): ... + def aggregate(self, func=..., *args, **kwargs): ... agg = aggregate def transform(self, arg, *args, **kwargs): ... def pad(self, limit=...): ... diff --git a/pandas-stubs/core/reshape/reshape.pyi b/pandas-stubs/core/reshape/reshape.pyi index dc245ba20..f912c1cd1 100644 --- a/pandas-stubs/core/reshape/reshape.pyi +++ b/pandas-stubs/core/reshape/reshape.pyi @@ -1,33 +1,6 @@ import numpy as np from pandas.core.frame import DataFrame -class _Unstacker: - values = ... - value_columns = ... - fill_value = ... - constructor = ... - index = ... - level = ... - lift = ... - new_index_levels = ... - new_index_names = ... - removed_name = ... - removed_level = ... - removed_level_full = ... - def __init__( - self, - values: np.ndarray, - index, - level=..., - value_columns=..., - fill_value=..., - constructor=..., - ) -> None: ... - def get_result(self): ... - def get_new_values(self): ... - def get_new_columns(self): ... - def get_new_index(self): ... - def unstack(obj, level, fill_value=...): ... def stack(frame, level: int = ..., dropna: bool = ...): ... def stack_multiple(frame, level, dropna: bool = ...): ... diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index dd831af46..554de0a00 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -393,7 +393,7 @@ class Series(IndexOpsMixin, NDFrame, Generic[S1]): def count(self, level: None = ...) -> int: ... @overload def count(self, level: Hashable) -> Series[S1]: ... - def mode(self, dropna) -> Series[S1]: ... + def mode(self, dropna=...) -> Series[S1]: ... def unique(self) -> np.ndarray: ... @overload def drop_duplicates( diff --git a/pandas-stubs/core/window/rolling.pyi b/pandas-stubs/core/window/rolling.pyi index 17eae6ca3..6faaffd58 100644 --- a/pandas-stubs/core/window/rolling.pyi +++ b/pandas-stubs/core/window/rolling.pyi @@ -46,7 +46,7 @@ class _Window(PandasObject, ShallowMixin, SelectionMixin): def __getattr__(self, attr: str): ... def __iter__(self): ... def aggregate( - self, func: Callable | None = ..., *args, **kwargs + self, func: Callable | None, *args, **kwargs ) -> Scalar | FrameOrSeries: ... agg = aggregate diff --git a/pandas-stubs/io/gcs.pyi b/pandas-stubs/io/gcs.pyi deleted file mode 100644 index 99abd578e..000000000 --- a/pandas-stubs/io/gcs.pyi +++ /dev/null @@ -1,7 +0,0 @@ -from pandas._typing import FilePathOrBuffer - -gcsfs = ... - -def get_filepath_or_buffer( - filepath_or_buffer: FilePathOrBuffer, encoding=..., compression=..., mode=... -): ... diff --git a/pandas-stubs/io/parquet.pyi b/pandas-stubs/io/parquet.pyi index 2a81f976d..d4d299c7b 100644 --- a/pandas-stubs/io/parquet.pyi +++ b/pandas-stubs/io/parquet.pyi @@ -43,7 +43,7 @@ class FastParquetImpl(BaseImpl): def to_parquet( df: DataFrame, - path, + path=..., engine: str = ..., compression=..., index: bool | None = ..., diff --git a/pandas-stubs/io/s3.pyi b/pandas-stubs/io/s3.pyi deleted file mode 100644 index c284b6b8f..000000000 --- a/pandas-stubs/io/s3.pyi +++ /dev/null @@ -1,18 +0,0 @@ -from typing import ( - IO, - Any, -) - -from pandas._typing import FilePathOrBuffer - -s3fs = ... - -def get_file_and_filesystem( - filepath_or_buffer: FilePathOrBuffer, mode: str | None = ... -) -> tuple[IO, Any]: ... -def get_filepath_or_buffer( - filepath_or_buffer: FilePathOrBuffer, - encoding: str | None = ..., - compression: str | None = ..., - mode: str | None = ..., -) -> tuple[IO, str | None, str | None, bool]: ... diff --git a/pandas-stubs/io/sql.pyi b/pandas-stubs/io/sql.pyi index f6d01d72c..1616176e1 100644 --- a/pandas-stubs/io/sql.pyi +++ b/pandas-stubs/io/sql.pyi @@ -7,7 +7,6 @@ from typing import ( from pandas.core.base import PandasObject from pandas.core.frame import DataFrame -class SQLAlchemyRequired(ImportError): ... class DatabaseError(IOError): ... def execute(sql, con, cur=..., params=...): ... @@ -33,7 +32,7 @@ def read_sql_query( ) -> DataFrame: ... def read_sql( sql: str | Any, - con: str | Any = ..., + con: str | Any, index_col: str | Sequence[str] | None = ..., coerce_float: bool = ..., params: Sequence[str] | tuple[str, ...] | Mapping[str, str] | None = ..., diff --git a/pandas-stubs/util/_print_versions.pyi b/pandas-stubs/util/_print_versions.pyi index f38a3554c..54f00d0b2 100644 --- a/pandas-stubs/util/_print_versions.pyi +++ b/pandas-stubs/util/_print_versions.pyi @@ -1,3 +1,2 @@ -def get_sys_info() -> list[tuple[str, str | int | None]]: ... def show_versions(as_json: bool = ...) -> None: ... def main() -> int: ... diff --git a/pandas-stubs/util/_test_decorators.pyi b/pandas-stubs/util/_test_decorators.pyi index c591b4245..7496c9742 100644 --- a/pandas-stubs/util/_test_decorators.pyi +++ b/pandas-stubs/util/_test_decorators.pyi @@ -1,10 +1,6 @@ from typing import Callable def safe_import(mod_name: str, min_version: str | None = ...): ... - -tables = ... -xfail_non_writeable = ... - def skip_if_installed(package: str) -> Callable: ... def skip_if_no(package: str, min_version: str | None = ...) -> Callable: ... @@ -12,7 +8,6 @@ skip_if_no_mpl = ... skip_if_mpl = ... skip_if_32bit = ... skip_if_windows = ... -skip_if_windows_python_3 = ... skip_if_has_locale = ... skip_if_not_us_locale = ... skip_if_no_scipy = ... diff --git a/pyproject.toml b/pyproject.toml index 6bbaee95d..bffa994c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,10 @@ script = "scripts.test.run:pyright_src" help = "Run pyright on 'tests' using the installed stubs" script = "scripts.test:test(dist=True, type_checker='pyright')" +[tool.poe.tasks.stubtest] +script = "scripts.test:stubtest(allowlist)" +help = "Run stubtest to compare the installed stubs against pandas" +args = [{ name = "allowlist", positional = true, default = "", required = false, help= "Path to an allowlist (optional)" }] [tool.black] diff --git a/scripts/_job.py b/scripts/_job.py index 8f7fa17b2..2cf2bb579 100644 --- a/scripts/_job.py +++ b/scripts/_job.py @@ -63,5 +63,8 @@ def run_job(steps: List[Step]) -> None: end = time.perf_counter() logger.success(f"End: '{step.name}', runtime: {end - start:.3f} seconds.") + if not failed: + __rollback_job(rollback_steps) + if failed: sys.exit(1) diff --git a/scripts/test/__init__.py b/scripts/test/__init__.py index e6a0e1fe5..b5422a2fe 100644 --- a/scripts/test/__init__.py +++ b/scripts/test/__init__.py @@ -1,3 +1,5 @@ +import dataclasses +from functools import partial from typing import Literal from scripts._job import run_job @@ -10,8 +12,6 @@ _step.rename_src, _step.mypy_dist, _step.pyright_dist, - _step.uninstall_dist, - _step.restore_src, ] @@ -33,3 +33,10 @@ def test( steps = [step for step in steps if remove not in step.name] run_job(steps) + + +def stubtest(allowlist: str): + stubtest = dataclasses.replace( + _step.stubtest, run=partial(_step.stubtest.run, allowlist=allowlist) + ) + run_job(_DIST_STEPS[:-2] + [stubtest]) diff --git a/scripts/test/_step.py b/scripts/test/_step.py index 73bf49430..f64fefbbe 100644 --- a/scripts/test/_step.py +++ b/scripts/test/_step.py @@ -26,5 +26,6 @@ pyright_dist = Step( name="Run pyright on 'tests' using the installed stubs", run=run.pyright_dist ) -uninstall_dist = Step(name="Uninstall pandas-stubs", run=run.uninstall_dist) -restore_src = Step(name="Restore local stubs", run=run.restore_src) +stubtest = Step( + name="Run stubtest to compare the installed stubs against pandas", run=run.stubtest +) diff --git a/scripts/test/run.py b/scripts/test/run.py index a4242f8df..4cd9e2dd1 100644 --- a/scripts/test/run.py +++ b/scripts/test/run.py @@ -1,5 +1,6 @@ from pathlib import Path import subprocess +import sys def mypy_src(): @@ -22,6 +23,22 @@ def style(): subprocess.run(cmd, check=True) +def stubtest(allowlist: str = ""): + cmd = [ + sys.executable, + "-m", + "mypy.stubtest", + "pandas", + "--concise", + "--ignore-missing-stub", + "--mypy-config-file", + "pyproject.toml", + ] + if allowlist: + cmd += ["--allowlist", allowlist] + subprocess.run(cmd, check=True) + + def build_dist(): cmd = ["poetry", "build", "-f", "wheel"] subprocess.run(cmd, check=True)