Repository: pandas-dev/pandas-stubs (language: python)

Issue:
Changes to `groupby.size()` don't work for all possible groupby 
**Describe the bug**
```python
import pandas as pd
ddf = pd.DataFrame({"a": [1, 1, 2], "b": [2, 3, 2]})
reveal_type(ddf.groupby(["a", "b"]).size())

```
pyright reports
```text
 - information: Type of "ddf.groupby(["a", "b"]).size()" is "Unknown"
  c:\Code\pandas-stubs\play\issue1044.py:3:37 - error: Cannot access attribute "size" for class "DataFrameGroupBy[tuple[Unknown, ...], bool]"
    Could not bind method "size" because "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to parameter "self"
      "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to "DataFrameGroupBy[tuple[Unknown, ...], Literal[True]]"
        Type parameter "_TT@DataFrameGroupBy" is invariant, but "bool" is not the same as "Literal[True]"
    Could not bind method "size" because "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to parameter "self"
      "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to "DataFrameGroupBy[tuple[Unknown, ...], Literal[False]]"
        Type parameter "_TT@DataFrameGroupBy" is invariant, but "bool" is not the same as "Literal[False]"
    Could not bind method "size" because "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to parameter "self"
      "DataFrameGroupBy[tuple[Unknown, ...], bool]" is not assignable to "DataFrameGroupBy[Timestamp, Literal[True]]" (reportAttributeAccessIssue)
```
mypy reports
```python
note: Revealed type is "Any"
```

The fix here is that any of the methods in `frame.pyi` for `def groupby()` that return `DataFrameGroupBy[SOMETYPE, bool]` need to be split into 2 where there is one overload that returns `DataFrameGroupBy[SOMETYPE, True]` and another that returns `DataFrameGroupBy[SOMETYPE, False]` like is done with `DataFrameGroupBy[Scalar, True]` and `DataFrameGroupBy[Scalar, False]` .

**Please complete the following information:**
 - OS:  Windows 11
 - python version   3.10
 - version of type checker   pyright 1.1.389, mypy 1.13.0
 - version of installed `pandas-stubs`:   Dev version


**Additional context**

This was missed in PR #1014 by @loicdiridollou .  Hopefully he can fix soon, since the prerelease of VS Code pylance has this bug.


Discussion / hints:

Fix patch:
diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi
index 295aad8b..b574fb21 100644
--- a/pandas-stubs/core/frame.pyi
+++ b/pandas-stubs/core/frame.pyi
@@ -1112,7 +1112,7 @@ class DataFrame(NDFrame, OpsMixin):
         dropna: _bool = ...,
     ) -> DataFrameGroupBy[Timestamp, Literal[True]]: ...
     @overload
