Skip to content

Commit

Permalink
Url fix (#143)
Browse files Browse the repository at this point in the history
* parse-w-params

* no unwrap in params

* clippy fix
  • Loading branch information
jessekrubin authored Dec 12, 2024
1 parent 0c30efd commit 5768652
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 43 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
- Internal refactoring
- added `globster()` method to `ry.Glob` and `ry.GlobSet` to return a `ry.Globster` obj
- added `globset()` method to `ry.Glob` to return a `ry.GlobSet` obj from a `ry.Glob` obj
- `url`
- python `Url` changed name `URL`; aligns with jawascript and other python libs
- `bzip2`
- update to v5
- `jiff`
- conversions for jiff-round-mode/unit/weekday
- not-implemented placeholders and new impls
Expand Down
26 changes: 12 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/ryo3-bzip2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license.workspace = true
repository.workspace = true

[dependencies]
bzip2 = "0.4.4"
bzip2 = "0.5.0"
pyo3 = { workspace = true, features = ["experimental-inspect"] }

[lints]
Expand Down
59 changes: 52 additions & 7 deletions crates/ryo3-url/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
//! ryo3 url wrapper library for python
use pyo3::basic::CompareOp;
use pyo3::prelude::*;
use pyo3::types::{PyAnyMethods, PyTuple};
use pyo3::types::{PyAnyMethods, PyDict, PyTuple};
use pyo3::types::{PyModule, PyType};
use pyo3::{pyclass, Bound, PyResult};
use std::hash::{Hash, Hasher};
use std::path::PathBuf;

#[derive(Debug, Clone)]
#[pyclass(name = "Url", module = "ryo3")]
#[pyclass(name = "URL", module = "ryo3")]
pub struct PyUrl(pub(crate) url::Url);

#[pymethods]
impl PyUrl {
#[new]
fn new(url: &str) -> PyResult<Self> {
url::Url::parse(url).map(PyUrl).map_err(|e| {
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{e} (url={url})"))
})
#[pyo3(signature = (url, *, params = None))]
fn new(url: &str, params: Option<&Bound<'_, PyDict>>) -> PyResult<Self> {
if let Some(params) = params {
let params = params
.into_iter()
.map(|(k, v)| {
let k_str: String = k.extract()?;
let v_str: String = v.extract()?;
Ok((k_str, v_str))
})
.collect::<PyResult<Vec<(String, String)>>>()?;
url::Url::parse_with_params(url, params)
.map(PyUrl)
.map_err(|e| {
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{e} (url={url})"))
})
} else {
url::Url::parse(url).map(PyUrl).map_err(|e| {
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{e} (url={url})"))
})
}
}

#[classmethod]
Expand All @@ -27,12 +45,34 @@ impl PyUrl {
})
}

#[classmethod]
fn parse_with_params<'py>(
_cls: &Bound<'py, PyType>,
url: &str,
params: &Bound<'py, PyDict>,
) -> PyResult<Self> {
let params = params
.into_iter()
.map(|(k, v)| {
let k_str: String = k.extract()?;
let v_str: String = v.extract()?;
Ok((k_str, v_str))
})
.collect::<PyResult<Vec<(String, String)>>>()?;

url::Url::parse_with_params(url, params)
.map(PyUrl)
.map_err(|e| {
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{e} (url={url})"))
})
}

fn __str__(&self) -> &str {
self.0.as_str()
}

fn __repr__(&self) -> String {
format!("Url(\'{}\')", self.0.as_str())
format!("URL(\'{}\')", self.0.as_str())
}

