Repository: fsspec/universal_pathlib (python)

Issue:
expanduser implementation for geopandas
GeoPandas `read_file` and `to_file` attempts a [`path.expanduser()` ](https://github.com/geopandas/geopandas/blob/a78dbf3a48eaa0f84d12d9935fce4927e381d146/geopandas/io/file.py#L177-L178) which raises `NotImplementedError` when `path` is a `UPath`.

Is it intended that `UPath.expanduser` must raise `NotImplementedError`, or is it a matter of not getting round to it yet? For context I have had this issue when attempting to read/write on GCS.

In the meantime I am using a workaround to read/write via a `io.BytesIO` buffer, e.g.:
```python
# write
with io.BytesIO() as buffer:
  gdf.to_file(buffer, driver="GeoJSON")
  buffer.seek(0)
  path.write_bytes(buffer.read())

# read
with io.BytesIO(path.read_bytes()) as buffer:
  gdf = gpd.read_file(buffer)
```

Pull request:
feat: expanduser no-op
Fixes #292 

Patch:
diff --git a/pyproject.toml b/pyproject.toml
index 54b638e..53a427e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -61,6 +61,7 @@ dev = [
     "pydantic",
     "pydantic-settings",
     "smbprotocol",
+    "typing_extensions; python_version<'3.11'",
 ]
 
 [project.urls]
diff --git a/upath/core.py b/upath/core.py
index 2003fa9..49d997c 100644
--- a/upath/core.py
+++ b/upath/core.py
@@ -19,11 +19,6 @@ from typing import TypeVar
 from typing import overload
 from urllib.parse import urlsplit
 
-if sys.version_info >= (3, 11):
-    from typing import Self
-else:
-    from typing_extensions import Self
-
 from fsspec.registry import get_filesystem_class
 from fsspec.spec import AbstractFileSystem
 
@@ -43,6 +38,11 @@ from upath.registry import get_upath_class
 if TYPE_CHECKING:
     from urllib.parse import SplitResult
 
+    if sys.version_info >= (3, 11):
+        from typing import Self
+    else:
+        from typing_extensions import Self
+
 __all__ = ["UPath"]
 
 
@@ -1072,4 +1072,4 @@ class UPath(PathlibPathShim, Path):
         raise NotImplementedError
 
     def expanduser(self) -> Self:
-        raise NotImplementedError
+        return self
diff --git a/upath/implementations/sftp.py b/upath/implementations/sftp.py
index ed9b447..fe1511e 100644
--- a/upath/implementations/sftp.py
+++ b/upath/implementations/sftp.py
@@ -1,13 +1,15 @@
 from __future__ import annotations
 
 import sys
+from typing import TYPE_CHECKING
 from typing import Any
 from typing import Generator
 
-if sys.version_info >= (3, 11):
-    from typing import Self
-else:
-    from typing_extensions import Self
+if TYPE_CHECKING:
+    if sys.version_info >= (3, 11):
+        from typing import Self
+    else:
+        from typing_extensions import Self
 
 from upath import UPath
 
diff --git a/upath/implementations/smb.py b/upath/implementations/smb.py
index f8505a2..492d738 100644
--- a/upath/implementations/smb.py
+++ b/upath/implementations/smb.py
@@ -3,12 +3,14 @@ from __future__ import annotations
 import os
 import sys
 import warnings
+from typing import TYPE_CHECKING
 from typing import Any
 
-if sys.version_info >= (3, 11):
-    from typing import Self
-else:
-    from typing_extensions import Self
+if TYPE_CHECKING:
+    if sys.version_info >= (3, 11):
+        from typing import Self
+    else:
+        from typing_extensions import Self
 
 import smbprotocol.exceptions
 


Test patch:
diff --git a/upath/tests/cases.py b/upath/tests/cases.py
index 6c9b914..e8353ef 100644
--- a/upath/tests/cases.py
+++ b/upath/tests/cases.py
@@ -65,8 +65,7 @@ class BaseTests:
         assert path.exists() == expected
 
     def test_expanduser(self):
-        with pytest.raises(NotImplementedError):
-            self.path.expanduser()
+        assert self.path.expanduser() == self.path
 
     @pytest.mark.parametrize(
         "pattern",
diff --git a/upath/tests/test_core.py b/upath/tests/test_core.py
index a44a3fe..8b99d11 100644
--- a/upath/tests/test_core.py
+++ b/upath/tests/test_core.py
@@ -445,3 +445,8 @@ def test_joinpath_on_protocol_mismatch(base, join):
 )
 def test_joinuri_on_protocol_mismatch(base, join):
     assert UPath(base).joinuri(UPath(join)) == UPath(join)
+
+
+def test_upath_expanduser():
+    assert UPath("~").expanduser() == UPath(os.path.expanduser("~"))
+    assert UPath("~") != UPath("~").expanduser()