Skip to content

Commit

Permalink
conversion for FsPath to pathlib.Path (#203)
Browse files Browse the repository at this point in the history
* to pathlib!

* clippy fix

* prepare for v0.0.30

* prepare for v0.0.30
  • Loading branch information
jessekrubin authored Feb 19, 2025
1 parent db86100 commit 9dcf9ef
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 12 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CHANGELOG

## v0.0.30 [2025-02-11]
## v0.0.30 [2025-02-18]

- `jiff`
- Upgraded jiff to version 2
Expand All @@ -9,6 +9,8 @@
- Removed a bunch o commented out code
- `ryo3-std`
- added several `std::fs` structs
- `ryo3-fspath`
- conversion to `pathlib.Path` by way of `FsPath.to_pathlib()`

___

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ ___
import datetime as pydt
import typing as t
from os import PathLike
from pathlib import Path

import typing_extensions as te

Expand Down Expand Up @@ -367,6 +368,7 @@ class FsPath:
def __ge__(self, other: PathLike[str] | str) -> bool: ...
def __truediv__(self, other: PathLike[str] | str) -> FsPath: ...
def __rtruediv__(self, other: PathLike[str] | str) -> FsPath: ...
def to_pathlib(self) -> Path: ...
def read_text(self) -> str: ...
def read_bytes(self) -> bytes: ...
def absolute(self) -> FsPath: ...
Expand Down Expand Up @@ -2983,8 +2985,7 @@ import ry

if t.TYPE_CHECKING:
from ry import Duration, Headers

from ._url import URL
from ry.ryo3._url import URL


class HttpClient:
Expand Down
5 changes: 5 additions & 0 deletions crates/ryo3-fspath/src/fspath.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! `FsPath` struct python module
// TODO: remove in future? if possible?
use crate::pathlib::path2pathlib;
use pyo3::basic::CompareOp;
use pyo3::exceptions::{
PyFileNotFoundError, PyNotADirectoryError, PyUnicodeDecodeError, PyValueError,
Expand Down Expand Up @@ -83,6 +84,10 @@ impl PyFsPath {
self.pth == other
}

fn to_pathlib<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
path2pathlib(py, &self.pth)
}

fn __hash__(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.pth.hash(&mut hasher);
Expand Down
2 changes: 2 additions & 0 deletions crates/ryo3-fspath/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! `FsPath` python module
use pyo3::prelude::*;
mod fspath;
mod pathlib;

pub use fspath::PyFsPath;

pub fn pymod_add(m: &Bound<'_, PyModule>) -> PyResult<()> {
Expand Down
13 changes: 13 additions & 0 deletions crates/ryo3-fspath/src/pathlib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use pyo3::prelude::*;
use pyo3::sync::GILOnceCell;
use pyo3::types::PyType;
use pyo3::{PyAny, PyResult};
use std::path::Path;

pub(crate) fn path2pathlib<T: AsRef<Path>>(py: Python<'_>, path: T) -> PyResult<Bound<'_, PyAny>> {
static PATHLIB: GILOnceCell<Py<PyType>> = GILOnceCell::new();

PATHLIB
.import(py, "pathlib", "Path")?
.call1((path.as_ref().to_string_lossy().to_string(),))
}
5 changes: 3 additions & 2 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import datetime as pydt
import typing as t
from os import PathLike
from pathlib import Path

import typing_extensions as te

Expand Down Expand Up @@ -264,6 +265,7 @@ class FsPath:
def __ge__(self, other: PathLike[str] | str) -> bool: ...
def __truediv__(self, other: PathLike[str] | str) -> FsPath: ...
def __rtruediv__(self, other: PathLike[str] | str) -> FsPath: ...
def to_pathlib(self) -> Path: ...
def read_text(self) -> str: ...
def read_bytes(self) -> bytes: ...
def absolute(self) -> FsPath: ...
Expand Down Expand Up @@ -2880,8 +2882,7 @@ import ry

if t.TYPE_CHECKING:
from ry import Duration, Headers

from ._url import URL
from ry.ryo3._url import URL


class HttpClient:
Expand Down
2 changes: 2 additions & 0 deletions python/ry/ryo3/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datetime as pydt
import typing as t
from os import PathLike
from pathlib import Path

import typing_extensions as te

Expand Down Expand Up @@ -248,6 +249,7 @@ class FsPath:
def __ge__(self, other: PathLike[str] | str) -> bool: ...
def __truediv__(self, other: PathLike[str] | str) -> FsPath: ...
def __rtruediv__(self, other: PathLike[str] | str) -> FsPath: ...
def to_pathlib(self) -> Path: ...
def read_text(self) -> str: ...
def read_bytes(self) -> bytes: ...
def absolute(self) -> FsPath: ...
Expand Down
3 changes: 1 addition & 2 deletions python/ry/ryo3/reqwest.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import ry

if t.TYPE_CHECKING:
from ry import Duration, Headers

from ._url import URL
from ry.ryo3._url import URL

class HttpClient:
def __init__(
Expand Down
2 changes: 1 addition & 1 deletion tests/bytes/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_uno_byte_bytes_repr(b: bytes) -> None:
"""Test the repr of Bytes and bytes for single byte values."""
rust_bytes = Bytes(b)
rust_bytes_str = repr(rust_bytes)
rust_bytes_str_eval = eval(rust_bytes_str) # noqa: S307
rust_bytes_str_eval = eval(rust_bytes_str)
assert rust_bytes_str_eval == rust_bytes == b


Expand Down
13 changes: 13 additions & 0 deletions tests/fspath/test_pathlib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pathlib import Path

import ry


def test_fspath2pathlib(tmp_path: Path) -> None:
p = ry.FsPath(tmp_path)
pypath = Path(tmp_path)
pypath_conversion = p.to_pathlib()
print(pypath_conversion, type(pypath_conversion))
assert isinstance(pypath_conversion, tmp_path.__class__)
assert p == pypath
assert pypath == pypath_conversion
2 changes: 1 addition & 1 deletion tests/test_sh.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_ls_objects(tmp_path: Path) -> None:
(tmp_path / "b.txt").write_text("world")
paths = ry.ls(tmp_path, objects=True)
assert all(isinstance(p, ry.FsPath) for p in paths)
assert set(str(e) for e in ry.ls(tmp_path, objects=True)) == {
assert {str(e) for e in ry.ls(tmp_path, objects=True)} == {
"a.txt",
"b.txt",
}
Expand Down
2 changes: 1 addition & 1 deletion tests/xxhash/_xxhash_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class XXHashDataRecord(TypedDict):


def _load_data() -> list[XXHashDataRecord]:
with open(_TEST_DATA, "r") as f:
with open(_TEST_DATA) as f:
xx32_test_data = f.read()
lines = xx32_test_data.split("\n")
return [
Expand Down
4 changes: 2 additions & 2 deletions tests/xxhash/test_xxhash.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ def _assert_xxh32_all_forms(
# hexdigest
actual_hexes = [ry_xxh.xxh32_hexdigest(data, seed=s) for s in seeds]
assert [int(h, 16) for h in actual_hexes] == expected_ints, (
f"xxh32_hexdigest mismatch"
"xxh32_hexdigest mismatch"
)

# digest (raw bytes)
actual_digests = [ry_xxh.xxh32_digest(data, seed=s) for s in seeds]
actual_from_bytes = [int.from_bytes(d, "big") for d in actual_digests]
assert actual_from_bytes == expected_ints, f"xxh32_digest mismatch"
assert actual_from_bytes == expected_ints, "xxh32_digest mismatch"


def _assert_xxh64_all_forms(
Expand Down

0 comments on commit 9dcf9ef

Please sign in to comment.