We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Require pyspark minimal version is v3.2.0 to cut duplicates codes
Since [pyspark v3.2.0](https://github.com/apache/spark/blob/5d45a415f3a29898d92380380cfd82bfc7f579ea/python/pyspark/pandas/extensions.py#L28-L64), it has contained `CachedAccessor`, `_register_accessor`, `_register_accessor`

janitor requires pyspark minimal version is v3.1.2 at present.
Compared to v3.1.2, v3.2.0 is a minor version.

https://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/janitor/spark/backend.py#L9-L37

Note: The pyspark in the [setup.py](https://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/.requirements/spark.txt#L9) file requires v3.2.1 but ci ([environment-dev.yml](https://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/environment-dev.yml#L41)) requires v3.1.2.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `janitor/spark/backend.py`
Content:
```
1 """ Backend functions for pyspark."""
2 
3 import warnings
4 from functools import wraps
5 
6 from janitor.utils import import_message
7 
8 
9 class CachedAccessor:
10     """
11     Custom property-like object (descriptor) for caching accessors.
12 
13     Parameters
14     ----------
15     name : str
16         The namespace this will be accessed under, e.g. `df.foo`
17     accessor : cls
18         The class with the extension methods.
19 
20     NOTE
21     ----
22     Modified based on pandas.core.accessor.
23     """
24 
25     def __init__(self, name, accessor):
26         self._name = name
27         self._accessor = accessor
28 
29     def __get__(self, obj, cls):
30         if obj is None:
31             # we're accessing the attribute of the class, i.e., Dataset.geo
32             return self._accessor
33         accessor_obj = self._accessor(obj)
34         # Replace the property with the accessor object. Inspired by:
35         # http://www.pydanny.com/cached-property.html
36         setattr(obj, self._name, accessor_obj)
37         return accessor_obj
38 
39 
40 def _register_accessor(name, cls):
41     """
42     NOTE
43     ----
44     Modified based on pandas.core.accessor.
45     """
46 
47     def decorator(accessor):
48         if hasattr(cls, name):
49             warnings.warn(
50                 "registration of accessor {!r} under name {!r} for type "
51                 "{!r} is overriding a preexisting attribute with the same "
52                 "name.".format(accessor, name, cls),
53                 UserWarning,
54                 stacklevel=2,
55             )
56         setattr(cls, name, CachedAccessor(name, accessor))
57         return accessor
58 
59     return decorator
60 
61 
62 def register_dataframe_accessor(name):
63     """
64     NOTE
65     ----
66     Modified based on pandas.core.accessor.
67 
68     .. # noqa: DAR101 name
69     .. # noqa: DAR201
70     """
71     try:
72         from pyspark.sql import DataFrame
73     except ImportError:
74         import_message(
75             submodule="spark",
76             package="pyspark",
77             conda_channel="conda-forge",
78             pip_install=True,
79         )
80 
81     return _register_accessor(name, DataFrame)
82 
83 
84 def register_dataframe_method(method):
85     """Register a function as a method attached to the Pyspark DataFrame.
86 
87     NOTE
88     ----
89     Modified based on pandas_flavor.register.
90 
91     .. # noqa: DAR101 method
92     .. # noqa: DAR201
93     """
94 
95     def inner(*args, **kwargs):
96         class AccessorMethod:
97             def __init__(self, pyspark_obj):
98                 self._obj = pyspark_obj
99 
100             @wraps(method)
101             def __call__(self, *args, **kwargs):
102                 return method(self._obj, *args, **kwargs)
103 
104         register_dataframe_accessor(method.__name__)(AccessorMethod)
105 
106         return method
107 
108     return inner()
109
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/janitor/spark/backend.py b/janitor/spark/backend.py
--- a/janitor/spark/backend.py
+++ b/janitor/spark/backend.py
@@ -1,84 +1,20 @@
 """ Backend functions for pyspark."""
 
-import warnings
 from functools import wraps
 
-from janitor.utils import import_message
 
+try:
+    from pyspark.pandas.extensions import register_dataframe_accessor
 
-class CachedAccessor:
-    """
-    Custom property-like object (descriptor) for caching accessors.
-
-    Parameters
-    ----------
-    name : str
-        The namespace this will be accessed under, e.g. `df.foo`
-    accessor : cls
-        The class with the extension methods.
-
-    NOTE
-    ----
-    Modified based on pandas.core.accessor.
-    """
-
-    def __init__(self, name, accessor):
-        self._name = name
-        self._accessor = accessor
-
-    def __get__(self, obj, cls):
-        if obj is None:
-            # we're accessing the attribute of the class, i.e., Dataset.geo
-            return self._accessor
-        accessor_obj = self._accessor(obj)
-        # Replace the property with the accessor object. Inspired by:
-        # http://www.pydanny.com/cached-property.html
-        setattr(obj, self._name, accessor_obj)
-        return accessor_obj
-
-
-def _register_accessor(name, cls):
-    """
-    NOTE
-    ----
-    Modified based on pandas.core.accessor.
-    """
-
-    def decorator(accessor):
-        if hasattr(cls, name):
-            warnings.warn(
-                "registration of accessor {!r} under name {!r} for type "
-                "{!r} is overriding a preexisting attribute with the same "
-                "name.".format(accessor, name, cls),
-                UserWarning,
-                stacklevel=2,
-            )
-        setattr(cls, name, CachedAccessor(name, accessor))
-        return accessor
-
-    return decorator
-
-
-def register_dataframe_accessor(name):
-    """
-    NOTE
-    ----
-    Modified based on pandas.core.accessor.
-
-    .. # noqa: DAR101 name
-    .. # noqa: DAR201
-    """
-    try:
-        from pyspark.sql import DataFrame
-    except ImportError:
-        import_message(
-            submodule="spark",
-            package="pyspark",
-            conda_channel="conda-forge",
-            pip_install=True,
-        )
+except ImportError:
+    from janitor.utils import import_message
 
-    return _register_accessor(name, DataFrame)
+    import_message(
+        submodule="spark",
+        package="pyspark",
+        conda_channel="conda-forge",
+        pip_install=True,
+    )
 
 
 def register_dataframe_method(method):

{"golden_diff": "diff --git a/janitor/spark/backend.py b/janitor/spark/backend.py\n--- a/janitor/spark/backend.py\n+++ b/janitor/spark/backend.py\n@@ -1,84 +1,20 @@\n \"\"\" Backend functions for pyspark.\"\"\"\n \n-import warnings\n from functools import wraps\n \n-from janitor.utils import import_message\n \n+try:\n+    from pyspark.pandas.extensions import register_dataframe_accessor\n \n-class CachedAccessor:\n-    \"\"\"\n-    Custom property-like object (descriptor) for caching accessors.\n-\n-    Parameters\n-    ----------\n-    name : str\n-        The namespace this will be accessed under, e.g. `df.foo`\n-    accessor : cls\n-        The class with the extension methods.\n-\n-    NOTE\n-    ----\n-    Modified based on pandas.core.accessor.\n-    \"\"\"\n-\n-    def __init__(self, name, accessor):\n-        self._name = name\n-        self._accessor = accessor\n-\n-    def __get__(self, obj, cls):\n-        if obj is None:\n-            # we're accessing the attribute of the class, i.e., Dataset.geo\n-            return self._accessor\n-        accessor_obj = self._accessor(obj)\n-        # Replace the property with the accessor object. Inspired by:\n-        # http://www.pydanny.com/cached-property.html\n-        setattr(obj, self._name, accessor_obj)\n-        return accessor_obj\n-\n-\n-def _register_accessor(name, cls):\n-    \"\"\"\n-    NOTE\n-    ----\n-    Modified based on pandas.core.accessor.\n-    \"\"\"\n-\n-    def decorator(accessor):\n-        if hasattr(cls, name):\n-            warnings.warn(\n-                \"registration of accessor {!r} under name {!r} for type \"\n-                \"{!r} is overriding a preexisting attribute with the same \"\n-                \"name.\".format(accessor, name, cls),\n-                UserWarning,\n-                stacklevel=2,\n-            )\n-        setattr(cls, name, CachedAccessor(name, accessor))\n-        return accessor\n-\n-    return decorator\n-\n-\n-def register_dataframe_accessor(name):\n-    \"\"\"\n-    NOTE\n-    ----\n-    Modified based on pandas.core.accessor.\n-\n-    .. # noqa: DAR101 name\n-    .. # noqa: DAR201\n-    \"\"\"\n-    try:\n-        from pyspark.sql import DataFrame\n-    except ImportError:\n-        import_message(\n-            submodule=\"spark\",\n-            package=\"pyspark\",\n-            conda_channel=\"conda-forge\",\n-            pip_install=True,\n-        )\n+except ImportError:\n+    from janitor.utils import import_message\n \n-    return _register_accessor(name, DataFrame)\n+    import_message(\n+        submodule=\"spark\",\n+        package=\"pyspark\",\n+        conda_channel=\"conda-forge\",\n+        pip_install=True,\n+    )\n \n \n def register_dataframe_method(method):\n", "issue": "Require pyspark minimal version is v3.2.0 to cut duplicates codes\nSince [pyspark v3.2.0](https://github.com/apache/spark/blob/5d45a415f3a29898d92380380cfd82bfc7f579ea/python/pyspark/pandas/extensions.py#L28-L64), it has contained `CachedAccessor`, `_register_accessor`, `_register_accessor`\r\n\r\njanitor requires pyspark minimal version is v3.1.2 at present.\r\nCompared to v3.1.2, v3.2.0 is a minor version.\r\n\r\nhttps://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/janitor/spark/backend.py#L9-L37\r\n\r\nNote: The pyspark in the [setup.py](https://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/.requirements/spark.txt#L9) file requires v3.2.1 but ci ([environment-dev.yml](https://github.com/pyjanitor-devs/pyjanitor/blob/4867b20273e99cb2f4c32278123d5c23c2ccaa67/environment-dev.yml#L41)) requires v3.1.2.\n", "before_files": [{"content": "\"\"\" Backend functions for pyspark.\"\"\"\n\nimport warnings\nfrom functools import wraps\n\nfrom janitor.utils import import_message\n\n\nclass CachedAccessor:\n    \"\"\"\n    Custom property-like object (descriptor) for caching accessors.\n\n    Parameters\n    ----------\n    name : str\n        The namespace this will be accessed under, e.g. `df.foo`\n    accessor : cls\n        The class with the extension methods.\n\n    NOTE\n    ----\n    Modified based on pandas.core.accessor.\n    \"\"\"\n\n    def __init__(self, name, accessor):\n        self._name = name\n        self._accessor = accessor\n\n    def __get__(self, obj, cls):\n        if obj is None:\n            # we're accessing the attribute of the class, i.e., Dataset.geo\n            return self._accessor\n        accessor_obj = self._accessor(obj)\n        # Replace the property with the accessor object. Inspired by:\n        # http://www.pydanny.com/cached-property.html\n        setattr(obj, self._name, accessor_obj)\n        return accessor_obj\n\n\ndef _register_accessor(name, cls):\n    \"\"\"\n    NOTE\n    ----\n    Modified based on pandas.core.accessor.\n    \"\"\"\n\n    def decorator(accessor):\n        if hasattr(cls, name):\n            warnings.warn(\n                \"registration of accessor {!r} under name {!r} for type \"\n                \"{!r} is overriding a preexisting attribute with the same \"\n                \"name.\".format(accessor, name, cls),\n                UserWarning,\n                stacklevel=2,\n            )\n        setattr(cls, name, CachedAccessor(name, accessor))\n        return accessor\n\n    return decorator\n\n\ndef register_dataframe_accessor(name):\n    \"\"\"\n    NOTE\n    ----\n    Modified based on pandas.core.accessor.\n\n    .. # noqa: DAR101 name\n    .. # noqa: DAR201\n    \"\"\"\n    try:\n        from pyspark.sql import DataFrame\n    except ImportError:\n        import_message(\n            submodule=\"spark\",\n            package=\"pyspark\",\n            conda_channel=\"conda-forge\",\n            pip_install=True,\n        )\n\n    return _register_accessor(name, DataFrame)\n\n\ndef register_dataframe_method(method):\n    \"\"\"Register a function as a method attached to the Pyspark DataFrame.\n\n    NOTE\n    ----\n    Modified based on pandas_flavor.register.\n\n    .. # noqa: DAR101 method\n    .. # noqa: DAR201\n    \"\"\"\n\n    def inner(*args, **kwargs):\n        class AccessorMethod:\n            def __init__(self, pyspark_obj):\n                self._obj = pyspark_obj\n\n            @wraps(method)\n            def __call__(self, *args, **kwargs):\n                return method(self._obj, *args, **kwargs)\n\n        register_dataframe_accessor(method.__name__)(AccessorMethod)\n\n        return method\n\n    return inner()\n", "path": "janitor/spark/backend.py"}], "after_files": [{"content": "\"\"\" Backend functions for pyspark.\"\"\"\n\nfrom functools import wraps\n\n\ntry:\n    from pyspark.pandas.extensions import register_dataframe_accessor\n\nexcept ImportError:\n    from janitor.utils import import_message\n\n    import_message(\n        submodule=\"spark\",\n        package=\"pyspark\",\n        conda_channel=\"conda-forge\",\n        pip_install=True,\n    )\n\n\ndef register_dataframe_method(method):\n    \"\"\"Register a function as a method attached to the Pyspark DataFrame.\n\n    NOTE\n    ----\n    Modified based on pandas_flavor.register.\n\n    .. # noqa: DAR101 method\n    .. # noqa: DAR201\n    \"\"\"\n\n    def inner(*args, **kwargs):\n        class AccessorMethod:\n            def __init__(self, pyspark_obj):\n                self._obj = pyspark_obj\n\n            @wraps(method)\n            def __call__(self, *args, **kwargs):\n                return method(self._obj, *args, **kwargs)\n\n        register_dataframe_accessor(method.__name__)(AccessorMethod)\n\n        return method\n\n    return inner()\n", "path": "janitor/spark/backend.py"}]}