Repository: modin-project/modin

Issue:
Series.str accessor doesn't have removeprefix, removesuffix, fullmatch methods



Discussion:

Resolving patch:
diff --git a/modin/core/storage_formats/base/query_compiler.py b/modin/core/storage_formats/base/query_compiler.py
--- a/modin/core/storage_formats/base/query_compiler.py
+++ b/modin/core/storage_formats/base/query_compiler.py
@@ -4519,6 +4519,19 @@ def str_findall(self, pat, flags=0, **kwargs):
             self, pat, flags, **kwargs
         )
 
+    @doc_utils.doc_str_method(
+        refer_to="fullmatch",
+        params="""
+        pat : str
+        case : bool, default: True
+        flags : int, default: 0
+        na : object, default: None""",
+    )
+    def str_fullmatch(self, pat, case=True, flags=0, na=None):
+        return StrDefault.register(pandas.Series.str.fullmatch)(
+            self, pat, case, flags, na
+        )
+
     @doc_utils.doc_str_method(refer_to="get", params="i : int")
     def str_get(self, i):
         return StrDefault.register(pandas.Series.str.get)(self, i)
@@ -4640,6 +4653,14 @@ def str_pad(self, width, side="left", fillchar=" "):
     def str_partition(self, sep=" ", expand=True):
         return StrDefault.register(pandas.Series.str.partition)(self, sep, expand)
 
+    @doc_utils.doc_str_method(refer_to="removeprefix", params="prefix : str")
+    def str_removeprefix(self, prefix):
+        return StrDefault.register(pandas.Series.str.removeprefix)(self, prefix)
+
+    @doc_utils.doc_str_method(refer_to="removesuffix", params="suffix : str")
+    def str_removesuffix(self, suffix):
+        return StrDefault.register(pandas.Series.str.removesuffix)(self, suffix)
+
     @doc_utils.doc_str_method(refer_to="repeat", params="repeats : int")
     def str_repeat(self, repeats):
         return StrDefault.register(pandas.Series.str.repeat)(self, repeats)
diff --git a/modin/pandas/series_utils.py b/modin/pandas/series_utils.py
--- a/modin/pandas/series_utils.py
+++ b/modin/pandas/series_utils.py
@@ -288,6 +288,15 @@ def findall(self, pat, flags=0, **kwargs):
             query_compiler=self._query_compiler.str_findall(pat, flags=flags, **kwargs)
         )
 
+    def fullmatch(self, pat, case=True, flags=0, na=None):
+        if not isinstance(pat, (str, _pattern_type)):
+            raise TypeError("first argument must be string or compiled pattern")
+        return Series(
+            query_compiler=self._query_compiler.str_fullmatch(
+                pat, case=case, flags=flags, na=na
+            )
+        )
+
     def match(self, pat, case=True, flags=0, na=np.NaN):
         if not isinstance(pat, (str, _pattern_type)):
             raise TypeError("first argument must be string or compiled pattern")
@@ -338,6 +347,12 @@ def partition(self, sep=" ", expand=True):
                 )
             )
 
+    def removeprefix(self, prefix):
+        return Series(query_compiler=self._query_compiler.str_removeprefix(prefix))
+
+    def removesuffix(self, suffix):
+        return Series(query_compiler=self._query_compiler.str_removesuffix(suffix))
+
     def repeat(self, repeats):
         return Series(query_compiler=self._query_compiler.str_repeat(repeats))
 


Test patch:
diff --git a/modin/pandas/test/test_api.py b/modin/pandas/test/test_api.py
--- a/modin/pandas/test/test_api.py
+++ b/modin/pandas/test/test_api.py
@@ -235,6 +235,20 @@ def test_dataframe_api_equality():
     assert not len(difference), "Extra params found in API: {}".format(difference)
 
 
+def test_series_str_api_equality():
+    modin_dir = [obj for obj in dir(pd.Series().str) if obj[0] != "_"]
+    pandas_dir = [obj for obj in dir(pandas.Series.str) if obj[0] != "_"]
+
+    missing_from_modin = set(pandas_dir) - set(modin_dir)
+    assert not len(missing_from_modin), "Differences found in API: {}".format(
+        missing_from_modin
+    )
+    extra_in_modin = set(modin_dir) - set(pandas_dir)
+    assert not len(extra_in_modin), "Differences found in API: {}".format(
+        extra_in_modin
+    )
+
+
 def test_series_api_equality():
     modin_dir = [obj for obj in dir(pd.Series) if obj[0] != "_"]
     pandas_dir = [obj for obj in dir(pandas.Series) if obj[0] != "_"]
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -3873,6 +3873,28 @@ def test_str_repeat(data, repeats):
     eval_general(modin_series, pandas_series, lambda series: series.str.repeat(repeats))
 
 
+@pytest.mark.parametrize("data", test_string_data_values, ids=test_string_data_keys)
+def test_str_removeprefix(data):
+    modin_series, pandas_series = create_test_series(data)
+    prefix = "test_prefix"
+    eval_general(
+        modin_series,
+        pandas_series,
+        lambda series: (prefix + series).str.removeprefix(prefix),
+    )
+
+
+@pytest.mark.parametrize("data", test_string_data_values, ids=test_string_data_keys)
+def test_str_removesuffix(data):
+    modin_series, pandas_series = create_test_series(data)
+    suffix = "test_suffix"
+    eval_general(
+        modin_series,
+        pandas_series,
+        lambda series: (series + suffix).str.removesuffix(suffix),
+    )
+
+
 @pytest.mark.parametrize("data", test_string_data_values, ids=test_string_data_keys)
 @pytest.mark.parametrize("width", int_arg_values, ids=int_arg_keys)
 @pytest.mark.parametrize(
@@ -3998,6 +4020,13 @@ def test_str_findall(data, pat):
     eval_general(modin_series, pandas_series, lambda series: series.str.findall(pat))
 
 
+@pytest.mark.parametrize("data", test_string_data_values, ids=test_string_data_keys)
+@pytest.mark.parametrize("pat", string_sep_values, ids=string_sep_keys)
+def test_str_fullmatch(data, pat):
+    modin_series, pandas_series = create_test_series(data)
+    eval_general(modin_series, pandas_series, lambda series: series.str.fullmatch(pat))
+
+
 @pytest.mark.parametrize("data", test_string_data_values, ids=test_string_data_keys)
 @pytest.mark.parametrize("pat", string_sep_values, ids=string_sep_keys)
 @pytest.mark.parametrize("case", bool_arg_values, ids=bool_arg_keys)