diff --git a/src/hyperlink/_url.py b/src/hyperlink/_url.py index 8797b5c..b731122 100644 --- a/src/hyperlink/_url.py +++ b/src/hyperlink/_url.py @@ -163,6 +163,7 @@ def __nonzero__(self): r"|(?P.*?))?" r"(?::(?P.*))?$" ) +_PORT_RE = re.compile(r"\A[0-9]+\Z") _HEX_CHAR_MAP = dict( @@ -1407,12 +1408,11 @@ def from_text(cls, text): host = au_gs["ipv6_host"] or au_gs["plain_host"] port = au_gs["port"] if port is not None: - try: - port = int(port) # type: ignore[assignment] # FIXME, see below - except ValueError: - if not port: # TODO: excessive? - raise URLParseError("port must not be empty: %r" % au_text) + if not port: # TODO: excessive? + raise URLParseError("port must not be empty: %r" % au_text) + if not _PORT_RE.match(port): raise URLParseError("expected integer for port, not %r" % port) + port = int(port) # type: ignore[assignment] # FIXME, see below scheme = gs["scheme"] or u"" fragment = gs["fragment"] or u"" diff --git a/src/hyperlink/test/test_url.py b/src/hyperlink/test/test_url.py index 37c9172..cead8ae 100644 --- a/src/hyperlink/test/test_url.py +++ b/src/hyperlink/test/test_url.py @@ -1114,6 +1114,14 @@ def test_invalid_ipv6(self): def test_invalid_port(self): # type: () -> None self.assertRaises(URLParseError, URL.from_text, "ftp://portmouth:smash") + for url_text in [ + "http://example.com: 11", + "http://example.com:+11", + "http://example.com:-11", + "http://example.com:1_1", + u"http://example.com:\u0ed1", + ]: + self.assertRaises(URLParseError, URL.from_text, url_text) self.assertRaises( ValueError, URL.from_text,