Skip to content

Environment variables

proxylib.env

Proxy configuration from *_proxy environment variables.

:class:EnvProxyConfig reads a proxy per scheme plus bypass rules from an environment mapping -- the process environment by default -- the way curl and Go's httpproxy do.

__all__ = ('EnvProxyConfig',) module-attribute

EnvProxyConfig(env=None, *, proxies=None, no_proxy=None)

A proxy map from *_proxy environment variables.

env is the environment to read: None (the default) reads os.environ, any other mapping is read instead ({} reads nothing). It supplies every <scheme>_proxy, all_proxy and no_proxy; a lowercase name wins over its uppercase form. Inside a CGI request (REQUEST_METHOD set) an uppercase HTTP_PROXY is ignored, since a client's Proxy: header can set it (httpoxy); with os.environ on Windows, whose names are case-insensitive, http_proxy is then ignored too.

proxies and no_proxy, when given, are applied over what env supplied. proxies maps a URL scheme, or "all", to a proxy: a Proxy, a proxy URL (scheme-less means http), or a PAC location (a file: URL, or an http(s) URL with a path), which is loaded on the first lookup that needs it. no_proxy is :class:BypassRules or NO_PROXY text, and replaces the environment's.

A lookup answers DIRECT for a bypassed URL, else the proxy for the URL's scheme (ws/wss fall back to http/https), else the "all" proxy; with none of those it raises KeyError (no opinion), since the environment cannot say "DIRECT".

Source code in src/proxylib/env.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(
    self,
    env: "Optional[Mapping[str, str]]" = None,
    *,
    proxies: "Optional[Mapping[str, Union[str, Proxy]]]" = None,
    no_proxy: "Union[BypassRules, str, Iterable[str], None]" = None,
) -> None:
    env_proxies, env_no_proxy = _read_env(os.environ if env is None else env)
    merged: "Dict[str, Union[str, Proxy]]" = dict(env_proxies)
    merged.update({k.lower(): v for k, v in (proxies or {}).items()})
    self.proxies: "Dict[str, Union[str, Proxy]]" = {k: v for k, v in merged.items() if v}
    if no_proxy is None:
        no_proxy = env_no_proxy
    self.no_proxy: BypassRules = (
        no_proxy if isinstance(no_proxy, BypassRules) else BypassRules.parse(no_proxy)
    )
    self._maps: "Dict[str, ProxyMap]" = {
        scheme: ProxyMap(value) for scheme, value in self.proxies.items()
    }

__slots__ = ('proxies', 'no_proxy', '_maps') class-attribute instance-attribute

no_proxy = no_proxy if isinstance(no_proxy, BypassRules) else BypassRules.parse(no_proxy) instance-attribute

proxies = {k: v for k, v in merged.items() if v} instance-attribute

__getitem__(url)

Source code in src/proxylib/env.py
80
81
82
83
84
85
86
87
def __getitem__(self, url: str) -> "Tuple[Optional[Proxy], ...]":
    scheme = _request_scheme(url)
    if self.no_proxy.matches(url):
        return (None,)
    for key in (scheme, _SCHEME_FALLBACK.get(scheme), "all"):
        if key and key in self._maps:
            return lookup(self._maps[key], url)
    raise KeyError(url)

__repr__()

Source code in src/proxylib/env.py
71
72
73
def __repr__(self) -> str:
    shown = {k: (v.url if isinstance(v, Proxy) else _redact(v)) for k, v in self.proxies.items()}
    return f"EnvProxyConfig(proxies={shown!r}, no_proxy={self.no_proxy!r})"

close()

End the helper process of any PAC a variable named.

Source code in src/proxylib/env.py
75
76
77
78
def close(self) -> None:
    """End the helper process of any PAC a variable named."""
    for proxymap in self._maps.values():
        _close(proxymap)