Skip to content

Commit 30a6eb9

Browse files
authored
Support URL authority (#478)
1 parent 698ddfb commit 30a6eb9

File tree

6 files changed

+107
-3
lines changed

6 files changed

+107
-3
lines changed

CHANGES/478.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support URL authority/raw_authority properties and authority argument of build() method.

docs/api.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,34 @@ There are two kinds of properties: *decoded* and *encoded* (with
229229

230230
.. versionadded:: 1.3
231231

232+
.. attribute:: URL.authority
233+
234+
Decoded *authority* part of URL, a combination of *user*, *password*, *host*, and
235+
*port*.
236+
237+
``authority = [ user [ ":" password ] "@" ] host [ ":" port ]``.
238+
239+
*authority* is ``None`` if all parts are missing.
240+
241+
.. doctest::
242+
243+
>>> URL('http://john:[email protected]:8000').authority
244+
'john:[email protected]:8000'
245+
246+
.. versionadded:: 1.5
247+
248+
.. attribute:: URL.raw_authority
249+
250+
Encoded *authority* part of URL, a combination of *user*, *password*, *host*, and
251+
*port*. ``None`` if all parts are missing.
252+
253+
.. doctest::
254+
255+
>>> URL('http://john:pass@хост.домен:8000').raw_authority
256+
'john:[email protected]:8000'
257+
258+
.. versionadded:: 1.5
259+
232260
.. attribute:: URL.path
233261

234262
Decoded *path* part of URL, ``'/'`` for absolute URLs without *path* part.

tests/test_url.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,26 @@ def test_raw_host_from_str_with_ipv6():
190190
assert url.raw_host == "::1"
191191

192192

193+
def test_authority_full() -> None:
194+
url = URL("http://user:[email protected]:8080/path")
195+
assert url.raw_authority == "user:[email protected]:8080"
196+
assert url.authority == "user:[email protected]:8080"
197+
198+
199+
def test_authority_short() -> None:
200+
url = URL("http://host.com/path")
201+
assert url.raw_authority == "host.com"
202+
203+
204+
def test_authority_full_nonasci() -> None:
205+
url = URL("http://ваня:пароль@айдеко.рф:8080/path")
206+
assert url.raw_authority == (
207+
"%D0%B2%D0%B0%D0%BD%D1%8F:%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@"
208+
"xn--80aidohy.xn--p1ai:8080"
209+
)
210+
assert url.authority == "ваня:пароль@айдеко.рф:8080"
211+
212+
193213
def test_lowercase():
194214
url = URL("http://gitHUB.com")
195215
assert url.raw_host == "github.com"

tests/test_url_build.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,23 @@ def test_build_with_all():
8080
assert str(u) == "http://foo:[email protected]:8000/index.html?arg=value1#top"
8181

8282

83+
def test_build_with_authority_and_host():
84+
with pytest.raises(ValueError):
85+
URL.build(authority="host.com", host="example.com")
86+
87+
88+
def test_build_with_authority():
89+
url = URL.build(scheme="http", authority="ваня:[email protected]:8000", path="path")
90+
assert str(url) == "http://%D0%B2%D0%B0%D0%BD%D1%8F:[email protected]:8000/path"
91+
92+
93+
def test_build_with_authority_without_encoding():
94+
url = URL.build(
95+
scheme="http", authority="foo:[email protected]:8000", path="path", encoded=True
96+
)
97+
assert str(url) == "http://foo:[email protected]:8000/path"
98+
99+
83100
def test_query_str():
84101
u = URL.build(scheme="http", host="127.0.0.1", path="/", query_string="arg=value1")
85102
assert str(u) == "http://127.0.0.1/?arg=value1"

yarl/__init__.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ def build(
187187
cls,
188188
*,
189189
scheme="",
190+
authority=None,
190191
user=None,
191192
password=None,
192193
host="",
@@ -199,8 +200,14 @@ def build(
199200
):
200201
"""Creates and returns a new URL"""
201202

202-
if not host and scheme:
203-
raise ValueError('Can\'t build URL with "scheme" but without "host".')
203+
if scheme and (not host and not authority):
204+
raise ValueError(
205+
'Can\'t build URL with "scheme" but without "host" or "authority".'
206+
)
207+
if authority and (user or password or host or port):
208+
raise ValueError(
209+
'Can\'t mix "authority" with "user", "password", "host" or "port".'
210+
)
204211
if port and not host:
205212
raise ValueError('Can\'t build URL with "port" but without "host".')
206213
if query and query_string:
@@ -211,7 +218,15 @@ def build(
211218
'"fragment" args, use string values instead.'
212219
)
213220

214-
if not user and not password and not host and not port:
221+
if authority:
222+
if encoded:
223+
netloc = authority
224+
else:
225+
tmp = SplitResult("", authority, "", "", "")
226+
netloc = cls._make_netloc(
227+
tmp.username, tmp.password, tmp.hostname, tmp.port, encode=True
228+
)
229+
elif not user and not password and not host and not port:
215230
netloc = ""
216231
else:
217232
netloc = cls._make_netloc(user, password, host, port, encode=not encoded)
@@ -388,6 +403,26 @@ def scheme(self):
388403
"""
389404
return self._val.scheme
390405

406+
@property
407+
def raw_authority(self):
408+
"""Encoded authority part of URL.
409+
410+
Empty string for relative URLs.
411+
412+
"""
413+
return self._val.netloc
414+
415+
@cached_property
416+
def authority(self):
417+
"""Decoded authority part of URL.
418+
419+
Empty string for relative URLs.
420+
421+
"""
422+
return self._make_netloc(
423+
self.user, self.password, self.host, self.port, encode=False
424+
)
425+
391426
@property
392427
def raw_user(self):
393428
"""Encoded user part of URL.

yarl/__init__.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class URL:
1717
raw_host: Final[Optional[str]]
1818
host: Final[Optional[str]]
1919
port: Final[Optional[int]]
20+
raw_authority: Final[Optional[str]]
21+
authority: Final[Optional[str]]
2022
raw_path: Final[str]
2123
path: Final[str]
2224
raw_query_string: Final[str]
@@ -39,6 +41,7 @@ class URL:
3941
cls,
4042
*,
4143
scheme: str = ...,
44+
authority: Optional[str] = ...,
4245
user: Optional[str] = ...,
4346
password: Optional[str] = ...,
4447
host: str = ...,

0 commit comments

Comments
 (0)