-    def groupby(
+    def groupby(  # pyright: ignore reportOverlappingOverload
         self,
         by: DatetimeIndex,
         axis: AxisIndex | NoDefault = ...,
@@ -1124,77 +1124,149 @@ class DataFrame(NDFrame, OpsMixin):
         dropna: _bool = ...,
     ) -> DataFrameGroupBy[Timestamp, Literal[False]]: ...
     @overload
-    def groupby(
+    def groupby(  # pyright: ignore reportOverlappingOverload
         self,
         by: TimedeltaIndex,
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[True] = True,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[Timedelta, bool]: ...
+    ) -> DataFrameGroupBy[Timedelta, Literal[True]]: ...
     @overload
     def groupby(
+        self,
+        by: TimedeltaIndex,
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[False] = ...,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[Timedelta, Literal[False]]: ...
+    @overload
+    def groupby(  # pyright: ignore reportOverlappingOverload
         self,
         by: PeriodIndex,
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[True] = True,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[Period, bool]: ...
+    ) -> DataFrameGroupBy[Period, Literal[True]]: ...
     @overload
     def groupby(
+        self,
+        by: PeriodIndex,
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[False] = ...,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[Period, Literal[False]]: ...
+    @overload
+    def groupby(  # pyright: ignore reportOverlappingOverload
         self,
         by: IntervalIndex[IntervalT],
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[True] = True,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[IntervalT, bool]: ...
+    ) -> DataFrameGroupBy[IntervalT, Literal[True]]: ...
     @overload
     def groupby(
+        self,
+        by: IntervalIndex[IntervalT],
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[False] = ...,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[IntervalT, Literal[False]]: ...
+    @overload
+    def groupby(  # type: ignore[overload-overlap] # pyright: ignore reportOverlappingOverload
         self,
         by: MultiIndex | GroupByObjectNonScalar | None = ...,
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[True] = True,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[tuple, bool]: ...
+    ) -> DataFrameGroupBy[tuple, Literal[True]]: ...
+    @overload
+    def groupby(  # type: ignore[overload-overlap]
+        self,
+        by: MultiIndex | GroupByObjectNonScalar | None = ...,
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[False] = ...,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[tuple, Literal[False]]: ...
+    @overload
+    def groupby(  # pyright: ignore reportOverlappingOverload
+        self,
+        by: Series[SeriesByT],
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[True] = True,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[SeriesByT, Literal[True]]: ...
     @overload
     def groupby(
         self,
         by: Series[SeriesByT],
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[False] = ...,
+        sort: _bool = ...,
+        group_keys: _bool = ...,
+        observed: _bool | NoDefault = ...,
+        dropna: _bool = ...,
+    ) -> DataFrameGroupBy[SeriesByT, Literal[False]]: ...
+    @overload
+    def groupby(
+        self,
+        by: CategoricalIndex | Index | Series,
+        axis: AxisIndex | NoDefault = ...,
+        level: IndexLabel | None = ...,
+        as_index: Literal[True] = True,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[SeriesByT, bool]: ...
+    ) -> DataFrameGroupBy[Any, Literal[True]]: ...
     @overload
     def groupby(
         self,
         by: CategoricalIndex | Index | Series,
         axis: AxisIndex | NoDefault = ...,
         level: IndexLabel | None = ...,
-        as_index: _bool = ...,
+        as_index: Literal[False] = ...,
         sort: _bool = ...,
         group_keys: _bool = ...,
         observed: _bool | NoDefault = ...,
         dropna: _bool = ...,
-    ) -> DataFrameGroupBy[Any, bool]: ...
+    ) -> DataFrameGroupBy[Any, Literal[False]]: ...
     def pivot(
         self,
         *,


Test patch:
diff --git a/tests/test_frame.py b/tests/test_frame.py
index f67294d5..1d7e0468 100644
--- a/tests/test_frame.py
+++ b/tests/test_frame.py
@@ -504,8 +504,8 @@ def test_types_mean() -> None:
     s2: pd.Series = df.mean(axis=0)
     df2: pd.DataFrame = df.groupby(level=0).mean()
     if TYPE_CHECKING_INVALID_USAGE:
-        df3: pd.DataFrame = df.groupby(axis=1, level=0).mean()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType]
-        df4: pd.DataFrame = df.groupby(axis=1, level=0, dropna=True).mean()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType]
+        df3: pd.DataFrame = df.groupby(axis=1, level=0).mean()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType, reportCallIssue]
+        df4: pd.DataFrame = df.groupby(axis=1, level=0, dropna=True).mean()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType, reportCallIssue]
     s3: pd.Series = df.mean(axis=1, skipna=True, numeric_only=False)
 
 
@@ -515,8 +515,8 @@ def test_types_median() -> None:
     s2: pd.Series = df.median(axis=0)
     df2: pd.DataFrame = df.groupby(level=0).median()
     if TYPE_CHECKING_INVALID_USAGE:
-        df3: pd.DataFrame = df.groupby(axis=1, level=0).median()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType]
-        df4: pd.DataFrame = df.groupby(axis=1, level=0, dropna=True).median()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType]
+        df3: pd.DataFrame = df.groupby(axis=1, level=0).median()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType, reportCallIssue]
+        df4: pd.DataFrame = df.groupby(axis=1, level=0, dropna=True).median()  # type: ignore[call-overload] # pyright: ignore[reportArgumentType, reportCallIssue]
     s3: pd.Series = df.median(axis=1, skipna=True, numeric_only=False)
 
 
@@ -1064,6 +1064,39 @@ def test_types_groupby_as_index() -> None:
         ),
         pd.Series,
     )
+    check(
+        assert_type(
+            df.groupby("a").size(),
+            "pd.Series[int]",
+        ),
+        pd.Series,
+    )
+
+
+def test_types_groupby_as_index_list() -> None:
+    """Test type of groupby.size method depending on list of grouper GH1045."""
+    df = pd.DataFrame({"a": [1, 1, 2], "b": [2, 3, 2]})
+    check(
+        assert_type(
+            df.groupby(["a", "b"], as_index=False).size(),
+            pd.DataFrame,
+        ),
+        pd.DataFrame,
+    )
+    check(
+        assert_type(
+            df.groupby(["a", "b"], as_index=True).size(),
+            "pd.Series[int]",
+        ),
+        pd.Series,
+    )
+    check(
+        assert_type(
+            df.groupby(["a", "b"]).size(),
+            "pd.Series[int]",
+        ),
+        pd.Series,
+    )
 
 
 def test_types_groupby_as_index_value_counts() -> None: