Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-102251: Updates to test_imp Toward Fixing Some Refleaks#102254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Changes from1 commit
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
2be855c
Stop skipping the tests.
ericsnowcurrentlyFeb 17, 2023
9bbedda
_modules_by_index_clear() -> _modules_by_index_clear_one().
ericsnowcurrentlyFeb 17, 2023
83f42d6
Add an explanation of what happens when extensions are loaded.
ericsnowcurrentlyFeb 17, 2023
828a734
Add _forget_extension().
ericsnowcurrentlyFeb 17, 2023
e9096cd
Clean up test_singlephase_multiple_interpreters.
ericsnowcurrentlyFeb 17, 2023
c993f97
Add another test case.
ericsnowcurrentlyFeb 17, 2023
cc5c771
Expand test_singlephase_multiple_interpreters.
ericsnowcurrentlyFeb 18, 2023
08741b8
Clean up the tests.
ericsnowcurrentlyFeb 22, 2023
31716e9
Clean up clear_singlephase_extension().
ericsnowcurrentlyFeb 22, 2023
2c3dd42
Add a check.
ericsnowcurrentlyFeb 22, 2023
a353e7e
Fix a typo.
ericsnowcurrentlyFeb 22, 2023
c6a39e3
script -> snapshot_script.
ericsnowcurrentlyFeb 22, 2023
0c65700
Use spec_from_file_location() in imp.load_dynamic().
ericsnowcurrentlyFeb 22, 2023
993484e
Update single-phase-init tests.
ericsnowcurrentlyFeb 23, 2023
fc94060
Do the initial cleanup in setUpClass().
ericsnowcurrentlyFeb 24, 2023
3e2a1dd
Move the requires_load_dynamic decorator to the class.
ericsnowcurrentlyFeb 25, 2023
f0ce607
For now, skip the tests during refleak detection.
ericsnowcurrentlyFeb 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
PrevPrevious commit
NextNext commit
Expand test_singlephase_multiple_interpreters.
  • Loading branch information
@ericsnowcurrently
ericsnowcurrently committedFeb 21, 2023
commitcc5c771a441ac2c0b7df22e9d7a0afa768087b6f
277 changes: 178 additions & 99 deletionsLib/test/test_imp.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import gc
import json
import importlib
import importlib.util
import os
Expand DownExpand Up@@ -312,6 +313,78 @@ def test_singlephase_multiple_interpreters(self):
# in multiple interpreters, those interpreters share a
# PyModuleDef for that object, which can be a problem.

def parse_snapshot(data):
if isinstance(data, str):
data = json.loads(data.decode())
elif isinstance(data, bytes):
data = json.loads(data)
spam = data.pop('spam', None)
has_spam = data.pop('has_spam', False)
snapshot = type(sys.implementation)(**data)
if has_spam:
snapshot.spam = spam
return snapshot

def check_common(snapshot):
# The "looked up" module is interpreter-specific
# (interp->imports.modules_by_index was set for the module).
self.assertEqual(snapshot.lookedup, snapshot.objid)
with self.assertRaises(AttributeError):
snapshot.spam

def check_fresh(snapshot):
"""
The module had not been loaded before (at least since fully reset).
"""
# The module's init func was run.
# A copy of the module's __dict__ was stored in def->m_base.m_copy.
# The previous m_copy was deleted first.
# _PyRuntime.imports.extensions was set.
self.assertEqual(snapshot.init_count, 1)
# The global state was initialized.
# The module attrs were initialized from that state.
self.assertEqual(snapshot.module_initialized,
snapshot.state_initialized)

def check_semi_fresh(snapshot, base, prev):
"""
The module had been loaded before and then reset
(but the module global state wasn't).
"""
# The module's init func was run again.
# A copy of the module's __dict__ was stored in def->m_base.m_copy.
# The previous m_copy was deleted first.
# The module globals did not get reset.
self.assertNotEqual(snapshot.objid, base.objid)
self.assertNotEqual(snapshot.objid, prev.objid)
self.assertEqual(snapshot.init_count, prev.init_count + 1)
# The global state was updated.
# The module attrs were initialized from that state.
self.assertEqual(snapshot.module_initialized,
snapshot.state_initialized)
self.assertNotEqual(snapshot.state_initialized,
base.state_initialized)
self.assertNotEqual(snapshot.state_initialized,
prev.state_initialized)