fn __hash__(&self) -> u64 {
Expand Down Expand Up @@ -175,6 +215,11 @@ impl PyUrl {
self.0.has_authority()
}

#[getter]
fn netloc(&self) -> &str {
// not provided by python
self.0.authority()
}
fn has_host(&self) -> bool {
self.0.has_host()
}
Expand Down
4 changes: 2 additions & 2 deletions python/ry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ry import ryo3
from ry.ryo3 import (
URL,
Date,
DateTime,
DateTimeRound,
Expand All @@ -19,7 +20,6 @@
TimeSpan,
Timestamp,
TimeZone,
Url,
WalkdirGen,
Xxh3,
Xxh32,
Expand Down Expand Up @@ -114,6 +114,7 @@
)

__all__ = (
"URL",
"Date",
"DateTime",
"DateTimeRound",
Expand All @@ -131,7 +132,6 @@
"TimeSpan",
"TimeZone",
"Timestamp",
"Url",
"WalkdirGen",
"Xxh3",
"Xxh32",
Expand Down
16 changes: 9 additions & 7 deletions python/ry/ryo3.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -551,16 +551,18 @@ def sqlfmt(
# URL
# ==============================================================================

class Url:
def __init__(self, url: str) -> None: ...
class URL:
def __init__(self, url: str, *, params: dict[str, str] | None = None) -> None: ...
@classmethod
def parse(cls, url: str) -> Url: ...
def parse(cls, url: str) -> URL: ...
@classmethod
def parse_with_params(cls, url: str, params: dict[str, str]) -> URL: ...
def __str__(self) -> str: ...
def __repr__(self) -> str: ...
def __hash__(self) -> int: ...
def join(self, *parts: str) -> Url: ...
def __truediv__(self, relative: str) -> Url: ...
def __rtruediv__(self, relative: str) -> Url: ...
def join(self, *parts: str) -> URL: ...
def __truediv__(self, relative: str) -> URL: ...
def __rtruediv__(self, relative: str) -> URL: ...
def __eq__(self, other: object) -> bool: ...
def __lt__(self, other: object) -> bool: ...
def __le__(self, other: object) -> bool: ...
Expand Down Expand Up @@ -596,7 +598,7 @@ class Url:
def has_host(self) -> bool: ...
def is_special(self) -> bool: ...
@classmethod
def from_directory_path(cls, path: str) -> Url: ...
def from_directory_path(cls, path: str) -> URL: ...
def to_filepath(self) -> str: ...
def set_fragment(self, fragment: str) -> None: ...
def set_host(self, host: str) -> None: ...
Expand Down
24 changes: 12 additions & 12 deletions tests/url/test_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_parse_error() -> None:
"""

with pytest.raises(ValueError):
ry.Url.parse("http://[:::1]")
ry.URL.parse("http://[:::1]")


def test_parse_url_readme() -> None:
Expand All @@ -38,7 +38,7 @@ def test_parse_url_readme() -> None:
assert!(issue_list_url.fragment() == None);
assert!(!issue_list_url.cannot_be_a_base());
"""
u = ry.Url.parse(
u = ry.URL.parse(
"https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
)
assert u.scheme == "https"
Expand All @@ -59,40 +59,40 @@ def test_parse_url_readme() -> None:
def test_inheritance() -> None:
with pytest.raises(TypeError):

class MyURL(ry.Url):
class MyURL(ry.URL):
pass


def test_str_subclass() -> None:
class S(str):
pass

assert str(ry.Url(S("http://example.com"))) == "http://example.com/"
assert str(ry.URL(S("http://example.com"))) == "http://example.com/"


#
def test_absolute_url_without_host() -> None:
with pytest.raises(ValueError):
ry.Url("http://:8080/")
ry.URL("http://:8080/")


def test_url_is_not_str() -> None:
url = ry.Url("http://example.com")
url = ry.URL("http://example.com")
assert not isinstance(url, str)


def test_str() -> None:
url = ry.Url("http://example.com:8888/path/to?a=1&b=2")
url = ry.URL("http://example.com:8888/path/to?a=1&b=2")
assert str(url) == "http://example.com:8888/path/to?a=1&b=2"


def test_repr() -> None:
url = ry.Url("http://example.com")
assert "Url('http://example.com/')" == repr(url)
url = ry.URL("http://example.com")
assert "URL('http://example.com/')" == repr(url)


def test_join() -> None:
u = ry.Url("http://example.com")
u = ry.URL("http://example.com")
joined = u.join("foo")
assert str(joined) == "http://example.com/foo"
joined_multiple = u.join("foo").join("bar")
Expand All @@ -102,7 +102,7 @@ def test_join() -> None:


def test_join_truediv() -> None:
u = ry.Url("http://example.com")
u = ry.URL("http://example.com")
joined = u / "foo"
assert str(joined) == "http://example.com/foo"
joined_multiple = u / "foo" / "bar"
Expand All @@ -112,7 +112,7 @@ def test_join_truediv() -> None:


def test_join_truediv_trailing_slash() -> None:
u = ry.Url("http://example.com")
u = ry.URL("http://example.com")
joined = u / "foo"
assert str(joined) == "http://example.com/foo"
joined_multiple = u / "foo/" / "bar"
Expand Down
23 changes: 23 additions & 0 deletions tests/url/test_url_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ry


def test_parse_with_params_dict() -> None:
url = ry.URL.parse_with_params(
"https://example.net?dont=clobberme",
dict([("lang", "rust"), ("browser", "servo")]),
)
assert str(url) == "https://example.net/?dont=clobberme&lang=rust&browser=servo"


def test_parse_new_params_kwarg() -> None:
url = ry.URL(
"https://example.net?dont=clobberme",
params=dict([("lang", "rust"), ("browser", "servo")]),
)
assert str(url) == "https://example.net/?dont=clobberme&lang=rust&browser=servo"


# def test_parse_with_params_list() -> None:
# url = ry.URL.parse_with_params("https://example.net?dont=clobberme",
# [("lang", "rust"), ("browser", "servo")])
# assert url.as_str() == "https://example.net/?dont=clobberme&lang=rust&browser=servo"

0 comments on commit 5768652

Please sign in to comment.