yarl icon indicating copy to clipboard operation
yarl copied to clipboard

Rework of the URL subtraction feature

Open babieiev opened this issue 1 year ago โ€ข 9 comments

What do these changes do?

Rework of the URL subtraction feature (added in #1340; removed in #1391)

Are there changes in behavior for the user?

Being able to calculate the relative path between two URLs:

from yarl import URL

target_url = URL("http://example.com/path/index.html")
base_url = URL("http://example.com/path/")

relative_url = target_url.relative_to(base_url)

print(relative_url)  # output: "index.html"

Related issue number

Resolves #1183

Checklist

  • [ ] I think the code is well written
  • [x] Unit tests for the changes exist
  • [x] Documentation reflects the changes

Known issues

  • [x] Time complexity is roughly O(base_path_segments * target_path_segments)
  • [x] Empty segments are not handled correctly #1388
  • [x] Potentially handling special cases with leading/trailing slashes #1388

babieiev avatar Oct 25 '24 17:10 babieiev

CodSpeed Performance Report

Merging #1392 will not alter performance

Comparing oleksbabieiev:subtraction (ad61c24) with master (c1f6ef6)

Summary

โœ… 98 untouched
๐Ÿ†• 2 new

Benchmarks breakdown

Benchmark BASE HEAD Change
๐Ÿ†• test_relative_to N/A 778.9 ยตs N/A
๐Ÿ†• test_relative_to_long_urls N/A 4.4 ms N/A

codspeed-hq[bot] avatar Oct 25 '24 17:10 codspeed-hq[bot]

Codecov Report

:white_check_mark: All modified and coverable lines are covered by tests. :white_check_mark: Project coverage is 98.84%. Comparing base (937f030) to head (e15878f). :warning: Report is 57 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1392      +/-   ##
==========================================
- Coverage   98.93%   98.84%   -0.10%     
==========================================
  Files          32       25       -7     
  Lines        6091     5007    -1084     
  Branches      365      347      -18     
==========================================
- Hits         6026     4949    -1077     
+ Misses         62       56       -6     
+ Partials        3        2       -1     
Flag Coverage ฮ”
CI-GHA 98.80% <100.00%> (-0.14%) :arrow_down:
MyPy ?
OS-Linux 98.80% <100.00%> (+0.01%) :arrow_up:
OS-Windows 98.83% <100.00%> (+0.01%) :arrow_up:
OS-macOS 98.58% <100.00%> (+0.01%) :arrow_up:
Py-3.10.11 98.56% <100.00%> (+0.01%) :arrow_up:
Py-3.10.16 98.76% <100.00%> (+0.01%) :arrow_up:
Py-3.11.11 98.76% <100.00%> (+0.01%) :arrow_up:
Py-3.11.9 98.56% <100.00%> (+0.01%) :arrow_up:
Py-3.12.9 98.76% <100.00%> (+0.01%) :arrow_up:
Py-3.13.2 98.76% <100.00%> (+0.01%) :arrow_up:
Py-3.9.13 98.52% <100.00%> (+0.01%) :arrow_up:
Py-3.9.21 98.72% <100.00%> (+0.01%) :arrow_up:
Py-pypy7.3.16 98.71% <100.00%> (+0.01%) :arrow_up:
Py-pypy7.3.19 98.73% <100.00%> (+0.01%) :arrow_up:
VM-macos-latest 98.58% <100.00%> (+0.01%) :arrow_up:
VM-ubuntu-latest 98.80% <100.00%> (+0.01%) :arrow_up:
VM-windows-latest 98.83% <100.00%> (+0.01%) :arrow_up:
pytest 98.80% <100.00%> (+0.01%) :arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar Oct 25 '24 17:10 codecov[bot]

I'm going to do a 1.17.0 release so we can start preparing aiohttp 3.11.x and I want do to some more downstream cleanups. We can do a 1.18.0 for this once its ready

bdraco avatar Oct 28 '24 19:10 bdraco

Some conflicts happened. I think I've resolved them correctly

bdraco avatar Oct 30 '24 17:10 bdraco

I disagree with some of the unit tests, as I expect the returned relative path to add up to the complete path when concatenated as a relative path.

def test_relative_to(target: str, base: str, expected: str):
    expected_url = URL(expected)
    target_url = URL(target)
    base_url = URL(base)
    result_url = target_url.relative_to(base_url)
    assert result_url == expected_url
    combined = base_url / expected
    assert target_url == combined

Please include this in the unit tests, it'll help to align those with the idea of a relative path and harden the semantics on this operation wrt. to trailing / in base and result.

commonism avatar Jan 02 '25 16:01 commonism

Please, avoid touching anything in the packaging/ directory.

Ok, but this was an auto-fix from pre-commit hooks. The bot added it after I had already pushed the changes ๐Ÿคทโ€โ™‚๏ธ

babieiev avatar Jan 03 '25 14:01 babieiev

Oh, that's weird. I wonder if the pre-commit.ci cache got invalidated randomly and it pulled in some newer transitive deps...

webknjaz avatar Jan 03 '25 15:01 webknjaz

    combined = base_url / expected
    assert target_url == combined

Please include this in the unit tests, it'll help to align those with the idea of a relative path and harden the semantics on this operation wrt. to trailing / in base and result.

@commonism this condition will only be true if we first normalize the URLs before comparing, so I've modified this code a bit:

combined_url = base_url / expected
combined_path = normalized_path(combined_url.path)
target_path = normalized_path(target_url.path)
assert combined_path == target_path

If we don't normalize them, the following can happen:

target_url = URL("/path/to")
base_url = URL("/spam")
relative_url = target_url.relative_to(base_url)
print(relative_url) # URL("../path/to")
combined_url = base_url / relative_url
print(combined_url) # URL("/spam/../path/to") => combined_url != target_url

But now everything seems to be working properly.

babieiev avatar Mar 03 '25 15:03 babieiev

@babieiev could you fix linting and rebase?

webknjaz avatar Jun 16 '25 07:06 webknjaz

Sorry, I messed up a few things, so I'll open a new pull request ๐Ÿ˜…

babieiev avatar Jun 26 '25 17:06 babieiev