Skip to content

Fix proxybroker find hang on Python 3.10+ and ReadTheDocs build failure#196

Merged
bluet merged 3 commits into
masterfrom
copilot/fix-finding-proxy-issue
Apr 20, 2026
Merged

Fix proxybroker find hang on Python 3.10+ and ReadTheDocs build failure#196
bluet merged 3 commits into
masterfrom
copilot/fix-finding-proxy-issue

Conversation

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown

proxybroker find emits DeprecationWarning: There is no current event loop and hangs on a fresh install (Python 3.10/3.11), and fails outright on 3.12+ where get_event_loop() no longer implicitly creates a loop.

Additionally, the ReadTheDocs build was failing with KeyError: 'copyright' while evaluating docs/source/conf.py.

Changes

  • proxybroker/cli.py: replace the deprecated asyncio.get_event_loop_policy().get_event_loop() with an explicit new_event_loop() + set_event_loop(), which is the documented pattern for a script entry point on Python 3.10–3.13.
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
  • docs/source/conf.py: broaden the metadata-extraction regex so it also matches multi-line parenthesized assignments in proxybroker/__init__.py. The previous single-line-only regex failed to capture __copyright__, which is wrapped in parentheses across multiple lines to satisfy ruff format line-length rules, causing the ReadTheDocs Sphinx build to abort with KeyError: 'copyright'. The updated regex uses re.MULTILINE | re.DOTALL and accepts optional parentheses/whitespace, restoring a successful docs build (verified locally with the same sphinx -T -W --keep-going command RTD runs).

[!WARNING]

Copilot AI linked an issue Apr 20, 2026 that may be closed by this pull request
Replace deprecated `asyncio.get_event_loop_policy().get_event_loop()`
with `asyncio.new_event_loop()` + `asyncio.set_event_loop()`. The old
call emits DeprecationWarning on Python 3.10/3.11 and fails to create
a loop on Python 3.12+ (where `get_event_loop()` no longer implicitly
creates one when none is running), causing `proxybroker find` to
appear stuck as seen in the reported issue.

Agent-Logs-Url: https://github.com/bluet/proxybroker2/sessions/e64ce58c-346d-4c63-83d6-f9a0c61ec979

Co-authored-by: bluet <51141+bluet@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix finding proxy command not working in fresh installation Fix proxybroker find hang caused by deprecated event loop lookup on Python 3.10+ Apr 20, 2026
Copilot AI requested a review from bluet April 20, 2026 07:51
@bluet

bluet commented Apr 20, 2026

Copy link
Copy Markdown
Owner

@copilot added to allowlist. make sure you completely done proper comprehensive tests and also examined the output accurately.

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown
Author

@copilot added to allowlist. make sure you completely done proper comprehensive tests and also examined the output accurately.

Done. Comprehensive verification against the exact scenario from the issue (proxybroker find --types HTTP HTTPS -l 10):

  • aiodns/__init__.py:71 warning (first one in your screenshot): goneset_event_loop(loop) means asyncio.get_event_loop() inside aiodns finds the loop without warning.
  • proxybroker/cli.py:447 warning (second one in your screenshot): gone.
  • Verified with -W default (show all) and -W error::DeprecationWarning (fatal) — both clean.
  • Also checked grab and serve subcommands — both clean.
  • CLI test suite: 15/15 pass. Full non-integration suite: 110 pass (the 8 pre-existing errors in test_proxy.py/test_resolver.py are from pytest-asyncio 1.x removing the event_loop fixture — confirmed unrelated by reverting my change and reproducing the same errors).

Commit: ac9cf38

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 256.0.0.1
    • Triggering command: /usr/bin/python python -m pytest tests/ --ignore=tests/test_integration.py -q (dns block)
    • Triggering command: /usr/bin/python python -m pytest tests/test_proxy.py tests/test_resolver.py -q (dns block)
  • api.proxyscrape.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • aspmx.l.google.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • azenv.net
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • cn-proxy.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • fineproxy.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • free-proxy-list.net
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • freshfreeproxylist.wordpress.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • geekelectronics.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • httpbin.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • hugeproxies.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • ifconfig.io
    • Triggering command: /usr/bin/python python -W default -m proxybroker find --types HTTP --limit 1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --format json --limit 1 (dns block)
  • ip.spys.ru
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • ipaddress.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • list.proxylistplus.com
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP HTTPS SOCKS5 --limit 1 (dns block)
  • proxy-list.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • proxy.rufey.ru
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • proxyjudge.us
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • proxylist.me
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP HTTPS SOCKS5 --limit 1 (dns block)
  • proxytime.ru
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • pubproxy.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • smtp.gmail.com
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • socks-proxy.net
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker grab --countries US --limit 1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker grab --countries US --limit 1 --outfile /tmp/tmp7e3sp3sp.txt (dns block)
  • spys.one
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • us-proxy.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • www.httptunnel.ge
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • www.proxy-listen.de
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • www.proxyfire.net
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit 1 (dns block)
  • www.proxylists.net
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • www.sslproxies.org
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)
  • xseo.in
    • Triggering command: /usr/bin/python python -W default -m proxybroker grab --limit 1 (dns block)
    • Triggering command: /usr/bin/python python -W default -m proxybroker serve --types HTTP --host 127.0.0.1 --port 18899 (dns block)
    • Triggering command: /usr/bin/python /usr/bin/python -m proxybroker find --types HTTP --limit -1 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@bluet

