Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add path_safe method #1150

Merged
merged 15 commits into from
Sep 23, 2024
17 changes: 17 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,23 @@ There are two kinds of properties: *decoded* and *encoded* (with
>>> URL('http://example.com').path
'/'

.. warning::

In many situations it is important to distinguish between path separators
(a literal ``/``) and other forward slashes (a literal `%2F`). Use
:attr:`URL.path_safe` for these cases.

.. attribute:: URL.path_safe

Similar to :attr:`URL.path` except it doesn't decode ``%2F`` or ``%25``.
This allows to distinguish between path separators (``/``) and encoded
slashes (``%2F``).

Note that ``%25`` is also not decoded to avoid issues with double unquoting
of values. e.g. You can unquote the value with
``URL.path_safe.replace("%2F", "/").replace("%25", %")`` to get the same
result as :meth:`URL.path_safe`. If the `%25` was unquoted, it would be
impossible to tell the difference between ``%2F`` and ``%252F``.

.. attribute:: URL.path_qs

Expand Down
14 changes: 13 additions & 1 deletion yarl/_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ class URL:
_FRAGMENT_REQUOTER = _Quoter(safe="?/:@")

_UNQUOTER = _Unquoter()
_PATH_UNQUOTER = _Unquoter(ignore="/", unsafe="+")
_PATH_UNQUOTER = _Unquoter(unsafe="+")
bdraco marked this conversation as resolved.
Show resolved Hide resolved
_PATH_SAFE_UNQUOTER = _Unquoter(ignore="/%", unsafe="+")
_QS_UNQUOTER = _Unquoter(qs=True)

_val: SplitResult
Expand Down Expand Up @@ -710,6 +711,17 @@ def path(self) -> str:
"""
return self._PATH_UNQUOTER(self.raw_path)

@cached_property
def path_safe(self) -> str:
"""Decoded path of URL.

/ for absolute URLs without path part.

/ (%2F) and % (%25) are not decoded

"""
return self._PATH_SAFE_UNQUOTER(self.raw_path)

@cached_property
def _parsed_query(self) -> List[Tuple[str, str]]:
"""Parse query part of URL."""
Expand Down
Loading