Skip to content

Commit e9fef81

Browse files
authored
Revert URL subtraction (#1391)
1 parent 6a2a90a commit e9fef81

File tree

8 files changed

+1
-198
lines changed

8 files changed

+1
-198
lines changed

CHANGES/1340.feature.rst

Lines changed: 0 additions & 22 deletions
This file was deleted.

CHANGES/1379.feature.rst

Lines changed: 0 additions & 1 deletion
This file was deleted.

CHANGES/1382.feature.rst

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/api.rst

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -997,21 +997,6 @@ The path is encoded if needed.
997997
>>> base.join(URL('//python.org/page.html'))
998998
URL('http://python.org/page.html')
999999

1000-
The subtraction (``-``) operator creates a new URL with
1001-
a relative *path* to the target URL from the given base URL.
1002-
*scheme*, *user*, *password*, *host*, *port*, *query* and *fragment* are removed.
1003-
1004-
.. method:: URL.__sub__(url)
1005-
1006-
Returns a new URL with a relative *path* between two other URL objects.
1007-
1008-
.. doctest::
1009-
1010-
>>> target = URL('http://example.com/path/index.html')
1011-
>>> base = URL('http://example.com/')
1012-
>>> target - base
1013-
URL('path/index.html')
1014-
10151000
Human readable representation
10161001
-----------------------------
10171002

tests/test_url.py

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -61,89 +61,6 @@ def test_str():
6161
assert str(url) == "http://example.com:8888/path/to?a=1&b=2"
6262

6363

64-
@pytest.mark.parametrize(
65-
("target", "base", "expected"),
66-
[
67-
("http://example.com/path/to", "http://example.com/", "path/to"),
68-
("http://example.com/path/to", "http://example.com/spam", "path/to"),
69-
("http://example.com/this/is/a/test", "http://example.com/this/", "is/a/test"),
70-
(
71-
"http://example.com/this/./is/a/test",
72-
"http://example.com/this/",
73-
"is/a/test",
74-
),
75-
("http://example.com/this/is/../a//test", "http://example.com/this/", "a/test"),
76-
("http://example.com/path/to", "http://example.com/spam/", "../path/to"),
77-
("http://example.com/path", "http://example.com/path/to/", ".."),
78-
("http://example.com/path", "http://example.com/other/../path/to/", ".."),
79-
("http://example.com/", "http://example.com/", "."),
80-
("http://example.com", "http://example.com", "."),
81-
("http://example.com/", "http://example.com", "."),
82-
("http://example.com", "http://example.com/", "."),
83-
("//example.com", "//example.com", "."),
84-
("/path/to", "/spam/", "../path/to"),
85-
("path/to", "spam/", "../path/to"),
86-
("path/../to", "path/", "../to"),
87-
("path/..", ".", "path/.."),
88-
("path/../replace/me", "path/../replace", "replace/me"),
89-
("path/../replace/me", "path/../replace/", "me"),
90-
("path/to", "spam", "path/to"),
91-
("..", ".", ".."),
92-
(".", "..", "."),
93-
],
94-
)
95-
def test_sub(target: str, base: str, expected: str):
96-
expected_url = URL(expected)
97-
result_url = URL(target) - URL(base)
98-
assert result_url == expected_url
99-
100-
101-
@pytest.mark.xfail(reason="Empty segments are not preserved")
102-
@pytest.mark.parametrize(
103-
("target", "base", "expected"),
104-
[
105-
(
106-
"http://example.com/////path/////to",
107-
"http://example.com/////spam",
108-
"path/////to",
109-
),
110-
(
111-
"http://example.com////path/////to",
112-
"http://example.com/////spam",
113-
"..//path/////to",
114-
),
115-
],
116-
)
117-
def test_sub_empty_segments(target: str, base: str, expected: str):
118-
expected_url = URL(expected)
119-
result_url = URL(target) - URL(base)
120-
assert result_url == expected_url
121-
122-
123-
def test_sub_with_different_schemes():
124-
expected_error_msg = "Both URLs should have the same scheme"
125-
with pytest.raises(ValueError, match=expected_error_msg):
126-
URL("http://example.com/") - URL("https://example.com/")
127-
128-
129-
def test_sub_with_different_netlocs():
130-
expected_error_msg = "Both URLs should have the same netloc"
131-
with pytest.raises(ValueError, match=expected_error_msg):
132-
URL("https://spam.com/") - URL("https://ham.com/")
133-
134-
135-
def test_sub_with_different_anchors():
136-
expected_error_msg = "'path/to' and '/path' have different anchors"
137-
with pytest.raises(ValueError, match=expected_error_msg):
138-
URL("path/to") - URL("/path/from")
139-
140-
141-
def test_sub_with_two_dots_in_base():
142-
expected_error_msg = "'..' segment in '/path/..' cannot be walked"
143-
with pytest.raises(ValueError, match=expected_error_msg):
144-
URL("path/to") - URL("/path/../from")
145-
146-
14764
def test_repr():
14865
url = URL("http://example.com")
14966
assert "URL('http://example.com')" == repr(url)

tests/test_url_benchmarks.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@
1818
QUERY_URL = URL(QUERY_URL_STR)
1919
URL_WITH_PATH_STR = "http://www.domain.tld/req"
2020
URL_WITH_PATH = URL(URL_WITH_PATH_STR)
21-
URL_WITH_LONGER_PATH = URL("http://www.domain.tld/req/req/req")
2221
REL_URL = URL("/req")
2322
QUERY_SEQ = {str(i): tuple(str(j) for j in range(10)) for i in range(10)}
2423
SIMPLE_QUERY = {str(i): str(i) for i in range(10)}
2524
SIMPLE_INT_QUERY = {str(i): i for i in range(10)}
2625
QUERY_STRING = "x=y&z=1"
27-
URL_VERY_LONG_PATH = URL("http://www.domain.tld/" + "req/" * 100)
28-
URL_LONG_PATH = URL("http://www.domain.tld/" + "req/" * 30)
2926

3027

3128
class _SubClassedStr(str):
@@ -589,20 +586,6 @@ def _run() -> None:
589586
url.query
590587

591588

592-
def test_url_subtract(benchmark: BenchmarkFixture) -> None:
593-
@benchmark
594-
def _run() -> None:
595-
for _ in range(100):
596-
URL_WITH_LONGER_PATH - URL_WITH_PATH
597-
598-
599-
def test_url_subtract_long_urls(benchmark: BenchmarkFixture) -> None:
600-
@benchmark
601-
def _run() -> None:
602-
for _ in range(100):
603-
URL_VERY_LONG_PATH - URL_LONG_PATH
604-
605-
606589
def test_url_host_port_subcomponent(benchmark: BenchmarkFixture) -> None:
607590
cache_non_default = URL_WITH_NOT_DEFAULT_PORT._cache
608591
cache = BASE_URL._cache

yarl/_path.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
from collections.abc import Sequence
44
from contextlib import suppress
5-
from itertools import chain
6-
from pathlib import PurePosixPath
75

86

97
def normalize_path_segments(segments: Sequence[str]) -> list[str]:
@@ -41,31 +39,3 @@ def normalize_path(path: str) -> str:
4139

4240
segments = path.split("/")
4341
return prefix + "/".join(normalize_path_segments(segments))
44-
45-
46-
def calculate_relative_path(target: str, base: str) -> str:
47-
"""Return the relative path between two other paths.
48-
49-
If the operation is not possible, raise ValueError.
50-
"""
51-
52-
target = target or "/"
53-
base = base or "/"
54-
55-
target_path = PurePosixPath(target)
56-
base_path = PurePosixPath(base)
57-
58-
if base[-1] != "/":
59-
base_path = base_path.parent
60-
61-
for step, path in enumerate(chain((base_path,), base_path.parents)):
62-
if path == target_path or path in target_path.parents:
63-
break
64-
elif path.name == "..":
65-
raise ValueError(f"'..' segment in {str(base_path)!r} cannot be walked")
66-
else:
67-
raise ValueError(
68-
f"{str(target_path)!r} and {str(base_path)!r} have different anchors"
69-
)
70-
offset = len(path.parts)
71-
return str(PurePosixPath(*("..",) * step, *target_path.parts[offset:]))

yarl/_url.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from propcache.api import under_cached_property as cached_property
1313

1414
from ._parse import USES_AUTHORITY, make_netloc, split_netloc, split_url, unsplit_result
15-
from ._path import calculate_relative_path, normalize_path, normalize_path_segments
15+
from ._path import normalize_path, normalize_path_segments
1616
from ._query import (
1717
Query,
1818
QueryVariable,
@@ -477,34 +477,6 @@ def __truediv__(self, name: str) -> "URL":
477477
return NotImplemented
478478
return self._make_child((str(name),))
479479

480-
def __sub__(self, other: object) -> "URL":
481-
"""Return a new URL with a relative path between two other URL objects.
482-
483-
Note that both URLs must have the same scheme and netloc.
484-
The new relative URL has only path:
485-
scheme, user, password, host, port, query and fragment are removed.
486-
487-
Example:
488-
>>> target = URL("http://example.com/path/index.html")
489-
>>> base = URL("http://example.com/")
490-
>>> target - base
491-
URL('path/index.html')
492-
"""
493-
494-
if type(other) is not URL:
495-
return NotImplemented
496-
497-
target_scheme, target_netloc, target_path, _, _ = self._val
498-
base_scheme, base_netloc, base_path, _, _ = other._val
499-
500-
if target_scheme != base_scheme:
501-
raise ValueError("Both URLs should have the same scheme")
502-
if target_netloc != base_netloc:
503-
raise ValueError("Both URLs should have the same netloc")
504-
505-
path = calculate_relative_path(target_path, base_path)
506-
return self._from_tup(("", "", path, "", ""))
507-
508480
def __mod__(self, query: Query) -> "URL":
509481
return self.update_query(query)
510482

0 commit comments

Comments
 (0)