Skip to content

gh-88037 - Move port validation logic to parsing time - #159122

Open
orsenthil wants to merge 1 commit into
python:mainfrom
orsenthil:gh-88037-port-validation
Open

orsenthil wants to merge 1 commit into
python:mainfrom
orsenthil:gh-88037-port-validation

Conversation

@orsenthil

@orsenthil orsenthil commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Currently urlsplit()/urlparse() accept a netloc with an invalid port without complaint, the error only surfaces later, and only if something actually reads .port:


    >>> p = urllib.parse.urlsplit("http://example.com:oracle/")
    >>> p.netloc          # parses "successfully"
    'example.com:oracle'
    >>> p.path
    '/'
    >>> p.port             # ... raises, but only now, and only if you ask
    ValueError: Port could not be cast to integer value as 'oracle'

This moves that validation to parse time, so urlsplit()/urlparse() raise immediately on a malformed port instead of returning a result that looks valid until .port happens to be accessed.

Supersedes #25774 - Rebase of the old branch wasn't suitable: Lib/urllib/parse.py has seen significant changes since 2021 (WHATWG-compliance work, bracketed IPv6 host validation) that the original patch's diff didn't account for. The original commit is kept in-tact.


I was curious on performance of moving this lazy validation to eager, I let the claude run in tiny benchmark.

Benchmark

Measured with timeit (best of 5 runs) on a local ./python build
of this branch (3.16.0a0), comparing the real, unmodified
Lib/urllib/parse.py from main (lazy validation) against this
branch's version (eager, parse-time validation).

_checknetloc in isolation (500k calls each):

netloc lazy (main) eager (this PR) delta
ASCII, no port 54.0 ns 288.4 ns +234.5 ns
ASCII, with port 53.2 ns 468.2 ns +415.0 ns
Bracketed IPv6 53.6 ns 575.6 ns +522.0 ns

Public urlsplit(), which is @functools.lru_cache-wrapped
(128 entries):

scenario lazy (main) eager (this PR) delta
cache hit (same URL reparsed) 124.1 ns 124.2 ns +0.1 ns (+0.08%)
cache miss (unique URL each call) 2626.8 ns 3131.9 ns +505.1 ns (+19.2%)

Takeaways:

  • When a URL has already been parsed once, the cache absorbs the
    difference entirely — effectively zero added cost.
  • For a never-before-seen URL, the added cost is ~500 ns, sub-microsecond
    in absolute terms and negligible next to any I/O the parsed result is
    normally used for (DNS/connect/TLS are milliseconds; this is
    nanoseconds).

@orsenthil
orsenthil force-pushed the gh-88037-port-validation branch from 802a54b to 24fb8bf Compare October 10, 2026 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants