Repository: drivendataorg/cloudpathlib
Pull request #354 (2023-07-24 15:47:59)

Live Tests: Added with_stem support (#290)
Live test PR for #290

------------

* Added with_stem support

* Update readme (+small fix to make_support_table)

* Added test

* Update history.md

* Fallback to trivial CPython implementation if `with_stem` is not available

* Add a clarification comment

github-actions[bot]: <!-- NETLIFY DEPLOY COMMENT GENERATED BY ACTIONS_NETLIFY - APP ID SHA256: 9d0998be292ddace9530fffad95434f4e2279530e25cf278a25f07954faa426e -->
🚀 Deployed on https://deploy-preview-354--gallant-agnesi-5f7bb3.netlify.app
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/drivendataorg/cloudpathlib/pull/354?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg) Report
> Merging [#354](https://app.codecov.io/gh/drivendataorg/cloudpathlib/pull/354?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg) (4a32c68) into [master](https://app.codecov.io/gh/drivendataorg/cloudpathlib/commit/b7d394f37a6b66d5a24802ae7bb07ebf9e703ed7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg) (b7d394f) will **decrease** coverage by `0.4%`.
> The diff coverage is `100.0%`.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff            @@
##           master    #354     +/-   ##
========================================
- Coverage    93.6%   93.3%   -0.4%     
========================================
  Files          22      22             
  Lines        1507    1512      +5     
========================================
  Hits         1412    1412             
- Misses         95     100      +5     
```


| [Impacted Files](https://app.codecov.io/gh/drivendataorg/cloudpathlib/pull/354?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg) | Coverage Δ | |
|---|---|---|
| [cloudpathlib/cloudpath.py](https://app.codecov.io/gh/drivendataorg/cloudpathlib/pull/354?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg#diff-Y2xvdWRwYXRobGliL2Nsb3VkcGF0aC5weQ==) | `91.7% <100.0%> (+<0.1%)` | :arrow_up: |

... and [3 files with indirect coverage changes](https://app.codecov.io/gh/drivendataorg/cloudpathlib/pull/354/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=drivendataorg)

</details>

Patch:
diff --git a/HISTORY.md b/HISTORY.md
index 6cb0be6..9ec7210 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,7 @@
 
 ## UNRELEASED
  - Add "CloudPath" as return type on `__init__` for mypy issues. ([Issue #179](https://github.com/drivendataorg/cloudpathlib/issues/179), [PR #342](https://github.com/drivendataorg/cloudpathlib/pull/342))
+ - Add `with_stem` to all path types when python version supports it (>=3.9). ([Issue #287](https://github.com/drivendataorg/cloudpathlib/issues/287), [PR #290](https://github.com/drivendataorg/cloudpathlib/pull/290), thanks to [@Gilthans](https://github.com/Gilthans)) 
 
 ## v0.15.1 (2023-07-12)
 
diff --git a/README.md b/README.md
index 4e77cb4..1343a84 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,7 @@ Most methods and properties from `pathlib.Path` are supported except for the one
 | `touch`                | ✅                | ✅         | ✅         |
 | `unlink`               | ✅                | ✅         | ✅         |
 | `with_name`            | ✅                | ✅         | ✅         |
+| `with_stem`            | ✅                | ✅         | ✅         |
 | `with_suffix`          | ✅                | ✅         | ✅         |
 | `write_bytes`          | ✅                | ✅         | ✅         |
 | `write_text`           | ✅                | ✅         | ✅         |
@@ -184,7 +185,7 @@ Most methods and properties from `pathlib.Path` are supported except for the one
 | `readlink`             | ❌                | ❌         | ❌         |
 | `root`                 | ❌                | ❌         | ❌         |
 | `symlink_to`           | ❌                | ❌         | ❌         |
-| `with_stem`            | ❌                | ❌         | ❌         |
+| `clear_cache`          | ✅                | ✅         | ✅         |
 | `cloud_prefix`         | ✅                | ✅         | ✅         |
 | `copy`                 | ✅                | ✅         | ✅         |
 | `copytree`             | ✅                | ✅         | ✅         |
@@ -194,6 +195,7 @@ Most methods and properties from `pathlib.Path` are supported except for the one
 | `is_valid_cloudpath`   | ✅                | ✅         | ✅         |
 | `rmtree`               | ✅                | ✅         | ✅         |
 | `upload_from`          | ✅                | ✅         | ✅         |
+| `validate`             | ✅                | ✅         | ✅         |
 | `blob`                 | ✅                | ❌         | ✅         |
 | `bucket`               | ❌                | ✅         | ✅         |
 | `container`            | ✅                | ❌         | ❌         |
diff --git a/cloudpathlib/cloudpath.py b/cloudpathlib/cloudpath.py
index 09fdb49..898eaf0 100644
--- a/cloudpathlib/cloudpath.py
+++ b/cloudpathlib/cloudpath.py
@@ -759,6 +759,13 @@ class CloudPath(metaclass=CloudPathMeta):
     def suffixes(self) -> List[str]:
         return self._dispatch_to_path("suffixes")
 
+    def with_stem(self, stem: str) -> Self:
+        try:
+            return self._dispatch_to_path("with_stem", stem)
+        except AttributeError:
+            # with_stem was only added in python 3.9, so we fallback for compatibility
+            return self.with_name(stem + self.suffix)
+
     def with_name(self, name: str) -> Self:
         return self._dispatch_to_path("with_name", name)
 
diff --git a/docs/make_support_table.py b/docs/make_support_table.py
index f170fc1..ad06142 100644
--- a/docs/make_support_table.py
+++ b/docs/make_support_table.py
@@ -19,7 +19,7 @@ def print_table():
     for _cls, methods in lib_methods.items():
         all_methods = all_methods.union(methods)
 
-    df = pd.DataFrame(index=all_methods)
+    df = pd.DataFrame(index=list(all_methods))
     df.index.name = "Methods + properties"
 
     for _cls, methods in lib_methods.items():


Test patch:
diff --git a/tests/test_cloudpath_manipulation.py b/tests/test_cloudpath_manipulation.py
index 98c2dfa..1b40595 100644
--- a/tests/test_cloudpath_manipulation.py
+++ b/tests/test_cloudpath_manipulation.py
@@ -27,6 +27,13 @@ def test_with_suffix(rig):
     )
 
 
+def test_with_stem(rig):
+    assert (
+        str(rig.create_cloud_path("a/b/c/old.file").with_stem("new"))
+        == f"{rig.cloud_prefix}{rig.drive}/{rig.test_dir}/a/b/c/new.file"
+    )
+
+
 def test_no_op_actions(rig):
     path = rig.create_cloud_path("a/b/c/d.file")
     assert path == path.absolute()