@@ -11976,116 +11976,6 @@ def foo():
1197611976 for k , v in apsw .faultdict .items ():
1197711977 assert v is False , f"faultdict { k } never fired"
1197811978
11979- # This test is run last by deliberate name choice. If it did
11980- # uncover any bugs there isn't much that can be done to turn the
11981- # checker off.
11982- def testzzForkChecker (self ):
11983- "Test detection of using objects across fork"
11984- # need to free up everything that already exists
11985- self .db .close ()
11986- self .db = None
11987- gc .collect ()
11988- # install it
11989- apsw .fork_checker ()
11990-
11991- # return some objects
11992- def getstuff ():
11993- db = apsw .Connection (":memory:" )
11994- cur = db .cursor ()
11995- for row in cur .execute (
11996- "create table foo(x);insert into foo values(1);insert into foo values(x'aabbcc'); select last_insert_rowid()"
11997- ):
11998- blobid = row [0 ]
11999- blob = db .blob_open ("main" , "foo" , "x" , blobid , 0 )
12000- db2 = apsw .Connection (":memory:" )
12001- backup = db2 .backup ("main" , db , "main" )
12002- return (db , cur , blob , backup )
12003-
12004- # test the objects
12005- def teststuff (db , cur , blob , backup ):
12006- if db :
12007- db .cursor ().execute ("select 3" )
12008- if cur :
12009- cur .execute ("select 3" )
12010- if blob :
12011- blob .read (1 )
12012- if backup :
12013- backup .step ()
12014-
12015- # Sanity check
12016- teststuff (* getstuff ())
12017- # get some to use in parent
12018- parent = getstuff ()
12019- # to be used (and fail with error) in child
12020- child = getstuff ()
12021-
12022- def childtest (* args ):
12023- # we can't use unittest methods here since we are in a different process
12024-
12025- # this should work
12026- teststuff (* getstuff ())
12027-
12028- # ignore the unraisable stuff sent to sys.excepthook
12029- def eh (* args ):
12030- pass
12031-
12032- sys .excepthook = eh
12033-
12034- # call with each separate item to check
12035- try :
12036- for i in range (len (args )):
12037- a = [None ] * len (args )
12038- a [i ] = args [i ]
12039- try :
12040- teststuff (* a )
12041- except apsw .ForkingViolationError :
12042- pass
12043- except apsw .ForkingViolationError :
12044- # we get one final exception "between" line due to the
12045- # nature of how the exception is raised
12046- pass
12047- # this should work again
12048- teststuff (* getstuff ())
12049- os ._exit (0 )
12050-
12051- suppressWarning ("DeprecationWarning" ) # we are deliberately forking
12052- pid = os .fork ()
12053-
12054- if pid == 0 :
12055- # child
12056- counter = 0
12057-
12058- def ueh (unraisable ):
12059- if unraisable .exc_type != apsw .ForkingViolationError :
12060- print ("\n \n Unraisable exception in child process" , unraisable )
12061- return sys .__unraisablehook__ (unraisable )
12062- nonlocal counter
12063- counter += 1
12064- if counter > 100 :
12065- os ._exit (0 )
12066-
12067- sys .unraisablehook = ueh
12068- try :
12069- childtest (* child )
12070- except :
12071- print ("\n \n This exception in THE CHILD PROCESS OF FORK CHECKER\n " , file = sys .stderr )
12072- traceback .print_exc ()
12073- print ("\n End CHILD traceback\n \n " )
12074- os ._exit (1 )
12075- os ._exit (0 )
12076-
12077- rc = os .waitpid (pid , 0 )
12078- self .assertEqual (0 , os .waitstatus_to_exitcode (rc [1 ]))
12079-
12080- teststuff (* parent )
12081-
12082- # we call shutdown to free mutexes used in fork checker,
12083- # so clear out all the things first
12084- del child
12085- del parent
12086- gc .collect ()
12087- apsw .shutdown ()
12088-
1208911979
1209011980testtimeout = False # timeout testing adds several seconds to each run
1209111981
@@ -12214,33 +12104,6 @@ def setup():
1221412104 if not getattr (memdb , "enableloadextension" , None ):
1221512105 del APSW .testLoadExtension
1221612106
12217- # Fork checker is becoming less usefull on newer Pythons because
12218- # multiprocessing really doesn't want you to use fork and does
12219- # alternate methods instead. We also run sanitizers on most
12220- # recent Python which makes things even more convoluted.
12221- forkcheck = False
12222- if (
12223- hasattr (apsw , "fork_checker" )
12224- and hasattr (os , "fork" )
12225- and platform .python_implementation () != "PyPy"
12226- and sys .version_info < (3 , 13 )
12227- ):
12228- try :
12229- import multiprocessing
12230-
12231- if hasattr (multiprocessing , "get_start_method" ):
12232- if multiprocessing .get_start_method () != "fork" :
12233- raise ImportError
12234- # sometimes the import works but doing anything fails
12235- val = multiprocessing .Value ("i" , 0 )
12236- forkcheck = True
12237- except ImportError :
12238- pass
12239-
12240- # we also remove forkchecker if doing multiple iterations
12241- if not forkcheck or "APSW_TEST_ITERATIONS" in os .environ :
12242- del ZZFaultInjection .testzzForkChecker
12243-
1224412107 if not is64bit or "APSW_TEST_LARGE" not in os .environ :
1224512108 del APSW .testLargeObjects
1224612109
@@ -12290,6 +12153,9 @@ def setup():
1229012153from .carray import *
1229112154from .aiotest import *
1229212155
12156+ if "APSW_TEST_ITERATIONS" not in os .environ :
12157+ from .fork_checker import *
12158+
1229312159if __name__ == "__main__" :
1229412160 setup ()
1229512161
0 commit comments