aio-libs / yarl

Yet another URL library

Home Page:https://yarl.aio-libs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Altering URLs

chey opened this issue · comments

commented

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

  • I agree to follow the aio-libs Code of Conduct
commented

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".

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')
commented

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.

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?

commented

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

Yes, that's what I was referring to.

commented

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.