yarl icon indicating copy to clipboard operation
yarl copied to clipboard

Altering URLs

Open chey opened this issue 2 years ago • 6 comments

Is your feature request related to a problem?

No

Describe the solution you'd like

Would like the ability to alter single/multiple parts of a URL in single statement.

Example:

>>> URL("http://localhost:8080").with(host="newhost", port=80)
URL("http://newhost")

or this

>>> URL("http://localhost:8080").with(port=80)
URL("http://localhost")

Describe alternatives you've considered

N/A

Additional context

No response

Code of Conduct

  • [X] I agree to follow the aio-libs Code of Conduct

chey avatar Jun 09 '22 16:06 chey

build doesn't alter URLs:

>>> URL("http://localhost:9090").build(port=8080)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/chey/work/vicinity/venv/lib/python3.10/site-packages/yarl/_url.py", line 216, in build
    raise ValueError('Can\'t build URL with "port" but without "host".')
ValueError: Can't build URL with "port" but without "host".

chey avatar Jun 09 '22 17:06 chey

build doesn't alter URLs:

No, URL.build() is a class method and builds a new URL from scratch. It has no knowledge of the existing instance.

You can chain up multiple with_* calls, instead:

>>> URL("http://localhost:8080").with_host("newhost").with_port(80)
URL('http://newhost:80')

Note that with is a keyword in Python so can't be used for a method name; it'd have to be with_ instead or a different name altogether.

That said I'm +0 on adding a URL.with_() method that passes on keyword arguments to .with_{key}(value) calls

def with_(self, **kwargs):
    new = self
    for key, value in kwargs.items():
        new = getattr(new, f"with_{key}")(value)
    return new

If this was added, the actual implementation would be a bit more robust, perhaps with cached mapping of known with_* method names to apply so an exception can be raised early if any of the keywords passed don't fit.

You can add the above locally if you want to:

>>> def with_(self, **kwargs):
...     new = self
...     for key, value in kwargs.items():
...         new = getattr(new, f"with_{key}")(value)
...     return new
...
>>> URL.with_ = with_
>>> URL("http://localhost:8080").with_(host="newhost", port=80)
URL('http://newhost:80')

mjpieters avatar Jun 14 '22 10:06 mjpieters

Yes, it's clear with will not be a good method choice. Maybe alter would be sufficient. Looking at pathlib i see names like unparse and unsplit which seem confusing at first glance.

chey avatar Jun 14 '22 12:06 chey

Yes, it's clear with will not be a good method choice. Maybe alter would be sufficient.

update() would echo update_query(). Still not sold it is really needed however, but not dead against it at the same time.

Looking at pathlib i see names like unparse and unsplit which seem confusing at first glance.

pathlib doesn't have such methods, maybe you were looking at urllib.parse instead?

mjpieters avatar Jun 14 '22 14:06 mjpieters

pathlib doesn't have such methods, maybe you were looking at urllib.parse instead?

Yes, that's what I was referring to.

chey avatar Jun 14 '22 15:06 chey

You can add the above locally if you want to:

>>> def with_(self, **kwargs):
...     new = self
...     for key, value in kwargs.items():
...         new = getattr(new, f"with_{key}")(value)
...     return new
...
>>> URL.with_ = with_
>>> URL("http://localhost:8080").with_(host="newhost", port=80)
URL('http://newhost:80')

I did make a similar function to this in order to test out the idea. I suppose one could do a dir scan of URL for with_* in order to add exceptions. Like you said though, that would need to be cached as it would get expensive to scan URL each time.

chey avatar Jun 14 '22 15:06 chey