changeset 52907:ba343f763595

typing: add an interface for url This is fairly simple. This is a dependency for the "path" class in the same module, that is itself imported by `mercurial.interfaces.repository`. So we get it out of the way first.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 10 Feb 2025 00:15:22 +0100
parents bde94bd8e8a2
children 180591a9a6a1
files mercurial/interfaces/misc.py mercurial/utils/urlutil.py
diffstat 2 files changed, 72 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/interfaces/misc.py	Sat Feb 08 18:15:18 2025 +0100
+++ b/mercurial/interfaces/misc.py	Mon Feb 10 00:15:22 2025 +0100
@@ -9,7 +9,9 @@
     Callable,
     Iterator,
     List,
+    Optional,
     Protocol,
+    Tuple,
 )
 
 
@@ -45,3 +47,64 @@
     @abc.abstractmethod
     def __contains__(self, d: bytes) -> bool:
         ...
+
+
+AuthInfoT = Tuple[
+    bytes,
+    Optional[
+        Tuple[
+            None,
+            Tuple[bytes, bytes],
+            bytes,
+            bytes,
+        ]
+    ],
+]
+
+
+class IUrl(Protocol):
+    r"""Reliable URL parser.
+
+    This parses URLs and provides attributes for the following
+    components:
+
+    <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
+
+    Missing components are set to None. The only exception is
+    fragment, which is set to '' if present but empty.
+
+    If parsefragment is False, fragment is included in query. If
+    parsequery is False, query is included in path. If both are
+    False, both fragment and query are included in path.
+
+    See http://www.ietf.org/rfc/rfc2396.txt for more information.
+    """
+
+    path: Optional[bytes]
+    scheme: Optional[bytes]
+    user: Optional[bytes]
+    passwd: Optional[bytes]
+    host: Optional[bytes]
+    port: Optional[bytes]
+    query: Optional[bytes]
+    fragment: Optional[bytes]
+
+    @abc.abstractmethod
+    def copy(self) -> IUrl:
+        ...
+
+    @abc.abstractmethod
+    def authinfo(self) -> AuthInfoT:
+        ...
+
+    @abc.abstractmethod
+    def isabs(self) -> bool:
+        ...
+
+    @abc.abstractmethod
+    def localpath(self) -> bytes:
+        ...
+
+    @abc.abstractmethod
+    def islocal(self) -> bool:
+        ...
--- a/mercurial/utils/urlutil.py	Sat Feb 08 18:15:18 2025 +0100
+++ b/mercurial/utils/urlutil.py	Mon Feb 10 00:15:22 2025 +0100
@@ -30,6 +30,10 @@
     stringutil,
 )
 
+from ..interfaces import (
+    misc as int_misc,
+)
+
 from ..revlogutils import (
     constants as revlog_constants,
 )
@@ -60,7 +64,7 @@
         )
 
 
-class url:
+class url(int_misc.IUrl):
     r"""Reliable URL parser.
 
     This parses URLs and provides attributes for the following
@@ -244,7 +248,7 @@
             if v is not None:
                 setattr(self, a, urlreq.unquote(v))
 
-    def copy(self):
+    def copy(self) -> url:
         u = url(b'temporary useless value')
         u.path = self.path
         u.scheme = self.scheme
@@ -361,7 +365,7 @@
 
     __str__ = encoding.strmethod(__bytes__)
 
-    def authinfo(self):
+    def authinfo(self) -> int_misc.AuthInfoT:
         user, passwd = self.user, self.passwd
         try:
             self.user, self.passwd = None, None
@@ -376,7 +380,7 @@
         # a password.
         return (s, (None, (s, self.host), self.user, self.passwd or b''))
 
-    def isabs(self):
+    def isabs(self) -> bool:
         if self.scheme and self.scheme != b'file':
             return True  # remote URL
         if hasdriveletter(self.path):
@@ -401,7 +405,7 @@
             return path
         return self._origpath
 
-    def islocal(self):
+    def islocal(self) -> bool:
         '''whether localpath will return something that posixfile can open'''
         return (
             not self.scheme