v1.0.0
Added
ProxyDict(inproxylib.proxy): the plain proxies-dict integration,
renamed and moved out of therequestsmodule since it never actually
depended on requests — usable with anything that accepts a
{scheme: proxy_uri}mapping.first_working_proxy()(inproxylib.netutils): failover helper that
probes aProxyMapresult (PROXY a; PROXY b; DIRECT) in order and
returns the first entry that accepts a TCP connection.- WPAD discovery results (including failures) are now cached per fqdn for 5
minutes (discover(..., cache_ttl=...)to tune or disable) — previously,
"auto-detect on but no WPAD server" re-probed DNS/HTTP with multi-second
timeouts on every call. proxylib.__version__, resolved lazily from the installed package metadata.Documentationproject URL (the GitHub Pages docs site) in the package
metadata shown on PyPI.- PEP 639
license = "MIT"/license-filesmetadata (replaces the license
classifier). .github/workflows/test.yaml: on-demand test matrix (workflow_dispatch or a
throwawayci-*tag push), separate from the release-gate test job — lets
platform-specific code (GNOME/gsettings, macOS) be validated on real CI.NO_PROXYentries now accept CIDR notation (10.0.0.0/8,2001:db8::/32),
matched against IP-literal request hosts.proxylib.set_default_no_proxy()/get_default_no_proxy(): process-wide
defaultNO_PROXYrules, merged into everyEnvProxyConfig.WinHttpProxyMap(proxylib.os.nt): aProxyMapbacked by WinHTTP's own
WinHttpGetProxyForUrlautoproxy engine — WPAD/DHCP-252 discovery, NTLM/
Kerberos SSO for fetching the PAC, and PAC JS execution all happen in the
OS, so nodukpyis needed on Windows.CFNetworkProxyMap(proxylib.os.darwin): the macOS equivalent, backed by
CFNetworkCopyProxiesForURL/CFNetworkExecuteProxyAutoConfigurationURL.provider="system"param onauto_proxy()/system_proxy(): opts into the
two native resolvers above (orLibProxyMapon POSIX, if its shared
library is loadable) instead of proxylib's own Python-side detection/PAC
path, which remains the default (provider="python").- gsettings-based detection (GNOME/MATE) now reads via one
gsettings list-recursivelycall instead of up to 7 single-key
gsettings getcalls;shutil.whichlookups forgsettings/nmcli/
dbus-sendare now cached (clearable, for tests). pac.load()now caches genuine network downloads (notfile:/inline-JS
sources) for 5 minutes by default (cache_ttl=param).PAC.dnsResolveresults are now cached for 30 seconds by default
(cache_ttl=param) — real-world PAC scripts often call it repeatedly for
the same host.ChainProxyMap(*maps)(inproxylib.proxy): sequential fallback across
ProxyMaps — aKeyError(no opinion) tries the next one, a definitive
[None]/[Proxy]result stops the chain.ConfigurableProxyMap(proxymap, ...)(inproxylib.proxy): decorates any
ProxyMapwith opt-in caching (cache_ttl=), active reachability probing
(probe=/probe_timeout=, vianetutils.first_working_proxy),
round-robin selection (round_robin=), browser-style HTTPS privacy
stripping (browser_compatibility=), and an implicit local-address bypass
(bypass_local=) plusno_proxy=rules (merged with the global
set_default_no_proxy()defaults).netutils.is_loopback_or_link_local(): shared helper (loopback +
169.254/16/fe80::/10) used by bothEnvProxyConfig's<local>
handling andConfigurableProxyMap'sbypass_local=.- Extensible patching registry (
proxylib.patching):patch(proxymap, targets=None)/unpatch()/register_patcher(name, patch_func, unpatch_func)globally wire an activeProxyMapinto third-party HTTP
clients. Built-in targets:"requests"(newly constructedSessions
mount aProxyMapAdapter) and"urllib"(installs a global opener).
ProxyMapinstances are now usable as a context manager
(with proxymap as active: ...) equivalent topatch()/unpatch(). - Pluggable PAC JS engines (
proxylib.pac.engines):JSProxyAutoConfig/
JSContextnow run against a smallJSEngineinterface instead of
dukpydirectly. New optionalquickjsbackend (proxylib[quickjs]
extra) alongside the existingdukpyone (proxylib[dukpy]). Select
with thePROXYLIB_JS_ENGINEenv var (comma-separated priority list,
e.g.PROXYLIB_JS_ENGINE=quickjs,dukpy); defaults to preferringdukpy.
proxylib[jspac]is now a meta-extra for "any working engine" (currently
aliasesproxylib[dukpy]). - WPAD discovery now fails fast on a hanging DNS lookup:
socket.gethostbyname
has no timeout parameter, so it's now run in a worker thread with a
future.result(timeout=...)deadline (sametimeoutthe HTTP fetch
already used) instead of blocking indefinitely. first_working_proxy()now has a circuit breaker: a proxy that fails its
probe is skipped (without re-probing) for 30 seconds by default
(circuit_breaker_ttl=), shared withConfigurableProxyMap(probe=True).JSProxyAutoConfig/JSContextaccept anoverrides=dict
(JSProxyAutoConfig(js, overrides={"dnsResolve": fn, ...})) to replace a
PAC utility function's Python-fallback implementation per-instance —
simplifies offline/sandboxed testing and DNS mocking. A PAC script that
redefines the same name in JS still overrides it, same as any other
exported function.release.yamlnow scrapes the pushed tag'sCHANGELOG.mdsection into
the GitHub Release body (this repo pushes commits directly, so GitHub's
own auto-generated release notes are thin on every release).
Changed
pac.load()/WPAD fetches now go through an explicit no-proxy opener
instead of the default (env-var-honoring) one — fetching a PAC/WPAD
script must never itself be routed through a configured proxy.PAClookups now pass the full request URL (path and query included) to
FindProxyForURL, per the PAC spec, instead of truncating to
scheme://netloc— PAC scripts with path-based rules now work.EnvProxyConfigno longer resolves DNS on every lookup; resolution only
happens when aNO_PROXY<local>entry actually needs it.- libproxy's shared library is now located lazily on first use instead of at
import proxylibtime. JSContext-exported callables (JSProxyAutoConfig's Python-backed PAC
utility functions) are now pre-bound once at construction instead of a
fresh closure allocated on every attribute access.proxylib.requests/proxylib.urllibmoved to
proxylib.integrations.requests/proxylib.integrations.urllib— no shim
kept at the old module paths (pre-1.0 breaking change; top-level
from proxylib import ProxyMapAdapter, ProxyMapHandleris unaffected).ProxyDictmoved fromproxylib.proxytoproxylib.integrations.dict,
and no longer subclassesProxyMap(composition instead — its
__getitem__returnsstr, which was an LSP violation as aProxyMap
subclass). Top-levelfrom proxylib import ProxyDictis unaffected.EnvProxyConfignow raisesKeyError(instead of returning DIRECT) for a
scheme with no configured env proxy, so a futureChainProxyMapcan fall
through to the next map instead of env "winning" by default.get_local_interfaces()results are now cached for 10s (cache_ttl=
param) — interface enumeration blocks, and<local>-in-NO_PROXYlookups
call it per request.<local>(NO_PROXY) now also bypasses link-local addresses
(169.254/16,fe80::/10) unconditionally, not just when they happen to
match a local interface's subnet.
Removed
RequestsProxies— renamed toProxyDict(see Added); the old name is
gone. Only ever published under a release candidate (1.0.0-rc.1), so no
stable release carried it.proxylib[requests]extra.requestssupport has always worked whenever
the package happens to be installed (guarded bytry/except ImportError);
installingrequestsisn't a proxylib feature, so there was nothing for
this extra to add overpip install requestsitself.
Fixed
ProxyMapAdapterno longer loses to environment-injected proxies:
requests.Session.merge_environment_settingsmergesHTTP_PROXY/
HTTPS_PROXYinto theproxiesdict at the scheme level before
adapter.send()runs, so the oldproxies.setdefault(resolved.scheme, ...)
always lost whentrust_env=True(therequestsdefault), and a DIRECT
result didn't disable the env proxy at all. Now writes the more-specific
scheme://hostnamekeyselect_proxy()checks first. As a consequence,
only an explicitproxies=entry at that same key still overrides the
ProxyMap's decision — a less-specific one (e.g. a bare"http"key)
no longer does.- Proxy URLs with a trailing slash (
HTTP_PROXY=http://proxy:8080/, a very
common shape) were misclassified as PAC-file URLs and routed to the PAC
loader, which tried to fetch the proxy itself as a PAC script and failed.
Both theProxyMap(...)factory andEnvProxyConfigare fixed
(EnvProxyConfigadditionally never treats proxy values as PAC sources —
PROXY_PAC/OS PAC settings cover that separately). URL.from_str("example.com")(bare hostname) now raises a clear
ValueErrorinstead of crashing withAttributeErrorlater.JSContextMetacompared the first character of attribute names against
_JSCONTEXT_EXCLUDEinstead of the name itself (dormant — the exclude list
was unused).PAC.dnsDomainLevelscounted split-length instead of dots (sub.example.com
wrongly returned 3 instead of 2, per the PAC spec's own definition).PAC.localHostOrDomainIsused a string-prefix check (hostdom.startswith(host)),
so e.g."ww"wrongly matched"www.example.com"; now compares the exact
host part.
Full Changelog: v1.0.0-rc.1...v1.0.0