Skip to content

Commit 2fe5882

Browse files
authored
Cut release 0.3.3 (#447)
* update readme with notes about __fspath__ * cut release 0.3.3
1 parent 57f6a54 commit 2fe5882

File tree

2 files changed

+117
-9
lines changed

2 files changed

+117
-9
lines changed

CHANGELOG.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
9-
...
9+
10+
## [0.3.3] - 2025-10-08
11+
### Added
12+
- upath.implementations: add `ZipPath` for ZIP archive filesystem access (#442)
13+
- upath.implementations: add `TarPath` for TAR archive filesystem access (#443)
14+
- tests: add chained ZIP and TAR path tests (#440)
15+
16+
### Fixed
17+
- upath.core: remove `chain_parser` parameter from type overloads to improve type narrowing (#436)
18+
19+
### Changed
20+
- docs: update README with notes about `__fspath__()` behavior
1021

1122
## [0.3.2] - 2025-10-05
1223
### Added
@@ -242,7 +253,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
242253
### Added
243254
- started a changelog to keep track of significant changes
244255

245-
[Unreleased]: https://github.com/fsspec/universal_pathlib/compare/v0.3.2...HEAD
256+
[Unreleased]: https://github.com/fsspec/universal_pathlib/compare/v0.3.3...HEAD
257+
[0.3.3]: https://github.com/fsspec/universal_pathlib/compare/v0.3.2...v0.3.3
246258
[0.3.2]: https://github.com/fsspec/universal_pathlib/compare/v0.3.1...v0.3.2
247259
[0.3.1]: https://github.com/fsspec/universal_pathlib/compare/v0.3.0...v0.3.1
248260
[0.3.0]: https://github.com/fsspec/universal_pathlib/compare/v0.2.6...v0.3.0

README.md

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -625,13 +625,109 @@ assert ExtraUPath("s3://bucket/file.txt").some_extra_method() == "hello world"
625625

626626
## Migration Guide
627627

628-
UPath's internal implementation is likely going to change with larger changes in
629-
CPython's stdlib `pathlib` landing in the next Python versions (`3.13`, `3.14`).
630-
To reduce the problems for user code, when these changes are landing in `UPath`,
631-
there have been some significant changes in `v0.2.0`. This migration guide tries
632-
to help migrating code that extensively relies on private implementation details
633-
of the `UPath` class of versions `v0.1.x` to the new and better supported public
634-
interface of `v0.2.0`
628+
UPath's internal implementation is converging towards a more stable state,
629+
with changes in CPython's stdlib `pathlib` having landed in newer Python versions
630+
(`3.13`, `3.14`) and the currently private interface for JoinablePaths,
631+
ReadablePaths, and WriteablePaths stabilizing. There will likely be other
632+
breaking changes down the line, but we'll make the transition as smooth
633+
as possible.
634+
635+
### migrating to `v0.3.0`
636+
637+
Version `0.3.0` introduced a breaking change to fix a longstanding bug related to
638+
`os.PathLike` protocol compliance. This change affects how UPath instances work
639+
with standard library functions that expect local filesystem paths.
640+
641+
#### Background: PathLike protocol and local filesystem paths
642+
643+
In Python, `os.PathLike` objects and `pathlib.Path` subclasses represent local
644+
filesystem paths. This is used by the standard library - functions like
645+
`os.remove()`, `shutil.copy()`, and similar expect paths that point to the local
646+
filesystem. However, UPath implementations like `S3Path` or `MemoryPath` do not
647+
represent local filesystem paths and should not be treated as such.
648+
649+
Prior to `v0.3.0`, all UPath instances incorrectly implemented `os.PathLike`,
650+
which could lead to runtime errors when non-local paths were passed to functions
651+
expecting local paths. Starting with `v0.3.0`, only local UPath implementations
652+
(`PosixUPath`, `WindowsUPath`, and `FilePath`) implement `os.PathLike`.
653+
654+
#### Migration strategy
655+
656+
If your code passes UPath instances to functions expecting `os.PathLike` objects,
657+
you have several options:
658+
659+
**Option 1: Explicitly request a local path** (Recommended)
660+
661+
```python
662+
import os
663+
from upath import UPath
664+
665+
# Explicitly specify the file:// protocol to get a FilePath instance
666+
path = UPath(__file__, protocol="file")
667+
assert isinstance(path, os.PathLike) # True
668+
669+
# Now you can safely use it with os functions
670+
os.remove(path)
671+
```
672+
673+
**Option 2: Use UPath's filesystem operations**
674+
675+
```python
676+
from upath import UPath
677+
678+
# Works for any UPath implementation, not just local paths
679+
path = UPath("s3://bucket/file.txt")
680+
path.unlink() # UPath's native unlink method
681+
```
682+
683+
**Option 3: Use type checking with upath.types**
684+
685+
For code that needs to work with different path types, use the type hints from
686+
`upath.types` to properly specify your requirements:
687+
688+
```python
689+
from upath import UPath
690+
from upath.types import (
691+
JoinablePathLike,
692+
ReadablePathLike,
693+
WritablePathLike,
694+
)
695+
696+
def read_only_local_file(path: os.PathLike) -> None:
697+
"""Read a file on the local filesystem."""
698+
with open(path) as f:
699+
return f.read_text()
700+
701+
def write_only_local_file(path: os.PathLike) -> None:
702+
"""Write to a file on the local filesystem."""
703+
with open(path) as f:
704+
f.write_text("hello world")
705+
706+
def read_any_file(path: WritablePathLike) -> None:
707+
"""Write a file on any filesystem."""
708+
return UPath(path).read_text()
709+
710+
def read_any_file(path: WritablePathLike) -> None:
711+
"""Write a file on any filesystem."""
712+
UPath(path).write_text("hello world")
713+
```
714+
715+
#### Example: Incorrect code that would fail
716+
717+
The following example shows code that would incorrectly work in `v0.2.x` but
718+
properly fail in `v0.3.0`:
719+
720+
```python
721+
import os
722+
from upath import UPath
723+
724+
# This creates a MemoryPath, which is not a local filesystem path
725+
path = UPath("memory:///file.txt")
726+
727+
# In v0.2.x this would incorrectly accept the path and fail at runtime
728+
# In v0.3.0 this correctly fails at type-check time
729+
os.remove(path) # TypeError: expected str, bytes or os.PathLike, not MemoryPath
730+
```
635731

636732
### migrating to `v0.2.0`
637733

0 commit comments

Comments
 (0)