Repository: pandas-dev/pandas (python)

Issue:
BUG:  ValueError when printing a DataFrame's column having a DataFrame in its attrs
### Pandas version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the [latest version](https://pandas.pydata.org/docs/whatsnew/index.html) of pandas.

- [X] I have confirmed this bug exists on the [main branch](https://pandas.pydata.org/docs/dev/getting_started/install.html#installing-the-development-version-of-pandas) of pandas.


### Reproducible Example

```python
import pandas as pd
import numpy as np

n=50

a = pd.DataFrame(np.random.randint(0, 10, size=(n,n)))
b = pd.DataFrame(np.random.randint(0, 10, size=(5,5)))

a.attrs['b'] = b

a[0]
```


### Issue Description

same error as in #60455 (which was already solved for DataFrames by #60459) but here output is a Series

### Expected Behavior

selected column is printed abbreviated form, no KeyError

<details>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/series.py", line 1437, in __repr__
    return self.to_string(**repr_params)
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/util/_decorators.py", line 332, in wrapper
    return func(*args, **kwargs)
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/series.py", line 1534, in to_string
    formatter = fmt.SeriesFormatter(
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/io/formats/format.py", line 226, in __init__
    self._chk_truncate()
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/io/formats/format.py", line 248, in _chk_truncate
    series = concat((series.iloc[:row_num], series.iloc[-row_num:]))
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/reshape/concat.py", line 440, in concat
    return _get_result(
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/reshape/concat.py", line 547, in _get_result
    return result.__finalize__(
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/generic.py", line 6070, in __finalize__
    have_same_attrs = all(obj.attrs == attrs for obj in objs[1:])
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/generic.py", line 6070, in <genexpr>
    have_same_attrs = all(obj.attrs == attrs for obj in objs[1:])
  File "/py/envs/pandas-dev/lib/python3.10/site-packages/pandas/core/generic.py", line 1496, in __bool__
    raise ValueError(
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
</details>

### Installed Versions

<details>
INSTALLED VERSIONS
------------------
commit                : 9501650e22767f8502a1e3edecfaf17c5769f150
python                : 3.10.16
python-bits           : 64
OS                    : Linux
OS-release            : 6.11.10-2-MANJARO
Version               : #1 SMP PREEMPT_DYNAMIC Mon, 25 Nov 2024 05:29:44 +0000
machine               : x86_64
processor             : 
byteorder             : little
LC_ALL                : None
LANG                  : de_DE.UTF-8
LOCALE                : de_DE.UTF-8

pandas                : 0+untagged.1.g9501650
numpy                 : 1.26.4
dateutil              : 2.9.0.post0
pip                   : 24.3.1
Cython                : 3.0.11
sphinx                : 8.1.3
IPython               : 8.30.0
adbc-driver-postgresql: None
adbc-driver-sqlite    : None
bs4                   : 4.12.3
blosc                 : None
bottleneck            : 1.4.2
fastparquet           : 2024.11.0
fsspec                : 2024.10.0
html5lib              : 1.1
hypothesis            : 6.122.3
gcsfs                 : 2024.10.0
jinja2                : 3.1.4
lxml.etree            : 5.3.0
matplotlib            : 3.9.4
numba                 : 0.60.0
numexpr               : 2.10.2
odfpy                 : None
openpyxl              : 3.1.5
psycopg2              : 2.9.9
pymysql               : 1.4.6
pyarrow               : 18.1.0
pyreadstat            : 1.2.8
pytest                : 8.3.4
python-calamine       : None
pytz                  : 2024.2
pyxlsb                : 1.0.10
s3fs                  : 2024.10.0
scipy                 : 1.14.1
sqlalchemy            : 2.0.36
tables                : 3.10.1
tabulate              : 0.9.0
xarray                : 2024.9.0
xlrd                  : 2.0.1
xlsxwriter            : 3.2.0
zstandard             : 0.23.0
tzdata                : 2024.2
qtpy                  : None
pyqt5                 : None
</details>


Pull request description:
BUG: fix ValueError when printing a Series with DataFrame in its attrs
- [x] closes #60568 (Replace xxxx with the GitHub issue number)
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

As #60459, replace `concat` with slice to avoid the error.

Patch:
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 005818b077..f33d56bbed 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -802,6 +802,7 @@ Other
 - Bug in ``Series.list`` methods not preserving the original :class:`Index`. (:issue:`58425`)
 - Bug in ``Series.list`` methods not preserving the original name. (:issue:`60522`)
 - Bug in printing a :class:`DataFrame` with a :class:`DataFrame` stored in :attr:`DataFrame.attrs` raised a ``ValueError`` (:issue:`60455`)
+- Bug in printing a :class:`Series` with a :class:`DataFrame` stored in :attr:`Series.attrs` raised a ``ValueError`` (:issue:`60568`)
 
 .. ***DO NOT USE THIS SECTION***
 
diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py
index 17460eae8c..46ecb2b9a8 100644
--- a/pandas/io/formats/format.py
+++ b/pandas/io/formats/format.py
@@ -78,7 +78,6 @@ from pandas.core.indexes.api import (
 )
 from pandas.core.indexes.datetimes import DatetimeIndex
 from pandas.core.indexes.timedeltas import TimedeltaIndex
-from pandas.core.reshape.concat import concat
 
 from pandas.io.common import (
     check_parent_directory,
@@ -245,7 +244,11 @@ class SeriesFormatter:
                 series = series.iloc[:max_rows]
             else:
                 row_num = max_rows // 2
-                series = concat((series.iloc[:row_num], series.iloc[-row_num:]))
+                _len = len(series)
+                _slice = np.hstack(
+                    [np.arange(row_num), np.arange(_len - row_num, _len)]
+                )
+                series = series.iloc[_slice]
             self.tr_row_num = row_num
         else:
             self.tr_row_num = None
diff --git a/pandas/io/sql.py b/pandas/io/sql.py
index 3c0c5cc64c..5652d7fab0 100644
--- a/pandas/io/sql.py
+++ b/pandas/io/sql.py
@@ -241,7 +241,7 @@ def read_sql_table(  # pyright: ignore[reportOverlappingOverload]
     schema=...,
     index_col: str | list[str] | None = ...,
     coerce_float=...,
-    parse_dates: list[str] | dict[str, str] | None = ...,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = ...,
     columns: list[str] | None = ...,
     chunksize: None = ...,
     dtype_backend: DtypeBackend | lib.NoDefault = ...,
@@ -255,7 +255,7 @@ def read_sql_table(
     schema=...,
     index_col: str | list[str] | None = ...,
     coerce_float=...,
-    parse_dates: list[str] | dict[str, str] | None = ...,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = ...,
     columns: list[str] | None = ...,
     chunksize: int = ...,
     dtype_backend: DtypeBackend | lib.NoDefault = ...,
@@ -268,7 +268,7 @@ def read_sql_table(
     schema: str | None = None,
     index_col: str | list[str] | None = None,
     coerce_float: bool = True,
-    parse_dates: list[str] | dict[str, str] | None = None,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = None,
     columns: list[str] | None = None,
     chunksize: int | None = None,
     dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default,
@@ -372,7 +372,7 @@ def read_sql_query(  # pyright: ignore[reportOverlappingOverload]
     index_col: str | list[str] | None = ...,
     coerce_float=...,
     params: list[Any] | Mapping[str, Any] | None = ...,
-    parse_dates: list[str] | dict[str, str] | None = ...,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = ...,
     chunksize: None = ...,
     dtype: DtypeArg | None = ...,
     dtype_backend: DtypeBackend | lib.NoDefault = ...,
@@ -386,7 +386,7 @@ def read_sql_query(
     index_col: str | list[str] | None = ...,
     coerce_float=...,
     params: list[Any] | Mapping[str, Any] | None = ...,
-    parse_dates: list[str] | dict[str, str] | None = ...,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = ...,
     chunksize: int = ...,
     dtype: DtypeArg | None = ...,
     dtype_backend: DtypeBackend | lib.NoDefault = ...,
@@ -399,7 +399,7 @@ def read_sql_query(
     index_col: str | list[str] | None = None,
     coerce_float: bool = True,
     params: list[Any] | Mapping[str, Any] | None = None,
-    parse_dates: list[str] | dict[str, str] | None = None,
+    parse_dates: list[str] | dict[str, str] | dict[str, dict[str, Any]] | None = None,
     chunksize: int | None = None,
     dtype: DtypeArg | None = None,
     dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default,


Test patch:
diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py
index d7db3d5082..86682e8160 100644
--- a/pandas/tests/io/formats/test_format.py
+++ b/pandas/tests/io/formats/test_format.py
@@ -136,6 +136,13 @@ class TestDataFrameFormatting:
         with option_context("display.max_columns", 2, "display.show_dimensions", False):
             assert repr(df) == "   0  ...  9\n0  0  ...  0"
 
+    def test_repr_truncation_series_with_dataframe_attrs(self):
+        # GH#60568
+        ser = Series([0] * 10)
+        ser.attrs["b"] = DataFrame([])
+        with option_context("display.max_rows", 2, "display.show_dimensions", False):
+            assert repr(ser) == "0    0\n    ..\n9    0\ndtype: int64"
+
     def test_max_colwidth_negative_int_raises(self):
         # Deprecation enforced from:
         # https://github.com/pandas-dev/pandas/issues/31532