Skip to content

Commit 52dd633

Browse files
committed
fix: final fixes
1 parent 5cf598f commit 52dd633

File tree

3 files changed

+74
-63
lines changed

3 files changed

+74
-63
lines changed

python/pydantic_core/_pydantic_core.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class Url(SupportsAllComparisons):
192192
cls,
193193
*,
194194
scheme: str,
195-
user: Optional[str] = None,
195+
username: Optional[str] = None,
196196
password: Optional[str] = None,
197197
host: str,
198198
port: Optional[int] = None,
@@ -222,10 +222,10 @@ class MultiHostUrl(SupportsAllComparisons):
222222
cls,
223223
*,
224224
scheme: str,
225-
user: Optional[str] = None,
225+
username: Optional[str] = None,
226226
password: Optional[str] = None,
227227
host: Optional[str] = None,
228-
hosts: Optional[list[dict]] = None,
228+
hosts: Optional[list[MultiHostHost]] = None,
229229
port: Optional[int] = None,
230230
path: Optional[str] = None,
231231
query: Optional[str] = None,

src/url.rs

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -156,30 +156,26 @@ impl PyUrl {
156156
}
157157

158158
#[classmethod]
159-
#[pyo3(signature=(*, scheme, host, user=None, password=None, port=None, path=None, query=None, fragment=None))]
159+
#[pyo3(signature=(*, scheme, host, username=None, password=None, port=None, path=None, query=None, fragment=None))]
160160
#[allow(clippy::too_many_arguments)]
161161
pub fn build<'a>(
162162
cls: &'a PyType,
163163
scheme: &str,
164164
host: &str,
165-
user: Option<&str>,
165+
username: Option<&str>,
166166
password: Option<&str>,
167167
port: Option<u16>,
168168
path: Option<&str>,
169169
query: Option<&str>,
170170
fragment: Option<&str>,
171171
) -> PyResult<&'a PyAny> {
172-
let user_password = match (user, password) {
173-
(Some(user), None) => format!("{user}@"),
174-
(None, Some(password)) => format!(":{password}@"),
175-
(Some(user), Some(password)) => format!("{user}:{password}@"),
176-
(None, None) => String::new(),
172+
let url_host = UrlHostParts {
173+
username: username.map(Into::into),
174+
password: password.map(Into::into),
175+
host: Some(host.into()),
176+
port,
177177
};
178-
let mut url = format!("{scheme}://{user_password}{host}");
179-
if let Some(port) = port {
180-
url.push(':');
181-
url.push_str(&format!("{port}"));
182-
}
178+
let mut url = format!("{scheme}://{url_host}");
183179
if let Some(path) = path {
184180
url.push('/');
185181
url.push_str(path);
@@ -360,52 +356,53 @@ impl PyMultiHostUrl {
360356
}
361357

362358
#[classmethod]
363-
#[pyo3(signature=(*, scheme, hosts=None, path=None, query=None, fragment=None, host=None, user=None, password=None, port=None))]
359+
#[pyo3(signature=(*, scheme, hosts=None, path=None, query=None, fragment=None, host=None, username=None, password=None, port=None))]
364360
#[allow(clippy::too_many_arguments)]
365361
pub fn build<'a>(
366362
cls: &'a PyType,
367363
scheme: &str,
368-
hosts: Option<Vec<MultiHostUrlHost>>,
364+
hosts: Option<Vec<UrlHostParts>>,
369365
path: Option<&str>,
370366
query: Option<&str>,
371367
fragment: Option<&str>,
372368
// convenience parameters to build with a single host
373369
host: Option<&str>,
374-
user: Option<&str>,
370+
username: Option<&str>,
375371
password: Option<&str>,
376372
port: Option<u16>,
377373
) -> PyResult<&'a PyAny> {
378-
let mut url = if hosts.is_some() && (host.is_some() || user.is_some() || password.is_some() || port.is_some()) {
379-
return Err(PyValueError::new_err(
380-
"expected one of `hosts` or singular values to be set.",
381-
));
382-
} else if let Some(hosts) = hosts {
383-
// check all of host / user / password / port empty
384-
// build multi-host url
385-
let mut multi_url = format!("{scheme}://");
386-
for (index, single_host) in hosts.iter().enumerate() {
387-
if single_host.is_empty() {
388-
return Err(PyValueError::new_err(
389-
"expected one of 'host', 'username', 'password' or 'port' to be set",
390-
));
374+
let mut url =
375+
if hosts.is_some() && (host.is_some() || username.is_some() || password.is_some() || port.is_some()) {
376+
return Err(PyValueError::new_err(
377+
"expected one of `hosts` or singular values to be set.",
378+
));
379+
} else if let Some(hosts) = hosts {
380+
// check all of host / user / password / port empty
381+
// build multi-host url
382+
let mut multi_url = format!("{scheme}://");
383+
for (index, single_host) in hosts.iter().enumerate() {
384+
if single_host.is_empty() {
385+
return Err(PyValueError::new_err(
386+
"expected one of 'host', 'username', 'password' or 'port' to be set",
387+
));
388+
}
389+
multi_url.push_str(&single_host.to_string());
390+
if index != hosts.len() - 1 {
391+
multi_url.push(',');
392+
};
391393
}
392-
multi_url.push_str(&single_host.to_string());
393-
if index != hosts.len() - 1 {
394-
multi_url.push(',');
394+
multi_url
395+
} else if host.is_some() {
396+
let url_host = UrlHostParts {
397+
username: username.map(Into::into),
398+
password: password.map(Into::into),
399+
host: host.map(Into::into),
400+
port: port.map(Into::into),
395401
};
396-
}
397-
multi_url
398-
} else if host.is_some() {
399-
let url_host = MultiHostUrlHost {
400-
username: user.map(Into::into),
401-
password: password.map(Into::into),
402-
host: host.map(Into::into),
403-
port: port.map(Into::into),
402+
format!("{scheme}://{url_host}")
403+
} else {
404+
return Err(PyValueError::new_err("expected either `host` or `hosts` to be set"));
404405
};
405-
format!("{scheme}://{url_host}")
406-
} else {
407-
return Err(PyValueError::new_err("expected either `host` or `hosts` to be set"));
408-
};
409406

410407
if let Some(path) = path {
411408
url.push('/');
@@ -423,24 +420,24 @@ impl PyMultiHostUrl {
423420
}
424421
}
425422

426-
pub struct MultiHostUrlHost {
423+
pub struct UrlHostParts {
427424
username: Option<String>,
428425
password: Option<String>,
429426
host: Option<String>,
430427
port: Option<u16>,
431428
}
432429

433-
impl MultiHostUrlHost {
430+
impl UrlHostParts {
434431
fn is_empty(&self) -> bool {
435432
self.host.is_none() && self.password.is_none() && self.host.is_none() && self.port.is_none()
436433
}
437434
}
438435

439-
impl FromPyObject<'_> for MultiHostUrlHost {
436+
impl FromPyObject<'_> for UrlHostParts {
440437
fn extract(ob: &'_ PyAny) -> PyResult<Self> {
441438
let py = ob.py();
442439
let dict = ob.downcast::<PyDict>()?;
443-
Ok(MultiHostUrlHost {
440+
Ok(UrlHostParts {
444441
username: dict.get_as(intern!(py, "username"))?,
445442
password: dict.get_as(intern!(py, "password"))?,
446443
host: dict.get_as(intern!(py, "host"))?,
@@ -449,16 +446,30 @@ impl FromPyObject<'_> for MultiHostUrlHost {
449446
}
450447
}
451448

452-
impl fmt::Display for MultiHostUrlHost {
449+
impl fmt::Display for UrlHostParts {
453450
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
454-
if self.username.is_some() && self.password.is_some() {
455-
write!(
456-
f,
457-
"{}:{}",
458-
self.username.as_ref().unwrap(),
459-
self.password.as_ref().unwrap()
460-
)?;
461-
}
451+
let _ = match self {
452+
UrlHostParts {
453+
username: Some(username),
454+
password: None,
455+
..
456+
} => write!(f, "{username}"),
457+
UrlHostParts {
458+
username: None,
459+
password: Some(password),
460+
..
461+
} => write!(f, ":{password}"),
462+
UrlHostParts {
463+
username: Some(username),
464+
password: Some(password),
465+
..
466+
} => write!(f, "{username}:{password}"),
467+
UrlHostParts {
468+
username: None,
469+
password: None,
470+
..
471+
} => Ok(()),
472+
};
462473
if self.host.is_some() {
463474
write!(f, "@{}", self.host.as_ref().unwrap())?;
464475
}

tests/validators/test_url.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ def test_url_deepcopy() -> None:
12211221
def test_multi_url_build() -> None:
12221222
url = MultiHostUrl.build(
12231223
scheme='postgresql',
1224-
user='testuser',
1224+
username='testuser',
12251225
password='testpassword',
12261226
host='127.0.0.1',
12271227
port=5432,
@@ -1233,7 +1233,7 @@ def test_multi_url_build() -> None:
12331233
assert str(url) == 'postgresql://testuser:[email protected]:5432/database?sslmode=require#test'
12341234

12351235

1236-
@pytest.mark.parametrize('field', ['host', 'password', 'user', 'port'])
1236+
@pytest.mark.parametrize('field', ['host', 'password', 'username', 'port'])
12371237
def test_multi_url_build_hosts_set_with_single_value(field) -> None:
12381238
"""Hosts can't be provided with any single url values."""
12391239
hosts = [
@@ -1277,7 +1277,7 @@ def test_multi_url_build_neither_host_and_hosts_set() -> None:
12771277
with pytest.raises(ValueError):
12781278
MultiHostUrl.build(
12791279
scheme='postgresql',
1280-
user='testuser',
1280+
username='testuser',
12811281
password='testpassword',
12821282
port=5432,
12831283
path='database',
@@ -1289,7 +1289,7 @@ def test_multi_url_build_neither_host_and_hosts_set() -> None:
12891289
def test_url_build() -> None:
12901290
url = Url.build(
12911291
scheme='postgresql',
1292-
user='testuser',
1292+
username='testuser',
12931293
password='testpassword',
12941294
host='127.0.0.1',
12951295
port=5432,

0 commit comments

Comments
 (0)