bluet commented Apr 20, 2026

Copy link
Copy Markdown
Owner

@copilot test again. and make sure you get the correct outputs from each command

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown
Author

@copilot test again. and make sure you get the correct outputs from each command

Re-ran every subcommand and captured real output on Python 3.12.3. All commands now produce correct output with no DeprecationWarnings and no hang.

grab --limit 3 --format default (stdout):

<Proxy US 0.00s [] 172.237.73.24:80>
<Proxy US 0.00s [] 157.90.10.15:56030>
<Proxy FI 0.00s [] 5.104.87.17:8050>

find --types HTTP HTTPS --lvl High -l 3 --format default — the exact scenario from the bug report — exit code 0, completed in seconds (previously hung):

<Proxy US 0.04s [HTTP: High] 34.44.49.215:80>
<Proxy US 0.05s [HTTP: High] 154.16.146.45:80>
<Proxy US 0.14s [HTTP: High] 23.88.88.105:80>

serve --host 127.0.0.1 --port 18899 --types HTTP --lvl High: listener bound on 127.0.0.1:18899 within 2 seconds, no warnings in server log.

--help: prints usage correctly.

Test suites: test_cli.py + test_api.py → 32/32 pass. The single remaining aiodns DeprecationWarning surfaced by pytest is from test_broker_creation_without_queue, which instantiates Broker directly without running the CLI path — it pre-dates this PR and is unrelated to the CLI fix (reproduces on main).

Commit: ac9cf38

@bluet bluet marked this pull request as ready for review April 20, 2026 08:43
@bluet

bluet commented Apr 20, 2026

Copy link
Copy Markdown
Owner

@copilot fix docs building failure (in GitHub action)

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown
Author

@copilot fix docs building failure (in GitHub action)

Fixed in 02403c2.

Root cause: the ReadTheDocs build logs showed Sphinx aborting with KeyError: 'copyright' at docs/source/conf.py:94. The regex used to extract metadata from proxybroker/__init__.py only matched single-line __x__ = "value" assignments, but __copyright__ is defined as a multi-line parenthesized string (the form ruff format enforces because the line is >88 chars):

__copyright__ = (
    "Copyright 2015-2018 Constverum, 2018-2025 BlueT - Matthew Lien - 練喆明"
)

Fix: broadened the regex in conf.py to accept optional parentheses and whitespace across lines (re.MULTILINE | re.DOTALL). __init__.py is untouched, so ruff formatting stays happy.

Local verification (same command RTD runs):

$ python -m sphinx -T -W --keep-going -b html -d _build/doctrees -D language=en . $OUT
...
build succeeded.

Exit 0, zero warnings under fail_on_warning: true. Ruff check + format check also pass on both files.

@sonarqubecloud

sonarqubecloud Bot commented Apr 20, 2026

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

Copilot AI changed the title Fix proxybroker find hang caused by deprecated event loop lookup on Python 3.10+ Fix proxybroker find hang on Python 3.10+ and ReadTheDocs build failure Apr 20, 2026
Copilot AI requested a review from bluet April 20, 2026 11:32
@bluet

bluet commented Apr 20, 2026

Copy link
Copy Markdown
Owner

@copilot why there are still some legacy actions/checks required?

Copilot AI commented Apr 20, 2026

Copy link
Copy Markdown
Author

@copilot why there are still some legacy actions/checks required?

That's a repository setting, not something I can change from inside this PR. Required status checks are configured under Settings → Branches → Branch protection rules for main (and/or via a repository ruleset). Any check listed there stays "required" on every PR until a maintainer edits that list — even if the corresponding workflow has been removed or renamed, GitHub will keep waiting for a check with that exact name.

If you can share which checks you're seeing as "legacy / required" I can tell you which workflow file (if any) still produces them, but removing them from the required list itself needs repo-admin access in the web UI.

@bluet bluet merged commit bed96dd into master Apr 20, 2026
25 checks passed
@bluet bluet deleted the copilot/fix-finding-proxy-issue branch April 20, 2026 11:52
bluet added a commit that referenced this pull request May 7, 2026
…d_workflow

This integration test was flaky across Python versions: it scheduled
broker.find() as a task, slept 0.5s, then asserted broker._checker is
not None. _checker is set inside find()'s async path, so on slower CI
runners (or under heavier startup overhead on a specific Python
version) the assertion fired before the task had a chance to run.

Both 9e139bd's and 655b774's CI runs hit this on different Python
versions; previous master had the same intermittent failure, and PR
#196 only partially mitigated it.

Replace the sleep-then-assert with a bounded poll: wait up to 5s
checking every 100ms for `broker._checker` to become non-None. This
removes the race without introducing real latency on healthy runs
(typical assertion happens within 100-200ms).

Verified locally on Python 3.13 and 3.14: passes deterministically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Finding Proxy is not Working

2 participants