python / python/cpython

test_httpservers leaves urllib.request._opener installed for the rest of the test run

Open
#156,031 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

tests type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

test_httpservers installs a process-wide default URL opener and never removes it, so regrtest reports it as altering the execution environment:

Warning -- urllib.requests._opener was modified by test_httpservers
Warning --   Before: None
Warning --   After:  <urllib.request.OpenerDirector object at 0x10d5f0f80>
test_httpservers failed (env changed)

Repro on current main, takes a couple of seconds:

./python.exe -m test test_urllib test_httpservers

The leak comes from CommandLineRunTimeTestCase.fetch_file():

def fetch_file(self, path, context=None):
    req = urllib.request.Request(path, method='GET')
    with urllib.request.urlopen(req, context=context) as res:
        return res.read()

context defaults to None, and the plain HTTP tests call it without one. urlopen() only builds a throwaway opener when a context is passed; otherwise it installs the module global:

https://github.com/python/cpython/blob/main/Lib/urllib/request.py#L177-L186

global _opener
if context:
    https_handler = HTTPSHandler(context=context)
    opener = build_opener(https_handler)
elif _opener is None:
    _opener = opener = build_opener()
else:
    opener = _opener

So after test_httpservers runs, urllib.request._opener is a live OpenerDirector for the rest of the process, and any later test that goes through urlopen() silently gets it instead of a fresh one.

Why this has not shown up in CI: save_env.py only watches the resource when urllib.request is already imported, since try_get_module() raises SkipTestEnvironment otherwise. Under -j, and when running test_httpservers on its own, the module is not in sys.modules at the point regrtest snapshots the environment, so the resource is never tracked and the leak is invisible. It only appears in a sequential run, after some earlier test has imported urllib.request.

test_urllib already guards against this with self.addCleanup(urllib.request.urlcleanup) in four places, and urlcleanup() resets _opener, so the same one-liner fits here.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS


sorry if I've got any of the details wrong here. I traced this myself and used Claude Code as a second pair of eyes on the reasoning, so any mistakes are mine and I'd rather be told than not. college freshman, just trying to be useful where I can :)

Linked PRs
  • gh-156032

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at CommandLineRunTimeTestCase.fetch_file() and compare its cleanup with the four self.addCleanup(urllib.request.urlcleanup) uses in test_urllib. Run ./python.exe -m test test_urllib test_httpservers and confirm that test_httpservers no longer leaves urllib.request._opener installed or triggers the environment-change warning.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.