def check_copied(snapshot, base):
"""
The module had been loaded before and never reset.
"""
# The module's init func was not run again.
# The interpreter copied m_copy, as set by the other interpreter,
# with objects owned by the other interpreter.
# The module globals did not get reset.
self.assertNotEqual(snapshot.objid, base.objid)
self.assertEqual(snapshot.init_count, base.init_count)
# The global state was not updated since the init func did not run.
# The module attrs were not directly initialized from that state.
# The state and module attrs still match the previous loading.
self.assertEqual(snapshot.module_initialized,
snapshot.state_initialized)
self.assertEqual(snapshot.state_initialized,
base.state_initialized)

# This single-phase module has global state, which is shared
# by all interpreters.
import _testsinglephase
Expand All@@ -323,142 +396,148 @@ def test_singlephase_multiple_interpreters(self):
import _testsinglephase
self.addCleanup(_forget_extension, _testsinglephase)

init_count = _testsinglephase.initialized_count()
lookedup = _testsinglephase.look_up_self()
_initialized = _testsinglephase._module_initialized
initialized = _testsinglephase.state_initialized()

self.assertEqual(init_count, 1)
self.assertIs(lookedup, _testsinglephase)
self.assertEqual(_initialized, initialized)

# Check the main interpreter.
main_snap = parse_snapshot(dict(
objid=id(_testsinglephase),
init_count=_testsinglephase.initialized_count(),
lookedup=id(_testsinglephase.look_up_self()),
state_initialized=_testsinglephase.state_initialized(),
module_initialized=_testsinglephase._module_initialized,
))
check_common(main_snap)
check_fresh(main_snap)

# Set up the interpreters.
setup_script = textwrap.dedent('''
import sys
import _testinternalcapi
''')
interp1 = _interpreters.create(isolated=False)
self.addCleanup(_interpreters.destroy, interp1)
interp2 = _interpreters.create(isolated=False)
self.addCleanup(_interpreters.destroy, interp2)
for interpid in [interp1, interp2]:
_interpreters.run_string(interpid,'import _testinternalcapi, sys')
_interpreters.run_string(interpid,setup_script)

cleanup_script = textwrap.dedent(f'''
sys.modules.pop({name!r}, None)
_testinternalcapi.clear_extension({name!r}, {filename!r})
''')
def clear_subinterp(interpid):
_interpreters.run_string(interpid, cleanup_script)
_interpreters.run_string(interpid, textwrap.dedent(f'''
del sys.modules[{name!r}]
_testsinglephase._clear_globals()
_testinternalcapi.clear_extension({name!r}, {filename!r})
'''))

r, w = os.pipe()
self.addCleanup(os.close, r)
self.addCleanup(os.close, w)

script = textwrap.dedent(f'''
import json
import os

import _testsinglephase

data = dict(
objid=id(_testsinglephase),
init_count=_testsinglephase.initialized_count(),
lookedup=id(_testsinglephase.look_up_self()),
state_initialized=_testsinglephase.state_initialized(),
module_initialized=_testsinglephase._module_initialized,
has_spam=hasattr(_testsinglephase, 'spam'),
spam=getattr(_testsinglephase, 'spam', None),
)
os.write({w}, json.dumps(data).encode())
''')

def read_data():
text = os.read(r, 500)
return parse_snapshot(text)

with self.subTest('without resetting; '
'already loaded in main interpreter'):
# Attrs set after loading are not in m_copy.
_testsinglephase.spam = 'spam, spam, spam, spam, eggs, and spam'
objid = id(_testsinglephase)

script = textwrap.dedent(f'''
import _testsinglephase

init_count = _testsinglephase.initialized_count()
if init_count != {init_count}:
raise Exception(init_count)

# The "looked up" module is interpreter-specific.
lookedup = _testsinglephase.look_up_self()
if lookedup is not _testsinglephase:
raise Exception((_testsinglephase, lookedup))

# Attrs set in the module init func are in m_copy.
# Both of the following were set in module init,
# which didn't happen in this interpreter
# (unfortunately).
_initialized = _testsinglephase._module_initialized
initialized = _testsinglephase.state_initialized()
if _initialized != initialized:
raise Exception((_initialized, initialized))
if _initialized != {initialized}:
raise Exception((_initialized, {initialized}))
if initialized != {initialized}:
raise Exception((initialized, {initialized}))

# Attrs set after loading are not in m_copy.
if hasattr(_testsinglephase, 'spam'):
raise Exception(_testsinglephase.spam)
_testsinglephase.spam = 'spam, spam, spam, spam, ...'
''')

