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

Issue:
pd.concat with names=None
**Describe the bug**
`pd.concat` should accept `names=None` as it is the default value.

**To Reproduce**
1. Provide a minimal runnable `pandas` example that is not properly checked by the stubs.

```
def maybe_concat(
    dd: dict[str, pd.DataFrame], names: list[str] | None = None
) -> pd.DataFrame:
    """Concatenate a dict of dataframes if available."""
    return pd.concat(dd, names=names) if dd else pd.DataFrame()
```

2. Indicate which type checker you are using (`mypy` or  `pyright`).
**mypy**
3. Show the error message received from that type checker while checking your example.
```
error: Argument "names" to "concat" has incompatible type "list[str] | None"; expected "list[str]"  [arg-type]
```

**Please complete the following information:**
 - OS: [e.g. Windows, Linux, MacOS] **Linux**
 - OS Version [e.g. 22] **Fedora 7**
 - python version **3.11.8**
 - version of type checker **1.8.0**
 - version of installed `pandas-stubs` **2.1.4**

pd.concat with names=None
**Describe the bug**
`pd.concat` should accept `names=None` as it is the default value.

**To Reproduce**
1. Provide a minimal runnable `pandas` example that is not properly checked by the stubs.

```
def maybe_concat(
    dd: dict[str, pd.DataFrame], names: list[str] | None = None
) -> pd.DataFrame:
    """Concatenate a dict of dataframes if available."""
    return pd.concat(dd, names=names) if dd else pd.DataFrame()
```

2. Indicate which type checker you are using (`mypy` or  `pyright`).
**mypy**
3. Show the error message received from that type checker while checking your example.
```
error: Argument "names" to "concat" has incompatible type "list[str] | None"; expected "list[str]"  [arg-type]
```

**Please complete the following information:**
 - OS: [e.g. Windows, Linux, MacOS] **Linux**
 - OS Version [e.g. 22] **Fedora 7**
 - python version **3.11.8**
 - version of type checker **1.8.0**
 - version of installed `pandas-stubs` **2.1.4**



Discussion / hints:
Yes, that's an error.  Easy fix to add the `| None` to the `names` argument in `pandas-stubs/core/reshape/concat.pyi`  PR with tests welcome.
Ill try and tackle this.
Hello, @Dr-Irv 
I'm assigning the issue to myself, as it seems no one else has done any improvement on it yet.
take
Yes, that's an error.  Easy fix to add the `| None` to the `names` argument in `pandas-stubs/core/reshape/concat.pyi`  PR with tests welcome.
Ill try and tackle this.
Hello, @Dr-Irv 
I'm assigning the issue to myself, as it seems no one else has done any improvement on it yet.
take

Fix patch:
diff --git a/pandas-stubs/core/reshape/concat.pyi b/pandas-stubs/core/reshape/concat.pyi
index bdd11b7e..61e547ac 100644
--- a/pandas-stubs/core/reshape/concat.pyi
+++ b/pandas-stubs/core/reshape/concat.pyi
@@ -32,7 +32,7 @@ def concat(
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -46,7 +46,7 @@ def concat(  # type: ignore[overload-overlap] # pyright: ignore[reportOverlappin
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -60,7 +60,7 @@ def concat(  # type: ignore[overload-overlap] # pyright: ignore[reportOverlappin
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -74,7 +74,7 @@ def concat(
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -88,7 +88,7 @@ def concat(  # type: ignore[overload-overlap]
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -102,7 +102,7 @@ def concat(  # type: ignore[overload-overlap]
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,
@@ -119,7 +119,7 @@ def concat(
     ignore_index: bool = ...,
     keys: Iterable[HashableT2] = ...,
     levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ...,
-    names: list[HashableT4] = ...,
+    names: list[HashableT4] | None = ...,
     verify_integrity: bool = ...,
     sort: bool = ...,
     copy: bool = ...,


Test patch:
diff --git a/tests/test_pandas.py b/tests/test_pandas.py
index fddce825..8bb10849 100644
--- a/tests/test_pandas.py
+++ b/tests/test_pandas.py
@@ -98,6 +98,13 @@ def test_types_concat() -> None:
         ),
         pd.Series,
     )
+    check(
+        assert_type(
+            pd.concat([s, s2], keys=["first", "second"], names=None),
+            pd.Series,
+        ),
+        pd.Series,
+    )
 
     # Depends on the axis
     check(
@@ -140,6 +147,13 @@ def test_types_concat() -> None:
         ),
         pd.DataFrame,
     )
+    check(
+        assert_type(
+            pd.concat([df, df2], keys=["first", "second"], names=None),
+            pd.DataFrame,
+        ),
+        pd.DataFrame,
+    )
 
     check(
         assert_type(
@@ -189,6 +203,12 @@ def test_concat_args() -> None:
         ),
         pd.DataFrame,
     )
+    check(
+        assert_type(
+            pd.concat([df, df2], keys=["df1", "df2"], names=None), pd.DataFrame
+        ),
+        pd.DataFrame,
+    )
     check(
         assert_type(
             pd.concat([df, df2], keys=["df1", "df2"], names=[pd.Timedelta(1, "D")]),
@@ -248,6 +268,18 @@ def test_concat_args() -> None:
         ),
         pd.DataFrame,
     )
+    check(
+        assert_type(
+            pd.concat(
+                [df, df2, df, df2],
+                keys=[("foo", "one"), ("foo", "two"), ("baz", "one"), ("baz", "two")],
+                levels=levels,
+                names=None,
+            ),
+            pd.DataFrame,
+        ),
+        pd.DataFrame,
+    )
 
     check(
         assert_type(