Parse any common proxy-string format and hand it to the major HTTP/SOCKS libraries.
Project description
proxy-converter
Parse any common proxy-string format once, then hand it to the major HTTP/SOCKS
libraries — the socks5h/socks4a incompatibility between requests/httpx and
aiohttp_socks/python-socks is handled for you.
Install
pip install proxy-converter
# or
uv add proxy-converter
Zero runtime dependencies. The PyPI name is proxy-converter; the import name is
proxy_converter.
Quick start
from proxy_converter import parse_proxy
# a messy provider format in -> clean, usable output out
proxy = parse_proxy("1.2.3.4:1080:user:pass", default_scheme="socks5")
proxy.to_url() # "socks5://user:pass@1.2.3.4:1080" -> httpx / requests
proxy.to_aiohttp_socks() # ready aiohttp_socks.ProxyConnector
proxy.host, proxy.port # ("1.2.3.4", 1080)
It also accepts schemeless and "weird" shapes — reversed credentials, the
proxy-list host:port:user:pass, a | separator, IPv6, and more. Schemeless
input needs an explicit default_scheme (the protocol can't be guessed from a
bare host:port):
parse_proxy("1.2.3.4:1080:user:pass", default_scheme="socks5") # proxy-list
parse_proxy("user:pass@1.2.3.4:1080", default_scheme="http") # standard creds
parse_proxy("1.2.3.4:1080@user:pass", default_scheme="socks5") # reversed creds
parse_proxy("user:pass|1.2.3.4:1080", default_scheme="socks5") # pipe separator
Why
Proxy providers hand out the same proxy in a dozen shapes, and the schemes are
not portable across clients: requests/httpx understand socks5h://, but
aiohttp_socks/python-socks reject it and instead want a base socks5 type
plus a separate rdns=True flag. parse_proxy normalizes every shape into one
ProxyInfo, and the to_* adapters emit exactly what each library expects — so
you never copy a socks5h:// URL into a connector that silently breaks.
Supported formats
The credential separator is @ or | (never both). The scheme is
case-insensitive; when absent, you must pass default_scheme — the protocol
can't be guessed from a bare host:port, so there is no built-in default.
| # | Example | Notes |
|---|---|---|
| 1 | socks5://user:pass@1.2.3.4:1080 |
URL, standard |
| 2 | socks5://1.2.3.4:1080@user:pass |
URL, reversed credentials |
| 3 | http://1.2.3.4:8080 |
URL, no credentials |
| 4 | http://1.2.3.4:1080:user:pass |
URL, proxy-list style |
| 5 | user:pass@1.2.3.4:1080 |
no scheme, with credentials |
| 6 | 1.2.3.4:1080@user:pass |
no scheme, reversed |
| 7 | 1.2.3.4:1080 |
no scheme, no credentials |
| 8 | 1.2.3.4:1080:user:pass |
proxy-list |
| is a drop-in alias for @: any @-form above (1, 2, 5, 6) also works with
|. Schemes: http, https, socks4, socks5, socks4a, socks5h — where
socks5h/socks4a mean "resolve DNS on the proxy" (stored as rdns=True).
Adapters
Each adapter returns plain data; the SOCKS/aiohttp ones import their target
library lazily, so the core stays dependency-free. Install the extras only for
what you use: pip install proxy-converter[aiohttp] /
proxy-converter[python-socks].
to_url(auth=True) — httpx, and a generic URL
import httpx
proxy = parse_proxy("socks5h://user:pass@1.2.3.4:1080")
client = httpx.Client(proxy=proxy.to_url())
proxy.to_url(auth=False) # "socks5h://1.2.3.4:1080" — credentials omitted
to_requests() — requests
import requests
requests.get("https://example.com", proxies=proxy.to_requests())
# {"http": "socks5h://...", "https": "socks5h://..."}
to_native_aiohttp() — aiohttp's built-in proxy (HTTP/HTTPS only)
proxy = parse_proxy("http://user:pass@1.2.3.4:8080")
async with session.get(url, **proxy.to_native_aiohttp()) as resp:
...
# {"proxy": "http://1.2.3.4:8080", "proxy_auth": BasicAuth(...)}
# Raises ProxyParseError on a SOCKS proxy — use to_aiohttp_socks() instead.
to_aiohttp_socks() — aiohttp_socks (SOCKS and HTTP)
import aiohttp
proxy = parse_proxy("socks5h://user:pass@1.2.3.4:1080")
async with aiohttp.ClientSession(connector=proxy.to_aiohttp_socks()) as session:
...
to_python_socks_kwargs() — python-socks (any backend)
from python_socks.async_.asyncio import Proxy # or .trio / .sync
p = Proxy(**proxy.to_python_socks_kwargs())
# {"proxy_type": ProxyType.SOCKS5, "host": ..., "port": ..., "rdns": True, ...}
The ProxyInfo object
parse_proxy returns a frozen ProxyInfo. Stored fields plus two read-only
properties:
proxy = parse_proxy("socks5h://user:pass@1.2.3.4:1080")
proxy.proxy_type # "socks5" (base type; never socks5h/socks4a)
proxy.host # "1.2.3.4"
proxy.port # 1080
proxy.username # "user"
proxy.password # "pass"
proxy.rdns # True (the "h"/"a" — resolve DNS on the proxy)
proxy.scheme # "socks5h" (computed: proxy_type + rdns)
proxy.auth # ("user", "pass") or None
It is immutable (hashable → usable in set() / as a dict key). To change a
field, build a new one — which also re-validates:
import dataclasses
other = dataclasses.replace(proxy, port=1234)
Gotchas
-
Don't feed a
socks5h://URL straight into aiohttp_socks/python-socks. They reject it. Useto_aiohttp_socks()/to_python_socks_kwargs(), which emit the base type +rdnsflag. (to_url()keepssocks5hfor httpx/requests.) -
Reversed-format detection is heuristic. For
creds@host:portvshost:port@creds, the side whose last:-segment is a valid port wins; a literal-IP host breaks ties. It can misreaduser:9050@admin:8080-style inputs where both sides look likehost:portand neither is an IP. Prefer the standarduser:pass@host:portor an explicit scheme when in doubt. -
Credentials are stored and emitted verbatim — never percent-decoded or encoded.
pa%40ssstayspa%40ss. (Proxy passwords aren't URL-encoded in practice, so this is what you want.) The only touch is trimming surrounding whitespace from the username/password; passstrip_credentials=Falseto keep even that byte-for-byte:parse_proxy("socks5://user: pass @1.2.3.4:1080").password # "pass" parse_proxy("socks5://user: pass @1.2.3.4:1080", strip_credentials=False).password # " pass "
Two more consequences of verbatim credentials: a proxy-list password can't contain
:,@, or|(they collide with the format's separators) — use an@/|form instead, picking the separator your password doesn't contain. And a password containing@/:makesto_url()ambiguous to a strict URL parser — for those, use the field-based adapters (to_python_socks_kwargs,to_aiohttp_socks). -
IPv6 needs brackets when a port is present:
[2001:db8::1]:1080. -
Passwords are masked.
str(),repr(), and error messages never reveal the password; only an explicitto_url()does.
Batch parsing
parse_many parses an iterable of lines (for proxy-list files) with an error
policy:
from proxy_converter import parse_many
# raise on first bad line
proxies = parse_many(lines, default_scheme="socks5")
# drop bad lines
proxies = parse_many(lines, default_scheme="socks5", on_error="skip")
# (parsed, [(index, line, exc), ...])
ok, errors = parse_many(lines, default_scheme="socks5", on_error="collect")
It is fast — ~650k lines/sec (~1.5 µs per line) on an Apple M4, CPython 3.12.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file proxy_converter-0.1.0.tar.gz.
File metadata
- Download URL: proxy_converter-0.1.0.tar.gz
- Upload date:
- Size: 91.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3be1c128975e64c951ed9c5b08c71458e45b29bfb0e6dd92bb23879ea6fc241
|
|
| MD5 |
8e981b82b7752507000d008946db9562
|
|
| BLAKE2b-256 |
02e42bb8b7f8cf6cb726b5c6dd2625bccd73a4b7eb98f5cbeee80f07dfe77f2d
|
File details
Details for the file proxy_converter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: proxy_converter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d710821d743bfb004fefe5ca9830a62ef435880cd17c462b975baf0e9639e0d
|
|
| MD5 |
be925ce90c1228a8cbe22c07def687a2
|
|
| BLAKE2b-256 |
851de0a4d28d789787dd68299df82a1e66c8cc50fe01935cd7e9985762b03f85
|