# Use an interpreter that gets destroyed right away.
ret = support.run_in_subinterp(script)
self.assertEqual(ret, 0)
snap = read_data()
check_common(snap)
check_copied(snap, main_snap)

# The module's init func gets run again.
# The module's globals did not get destroyed.
# Use several interpreters that overlap.
_interpreters.run_string(interp1, script)
snap = read_data()
check_common(snap)
check_copied(snap, main_snap)

# The module's init func is not run again.
# The second interpreter copies the module's m_copy.
# However, globals are still shared.
_interpreters.run_string(interp2, script)
snap = read_data()
check_common(snap)
check_copied(snap, main_snap)

_forget_extension(_testsinglephase)
for interpid in [interp1, interp2]:
clear_subinterp(interpid)

with self.subTest('without resetting; '
'already loaded in deleted interpreter'):
# Use an interpreter that gets destroyed right away.
ret = support.run_in_subinterp(os.linesep.join([
script,
textwrap.dedent('''
# Attrs set after loading are not in m_copy.
_testsinglephase.spam = 'spam, spam, mash, spam, eggs, and spam'
''')]))
self.assertEqual(ret, 0)
base = read_data()
check_common(base)
check_fresh(base)

# Use several interpreters that overlap.
_interpreters.run_string(interp1, script)
interp1_snap = read_data()
check_common(interp1_snap)
check_semi_fresh(interp1_snap, main_snap, base)

_interpreters.run_string(interp2, script)
snap = read_data()
check_common(snap)
check_copied(snap, interp1_snap)

with self.subTest('resetting between each interpreter'):
_testsinglephase._clear_globals()

# Use an interpreter that gets destroyed right away.
ret = support.run_in_subinterp(textwrap.dedent(f'''
import _testsinglephase

# This is the first time loaded since reset.
init_count = _testsinglephase.initialized_count()
if init_count != 1:
raise Exception(init_count)

# Attrs set in the module init func are in m_copy.
_initialized = _testsinglephase._module_initialized
initialized = _testsinglephase.state_initialized()
if _initialized != initialized:
raise Exception((_initialized, initialized))
if _initialized == {initialized}:
raise Exception((_initialized, {initialized}))
if initialized == {initialized}:
raise Exception((initialized, {initialized}))

# Attrs set after loading are not in m_copy.
if hasattr(_testsinglephase, 'spam'):
raise Exception(_testsinglephase.spam)
_testsinglephase.spam = 'spam, spam, mash, spam, eggs, and spam'
'''))
ret = support.run_in_subinterp(os.linesep.join([
setup_script,
cleanup_script,
script,
textwrap.dedent('''
# Attrs set after loading are not in m_copy.
_testsinglephase.spam = 'spam, spam, mash, spam, eggs, and spam'
''')]))
self.assertEqual(ret, 0)
base = read_data()
check_common(base)
check_fresh(base)

script = textwrap.dedent(f'''
import _testsinglephase

init_count = _testsinglephase.initialized_count()
if init_count != 2:
raise Exception(init_count)

# Attrs set in the module init func are in m_copy.
# Both of the following were set in module init,
# which didn't happen in this interpreter
# (unfortunately).
_initialized = _testsinglephase._module_initialized
initialized = _testsinglephase.state_initialized()
if _initialized != initialized:
raise Exception((_initialized, initialized))

# Attrs set after loading are not in m_copy.
if hasattr(_testsinglephase, 'spam'):
raise Exception(_testsinglephase.spam)
_testsinglephase.spam = 'spam, spam, spam, spam, ...'
''')

# The module's init func gets run again.
# The module's globals did not get destroyed.
# Use several interpreters that overlap.
clear_subinterp(interp1)
#_interpreters.run_string(interpid, cleanup_script)
_interpreters.run_string(interp1, script)
snap = read_data()
check_common(snap)
check_fresh(snap)

# The module's init func is not run again.
# The second interpreter copies the module's m_copy.
# However, globals are still shared.
clear_subinterp(interp2)
#_interpreters.run_string(interpid, cleanup_script)
_interpreters.run_string(interp2, script)
snap = read_data()
check_common(snap)
check_fresh(snap)

@requires_load_dynamic
def test_singlephase_variants(self):
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp