Repository: pandas-dev/pandas (python)

Issue:
DEPR: Positional arguments in Series.to_markdown/string
While most method were captured in https://github.com/pandas-dev/pandas/issues/54229, `Series.to_string` and `Series.to_markdown` were missed

Pull request:
DEPR: Positional arguments in Series.to_string
- [X] closes #57280
- [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
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [X] 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.


Patch:
diff --git a/doc/source/user_guide/categorical.rst b/doc/source/user_guide/categorical.rst
index 8fb991dca0..7b2fd32303 100644
--- a/doc/source/user_guide/categorical.rst
+++ b/doc/source/user_guide/categorical.rst
@@ -245,7 +245,8 @@ Equality semantics
 
 Two instances of :class:`~pandas.api.types.CategoricalDtype` compare equal
 whenever they have the same categories and order. When comparing two
-unordered categoricals, the order of the ``categories`` is not considered.
+unordered categoricals, the order of the ``categories`` is not considered. Note
+that categories with different dtypes are not the same.
 
 .. ipython:: python
 
@@ -263,6 +264,16 @@ All instances of ``CategoricalDtype`` compare equal to the string ``'category'``
 
    c1 == "category"
 
+Notice that the ``categories_dtype`` should be considered, especially when comparing with
+two empty ``CategoricalDtype`` instances.
+
+.. ipython:: python
+
+    c2 = pd.Categorical(np.array([], dtype=object))
+    c3 = pd.Categorical(np.array([], dtype=float))
+
+    c2.dtype == c3.dtype
+
 Description
 -----------
 
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 483cf65908..77c80dcfe7 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -102,6 +102,7 @@ Deprecations
 ~~~~~~~~~~~~
 - Deprecated :meth:`Timestamp.utcfromtimestamp`, use ``Timestamp.fromtimestamp(ts, "UTC")`` instead (:issue:`56680`)
 - Deprecated :meth:`Timestamp.utcnow`, use ``Timestamp.now("UTC")`` instead (:issue:`56680`)
+- Deprecated allowing non-keyword arguments in :meth:`Series.to_string` except ``buf``. (:issue:`57280`)
 -
 
 .. ---------------------------------------------------------------------------
diff --git a/pandas/core/series.py b/pandas/core/series.py
index eb5b545092..27ae5d3a85 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -46,6 +46,7 @@ from pandas.errors.cow import (
 from pandas.util._decorators import (
     Appender,
     Substitution,
+    deprecate_nonkeyword_arguments,
     doc,
 )
 from pandas.util._exceptions import find_stack_level
@@ -1488,6 +1489,7 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
     def to_string(
         self,
         buf: None = ...,
+        *,
         na_rep: str = ...,
         float_format: str | None = ...,
         header: bool = ...,
@@ -1504,6 +1506,7 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
     def to_string(
         self,
         buf: FilePath | WriteBuffer[str],
+        *,
         na_rep: str = ...,
         float_format: str | None = ...,
         header: bool = ...,
@@ -1516,6 +1519,9 @@ class Series(base.IndexOpsMixin, NDFrame):  # type: ignore[misc]
     ) -> None:
         ...
 
+    @deprecate_nonkeyword_arguments(
+        version="3.0.0", allowed_args=["self", "buf"], name="to_string"
+    )
     def to_string(
         self,
         buf: FilePath | WriteBuffer[str] | None = None,


Test patch:
diff --git a/pandas/tests/io/formats/test_to_string.py b/pandas/tests/io/formats/test_to_string.py
index f91f46c846..7c7069aa74 100644
--- a/pandas/tests/io/formats/test_to_string.py
+++ b/pandas/tests/io/formats/test_to_string.py
@@ -35,6 +35,16 @@ def _three_digit_exp():
 
 
 class TestDataFrameToStringFormatters:
+    def test_keyword_deprecation(self):
+        # GH 57280
+        msg = (
+            "Starting with pandas version 3.0.0 all arguments of to_string "
+            "except for the argument 'buf' will be keyword-only."
+        )
+        s = Series(["a", "b"])
+        with tm.assert_produces_warning(FutureWarning, match=msg):
+            s.to_string(None, "NaN")
+
     def test_to_string_masked_ea_with_formatter(self):
         # GH#39336
         df = DataFrame(