A URL has components with different delimiter rules. In
https://api.example.test/v1/books?q=python#details, https is the scheme,
api.example.test is the authority, /v1/books is the path, q=python is the
query and details is the fragment. A fragment is client-side reference
information and is not sent as part of an HTTP request target. This lab creates
URLs with no fragment and performs no network operations.
The core distinction is data versus structure. If a user-supplied book ID
is literally a/b, appending it without encoding creates two segments. Encode
it as a%2Fb when the contract says it is one segment. Add real separator
slashes outside the quoting operation. Percent-encoding preserves data in its
particular component; it does not authorize a destination or guarantee that
a server interprets all paths identically.
from urllib.parse import quote, urlencode
segment = quote("a/b c", safe="")
query = urlencode([("tag", "python"), ("tag", "http"), ("q", "a+b c")])
print(segment) # a%2Fb%20c
print(query) # tag=python&tag=http&q=a%2Bb+cquote normally leaves / safe because callers often quote a whole path.
Here each input is one segment, so explicitly set safe="". A path space
becomes %20. Default urlencode uses the form-query convention: a space
becomes +, while a literal plus becomes %2B. These are different contexts,
not interchangeable global substitutions on a completed URL.
An ordered list of query pairs preserves repeated keys, blank values and
caller order. Turning [("tag", "python"), ("tag", "http")] into a dictionary
silently discards the first tag. A raw value "" still produces key=;
missing and blank parameters may mean different things to an API. Leave those
interpretations to its documented contract.
The build takes a restricted, trusted base with an already encoded path prefix.
It checks for http/https, a nonempty authority, no credentials and no nonempty
query or fragment. urlsplit helps separate those components, while
urlunsplit recombines them. This limited check is not comprehensive URL
validation, a host allowlist or protection against every server-side request
forgery scenario. No connection or DNS lookup occurs.
Raw appended segments and pairs are not pre-encoded. The literal raw text
%2F becomes %252F; decoding it first would reinterpret data as a slash.
By contrast, the supplied base path is already encoded and is preserved.
Maintaining that boundary avoids accidental double interpretation.
Before coding, predict one segment a/b versus two segments a and b.
Then add a blank value, duplicate tags, Unicode and ?, #, &, = inside
data. Verify that only the separators you intentionally create remain literal.
The fill supplies the module imports; the build asks you to keep component
encoding, base checks and order preservation in one small pure function.
Use Python's urllib.parse reference
for quote, urlencode, urlsplit and its explicit parsing-versus-validation
limits, and RFC 3986
for URI components and percent-encoding.