From 609d16dafd086aa65f0d4f7a663e9d3f1dd5db7c Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Sat, 19 Oct 2024 02:06:43 -1000
Subject: [PATCH] Reduce duplicate code to construct URL from a tuple (#1334)

---
 yarl/_url.py | 85 +++++++++++++++++++---------------------------------
 1 file changed, 30 insertions(+), 55 deletions(-)

diff --git a/yarl/_url.py b/yarl/_url.py
index 79aece464..7ef4ae702 100644
--- a/yarl/_url.py
+++ b/yarl/_url.py
@@ -428,10 +428,15 @@ def build(
         return url
 
     @classmethod
-    def _from_val(cls, val: SplitResult) -> "URL":
-        """Create a new URL from a SplitResult."""
+    def _from_tup(cls, val: tuple[str, str, str, str, str]) -> "URL":
+        """Create a new URL from a tuple.
+
+        The tuple should be in the form of a SplitResult.
+
+        (scheme, netloc, path, query, fragment)
+        """
         self = object.__new__(cls)
-        self._val = val
+        self._val = tuple.__new__(SplitResult, val)
         self._cache = {}
         return self
 
@@ -603,7 +608,7 @@ def _origin(self) -> "URL":
             netloc = self._make_netloc(None, None, encoded_host, self.explicit_port)
         elif not path and not query and not fragment:
             return self
-        return self._from_val(tuple.__new__(SplitResult, (scheme, netloc, "", "", "")))
+        return self._from_tup((scheme, netloc, "", "", ""))
 
     def relative(self) -> "URL":
         """Return a relative part of the URL.
@@ -614,8 +619,7 @@ def relative(self) -> "URL":
         _, netloc, path, query, fragment = self._val
         if not netloc:
             raise ValueError("URL should be absolute")
-        val = tuple.__new__(SplitResult, ("", "", path, query, fragment))
-        return self._from_val(val)
+        return self._from_tup(("", "", path, query, fragment))
 
     @cached_property
     def absolute(self) -> bool:
@@ -898,12 +902,10 @@ def parent(self) -> "URL":
         scheme, netloc, path, query, fragment = self._val
         if not path or path == "/":
             if fragment or query:
-                val = tuple.__new__(SplitResult, (scheme, netloc, path, "", ""))
-                return self._from_val(val)
+                return self._from_tup((scheme, netloc, path, "", ""))
             return self
         parts = path.split("/")
-        val = tuple.__new__(SplitResult, (scheme, netloc, "/".join(parts[:-1]), "", ""))
-        return self._from_val(val)
+        return self._from_tup((scheme, netloc, "/".join(parts[:-1]), "", ""))
 
     @cached_property
     def raw_name(self) -> str:
@@ -974,14 +976,14 @@ def _make_child(self, paths: "Sequence[str]", encoded: bool = False) -> "URL":
             parsed += segments[segment_slice_start:]
         parsed.reverse()
 
-        v = self._val
-        if v.path and (old_path_segments := v.path.split("/")):
+        scheme, netloc, path, _, _ = self._val
+        if path and (old_path_segments := path.split("/")):
             # If the old path ends with a slash, the last segment is an empty string
             # and should be removed before adding the new path segments.
             old_path_cutoff = -1 if old_path_segments[-1] == "" else None
             parsed = [*old_path_segments[:old_path_cutoff], *parsed]
 
-        if netloc := v.netloc:
+        if netloc := netloc:
             # If the netloc is present, we need to ensure that the path is normalized
             parsed = _normalize_path_segments(parsed) if needs_normalize else parsed
             if parsed and parsed[0] != "":
@@ -991,9 +993,7 @@ def _make_child(self, paths: "Sequence[str]", encoded: bool = False) -> "URL":
 
         new_path = "/".join(parsed)
 
-        return self._from_val(
-            tuple.__new__(SplitResult, (v.scheme, netloc, new_path, "", ""))
-        )
+        return self._from_tup((scheme, netloc, new_path, "", ""))
 
     @classmethod
     def _normalize_path(cls, path: str) -> str:
@@ -1165,8 +1165,7 @@ def with_scheme(self, scheme: str) -> "URL":
                 f"relative URLs for the {lower_scheme} scheme"
             )
             raise ValueError(msg)
-        val = tuple.__new__(SplitResult, (lower_scheme, netloc, path, query, fragment))
-        return self._from_val(val)
+        return self._from_tup((lower_scheme, netloc, path, query, fragment))
 
     def with_user(self, user: Union[str, None]) -> "URL":
         """Return a new URL with user replaced.
@@ -1189,9 +1188,7 @@ def with_user(self, user: Union[str, None]) -> "URL":
             raise ValueError("user replacement is not allowed for relative URLs")
         encoded_host = self.host_subcomponent or ""
         netloc = self._make_netloc(user, password, encoded_host, self.explicit_port)
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     def with_password(self, password: Union[str, None]) -> "URL":
         """Return a new URL with password replaced.
@@ -1214,9 +1211,7 @@ def with_password(self, password: Union[str, None]) -> "URL":
         encoded_host = self.host_subcomponent or ""
         port = self.explicit_port
         netloc = self._make_netloc(self.raw_user, password, encoded_host, port)
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     def with_host(self, host: str) -> "URL":
         """Return a new URL with host replaced.
@@ -1238,9 +1233,7 @@ def with_host(self, host: str) -> "URL":
         encoded_host = self._encode_host(host, validate_host=True) if host else ""
         port = self.explicit_port
         netloc = self._make_netloc(self.raw_user, self.raw_password, encoded_host, port)
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     def with_port(self, port: Union[int, None]) -> "URL":
         """Return a new URL with port replaced.
@@ -1259,9 +1252,7 @@ def with_port(self, port: Union[int, None]) -> "URL":
             raise ValueError("port replacement is not allowed for relative URLs")
         encoded_host = self.host_subcomponent or ""
         netloc = self._make_netloc(self.raw_user, self.raw_password, encoded_host, port)
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     def with_path(self, path: str, *, encoded: bool = False) -> "URL":
         """Return a new URL with path replaced."""
@@ -1272,9 +1263,7 @@ def with_path(self, path: str, *, encoded: bool = False) -> "URL":
                 path = self._normalize_path(path) if "." in path else path
         if path and path[0] != "/":
             path = f"/{path}"
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, "", ""))
-        )
+        return self._from_tup((scheme, netloc, path, "", ""))
 
     @classmethod
     def _get_str_query_from_sequence_iterable(
@@ -1401,9 +1390,7 @@ def with_query(self, *args: Any, **kwargs: Any) -> "URL":
         # N.B. doesn't cleanup query/fragment
         query = self._get_str_query(*args, **kwargs) or ""
         scheme, netloc, path, _, fragment = self._val
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     @overload
     def extend_query(self, query: Query) -> "URL": ...
@@ -1430,9 +1417,7 @@ def extend_query(self, *args: Any, **kwargs: Any) -> "URL":
             query += new_query if query[-1] == "&" else f"&{new_query}"
         else:
             query = new_query
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     @overload
     def update_query(self, query: Query) -> "URL": ...
@@ -1490,9 +1475,7 @@ def update_query(self, *args: Any, **kwargs: Any) -> "URL":
                 "Invalid query type: only str, mapping or "
                 "sequence of (key, value) pairs is allowed"
             )
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, fragment))
 
     def without_query_params(self, *query_params: str) -> "URL":
         """Remove some keys from query part and return new URL."""
@@ -1525,9 +1508,7 @@ def with_fragment(self, fragment: Union[str, None]) -> "URL":
         if self._val.fragment == raw_fragment:
             return self
         scheme, netloc, path, query, _ = self._val
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, netloc, path, query, raw_fragment))
-        )
+        return self._from_tup((scheme, netloc, path, query, raw_fragment))
 
     def with_name(self, name: str) -> "URL":
         """Return a new URL with name (last part of path) replaced.
@@ -1557,8 +1538,7 @@ def with_name(self, name: str) -> "URL":
             parts[-1] = name
             if parts[0] == "/":
                 parts[0] = ""  # replace leading '/'
-        val = tuple.__new__(SplitResult, (scheme, netloc, "/".join(parts), "", ""))
-        return self._from_val(val)
+        return self._from_tup((scheme, netloc, "/".join(parts), "", ""))
 
     def with_suffix(self, suffix: str) -> "URL":
         """Return a new URL with suffix (file extension of name) replaced.
@@ -1601,11 +1581,8 @@ def join(self, url: "URL") -> "URL":
 
         # scheme is in uses_authority as uses_authority is a superset of uses_relative
         if join_netloc and scheme in USES_AUTHORITY:
-            return self._from_val(
-                tuple.__new__(
-                    SplitResult,
-                    (scheme, join_netloc, join_path, join_query, join_fragment),
-                )
+            return self._from_tup(
+                (scheme, join_netloc, join_path, join_query, join_fragment)
             )
 
         fragment = join_fragment if join_path or join_fragment else orig_fragment
@@ -1631,9 +1608,7 @@ def join(self, url: "URL") -> "URL":
                     path = path[1:]
             path = self._normalize_path(path) if "." in path else path
 
-        return self._from_val(
-            tuple.__new__(SplitResult, (scheme, orig_netloc, path, query, fragment))
-        )
+        return self._from_tup((scheme, orig_netloc, path, query, fragment))
 
     def joinpath(self, *other: str, encoded: bool = False) -> "URL":
         """Return a new URL with the elements in other appended to the path."""